Add support for dry-run org policies (#2454)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2023 Google LLC
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -52,8 +52,6 @@ locals {
|
||||
org_policies = {
|
||||
for k, v in local._org_policies :
|
||||
k => merge(v, {
|
||||
name = "${local.folder_id}/policies/${k}"
|
||||
parent = local.folder_id
|
||||
is_boolean_policy = (
|
||||
alltrue([for r in v.rules : r.allow == null && r.deny == null])
|
||||
)
|
||||
@@ -75,37 +73,81 @@ locals {
|
||||
}
|
||||
|
||||
resource "google_org_policy_policy" "default" {
|
||||
for_each = local.org_policies
|
||||
name = each.value.name
|
||||
parent = each.value.parent
|
||||
spec {
|
||||
inherit_from_parent = each.value.inherit_from_parent
|
||||
reset = each.value.reset
|
||||
dynamic "rules" {
|
||||
for_each = each.value.rules
|
||||
iterator = rule
|
||||
content {
|
||||
allow_all = try(rule.value.allow.all, false) == true ? "TRUE" : null
|
||||
deny_all = try(rule.value.deny.all, false) == true ? "TRUE" : null
|
||||
enforce = (
|
||||
each.value.is_boolean_policy && rule.value.enforce != null
|
||||
? upper(tostring(rule.value.enforce))
|
||||
: null
|
||||
)
|
||||
dynamic "condition" {
|
||||
for_each = rule.value.condition.expression != null ? [1] : []
|
||||
content {
|
||||
description = rule.value.condition.description
|
||||
expression = rule.value.condition.expression
|
||||
location = rule.value.condition.location
|
||||
title = rule.value.condition.title
|
||||
for_each = toset([
|
||||
for k, v in local._org_policies : trimprefix(k, "dry_run:")
|
||||
])
|
||||
name = "${local.folder_id}/policies/${each.value}"
|
||||
parent = local.folder_id
|
||||
dynamic "spec" {
|
||||
for_each = lookup(local.org_policies, each.value, null) != null ? [local.org_policies[each.value]] : []
|
||||
iterator = spec
|
||||
content {
|
||||
inherit_from_parent = spec.value.inherit_from_parent
|
||||
reset = spec.value.reset
|
||||
dynamic "rules" {
|
||||
for_each = spec.value.rules
|
||||
iterator = rule
|
||||
content {
|
||||
allow_all = try(rule.value.allow.all, false) == true ? "TRUE" : null
|
||||
deny_all = try(rule.value.deny.all, false) == true ? "TRUE" : null
|
||||
enforce = (
|
||||
spec.value.is_boolean_policy && rule.value.enforce != null
|
||||
? upper(tostring(rule.value.enforce))
|
||||
: null
|
||||
)
|
||||
dynamic "condition" {
|
||||
for_each = rule.value.condition.expression != null ? [1] : []
|
||||
content {
|
||||
description = rule.value.condition.description
|
||||
expression = rule.value.condition.expression
|
||||
location = rule.value.condition.location
|
||||
title = rule.value.condition.title
|
||||
}
|
||||
}
|
||||
dynamic "values" {
|
||||
for_each = rule.value.has_values ? [1] : []
|
||||
content {
|
||||
allowed_values = try(rule.value.allow.values, null)
|
||||
denied_values = try(rule.value.deny.values, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
dynamic "values" {
|
||||
for_each = rule.value.has_values ? [1] : []
|
||||
content {
|
||||
allowed_values = try(rule.value.allow.values, null)
|
||||
denied_values = try(rule.value.deny.values, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "dry_run_spec" {
|
||||
for_each = lookup(local.org_policies, "dry_run:${each.value}", null) != null ? [local.org_policies["dry_run:${each.value}"]] : []
|
||||
iterator = spec
|
||||
content {
|
||||
inherit_from_parent = spec.value.inherit_from_parent
|
||||
reset = spec.value.reset
|
||||
dynamic "rules" {
|
||||
for_each = spec.value.rules
|
||||
iterator = rule
|
||||
content {
|
||||
allow_all = try(rule.value.allow.all, false) == true ? "TRUE" : null
|
||||
deny_all = try(rule.value.deny.all, false) == true ? "TRUE" : null
|
||||
enforce = (
|
||||
spec.value.is_boolean_policy && rule.value.enforce != null
|
||||
? upper(tostring(rule.value.enforce))
|
||||
: null
|
||||
)
|
||||
dynamic "condition" {
|
||||
for_each = rule.value.condition.expression != null ? [1] : []
|
||||
content {
|
||||
description = rule.value.condition.description
|
||||
expression = rule.value.condition.expression
|
||||
location = rule.value.condition.location
|
||||
title = rule.value.condition.title
|
||||
}
|
||||
}
|
||||
dynamic "values" {
|
||||
for_each = rule.value.has_values ? [1] : []
|
||||
content {
|
||||
allowed_values = try(rule.value.allow.values, null)
|
||||
denied_values = try(rule.value.deny.values, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2023 Google LLC
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -52,8 +52,6 @@ locals {
|
||||
org_policies = {
|
||||
for k, v in local._org_policies :
|
||||
k => merge(v, {
|
||||
name = "${var.organization_id}/policies/${k}"
|
||||
parent = var.organization_id
|
||||
is_boolean_policy = (
|
||||
alltrue([for r in v.rules : r.allow == null && r.deny == null])
|
||||
)
|
||||
@@ -75,37 +73,81 @@ locals {
|
||||
}
|
||||
|
||||
resource "google_org_policy_policy" "default" {
|
||||
for_each = local.org_policies
|
||||
name = each.value.name
|
||||
parent = each.value.parent
|
||||
spec {
|
||||
inherit_from_parent = each.value.inherit_from_parent
|
||||
reset = each.value.reset
|
||||
dynamic "rules" {
|
||||
for_each = each.value.rules
|
||||
iterator = rule
|
||||
content {
|
||||
allow_all = try(rule.value.allow.all, false) == true ? "TRUE" : null
|
||||
deny_all = try(rule.value.deny.all, false) == true ? "TRUE" : null
|
||||
enforce = (
|
||||
each.value.is_boolean_policy && rule.value.enforce != null
|
||||
? upper(tostring(rule.value.enforce))
|
||||
: null
|
||||
)
|
||||
dynamic "condition" {
|
||||
for_each = rule.value.condition.expression != null ? [1] : []
|
||||
content {
|
||||
description = rule.value.condition.description
|
||||
expression = rule.value.condition.expression
|
||||
location = rule.value.condition.location
|
||||
title = rule.value.condition.title
|
||||
for_each = toset([
|
||||
for k, v in local._org_policies : trimprefix(k, "dry_run:")
|
||||
])
|
||||
name = "${var.organization_id}/policies/${each.value}"
|
||||
parent = var.organization_id
|
||||
dynamic "spec" {
|
||||
for_each = lookup(local.org_policies, each.value, null) != null ? [local.org_policies[each.value]] : []
|
||||
iterator = spec
|
||||
content {
|
||||
inherit_from_parent = spec.value.inherit_from_parent
|
||||
reset = spec.value.reset
|
||||
dynamic "rules" {
|
||||
for_each = spec.value.rules
|
||||
iterator = rule
|
||||
content {
|
||||
allow_all = try(rule.value.allow.all, false) == true ? "TRUE" : null
|
||||
deny_all = try(rule.value.deny.all, false) == true ? "TRUE" : null
|
||||
enforce = (
|
||||
spec.value.is_boolean_policy && rule.value.enforce != null
|
||||
? upper(tostring(rule.value.enforce))
|
||||
: null
|
||||
)
|
||||
dynamic "condition" {
|
||||
for_each = rule.value.condition.expression != null ? [1] : []
|
||||
content {
|
||||
description = rule.value.condition.description
|
||||
expression = rule.value.condition.expression
|
||||
location = rule.value.condition.location
|
||||
title = rule.value.condition.title
|
||||
}
|
||||
}
|
||||
dynamic "values" {
|
||||
for_each = rule.value.has_values ? [1] : []
|
||||
content {
|
||||
allowed_values = try(rule.value.allow.values, null)
|
||||
denied_values = try(rule.value.deny.values, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
dynamic "values" {
|
||||
for_each = rule.value.has_values ? [1] : []
|
||||
content {
|
||||
allowed_values = try(rule.value.allow.values, null)
|
||||
denied_values = try(rule.value.deny.values, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "dry_run_spec" {
|
||||
for_each = lookup(local.org_policies, "dry_run:${each.value}", null) != null ? [local.org_policies["dry_run:${each.value}"]] : []
|
||||
iterator = spec
|
||||
content {
|
||||
inherit_from_parent = spec.value.inherit_from_parent
|
||||
reset = spec.value.reset
|
||||
dynamic "rules" {
|
||||
for_each = spec.value.rules
|
||||
iterator = rule
|
||||
content {
|
||||
allow_all = try(rule.value.allow.all, false) == true ? "TRUE" : null
|
||||
deny_all = try(rule.value.deny.all, false) == true ? "TRUE" : null
|
||||
enforce = (
|
||||
spec.value.is_boolean_policy && rule.value.enforce != null
|
||||
? upper(tostring(rule.value.enforce))
|
||||
: null
|
||||
)
|
||||
dynamic "condition" {
|
||||
for_each = rule.value.condition.expression != null ? [1] : []
|
||||
content {
|
||||
description = rule.value.condition.description
|
||||
expression = rule.value.condition.expression
|
||||
location = rule.value.condition.location
|
||||
title = rule.value.condition.title
|
||||
}
|
||||
}
|
||||
dynamic "values" {
|
||||
for_each = rule.value.has_values ? [1] : []
|
||||
content {
|
||||
allowed_values = try(rule.value.allow.values, null)
|
||||
denied_values = try(rule.value.deny.values, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ This module implements the creation and management of one GCP project including
|
||||
- [Shared VPC](#shared-vpc)
|
||||
- [Organization Policies](#organization-policies)
|
||||
- [Organization Policy Factory](#organization-policy-factory)
|
||||
- [Dry-Run Mode](#dry-run-mode)
|
||||
- [Log Sinks](#log-sinks)
|
||||
- [Data Access Logs](#data-access-logs)
|
||||
- [Cloud KMS Encryption Keys](#cloud-kms-encryption-keys)
|
||||
@@ -590,6 +591,27 @@ iam.allowedPolicyMemberDomains:
|
||||
- C0yyyyyyy
|
||||
```
|
||||
|
||||
### Dry-Run Mode
|
||||
|
||||
To enable dry-run mode, add the `dry_run:` prefix to the constraint name in your Terraform configuration:
|
||||
|
||||
```hcl
|
||||
module "project" {
|
||||
source = "./fabric/modules/project"
|
||||
name = "project"
|
||||
parent = var.folder_id
|
||||
org_policies = {
|
||||
"gcp.restrictTLSVersion" = {
|
||||
rules = [{ deny = { values = ["TLS_VERSION_1"] } }]
|
||||
}
|
||||
"dry_run:gcp.restrictTLSVersion" = {
|
||||
rules = [{ deny = { values = ["TLS_VERSION_1", "TLS_VERSION_1_1"] } }]
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=2 inventory=org-policies-dry-run.yaml
|
||||
```
|
||||
|
||||
## Log Sinks
|
||||
|
||||
```hcl
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2023 Google LLC
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -52,8 +52,6 @@ locals {
|
||||
org_policies = {
|
||||
for k, v in local._org_policies :
|
||||
k => merge(v, {
|
||||
name = "projects/${local.project.project_id}/policies/${k}"
|
||||
parent = "projects/${local.project.project_id}"
|
||||
is_boolean_policy = (
|
||||
alltrue([for r in v.rules : r.allow == null && r.deny == null])
|
||||
)
|
||||
@@ -75,37 +73,81 @@ locals {
|
||||
}
|
||||
|
||||
resource "google_org_policy_policy" "default" {
|
||||
for_each = local.org_policies
|
||||
name = each.value.name
|
||||
parent = each.value.parent
|
||||
spec {
|
||||
inherit_from_parent = each.value.inherit_from_parent
|
||||
reset = each.value.reset
|
||||
dynamic "rules" {
|
||||
for_each = each.value.rules
|
||||
iterator = rule
|
||||
content {
|
||||
allow_all = try(rule.value.allow.all, false) == true ? "TRUE" : null
|
||||
deny_all = try(rule.value.deny.all, false) == true ? "TRUE" : null
|
||||
enforce = (
|
||||
each.value.is_boolean_policy && rule.value.enforce != null
|
||||
? upper(tostring(rule.value.enforce))
|
||||
: null
|
||||
)
|
||||
dynamic "condition" {
|
||||
for_each = rule.value.condition.expression != null ? [1] : []
|
||||
content {
|
||||
description = rule.value.condition.description
|
||||
expression = rule.value.condition.expression
|
||||
location = rule.value.condition.location
|
||||
title = rule.value.condition.title
|
||||
for_each = toset([
|
||||
for k, v in local._org_policies : trimprefix(k, "dry_run:")
|
||||
])
|
||||
name = "projects/${local.project.project_id}/policies/${each.value}"
|
||||
parent = "projects/${local.project.project_id}"
|
||||
dynamic "spec" {
|
||||
for_each = lookup(local.org_policies, each.value, null) != null ? [local.org_policies[each.value]] : []
|
||||
iterator = spec
|
||||
content {
|
||||
inherit_from_parent = spec.value.inherit_from_parent
|
||||
reset = spec.value.reset
|
||||
dynamic "rules" {
|
||||
for_each = spec.value.rules
|
||||
iterator = rule
|
||||
content {
|
||||
allow_all = try(rule.value.allow.all, false) == true ? "TRUE" : null
|
||||
deny_all = try(rule.value.deny.all, false) == true ? "TRUE" : null
|
||||
enforce = (
|
||||
spec.value.is_boolean_policy && rule.value.enforce != null
|
||||
? upper(tostring(rule.value.enforce))
|
||||
: null
|
||||
)
|
||||
dynamic "condition" {
|
||||
for_each = rule.value.condition.expression != null ? [1] : []
|
||||
content {
|
||||
description = rule.value.condition.description
|
||||
expression = rule.value.condition.expression
|
||||
location = rule.value.condition.location
|
||||
title = rule.value.condition.title
|
||||
}
|
||||
}
|
||||
dynamic "values" {
|
||||
for_each = rule.value.has_values ? [1] : []
|
||||
content {
|
||||
allowed_values = try(rule.value.allow.values, null)
|
||||
denied_values = try(rule.value.deny.values, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
dynamic "values" {
|
||||
for_each = rule.value.has_values ? [1] : []
|
||||
content {
|
||||
allowed_values = try(rule.value.allow.values, null)
|
||||
denied_values = try(rule.value.deny.values, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "dry_run_spec" {
|
||||
for_each = lookup(local.org_policies, "dry_run:${each.value}", null) != null ? [local.org_policies["dry_run:${each.value}"]] : []
|
||||
iterator = spec
|
||||
content {
|
||||
inherit_from_parent = spec.value.inherit_from_parent
|
||||
reset = spec.value.reset
|
||||
dynamic "rules" {
|
||||
for_each = spec.value.rules
|
||||
iterator = rule
|
||||
content {
|
||||
allow_all = try(rule.value.allow.all, false) == true ? "TRUE" : null
|
||||
deny_all = try(rule.value.deny.all, false) == true ? "TRUE" : null
|
||||
enforce = (
|
||||
spec.value.is_boolean_policy && rule.value.enforce != null
|
||||
? upper(tostring(rule.value.enforce))
|
||||
: null
|
||||
)
|
||||
dynamic "condition" {
|
||||
for_each = rule.value.condition.expression != null ? [1] : []
|
||||
content {
|
||||
description = rule.value.condition.description
|
||||
expression = rule.value.condition.expression
|
||||
location = rule.value.condition.location
|
||||
title = rule.value.condition.title
|
||||
}
|
||||
}
|
||||
dynamic "values" {
|
||||
for_each = rule.value.has_values ? [1] : []
|
||||
content {
|
||||
allowed_values = try(rule.value.allow.values, null)
|
||||
denied_values = try(rule.value.deny.values, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright 2023 Google LLC
|
||||
# Copyright 2024 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -34,11 +34,11 @@ def test_policy_implementation():
|
||||
'@@ -17 +17 @@\n',
|
||||
'-# tfdoc:file:description Project-level organization policies.\n',
|
||||
'+# tfdoc:file:description Folder-level organization policies.\n',
|
||||
'@@ -55,2 +55,2 @@\n',
|
||||
'- name = "projects/${local.project.project_id}/policies/${k}"\n',
|
||||
'- parent = "projects/${local.project.project_id}"\n',
|
||||
'+ name = "${local.folder_id}/policies/${k}"\n',
|
||||
'+ parent = local.folder_id\n',
|
||||
'@@ -79,2 +79,2 @@\n',
|
||||
'- name = "projects/${local.project.project_id}/policies/${each.value}"\n',
|
||||
'- parent = "projects/${local.project.project_id}"\n',
|
||||
'+ name = "${local.folder_id}/policies/${each.value}"\n',
|
||||
'+ parent = local.folder_id\n',
|
||||
]
|
||||
|
||||
diff2 = difflib.unified_diff(lines['folder'], lines['organization'], 'folder',
|
||||
@@ -49,12 +49,12 @@ def test_policy_implementation():
|
||||
'@@ -17 +17 @@\n',
|
||||
'-# tfdoc:file:description Folder-level organization policies.\n',
|
||||
'+# tfdoc:file:description Organization-level organization policies.\n',
|
||||
'@@ -55,2 +55,2 @@\n',
|
||||
'- name = "${local.folder_id}/policies/${k}"\n',
|
||||
'- parent = local.folder_id\n',
|
||||
'+ name = "${var.organization_id}/policies/${k}"\n',
|
||||
'+ parent = var.organization_id\n',
|
||||
'@@ -113,0 +114,9 @@\n',
|
||||
'@@ -79,2 +79,2 @@\n',
|
||||
'- name = "${local.folder_id}/policies/${each.value}"\n',
|
||||
'- parent = local.folder_id\n',
|
||||
'+ name = "${var.organization_id}/policies/${each.value}"\n',
|
||||
'+ parent = var.organization_id\n',
|
||||
'@@ -155,0 +156,9 @@\n',
|
||||
'+ depends_on = [\n',
|
||||
'+ google_organization_iam_binding.authoritative,\n',
|
||||
'+ google_organization_iam_binding.bindings,\n',
|
||||
|
||||
62
tests/modules/project/examples/org-policies-dry-run.yaml
Normal file
62
tests/modules/project/examples/org-policies-dry-run.yaml
Normal file
@@ -0,0 +1,62 @@
|
||||
# Copyright 2024 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
values:
|
||||
module.project.google_org_policy_policy.default["gcp.restrictTLSVersion"]:
|
||||
dry_run_spec:
|
||||
- inherit_from_parent: null
|
||||
reset: null
|
||||
rules:
|
||||
- allow_all: null
|
||||
condition: []
|
||||
deny_all: null
|
||||
enforce: null
|
||||
values:
|
||||
- allowed_values: null
|
||||
denied_values:
|
||||
- TLS_VERSION_1
|
||||
- TLS_VERSION_1_1
|
||||
name: projects/project/policies/gcp.restrictTLSVersion
|
||||
parent: projects/project
|
||||
spec:
|
||||
- inherit_from_parent: null
|
||||
reset: null
|
||||
rules:
|
||||
- allow_all: null
|
||||
condition: []
|
||||
deny_all: null
|
||||
enforce: null
|
||||
values:
|
||||
- allowed_values: null
|
||||
denied_values:
|
||||
- TLS_VERSION_1
|
||||
timeouts: null
|
||||
module.project.google_project.project[0]:
|
||||
auto_create_network: false
|
||||
billing_account: null
|
||||
folder_id: '1122334455'
|
||||
labels: null
|
||||
name: project
|
||||
org_id: null
|
||||
project_id: project
|
||||
skip_delete: false
|
||||
timeouts: null
|
||||
|
||||
counts:
|
||||
google_org_policy_policy: 1
|
||||
google_project: 1
|
||||
modules: 1
|
||||
resources: 2
|
||||
|
||||
outputs: {}
|
||||
Reference in New Issue
Block a user