From d52fd9f76c6f02e518b89ff3d91e058fd613a1fc Mon Sep 17 00:00:00 2001 From: Peter Rifel Date: Mon, 15 Feb 2021 15:55:23 -0600 Subject: [PATCH] Add tagging support to AWS Instance Profiles and OIDC Providers --- cloudmock/aws/mockiam/iaminstanceprofile.go | 1 + cloudmock/aws/mockiam/oidcprovider.go | 2 ++ pkg/model/awsmodel/oidc_provider.go | 1 + pkg/model/iam.go | 1 + pkg/resources/aws/aws.go | 33 ++++++++++++++----- .../fi/cloudup/awstasks/iaminstanceprofile.go | 31 +++++++++++++++++ .../awstasks/iaminstanceprofilerole.go | 1 + .../fi/cloudup/awstasks/iamoidcprovider.go | 29 ++++++++++++++++ 8 files changed, 90 insertions(+), 9 deletions(-) diff --git a/cloudmock/aws/mockiam/iaminstanceprofile.go b/cloudmock/aws/mockiam/iaminstanceprofile.go index 5d19cbee19..e9d4e8a83d 100644 --- a/cloudmock/aws/mockiam/iaminstanceprofile.go +++ b/cloudmock/aws/mockiam/iaminstanceprofile.go @@ -57,6 +57,7 @@ func (m *MockIAM) CreateInstanceProfile(request *iam.CreateInstanceProfileInput) // Arn: request.Arn, // InstanceProfileId: request.InstanceProfileId, Path: request.Path, + Tags: request.Tags, // Roles: request.Roles, } diff --git a/cloudmock/aws/mockiam/oidcprovider.go b/cloudmock/aws/mockiam/oidcprovider.go index fb30a18137..885293af19 100644 --- a/cloudmock/aws/mockiam/oidcprovider.go +++ b/cloudmock/aws/mockiam/oidcprovider.go @@ -63,6 +63,7 @@ func (m *MockIAM) GetOpenIDConnectProviderWithContext(ctx aws.Context, request * response := &iam.GetOpenIDConnectProviderOutput{ ClientIDList: provider.ClientIDList, CreateDate: provider.CreateDate, + Tags: provider.Tags, ThumbprintList: provider.ThumbprintList, Url: provider.Url, } @@ -87,6 +88,7 @@ func (m *MockIAM) CreateOpenIDConnectProvider(request *iam.CreateOpenIDConnectPr p := &iam.GetOpenIDConnectProviderOutput{ ClientIDList: request.ClientIDList, + Tags: request.Tags, ThumbprintList: request.ThumbprintList, Url: request.Url, } diff --git a/pkg/model/awsmodel/oidc_provider.go b/pkg/model/awsmodel/oidc_provider.go index 74baa7afce..f45cca5e68 100644 --- a/pkg/model/awsmodel/oidc_provider.go +++ b/pkg/model/awsmodel/oidc_provider.go @@ -69,6 +69,7 @@ func (b *OIDCProviderBuilder) Build(c *fi.ModelBuilderContext) error { Lifecycle: b.Lifecycle, URL: fi.String(issuerURL), ClientIDs: []*string{fi.String(defaultAudience)}, + Tags: b.CloudTags(b.ClusterName(), false), Thumbprints: thumbprints, }) diff --git a/pkg/model/iam.go b/pkg/model/iam.go index e54fdf5d13..f371b6d13b 100644 --- a/pkg/model/iam.go +++ b/pkg/model/iam.go @@ -230,6 +230,7 @@ func (b *IAMModelBuilder) buildIAMTasks(role iam.Subject, iamName string, c *fi. Name: s(iamName), Lifecycle: b.Lifecycle, Shared: fi.Bool(shared), + Tags: b.CloudTags(iamName, false), } c.AddTask(iamInstanceProfile) } diff --git a/pkg/resources/aws/aws.go b/pkg/resources/aws/aws.go index e8872a79e3..da260fae12 100644 --- a/pkg/resources/aws/aws.go +++ b/pkg/resources/aws/aws.go @@ -307,6 +307,24 @@ func matchesElbV2Tags(tags map[string]string, actual []*elbv2.Tag) bool { return true } +func matchesIAMTags(tags map[string]string, actual []*iam.Tag) bool { + for k, v := range tags { + found := false + for _, a := range actual { + if aws.StringValue(a.Key) == k { + if aws.StringValue(a.Value) == v { + found = true + break + } + } + } + if !found { + return false + } + } + return true +} + //type DeletableResource interface { // Delete(cloud fi.Cloud) error //} @@ -2097,6 +2115,7 @@ func ListIAMInstanceProfiles(cloud fi.Cloud, clusterName string) ([]*resources.R func ListIAMOIDCProviders(cloud fi.Cloud, clusterName string) ([]*resources.Resource, error) { c := cloud.(awsup.AWSCloud) + tags := c.Tags() var providers []*string { @@ -2110,18 +2129,14 @@ func ListIAMOIDCProviders(cloud fi.Cloud, clusterName string) ([]*resources.Reso descReq := &iam.GetOpenIDConnectProviderInput{ OpenIDConnectProviderArn: arn, } - _, err := c.IAM().GetOpenIDConnectProvider(descReq) + resp, err := c.IAM().GetOpenIDConnectProvider(descReq) if err != nil { return nil, fmt.Errorf("error getting IAM OIDC Provider: %v", err) } - // TODO: only delete oidc providers if they're owned by the cluster. - // We need to figure out how we can determine that given only a cluster name. - // Providers dont support tagging or naming. - - // providers = append(providers, arn) - } - if err != nil { - return nil, fmt.Errorf("error listing IAM roles: %v", err) + if !matchesIAMTags(tags, resp.Tags) { + continue + } + providers = append(providers, arn) } } diff --git a/upup/pkg/fi/cloudup/awstasks/iaminstanceprofile.go b/upup/pkg/fi/cloudup/awstasks/iaminstanceprofile.go index 5d4dae6ddf..e263421b5b 100644 --- a/upup/pkg/fi/cloudup/awstasks/iaminstanceprofile.go +++ b/upup/pkg/fi/cloudup/awstasks/iaminstanceprofile.go @@ -35,6 +35,8 @@ type IAMInstanceProfile struct { Name *string Lifecycle *fi.Lifecycle + Tags map[string]string + ID *string Shared *bool } @@ -79,6 +81,7 @@ func (e *IAMInstanceProfile) Find(c *fi.Context) (*IAMInstanceProfile, error) { actual := &IAMInstanceProfile{ ID: p.InstanceProfileId, Name: p.InstanceProfileName, + Tags: mapIAMTagsToMap(p.Tags), } e.ID = actual.ID @@ -114,6 +117,7 @@ func (_ *IAMInstanceProfile) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *IAM request := &iam.CreateInstanceProfileInput{ InstanceProfileName: e.Name, + Tags: mapToIAMTags(e.Tags), } response, err := t.Cloud.IAM().CreateInstanceProfile(request) @@ -123,6 +127,33 @@ func (_ *IAMInstanceProfile) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *IAM e.ID = response.InstanceProfile.InstanceProfileId e.Name = response.InstanceProfile.InstanceProfileName + } else { + if changes.Tags != nil { + if len(a.Tags) > 0 { + existingTagKeys := make([]*string, 0) + for k := range a.Tags { + existingTagKeys = append(existingTagKeys, &k) + } + untagRequest := &iam.UntagInstanceProfileInput{ + InstanceProfileName: a.Name, + TagKeys: existingTagKeys, + } + _, err := t.Cloud.IAM().UntagInstanceProfile(untagRequest) + if err != nil { + return fmt.Errorf("error untagging IAMInstanceProfile: %v", err) + } + } + if len(e.Tags) > 0 { + tagRequest := &iam.TagInstanceProfileInput{ + InstanceProfileName: a.Name, + Tags: mapToIAMTags(e.Tags), + } + _, err := t.Cloud.IAM().TagInstanceProfile(tagRequest) + if err != nil { + return fmt.Errorf("error tagging IAMInstanceProfile: %v", err) + } + } + } } // TODO: Should we use path as our tag? diff --git a/upup/pkg/fi/cloudup/awstasks/iaminstanceprofilerole.go b/upup/pkg/fi/cloudup/awstasks/iaminstanceprofilerole.go index 4e8f3cf9d5..4af9c47026 100644 --- a/upup/pkg/fi/cloudup/awstasks/iaminstanceprofilerole.go +++ b/upup/pkg/fi/cloudup/awstasks/iaminstanceprofilerole.go @@ -114,6 +114,7 @@ func (_ *IAMInstanceProfileRole) RenderAWS(t *awsup.AWSAPITarget, a, e, changes type terraformIAMInstanceProfile struct { Name *string `json:"name" cty:"name"` Role *terraform.Literal `json:"role" cty:"role"` + // TODO(rifelpet): add tags field when terraform supports it } func (_ *IAMInstanceProfileRole) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *IAMInstanceProfileRole) error { diff --git a/upup/pkg/fi/cloudup/awstasks/iamoidcprovider.go b/upup/pkg/fi/cloudup/awstasks/iamoidcprovider.go index 3353cb3bd7..174670f7c7 100644 --- a/upup/pkg/fi/cloudup/awstasks/iamoidcprovider.go +++ b/upup/pkg/fi/cloudup/awstasks/iamoidcprovider.go @@ -39,6 +39,7 @@ type IAMOIDCProvider struct { URL *string Name *string + Tags map[string]string arn *string } @@ -83,6 +84,7 @@ func (e *IAMOIDCProvider) Find(c *fi.Context) (*IAMOIDCProvider, error) { ClientIDs: descResp.ClientIDList, Thumbprints: actualThumbprints, URL: &actualURL, + Tags: mapIAMTagsToMap(descResp.Tags), arn: arn, } @@ -135,6 +137,7 @@ func (p *IAMOIDCProvider) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *IAMOID ClientIDList: e.ClientIDs, ThumbprintList: aws.StringSlice(thumbprints), Url: e.URL, + Tags: mapToIAMTags(e.Tags), } response, err := t.Cloud.IAM().CreateOpenIDConnectProvider(request) @@ -156,6 +159,32 @@ func (p *IAMOIDCProvider) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *IAMOID return fmt.Errorf("error updating IAMOIDCProvider Thumbprints: %v", err) } } + if changes.Tags != nil { + if len(a.Tags) > 0 { + existingTagKeys := make([]*string, 0) + for k := range a.Tags { + existingTagKeys = append(existingTagKeys, &k) + } + untagRequest := &iam.UntagOpenIDConnectProviderInput{ + OpenIDConnectProviderArn: a.arn, + TagKeys: existingTagKeys, + } + _, err = t.Cloud.IAM().UntagOpenIDConnectProvider(untagRequest) + if err != nil { + return fmt.Errorf("error untagging IAMOIDCProvider: %v", err) + } + } + if len(e.Tags) > 0 { + tagRequest := &iam.TagOpenIDConnectProviderInput{ + OpenIDConnectProviderArn: a.arn, + Tags: mapToIAMTags(e.Tags), + } + _, err = t.Cloud.IAM().TagOpenIDConnectProvider(tagRequest) + if err != nil { + return fmt.Errorf("error tagging IAMOIDCProvider: %v", err) + } + } + } } return nil }