diff --git a/aws/resource_aws_organizations_organizational_unit.go b/aws/resource_aws_organizations_organizational_unit.go index 70ab43c4ba7..47a01be979e 100644 --- a/aws/resource_aws_organizations_organizational_unit.go +++ b/aws/resource_aws_organizations_organizational_unit.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "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 resourceAwsOrganizationsOrganizationalUnit() *schema.Resource { @@ -63,6 +64,7 @@ func resourceAwsOrganizationsOrganizationalUnit() *schema.Resource { Required: true, ValidateFunc: validation.StringMatch(regexp.MustCompile("^(r-[0-9a-z]{4,32})|(ou-[0-9a-z]{4,32}-[a-z0-9]{8,32})$"), "see https://docs.aws.amazon.com/organizations/latest/APIReference/API_CreateOrganizationalUnit.html#organizations-CreateOrganizationalUnit-request-ParentId"), }, + "tags": tagsSchema(), }, } } @@ -85,7 +87,7 @@ func resourceAwsOrganizationsOrganizationalUnitCreate(d *schema.ResourceData, me if err != nil { if isAWSErr(err, organizations.ErrCodeFinalizingOrganizationException, "") { - log.Printf("[DEBUG] Trying to create organizational unit again: %q", err.Error()) + log.Printf("[DEBUG] Trying to create Organizational Unit again: %q", err.Error()) return resource.RetryableError(err) } @@ -99,7 +101,7 @@ func resourceAwsOrganizationsOrganizationalUnitCreate(d *schema.ResourceData, me } if err != nil { - return fmt.Errorf("Error creating organizational unit: %s", err) + return fmt.Errorf("Error creating Organizational Unit: %s", err) } log.Printf("[DEBUG] Organizational Unit create response: %#v", resp) @@ -107,11 +109,19 @@ func resourceAwsOrganizationsOrganizationalUnitCreate(d *schema.ResourceData, me ouId := resp.OrganizationalUnit.Id d.SetId(*ouId) + if v := d.Get("tags").(map[string]interface{}); len(v) > 0 { + if err := keyvaluetags.OrganizationsUpdateTags(conn, d.Id(), nil, v); err != nil { + return fmt.Errorf("Error adding Organizational Unit (%s) tags: %s", d.Id(), err) + } + } + return resourceAwsOrganizationsOrganizationalUnitRead(d, meta) } func resourceAwsOrganizationsOrganizationalUnitRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).organizationsconn + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + describeOpts := &organizations.DescribeOrganizationalUnitInput{ OrganizationalUnitId: aws.String(d.Id()), } @@ -134,7 +144,7 @@ func resourceAwsOrganizationsOrganizationalUnitRead(d *schema.ResourceData, meta parentId, err := resourceAwsOrganizationsOrganizationalUnitGetParentId(conn, d.Id()) if err != nil { - log.Printf("[WARN] Unable to find parent organizational unit, removing from state: %s", d.Id()) + log.Printf("[WARN] Unable to find parent Organizational Unit, removing from state: %s", d.Id()) d.SetId("") return nil } @@ -163,13 +173,24 @@ func resourceAwsOrganizationsOrganizationalUnitRead(d *schema.ResourceData, meta d.Set("arn", ou.Arn) d.Set("name", ou.Name) d.Set("parent_id", parentId) + + tags, err := keyvaluetags.OrganizationsListTags(conn, d.Id()) + + if err != nil { + return fmt.Errorf("error listing tags for AWS Organizational Unit (%s): %s", d.Id(), err) + } + + if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %s", err) + } + return nil } func resourceAwsOrganizationsOrganizationalUnitUpdate(d *schema.ResourceData, meta interface{}) error { - if d.HasChange("name") { - conn := meta.(*AWSClient).organizationsconn + conn := meta.(*AWSClient).organizationsconn + if d.HasChange("name") { updateOpts := &organizations.UpdateOrganizationalUnitInput{ Name: aws.String(d.Get("name").(string)), OrganizationalUnitId: aws.String(d.Id()), @@ -178,11 +199,19 @@ func resourceAwsOrganizationsOrganizationalUnitUpdate(d *schema.ResourceData, me log.Printf("[DEBUG] Organizational Unit update config: %#v", updateOpts) resp, err := conn.UpdateOrganizationalUnit(updateOpts) if err != nil { - return fmt.Errorf("Error creating organizational unit: %s", err) + return fmt.Errorf("Error updating Organizational Unit: %s", err) } log.Printf("[DEBUG] Organizational Unit update response: %#v", resp) } + if d.HasChange("tags") { + o, n := d.GetChange("tags") + + if err := keyvaluetags.OrganizationsUpdateTags(conn, d.Id(), o, n); err != nil { + return fmt.Errorf("error updating Organizational Unit (%s) tags: %s", d.Id(), err) + } + } + return nil } @@ -192,7 +221,7 @@ func resourceAwsOrganizationsOrganizationalUnitDelete(d *schema.ResourceData, me input := &organizations.DeleteOrganizationalUnitInput{ OrganizationalUnitId: aws.String(d.Id()), } - log.Printf("[DEBUG] Removing AWS organizational unit from organization: %s", input) + log.Printf("[DEBUG] Removing AWS Organizational Unit from organization: %s", input) _, err := conn.DeleteOrganizationalUnit(input) if err != nil { if isAWSErr(err, organizations.ErrCodeOrganizationalUnitNotFoundException, "") { diff --git a/aws/resource_aws_organizations_organizational_unit_test.go b/aws/resource_aws_organizations_organizational_unit_test.go index 2d6f35afee6..02f8214cab8 100644 --- a/aws/resource_aws_organizations_organizational_unit_test.go +++ b/aws/resource_aws_organizations_organizational_unit_test.go @@ -30,6 +30,7 @@ func testAccAwsOrganizationsOrganizationalUnit_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "accounts.#", "0"), testAccMatchResourceAttrGlobalARN(resourceName, "arn", "organizations", regexp.MustCompile(`ou/o-.+/ou-.+`)), resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, { @@ -77,6 +78,51 @@ func testAccAwsOrganizationsOrganizationalUnit_Name(t *testing.T) { }) } +func testAccAwsOrganizationsOrganizationalUnit_Tags(t *testing.T) { + var unit organizations.OrganizationalUnit + + rInt := acctest.RandInt() + name := fmt.Sprintf("tf_outest_%d", rInt) + resourceName := "aws_organizations_organizational_unit.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsOrganizationsOrganizationalUnitDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsOrganizationsOrganizationalUnitConfigTags1(name, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsOrganizationsOrganizationalUnitExists(resourceName, &unit), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAwsOrganizationsOrganizationalUnitConfigTags2(name, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsOrganizationsOrganizationalUnitExists(resourceName, &unit), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccAwsOrganizationsOrganizationalUnitConfig(name), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsOrganizationsOrganizationalUnitExists(resourceName, &unit), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + }, + }) +} + func testAccCheckAwsOrganizationsOrganizationalUnitDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).organizationsconn @@ -151,3 +197,34 @@ resource "aws_organizations_organizational_unit" "test" { } `, name) } + +func testAccAwsOrganizationsOrganizationalUnitConfigTags1(name, tagKey1, tagValue1 string) string { + return fmt.Sprintf(` +resource "aws_organizations_organization" "test" {} + +resource "aws_organizations_organizational_unit" "test" { + name = %[1]q + parent_id = aws_organizations_organization.test.roots[0].id + + tags = { + %[2]q = %[3]q + } +} +`, name, tagKey1, tagValue1) +} + +func testAccAwsOrganizationsOrganizationalUnitConfigTags2(name, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return fmt.Sprintf(` +resource "aws_organizations_organization" "test" {} + +resource "aws_organizations_organizational_unit" "test" { + name = %[1]q + parent_id = aws_organizations_organization.test.roots[0].id + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } +} +`, name, tagKey1, tagValue1, tagKey2, tagValue2) +} diff --git a/aws/resource_aws_organizations_test.go b/aws/resource_aws_organizations_test.go index 409a9d1b42b..c2e9a72963b 100644 --- a/aws/resource_aws_organizations_test.go +++ b/aws/resource_aws_organizations_test.go @@ -21,6 +21,7 @@ func TestAccAWSOrganizations_serial(t *testing.T) { "OrganizationalUnit": { "basic": testAccAwsOrganizationsOrganizationalUnit_basic, "Name": testAccAwsOrganizationsOrganizationalUnit_Name, + "Tags": testAccAwsOrganizationsOrganizationalUnit_Tags, }, "OrganizationalUnits": { "DataSource": testAccDataSourceAwsOrganizationsOrganizationalUnits_basic, diff --git a/website/docs/r/organizations_organizational_unit.html.markdown b/website/docs/r/organizations_organizational_unit.html.markdown index 39acb76ef7e..d83081a40a8 100644 --- a/website/docs/r/organizations_organizational_unit.html.markdown +++ b/website/docs/r/organizations_organizational_unit.html.markdown @@ -25,6 +25,7 @@ The following arguments are supported: * `name` - The name for the organizational unit * `parent_id` - ID of the parent organizational unit, which may be the root +* `tags` - (Optional) Key-value map of resource tags. ## Attributes Reference