Skip to content

Commit a0180c9

Browse files
Add a custom diff function for org_policy rules to fix permadiff (#13068) (#9611)
[upstream:733d7d1b8ff4a1dea7630caa225f62f45f7f92fd] Signed-off-by: Modular Magician <[email protected]>
1 parent 1ceb86f commit a0180c9

File tree

3 files changed

+110
-6
lines changed

3 files changed

+110
-6
lines changed

.changelog/13068.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:bug
2+
orgpolicy: fix permadiff in `google_org_policy_policy` when multiple rules are present
3+
```

google-beta/services/orgpolicy/resource_org_policy_policy.go

+65-6
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,63 @@ func resourceOrgpolicyPolicyRulesConditionExpressionDiffSuppress(_, old, new str
4242
newReplaced := strings.ReplaceAll(strings.ReplaceAll(new, "Labels", "TagId"), "label", "tag")
4343
return oldReplaced == newReplaced
4444
}
45+
func resourceOrgPolicyPolicySpecRulesDiffSuppress(k, o, n string, d *schema.ResourceData) bool {
46+
oldCount, newCount := d.GetChange("spec.0.rules.#")
47+
var count int
48+
// There could be duplicates - worth continuing even if the counts are unequal.
49+
if oldCount.(int) < newCount.(int) {
50+
count = newCount.(int)
51+
} else {
52+
count = oldCount.(int)
53+
}
54+
55+
old := make([]interface{}, 0, count)
56+
new := make([]interface{}, 0, count)
57+
for i := 0; i < count; i++ {
58+
o, n := d.GetChange(fmt.Sprintf("spec.0.rules.%d", i))
59+
60+
if o != nil {
61+
old = append(old, o)
62+
}
63+
if n != nil {
64+
new = append(new, n)
65+
}
66+
}
67+
68+
oldSet := schema.NewSet(schema.HashResource(ResourceOrgPolicyPolicy().Schema["spec"].Elem.(*schema.Resource).Schema["rules"].Elem.(*schema.Resource)), old)
69+
newSet := schema.NewSet(schema.HashResource(ResourceOrgPolicyPolicy().Schema["spec"].Elem.(*schema.Resource).Schema["rules"].Elem.(*schema.Resource)), new)
70+
71+
return oldSet.Equal(newSet)
72+
}
73+
74+
func resourceOrgPolicyPolicyDryRunSpecRulesDiffSuppress(k, o, n string, d *schema.ResourceData) bool {
75+
oldCount, newCount := d.GetChange("dry_run_spec.0.rules.#")
76+
var count int
77+
// There could be duplicates - worth continuing even if the counts are unequal.
78+
if oldCount.(int) < newCount.(int) {
79+
count = newCount.(int)
80+
} else {
81+
count = oldCount.(int)
82+
}
83+
84+
old := make([]interface{}, 0, count)
85+
new := make([]interface{}, 0, count)
86+
for i := 0; i < count; i++ {
87+
o, n := d.GetChange(fmt.Sprintf("dry_run_spec.0.rules.%d", i))
88+
89+
if o != nil {
90+
old = append(old, o)
91+
}
92+
if n != nil {
93+
new = append(new, n)
94+
}
95+
}
96+
97+
oldSet := schema.NewSet(schema.HashResource(ResourceOrgPolicyPolicy().Schema["dry_run_spec"].Elem.(*schema.Resource).Schema["rules"].Elem.(*schema.Resource)), old)
98+
newSet := schema.NewSet(schema.HashResource(ResourceOrgPolicyPolicy().Schema["dry_run_spec"].Elem.(*schema.Resource).Schema["rules"].Elem.(*schema.Resource)), new)
99+
100+
return oldSet.Equal(newSet)
101+
}
45102

46103
func ResourceOrgPolicyPolicy() *schema.Resource {
47104
return &schema.Resource{
@@ -93,9 +150,10 @@ func ResourceOrgPolicyPolicy() *schema.Resource {
93150
Description: `Ignores policies set above this resource and restores the 'constraint_default' enforcement behavior of the specific constraint at this resource. This field can be set in policies for either list or boolean constraints. If set, 'rules' must be empty and 'inherit_from_parent' must be set to false.`,
94151
},
95152
"rules": {
96-
Type: schema.TypeList,
97-
Optional: true,
98-
Description: `In policies for boolean constraints, the following requirements apply: - There must be one and only one policy rule where condition is unset. - Boolean policy rules with conditions must set 'enforced' to the opposite of the policy rule without a condition. - During policy evaluation, policy rules with conditions that are true for a target resource take precedence.`,
153+
Type: schema.TypeList,
154+
Optional: true,
155+
DiffSuppressFunc: resourceOrgPolicyPolicyDryRunSpecRulesDiffSuppress,
156+
Description: `In policies for boolean constraints, the following requirements apply: - There must be one and only one policy rule where condition is unset. - Boolean policy rules with conditions must set 'enforced' to the opposite of the policy rule without a condition. - During policy evaluation, policy rules with conditions that are true for a target resource take precedence.`,
99157
Elem: &schema.Resource{
100158
Schema: map[string]*schema.Schema{
101159
"allow_all": {
@@ -211,9 +269,10 @@ func ResourceOrgPolicyPolicy() *schema.Resource {
211269
Description: `Ignores policies set above this resource and restores the 'constraint_default' enforcement behavior of the specific 'Constraint' at this resource. This field can be set in policies for either list or boolean constraints. If set, 'rules' must be empty and 'inherit_from_parent' must be set to false.`,
212270
},
213271
"rules": {
214-
Type: schema.TypeList,
215-
Optional: true,
216-
Description: `In Policies for boolean constraints, the following requirements apply: - There must be one and only one PolicyRule where condition is unset. - BooleanPolicyRules with conditions must set 'enforced' to the opposite of the PolicyRule without a condition. - During policy evaluation, PolicyRules with conditions that are true for a target resource take precedence.`,
272+
Type: schema.TypeList,
273+
Optional: true,
274+
DiffSuppressFunc: resourceOrgPolicyPolicySpecRulesDiffSuppress,
275+
Description: `In Policies for boolean constraints, the following requirements apply: - There must be one and only one PolicyRule where condition is unset. - BooleanPolicyRules with conditions must set 'enforced' to the opposite of the PolicyRule without a condition. - During policy evaluation, PolicyRules with conditions that are true for a target resource take precedence.`,
217276
Elem: &schema.Resource{
218277
Schema: map[string]*schema.Schema{
219278
"allow_all": {

google-beta/services/orgpolicy/resource_org_policy_policy_test.go

+42
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,15 @@ func TestAccOrgPolicyPolicy_ProjectPolicy(t *testing.T) {
140140
ImportStateVerify: true,
141141
ImportStateVerifyIgnore: []string{"name", "spec.0.rules.0.condition.0.expression"},
142142
},
143+
{
144+
Config: testAccOrgPolicyPolicy_ProjectPolicyUpdate1(context),
145+
},
146+
{
147+
ResourceName: "google_org_policy_policy.primary",
148+
ImportState: true,
149+
ImportStateVerify: true,
150+
ImportStateVerifyIgnore: []string{"name", "spec.0.rules.0.condition.0.expression"},
151+
},
143152
},
144153
})
145154
}
@@ -383,6 +392,39 @@ resource "google_project" "basic" {
383392
}
384393
385394
395+
`, context)
396+
}
397+
398+
func testAccOrgPolicyPolicy_ProjectPolicyUpdate1(context map[string]interface{}) string {
399+
return acctest.Nprintf(`
400+
resource "google_org_policy_policy" "primary" {
401+
name = "projects/${google_project.basic.name}/policies/gcp.resourceLocations"
402+
parent = "projects/${google_project.basic.name}"
403+
404+
spec {
405+
rules {
406+
deny_all = "TRUE"
407+
}
408+
rules {
409+
allow_all = "TRUE"
410+
condition {
411+
description = "A sample condition for the policy"
412+
expression = "resource.matchTagId('tagKeys/123', 'tagValues/345')"
413+
location = "sample-location.log"
414+
title = "sample-condition"
415+
}
416+
}
417+
}
418+
}
419+
420+
resource "google_project" "basic" {
421+
project_id = "tf-test-id%{random_suffix}"
422+
name = "tf-test-id%{random_suffix}"
423+
org_id = "%{org_id}"
424+
deletion_policy = "DELETE"
425+
}
426+
427+
386428
`, context)
387429
}
388430

0 commit comments

Comments
 (0)