Skip to content

Commit ee8ef9f

Browse files
Add create_ignore_already_exists to google_cloud_identity_group_membership (#14250) (#10229)
[upstream:527d99e9bc723085900474191259de7594e2e918] Signed-off-by: Modular Magician <magic-modules@google.com>
1 parent 52f73a7 commit ee8ef9f

File tree

5 files changed

+267
-0
lines changed

5 files changed

+267
-0
lines changed

.changelog/14250.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
unknown: Add create_ignore_already_exists to google_cloud_identity_group_membership

google-beta/services/cloudidentity/resource_cloud_identity_group_membership.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"time"
2929

3030
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
31+
"google.golang.org/api/googleapi"
3132

3233
"github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgresource"
3334
transport_tpg "github.com/hashicorp/terraform-provider-google-beta/google-beta/transport"
@@ -167,6 +168,12 @@ and must be in the form of 'identitysources/{identity_source_id}'.`,
167168
Computed: true,
168169
Description: `The time when the Membership was last updated.`,
169170
},
171+
"create_ignore_already_exists": {
172+
Type: schema.TypeBool,
173+
Optional: true,
174+
Description: `If set to true, skip group member creation if a membership with the same name already exists. Defaults to false.`,
175+
Default: false,
176+
},
170177
},
171178
UseJSONNumber: true,
172179
}
@@ -247,6 +254,75 @@ func resourceCloudIdentityGroupMembershipCreate(d *schema.ResourceData, meta int
247254
}
248255

249256
headers := make(http.Header)
257+
if d.Get("create_ignore_already_exists").(bool) {
258+
log.Printf("[DEBUG] Calling get GroupMembership to check if membership already exists")
259+
preferredMemberKeyPropTyped := tpgresource.CheckStringMap(preferredMemberKeyProp)
260+
261+
params := map[string]string{
262+
"memberKey.id": preferredMemberKeyPropTyped["id"],
263+
}
264+
if ns, ok := preferredMemberKeyPropTyped["namespace"]; ok && ns != "" {
265+
params["memberKey.namespace"] = ns
266+
}
267+
getUrl, err := transport_tpg.AddQueryParams(url+":lookup", params)
268+
if err != nil {
269+
return err
270+
}
271+
272+
res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
273+
Config: config,
274+
Method: "GET",
275+
Project: billingProject,
276+
RawURL: getUrl,
277+
UserAgent: userAgent,
278+
Headers: headers,
279+
})
280+
// Do normal create if membership does not exist
281+
282+
if err != nil {
283+
gerr, ok := err.(*googleapi.Error)
284+
notFound := ok && gerr.Code == 404
285+
// If group membership is not found, we can proceed with the create operation.
286+
if !notFound {
287+
return fmt.Errorf("Error checking if GroupMembership exists: %s", err)
288+
}
289+
} else {
290+
// Set computed resource properties from create API response so that they're available on the subsequent Read call.
291+
err = resourceCloudIdentityGroupMembershipPostCreateSetComputedFields(d, meta, res)
292+
if err != nil {
293+
return fmt.Errorf("setting computed ID format fields: %w", err)
294+
}
295+
296+
// Store the ID now
297+
id, err := tpgresource.ReplaceVars(d, config, "{{name}}")
298+
if err != nil {
299+
return fmt.Errorf("Error constructing id: %s", err)
300+
}
301+
d.SetId(id)
302+
303+
// `name` is autogenerated from the api so needs to be set post-create
304+
name, ok := res["name"]
305+
if !ok {
306+
respBody, ok := res["response"]
307+
if !ok {
308+
return fmt.Errorf("Create response didn't contain critical fields. Create may not have succeeded.")
309+
}
310+
311+
name, ok = respBody.(map[string]interface{})["name"]
312+
if !ok {
313+
return fmt.Errorf("Create response didn't contain critical fields. Create may not have succeeded.")
314+
}
315+
}
316+
if err := d.Set("name", name.(string)); err != nil {
317+
return fmt.Errorf("Error setting name: %s", err)
318+
}
319+
d.SetId(name.(string))
320+
321+
log.Printf("[DEBUG] Finished creating GroupMembership %q: %#v", d.Id(), res)
322+
323+
return resourceCloudIdentityGroupMembershipRead(d, meta)
324+
}
325+
}
250326
res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
251327
Config: config,
252328
Method: "POST",
@@ -329,6 +405,13 @@ func resourceCloudIdentityGroupMembershipRead(d *schema.ResourceData, meta inter
329405
return transport_tpg.HandleNotFoundError(transformCloudIdentityGroupMembershipReadError(err), d, fmt.Sprintf("CloudIdentityGroupMembership %q", d.Id()))
330406
}
331407

408+
// Explicitly set virtual fields to default values if unset
409+
if _, ok := d.GetOkExists("create_ignore_already_exists"); !ok {
410+
if err := d.Set("create_ignore_already_exists", false); err != nil {
411+
return fmt.Errorf("Error setting create_ignore_already_exists: %s", err)
412+
}
413+
}
414+
332415
if err := d.Set("name", flattenCloudIdentityGroupMembershipName(res["name"], d, config)); err != nil {
333416
return fmt.Errorf("Error reading GroupMembership: %s", err)
334417
}
@@ -540,6 +623,10 @@ func resourceCloudIdentityGroupMembershipImport(d *schema.ResourceData, meta int
540623
}
541624
d.SetId(id)
542625

626+
// Explicitly set virtual fields to default values on import
627+
if err := d.Set("create_ignore_already_exists", false); err != nil {
628+
return nil, fmt.Errorf("Error setting create_ignore_already_exists: %s", err)
629+
}
543630
// Configure "group" property, which does not appear in the response body.
544631
group := regexp.MustCompile(`groups/[^/]+`).FindString(id)
545632
if err := d.Set("group", group); err != nil {

google-beta/services/cloudidentity/resource_cloud_identity_group_membership_generated_meta.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ api_service_name: 'cloudidentity.googleapis.com'
55
api_version: 'v1beta1'
66
api_resource_type_kind: 'Membership'
77
fields:
8+
- field: 'create_ignore_already_exists'
9+
provider_only: true
810
- field: 'create_time'
911
- field: 'group'
1012
provider_only: true

google-beta/services/cloudidentity/resource_cloud_identity_group_membership_test.go

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,3 +575,178 @@ func testAccCheckCloudIdentityGroupMembershipDestroyProducer(t *testing.T) func(
575575
return nil
576576
}
577577
}
578+
579+
// Test setting create_ignore_already_exists on an existing resource
580+
func testAccCloudIdentityGroupMembership_existingResourceCreateIgnoreAlreadyExists(t *testing.T) {
581+
context := map[string]interface{}{
582+
"org_domain": envvar.GetTestOrgDomainFromEnv(t),
583+
"cust_id": envvar.GetTestCustIdFromEnv(t),
584+
"random_suffix": acctest.RandString(t, 10),
585+
}
586+
id := "groups/groupId/memberships/membershipId"
587+
588+
acctest.VcrTest(t, resource.TestCase{
589+
PreCheck: func() { acctest.AccTestPreCheck(t) },
590+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
591+
CheckDestroy: testAccCheckCloudIdentityGroupMembershipDestroyProducer(t),
592+
Steps: []resource.TestStep{
593+
// The first step creates a new resource with create_ignore_already_exists=false
594+
{
595+
Config: testAccCloudIdentityGroupMembershipCreateIgnoreAlreadyExists(context, false),
596+
Check: resource.TestCheckResourceAttr("google_cloud_identity_group_membership.acceptance", "id", id),
597+
},
598+
{
599+
ResourceName: "google_cloud_identity_group_membership.acceptance",
600+
ImportStateId: id,
601+
ImportState: true,
602+
ImportStateVerify: true,
603+
ImportStateVerifyIgnore: []string{"create_ignore_already_exists"}, // Import leaves this field out when false
604+
},
605+
// The second step updates the resource to have create_ignore_already_exists=true
606+
{
607+
Config: testAccCloudIdentityGroupMembershipCreateIgnoreAlreadyExists(context, true),
608+
Check: resource.TestCheckResourceAttr("google_cloud_identity_group_membership.acceptance", "id", id),
609+
},
610+
},
611+
})
612+
}
613+
614+
// Test the option to ignore ALREADY_EXISTS error from creating a Source Repository.
615+
func testAccCloudIdentityGroupMembership_createIgnoreAlreadyExists(t *testing.T) {
616+
context := map[string]interface{}{
617+
"org_domain": envvar.GetTestOrgDomainFromEnv(t),
618+
"cust_id": envvar.GetTestCustIdFromEnv(t),
619+
"random_suffix": acctest.RandString(t, 10),
620+
}
621+
id := "groups/groupId/memberships/membershipId"
622+
623+
acctest.VcrTest(t, resource.TestCase{
624+
PreCheck: func() { acctest.AccTestPreCheck(t) },
625+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
626+
CheckDestroy: testAccCheckCloudIdentityGroupMembershipDestroyProducer(t),
627+
Steps: []resource.TestStep{
628+
// The first step creates a group membership
629+
{
630+
Config: testAccCloudIdentityGroupMembershipCreateIgnoreAlreadyExists(context, false),
631+
Check: resource.TestCheckResourceAttr("google_cloud_identity_group_membership.acceptance", "id", id),
632+
},
633+
{
634+
ResourceName: "google_cloud_identity_group_membership.acceptance",
635+
ImportStateId: id,
636+
ImportState: true,
637+
ImportStateVerify: true,
638+
},
639+
// The second step creates a new resource that duplicates with the existing group membership
640+
{
641+
Config: testAccCloudIdentityGroupMembershipDuplicateIgnoreAlreadyExists(context),
642+
Check: resource.ComposeTestCheckFunc(
643+
resource.TestCheckResourceAttr("google_cloud_identity_group_membership.acceptance", "id", id),
644+
resource.TestCheckResourceAttr("google_cloud_identity_group_membership.duplicate", "id", id),
645+
),
646+
},
647+
},
648+
})
649+
}
650+
651+
func testAccCloudIdentityGroupMembershipCreateIgnoreAlreadyExists(context map[string]interface{}, ignore_already_exists bool) string {
652+
context["create_ignore_already_exists"] = fmt.Sprintf("%t", ignore_already_exists)
653+
return acctest.Nprintf(`
654+
resource "google_cloud_identity_group" "group" {
655+
display_name = "tf-test-my-identity-group%{random_suffix}"
656+
657+
parent = "customers/%{cust_id}"
658+
659+
group_key {
660+
id = "tf-test-my-identity-group%{random_suffix}@%{org_domain}"
661+
}
662+
663+
labels = {
664+
"cloudidentity.googleapis.com/groups.discussion_forum" = ""
665+
}
666+
}
667+
resource "google_cloud_identity_group" "child-group" {
668+
display_name = "tf-test-my-identity-group%{random_suffix}-child"
669+
670+
parent = "customers/%{cust_id}"
671+
672+
group_key {
673+
id = "tf-test-my-identity-group%{random_suffix}-child@%{org_domain}"
674+
}
675+
676+
labels = {
677+
"cloudidentity.googleapis.com/groups.discussion_forum" = ""
678+
}
679+
}
680+
681+
resource "google_cloud_identity_group_membership" "acceptance" {
682+
group = google_cloud_identity_group.group.id
683+
684+
preferred_member_key {
685+
id = google_cloud_identity_group.child-group.group_key[0].id
686+
}
687+
688+
roles {
689+
name = "MEMBER"
690+
}
691+
692+
create_ignore_already_exists = %{create_ignore_already_exists}
693+
}
694+
`, context)
695+
}
696+
697+
func testAccCloudIdentityGroupMembershipDuplicateIgnoreAlreadyExists(context map[string]interface{}) string {
698+
return acctest.Nprintf(`
699+
resource "google_cloud_identity_group" "group" {
700+
display_name = "tf-test-my-identity-group%{random_suffix}"
701+
702+
parent = "customers/%{cust_id}"
703+
704+
group_key {
705+
id = "tf-test-my-identity-group%{random_suffix}@%{org_domain}"
706+
}
707+
708+
labels = {
709+
"cloudidentity.googleapis.com/groups.discussion_forum" = ""
710+
}
711+
}
712+
resource "google_cloud_identity_group" "child-group" {
713+
display_name = "tf-test-my-identity-group%{random_suffix}-child"
714+
715+
parent = "customers/%{cust_id}"
716+
717+
group_key {
718+
id = "tf-test-my-identity-group%{random_suffix}-child@%{org_domain}"
719+
}
720+
721+
labels = {
722+
"cloudidentity.googleapis.com/groups.discussion_forum" = ""
723+
}
724+
}
725+
726+
resource "google_cloud_identity_group_membership" "acceptance" {
727+
group = google_cloud_identity_group.group.id
728+
729+
preferred_member_key {
730+
id = google_cloud_identity_group.child-group.group_key[0].id
731+
}
732+
733+
roles {
734+
name = "MEMBER"
735+
}
736+
}
737+
738+
resource "google_cloud_identity_group_membership" "duplicate" {
739+
group = google_cloud_identity_group.group.id
740+
741+
preferred_member_key {
742+
id = google_cloud_identity_group.child-group.group_key[0].id
743+
}
744+
745+
roles {
746+
name = "MEMBER"
747+
}
748+
749+
create_ignore_already_exists = true
750+
}
751+
`, context)
752+
}

website/docs/r/cloud_identity_group_membership.html.markdown

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ The following arguments are supported:
167167
EntityKey of the member.
168168
Structure is [documented below](#nested_preferred_member_key).
169169

170+
* `create_ignore_already_exists` - (Optional) If set to true, skip group member creation if a membership with the same name already exists. Defaults to false.
171+
170172

171173
<a name="nested_member_key"></a>The `member_key` block supports:
172174

0 commit comments

Comments
 (0)