From 4f35c8aaa99063b05ef948d62f129f334e15f049 Mon Sep 17 00:00:00 2001 From: abd-goog <156919569+abd-goog@users.noreply.github.com> Date: Thu, 5 Sep 2024 21:30:49 +0530 Subject: [PATCH] Add `tags` field to Folder resource (#11424) --- .../resourcemanager/resource_google_folder.go | 20 ++++- .../resource_google_folder_test.go | 73 +++++++++++++++++++ .../docs/r/google_folder.html.markdown | 11 +++ 3 files changed, 100 insertions(+), 4 deletions(-) diff --git a/mmv1/third_party/terraform/services/resourcemanager/resource_google_folder.go b/mmv1/third_party/terraform/services/resourcemanager/resource_google_folder.go index 653da5651bc9..78f68ae7506d 100644 --- a/mmv1/third_party/terraform/services/resourcemanager/resource_google_folder.go +++ b/mmv1/third_party/terraform/services/resourcemanager/resource_google_folder.go @@ -71,6 +71,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, } @@ -86,14 +93,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/mmv1/third_party/terraform/services/resourcemanager/resource_google_folder_test.go b/mmv1/third_party/terraform/services/resourcemanager/resource_google_folder_test.go index 75f1a08ca995..b90e63614f6f 100644 --- a/mmv1/third_party/terraform/services/resourcemanager/resource_google_folder_test.go +++ b/mmv1/third_party/terraform/services/resourcemanager/resource_google_folder_test.go @@ -2,6 +2,7 @@ package resourcemanager_test import ( "fmt" + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -88,6 +89,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) @@ -159,6 +199,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/mmv1/third_party/terraform/website/docs/r/google_folder.html.markdown b/mmv1/third_party/terraform/website/docs/r/google_folder.html.markdown index e0f47767a350..8d977035ea8b 100644 --- a/mmv1/third_party/terraform/website/docs/r/google_folder.html.markdown +++ b/mmv1/third_party/terraform/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