diff --git a/.changelog/11424.txt b/.changelog/11424.txt new file mode 100644 index 0000000000..320b11bee6 --- /dev/null +++ b/.changelog/11424.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resourcemanager: added `tags` field to `google_folder` to allow setting tags for folders at creation time +``` \ No newline at end of file diff --git a/google-beta/services/resourcemanager/resource_google_folder.go b/google-beta/services/resourcemanager/resource_google_folder.go index 3a5d11785e..554b4e18e5 100644 --- a/google-beta/services/resourcemanager/resource_google_folder.go +++ b/google-beta/services/resourcemanager/resource_google_folder.go @@ -73,6 +73,13 @@ func ResourceGoogleFolder() *schema.Resource { Default: true, Description: `When the field is set to true or unset in Terraform state, a terraform apply or terraform destroy that would delete the instance will fail. When the field is set to false, deleting the instance is allowed.`, }, + "tags": { + Type: schema.TypeMap, + Optional: true, + ForceNew: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: `A map of resource manager tags. Resource manager tag keys and values have the same definition as resource manager tags. Keys must be in the format tagKeys/{tag_key_id}, and values are in the format tagValues/456. The field is ignored when empty.`, + }, }, UseJSONNumber: true, } @@ -88,14 +95,19 @@ func resourceGoogleFolderCreate(d *schema.ResourceData, meta interface{}) error displayName := d.Get("display_name").(string) parent := d.Get("parent").(string) + folder := &resourceManagerV3.Folder{ + DisplayName: displayName, + Parent: parent, + } + if _, ok := d.GetOk("tags"); ok { + folder.Tags = tpgresource.ExpandStringMap(d, "tags") + } + var op *resourceManagerV3.Operation err = transport_tpg.Retry(transport_tpg.RetryOptions{ RetryFunc: func() error { var reqErr error - op, reqErr = config.NewResourceManagerV3Client(userAgent).Folders.Create(&resourceManagerV3.Folder{ - DisplayName: displayName, - Parent: parent, - }).Do() + op, reqErr = config.NewResourceManagerV3Client(userAgent).Folders.Create(folder).Do() return reqErr }, Timeout: d.Timeout(schema.TimeoutCreate), diff --git a/google-beta/services/resourcemanager/resource_google_folder_test.go b/google-beta/services/resourcemanager/resource_google_folder_test.go index b4c9eb3dac..a6e3990aa1 100644 --- a/google-beta/services/resourcemanager/resource_google_folder_test.go +++ b/google-beta/services/resourcemanager/resource_google_folder_test.go @@ -4,6 +4,7 @@ package resourcemanager_test import ( "fmt" + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -90,6 +91,45 @@ func TestAccFolder_moveParent(t *testing.T) { }) } +// Test that a Folder resource can be created with tags +func TestAccFolder_tags(t *testing.T) { + t.Parallel() + + org := envvar.GetTestOrgFromEnv(t) + parent := "organizations/" + org + folderDisplayName := "tf-test-" + acctest.RandString(t, 10) + tagKey := acctest.BootstrapSharedTestTagKey(t, "crm-folder-tagkey") + tagValue := acctest.BootstrapSharedTestTagValue(t, "crm-folder-tagvalue", tagKey) + folder_tags := resourceManagerV3.Folder{} + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccFolder_tags(folderDisplayName, parent, map[string]string{org + "/" + tagKey: tagValue}), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleFolderExists(t, "google_folder.folder_tags", &folder_tags), + ), + }, + // Make sure import supports tags + { + ResourceName: "google_folder.folder_tags", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"tags", "deletion_protection"}, // we don't read tags back + }, + // Update tags tries to replace the folder but fails due to deletion protection + { + Config: testAccFolder_tags(folderDisplayName, org, map[string]string{}), + ExpectError: regexp.MustCompile("deletion_protection"), + }, + { + Config: testAccFolder_tagsAllowDestroy(folderDisplayName, parent, map[string]string{org + "/" + tagKey: tagValue}), + }, + }, + }) +} + func testAccCheckGoogleFolderDestroyProducer(t *testing.T) func(s *terraform.State) error { return func(s *terraform.State) error { config := acctest.GoogleProviderConfig(t) @@ -161,6 +201,39 @@ resource "google_folder" "folder1" { `, folder, parent) } +func testAccFolder_tags(folder, parent string, tags map[string]string) string { + r := fmt.Sprintf(` +resource "google_folder" "folder_tags" { + display_name = "%s" + parent = "%s" + tags = {`, folder, parent) + + l := "" + for key, value := range tags { + l += fmt.Sprintf("%q = %q\n", key, value) + } + + l += fmt.Sprintf("}\n}") + return r + l +} + +func testAccFolder_tagsAllowDestroy(folder, parent string, tags map[string]string) string { + r := fmt.Sprintf(` +resource "google_folder" "folder_tags" { + display_name = "%s" + parent = "%s" + deletion_protection = false + tags = {`, folder, parent) + + l := "" + for key, value := range tags { + l += fmt.Sprintf("%q = %q\n", key, value) + } + + l += fmt.Sprintf("}\n}") + return r + l +} + func testAccFolder_move(folder1, folder2, parent string) string { return fmt.Sprintf(` resource "google_folder" "folder1" { diff --git a/website/docs/r/google_folder.html.markdown b/website/docs/r/google_folder.html.markdown index e0f47767a3..8d977035ea 100644 --- a/website/docs/r/google_folder.html.markdown +++ b/website/docs/r/google_folder.html.markdown @@ -20,6 +20,8 @@ resource must have `roles/resourcemanager.folderCreator`. See the [Access Control for Folders Using IAM](https://cloud.google.com/resource-manager/docs/access-control-folders) doc for more information. +~> It may take a while for the attached tag bindings to be deleted after the folder is scheduled to be deleted. + ## Example Usage ```hcl @@ -34,6 +36,13 @@ resource "google_folder" "team-abc" { display_name = "Team ABC" parent = google_folder.department1.name } + +# Folder with a tag +resource "google_folder" "department1" { + display_name = "Department 1" + parent = "organizations/1234567" + tags = {"1234567/env":"staging"} +} ``` ## Argument Reference @@ -46,6 +55,8 @@ The following arguments are supported: * `parent` - (Required) The resource name of the parent Folder or Organization. Must be of the form `folders/{folder_id}` or `organizations/{org_id}`. +* `tags` - (Optional) A map of resource manager tags. Resource manager tag keys and values have the same definition as resource manager tags. Keys must be in the format tagKeys/{tag_key_id}, and values are in the format tagValues/456. The field is ignored when empty. The field is immutable and causes resource replacement when mutated. + ## Attributes Reference In addition to the arguments listed above, the following computed attributes are