From d7fc1eaee20f7ccb88c0a85ef6a510a331a644c5 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Sat, 6 Mar 2021 00:52:48 +0200 Subject: [PATCH 1/3] add tagging support for saml provider --- aws/internal/keyvaluetags/iam_tags.go | 35 +++++++++ aws/resource_aws_iam_saml_provider.go | 43 ++++++++--- aws/resource_aws_iam_saml_provider_test.go | 71 +++++++++++++++++++ .../docs/r/iam_saml_provider.html.markdown | 1 + 4 files changed, 139 insertions(+), 11 deletions(-) diff --git a/aws/internal/keyvaluetags/iam_tags.go b/aws/internal/keyvaluetags/iam_tags.go index 1059708d280..1abe0e0977e 100644 --- a/aws/internal/keyvaluetags/iam_tags.go +++ b/aws/internal/keyvaluetags/iam_tags.go @@ -80,3 +80,38 @@ func IamUserUpdateTags(conn *iam.IAM, identifier string, oldTagsMap interface{}, return nil } + +// IamSAMLProviderUpdateTags updates IAM SAML Provider tags. +// The identifier is the SAML Provider ARN. +func IamSAMLProviderUpdateTags(conn *iam.IAM, identifier string, oldTagsMap interface{}, newTagsMap interface{}) error { + oldTags := New(oldTagsMap) + newTags := New(newTagsMap) + + if removedTags := oldTags.Removed(newTags); len(removedTags) > 0 { + input := &iam.UntagSAMLProviderInput{ + SAMLProviderArn: aws.String(identifier), + TagKeys: aws.StringSlice(removedTags.Keys()), + } + + _, err := conn.UntagSAMLProvider(input) + + if err != nil { + return fmt.Errorf("error untagging resource (%s): %w", identifier, err) + } + } + + if updatedTags := oldTags.Updated(newTags); len(updatedTags) > 0 { + input := &iam.TagSAMLProviderInput{ + SAMLProviderArn: aws.String(identifier), + Tags: updatedTags.IgnoreAws().IamTags(), + } + + _, err := conn.TagSAMLProvider(input) + + if err != nil { + return fmt.Errorf("error tagging resource (%s): %w", identifier, err) + } + } + + return nil +} diff --git a/aws/resource_aws_iam_saml_provider.go b/aws/resource_aws_iam_saml_provider.go index a51b1a01bc7..dccb51c9d45 100644 --- a/aws/resource_aws_iam_saml_provider.go +++ b/aws/resource_aws_iam_saml_provider.go @@ -10,6 +10,8 @@ import ( "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/iam" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" ) func resourceAwsIamSamlProvider() *schema.Resource { @@ -33,14 +35,17 @@ func resourceAwsIamSamlProvider() *schema.Resource { Computed: true, }, "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 128), }, "saml_metadata_document": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1000, 10000000), }, + "tags": tagsSchema(), }, } } @@ -51,6 +56,7 @@ func resourceAwsIamSamlProviderCreate(d *schema.ResourceData, meta interface{}) input := &iam.CreateSAMLProviderInput{ Name: aws.String(d.Get("name").(string)), SAMLMetadataDocument: aws.String(d.Get("saml_metadata_document").(string)), + Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().IamTags(), } out, err := conn.CreateSAMLProvider(input) @@ -65,6 +71,7 @@ func resourceAwsIamSamlProviderCreate(d *schema.ResourceData, meta interface{}) func resourceAwsIamSamlProviderRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).iamconn + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig input := &iam.GetSAMLProviderInput{ SAMLProviderArn: aws.String(d.Id()), @@ -88,19 +95,33 @@ func resourceAwsIamSamlProviderRead(d *schema.ResourceData, meta interface{}) er d.Set("valid_until", out.ValidUntil.Format(time.RFC1123)) d.Set("saml_metadata_document", out.SAMLMetadataDocument) + if err := d.Set("tags", keyvaluetags.IamKeyValueTags(out.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %w", err) + } + return nil } func resourceAwsIamSamlProviderUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).iamconn - input := &iam.UpdateSAMLProviderInput{ - SAMLProviderArn: aws.String(d.Id()), - SAMLMetadataDocument: aws.String(d.Get("saml_metadata_document").(string)), + if d.HasChangeExcept("tags") { + input := &iam.UpdateSAMLProviderInput{ + SAMLProviderArn: aws.String(d.Id()), + SAMLMetadataDocument: aws.String(d.Get("saml_metadata_document").(string)), + } + _, err := conn.UpdateSAMLProvider(input) + if err != nil { + return fmt.Errorf("error updating IAM SAML Provider (%q): %w", d.Id(), err) + } } - _, err := conn.UpdateSAMLProvider(input) - if err != nil { - return fmt.Errorf("error updating IAM SAML Provider (%q): %w", d.Id(), err) + + if d.HasChange("tags") { + o, n := d.GetChange("tags") + + if err := keyvaluetags.IamSAMLProviderUpdateTags(conn, d.Id(), o, n); err != nil { + return fmt.Errorf("error updating tags for IAM SAML Provider (%s): %w", d.Id(), err) + } } return resourceAwsIamSamlProviderRead(d, meta) diff --git a/aws/resource_aws_iam_saml_provider_test.go b/aws/resource_aws_iam_saml_provider_test.go index 53b278bdf1c..98d31f50bd9 100644 --- a/aws/resource_aws_iam_saml_provider_test.go +++ b/aws/resource_aws_iam_saml_provider_test.go @@ -76,6 +76,7 @@ func TestAccAWSIAMSamlProvider_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttrSet(resourceName, "saml_metadata_document"), resource.TestCheckResourceAttrSet(resourceName, "valid_until"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, { @@ -95,6 +96,49 @@ func TestAccAWSIAMSamlProvider_basic(t *testing.T) { }) } +func TestAccAWSIAMSamlProvider_tags(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_iam_saml_provider.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckIAMSamlProviderDestroy, + Steps: []resource.TestStep{ + { + Config: testAccIAMSamlProviderConfigTags1(rName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckIAMSamlProviderExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccIAMSamlProviderConfigTags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckIAMSamlProviderExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccIAMSamlProviderConfigTags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckIAMSamlProviderExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + }, + }) +} + func TestAccAWSIAMSamlProvider_disappears(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_iam_saml_provider.test" @@ -182,3 +226,30 @@ resource "aws_iam_saml_provider" "test" { } `, rName) } + +func testAccIAMSamlProviderConfigTags1(rName, tagKey1, tagValue1 string) string { + return fmt.Sprintf(` +resource "aws_iam_saml_provider" "test" { + name = %q + saml_metadata_document = file("./test-fixtures/saml-metadata.xml") + + tags = { + %[2]q = %[3]q + } +} +`, rName, tagKey1, tagValue1) +} + +func testAccIAMSamlProviderConfigTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return fmt.Sprintf(` +resource "aws_iam_saml_provider" "test" { + name = %q + saml_metadata_document = file("./test-fixtures/saml-metadata.xml") + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } +} +`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +} diff --git a/website/docs/r/iam_saml_provider.html.markdown b/website/docs/r/iam_saml_provider.html.markdown index 373bc0e4ccb..6aaf307a7c0 100644 --- a/website/docs/r/iam_saml_provider.html.markdown +++ b/website/docs/r/iam_saml_provider.html.markdown @@ -32,6 +32,7 @@ In addition to all arguments above, the following attributes are exported: * `arn` - The ARN assigned by AWS for this provider. * `valid_until` - The expiration date and time for the SAML provider in RFC1123 format, e.g. `Mon, 02 Jan 2006 15:04:05 MST`. +* `tags` - Key-value map of tags for the IAM SAML provider. ## Import From 7db5fb70e5fb91be85e1bd9fb52507e9646e342c Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Sat, 6 Mar 2021 00:57:15 +0200 Subject: [PATCH 2/3] changelog --- .changelog/17965.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changelog/17965.txt diff --git a/.changelog/17965.txt b/.changelog/17965.txt new file mode 100644 index 00000000000..9451408b5c6 --- /dev/null +++ b/.changelog/17965.txt @@ -0,0 +1,7 @@ +```release-note:enhancement +resource/aws_iam_saml_provider: Add tagging support +``` + +```release-note:enhancement +resource/aws_iam_saml_provider: Add plan time validation for `name` and `saml_metadata_document` +``` From 5ee95bffdd1b014935d6f61bb66e974fbc014ede Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Thu, 25 Mar 2021 13:59:05 -0400 Subject: [PATCH 3/3] Update aws/resource_aws_iam_saml_provider_test.go --- aws/resource_aws_iam_saml_provider_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/aws/resource_aws_iam_saml_provider_test.go b/aws/resource_aws_iam_saml_provider_test.go index 98d31f50bd9..d6782bf98ca 100644 --- a/aws/resource_aws_iam_saml_provider_test.go +++ b/aws/resource_aws_iam_saml_provider_test.go @@ -102,6 +102,7 @@ func TestAccAWSIAMSamlProvider_tags(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, iam.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckIAMSamlProviderDestroy, Steps: []resource.TestStep{