Skip to content

Commit 5a44bc1

Browse files
Fix cloud_identity_group_membership to properly handle 403 responses when membership does not exist (#6999) (#13331)
Signed-off-by: Modular Magician <[email protected]> Signed-off-by: Modular Magician <[email protected]>
1 parent 40e3e76 commit 5a44bc1

4 files changed

+113
-1
lines changed

.changelog/6999.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:bug
2+
fix `cloud_identity_group_membership` to properly handle 403 responses when membership does not exist
3+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package google
2+
3+
import (
4+
"log"
5+
"strings"
6+
7+
"github.com/hashicorp/errwrap"
8+
"google.golang.org/api/googleapi"
9+
)
10+
11+
func transformCloudIdentityGroupMembershipReadError(err error) error {
12+
if gErr, ok := errwrap.GetType(err, &googleapi.Error{}).(*googleapi.Error); ok {
13+
if gErr.Code == 403 && strings.Contains(gErr.Message, "(or it may not exist)") {
14+
// This error occurs when either the group membership does not exist, or permission is denied. It is
15+
// deliberately ambiguous so that existence information is not revealed to the caller. However, for
16+
// the Read function, we can only assume that the membership does not exist, and proceed with attempting
17+
// other operations. Since handleNotFoundError(...) expects an error code of 404 when a resource does not
18+
// exist, to get the desired behavior, we modify the error code to be 404.
19+
gErr.Code = 404
20+
}
21+
22+
log.Printf("[DEBUG] Transformed CloudIdentityGroupMembership error")
23+
return gErr
24+
}
25+
26+
return err
27+
}

google/resource_cloud_identity_group_membership.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ func resourceCloudIdentityGroupMembershipRead(d *schema.ResourceData, meta inter
228228

229229
res, err := sendRequest(config, "GET", billingProject, url, userAgent, nil)
230230
if err != nil {
231-
return handleNotFoundError(err, d, fmt.Sprintf("CloudIdentityGroupMembership %q", d.Id()))
231+
return handleNotFoundError(transformCloudIdentityGroupMembershipReadError(err), d, fmt.Sprintf("CloudIdentityGroupMembership %q", d.Id()))
232232
}
233233

234234
if err := d.Set("name", flattenCloudIdentityGroupMembershipName(res["name"], d, config)); err != nil {

google/resource_cloud_identity_group_membership_test.go

+82
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"testing"
55

66
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
7+
"google.golang.org/api/iam/v1"
78
)
89

910
func TestAccCloudIdentityGroupMembership_update(t *testing.T) {
@@ -174,3 +175,84 @@ resource "google_cloud_identity_group_membership" "basic" {
174175
}
175176
`, context)
176177
}
178+
179+
func TestAccCloudIdentityGroupMembership_membershipDoesNotExist(t *testing.T) {
180+
t.Parallel()
181+
182+
context := map[string]interface{}{
183+
"org_domain": getTestOrgDomainFromEnv(t),
184+
"cust_id": getTestCustIdFromEnv(t),
185+
"random_suffix": randString(t, 10),
186+
}
187+
188+
saId := "tf-test-sa-" + randString(t, 10)
189+
project := getTestProjectFromEnv()
190+
config := BootstrapConfig(t)
191+
192+
r := &iam.CreateServiceAccountRequest{
193+
AccountId: saId,
194+
ServiceAccount: &iam.ServiceAccount{},
195+
}
196+
197+
sa, err := config.NewIamClient(config.userAgent).Projects.ServiceAccounts.Create("projects/"+project, r).Do()
198+
if err != nil {
199+
t.Errorf("Error creating service account: %s", err)
200+
}
201+
202+
context["member_id"] = sa.Email
203+
204+
vcrTest(t, resource.TestCase{
205+
PreCheck: func() { testAccPreCheck(t) },
206+
Providers: testAccProviders,
207+
CheckDestroy: testAccCheckCloudIdentityGroupMembershipDestroyProducer(t),
208+
Steps: []resource.TestStep{
209+
{
210+
Config: testAccCloudIdentityGroupMembership_dne(context),
211+
},
212+
{
213+
PreConfig: func() {
214+
config := googleProviderConfig(t)
215+
216+
_, err := config.NewIamClient(config.userAgent).Projects.ServiceAccounts.Delete(sa.Name).Do()
217+
if err != nil {
218+
t.Errorf("cannot delete service account %s: %v", sa.Name, err)
219+
return
220+
}
221+
},
222+
Config: testAccCloudIdentityGroupMembership_dne(context),
223+
PlanOnly: true,
224+
ExpectNonEmptyPlan: true,
225+
},
226+
},
227+
})
228+
}
229+
230+
func testAccCloudIdentityGroupMembership_dne(context map[string]interface{}) string {
231+
return Nprintf(`
232+
resource "google_cloud_identity_group" "group" {
233+
display_name = "tf-test-my-identity-group-%{random_suffix}"
234+
235+
parent = "customers/%{cust_id}"
236+
237+
group_key {
238+
id = "tf-test-my-identity-group-%{random_suffix}@%{org_domain}"
239+
}
240+
241+
labels = {
242+
"cloudidentity.googleapis.com/groups.discussion_forum" = ""
243+
}
244+
}
245+
246+
resource "google_cloud_identity_group_membership" "basic" {
247+
group = google_cloud_identity_group.group.id
248+
249+
preferred_member_key {
250+
id = "%{member_id}"
251+
}
252+
253+
roles {
254+
name = "MEMBER"
255+
}
256+
}
257+
`, context)
258+
}

0 commit comments

Comments
 (0)