Skip to content

Commit e46200a

Browse files
emilymyemodular-magician
authored andcommitted
Fix IAM policy behavior regression for empty members (#2253)
Merged PR #2253.
1 parent c9c69e1 commit e46200a

File tree

5 files changed

+67
-5
lines changed

5 files changed

+67
-5
lines changed

build/terraform

build/terraform-beta

third_party/terraform/tests/resource_google_project_iam_policy_test.go

+38
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,23 @@ func TestAccProjectIamPolicy_basic(t *testing.T) {
4141
})
4242
}
4343

44+
// Test that an IAM policy with empty members does not cause a permadiff.
45+
func TestAccProjectIamPolicy_emptyMembers(t *testing.T) {
46+
t.Parallel()
47+
48+
org := getTestOrgFromEnv(t)
49+
pid := "terraform-" + acctest.RandString(10)
50+
resource.Test(t, resource.TestCase{
51+
PreCheck: func() { testAccPreCheck(t) },
52+
Providers: testAccProviders,
53+
Steps: []resource.TestStep{
54+
{
55+
Config: testAccProjectIamPolicyEmptyMembers(pid, pname, org),
56+
},
57+
},
58+
})
59+
}
60+
4461
// Test that a non-collapsed IAM policy doesn't perpetually diff
4562
func TestAccProjectIamPolicy_expanded(t *testing.T) {
4663
t.Parallel()
@@ -276,6 +293,27 @@ resource "google_project" "acceptance" {
276293
}`, pid, name, org)
277294
}
278295

296+
func testAccProjectIamPolicyEmptyMembers(pid, name, org string) string {
297+
return fmt.Sprintf(`
298+
resource "google_project" "acceptance" {
299+
project_id = "%s"
300+
name = "%s"
301+
org_id = "%s"
302+
}
303+
304+
resource "google_project_iam_policy" "acceptance" {
305+
project = "${google_project.acceptance.id}"
306+
policy_data = "${data.google_iam_policy.expanded.policy_data}"
307+
}
308+
309+
data "google_iam_policy" "expanded" {
310+
binding {
311+
role = "roles/viewer"
312+
members = []
313+
}
314+
}`, pid, name, org)
315+
}
316+
279317
func testAccProjectAssociatePolicyExpanded(pid, name, org string) string {
280318
return fmt.Sprintf(`
281319
resource "google_project" "acceptance" {

third_party/terraform/utils/iam.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,10 @@ func createIamBindingsMap(bindings []*cloudresourcemanager.Binding) map[string]m
193193
bm := make(map[string]map[string]struct{})
194194
// Get each binding
195195
for _, b := range bindings {
196+
members := make(map[string]struct{})
196197
// Initialize members map
197-
if _, ok := bm[b.Role]; !ok {
198-
bm[b.Role] = make(map[string]struct{})
198+
if _, ok := bm[b.Role]; ok {
199+
members = bm[b.Role]
199200
}
200201
// Get each member (user/principal) for the binding
201202
for _, m := range b.Members {
@@ -210,7 +211,12 @@ func createIamBindingsMap(bindings []*cloudresourcemanager.Binding) map[string]m
210211
m = strings.Join(pieces, ":")
211212

212213
// Add the member
213-
bm[b.Role][m] = struct{}{}
214+
members[m] = struct{}{}
215+
}
216+
if len(members) > 0 {
217+
bm[b.Role] = members
218+
} else {
219+
delete(bm, b.Role)
214220
}
215221
}
216222
return bm

third_party/terraform/utils/iam_test.go

+18
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,24 @@ func TestIamMergeBindings(t *testing.T) {
1717
input: []*cloudresourcemanager.Binding{},
1818
expect: []*cloudresourcemanager.Binding{},
1919
},
20+
// No members returns no binding
21+
{
22+
input: []*cloudresourcemanager.Binding{
23+
{
24+
Role: "role-1",
25+
},
26+
{
27+
Role: "role-2",
28+
Members: []string{"member-2"},
29+
},
30+
},
31+
expect: []*cloudresourcemanager.Binding{
32+
{
33+
Role: "role-2",
34+
Members: []string{"member-2"},
35+
},
36+
},
37+
},
2038
// Nothing to merge - return same list
2139
{
2240
input: []*cloudresourcemanager.Binding{

0 commit comments

Comments
 (0)