Incremental improvements to project factory and underlying modules (#3325)

* incremental improvements to project factory and underlying modules

* fix org policies diff test
This commit is contained in:
Ludovico Magnocavallo
2025-09-16 23:21:18 +02:00
committed by GitHub
parent 9b3291c395
commit c6cdc9c98c
8 changed files with 221 additions and 28 deletions

View File

@@ -362,9 +362,12 @@ automation:
| [automation](variables-fast.tf#L17) | Automation resources created by the bootstrap stage. | <code title="object&#40;&#123;&#10; outputs_bucket &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-org-setup</code> |
| [billing_account](variables-fast.tf#L26) | Billing account id. | <code title="object&#40;&#123;&#10; id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-org-setup</code> |
| [prefix](variables-fast.tf#L92) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | <code>string</code> | ✓ | | <code>0-org-setup</code> |
| [context](variables.tf#L17) | Context-specific interpolations. | <code title="object&#40;&#123;&#10; custom_roles &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; folder_ids &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; iam_principals &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; kms_keys &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; locations &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; notification_channels &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; project_ids &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; tag_values &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; vpc_host_projects &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; vpc_sc_perimeters &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [context](variables.tf#L17) | Context-specific interpolations. | <code title="object&#40;&#123;&#10; condition_vars &#61; optional&#40;map&#40;map&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; custom_roles &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; folder_ids &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; iam_principals &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; kms_keys &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; locations &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; notification_channels &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; project_ids &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; tag_values &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; vpc_host_projects &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; vpc_sc_perimeters &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [custom_roles](variables-fast.tf#L34) | Custom roles defined at the org level, in key => id format. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | <code>0-org-setup</code> |
| [factories_config](variables.tf#L35) | Path to folder with YAML resource description data files. | <code title="object&#40;&#123;&#10; folders &#61; optional&#40;string, &#34;data&#47;folders&#34;&#41;&#10; projects &#61; optional&#40;string, &#34;data&#47;projects&#34;&#41;&#10; budgets &#61; optional&#40;object&#40;&#123;&#10; billing_account_id &#61; string&#10; data &#61; string&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [data_defaults](variables.tf#L36) | Optional default values used when corresponding project or folder data from files are missing. | <code title="object&#40;&#123;&#10; billing_account &#61; optional&#40;string&#41;&#10; bucket &#61; optional&#40;object&#40;&#123;&#10; force_destroy &#61; optional&#40;bool&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; contacts &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; deletion_policy &#61; optional&#40;string&#41;&#10; factories_config &#61; optional&#40;object&#40;&#123;&#10; custom_roles &#61; optional&#40;string&#41;&#10; observability &#61; optional&#40;string&#41;&#10; org_policies &#61; optional&#40;string&#41;&#10; quotas &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; metric_scopes &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; parent &#61; optional&#40;string&#41;&#10; prefix &#61; optional&#40;string&#41;&#10; project_reuse &#61; optional&#40;object&#40;&#123;&#10; use_data_source &#61; optional&#40;bool, true&#41;&#10; attributes &#61; optional&#40;object&#40;&#123;&#10; name &#61; string&#10; number &#61; number&#10; services_enabled &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;&#10; service_encryption_key_ids &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; services &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; shared_vpc_service_config &#61; optional&#40;object&#40;&#123;&#10; host_project &#61; string&#10; iam_bindings_additive &#61; optional&#40;map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10; network_users &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; service_agent_iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; service_agent_subnet_iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; service_iam_grants &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; network_subnet_users &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; &#125;&#41;&#41;&#10; storage_location &#61; optional&#40;string&#41;&#10; tag_bindings &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; service_accounts &#61; optional&#40;map&#40;object&#40;&#123;&#10; display_name &#61; optional&#40;string, &#34;Terraform-managed.&#34;&#41;&#10; iam_self_roles &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10; universe &#61; optional&#40;object&#40;&#123;&#10; prefix &#61; string&#10; unavailable_service_identities &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; unavailable_services &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;&#41;&#10; vpc_sc &#61; optional&#40;object&#40;&#123;&#10; perimeter_name &#61; string&#10; is_dry_run &#61; optional&#40;bool, false&#41;&#10; &#125;&#41;&#41;&#10; logging_data_access &#61; optional&#40;map&#40;object&#40;&#123;&#10; ADMIN_READ &#61; optional&#40;object&#40;&#123; exempted_members &#61; optional&#40;list&#40;string&#41;&#41; &#125;&#41;&#41;,&#10; DATA_READ &#61; optional&#40;object&#40;&#123; exempted_members &#61; optional&#40;list&#40;string&#41;&#41; &#125;&#41;&#41;,&#10; DATA_WRITE &#61; optional&#40;object&#40;&#123; exempted_members &#61; optional&#40;list&#40;string&#41;&#41; &#125;&#41;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [data_merges](variables.tf#L108) | Optional values that will be merged with corresponding data from files. Combines with `data_defaults`, file data, and `data_overrides`. | <code title="object&#40;&#123;&#10; contacts &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; metric_scopes &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; service_encryption_key_ids &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; services &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; tag_bindings &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; service_accounts &#61; optional&#40;map&#40;object&#40;&#123;&#10; display_name &#61; optional&#40;string, &#34;Terraform-managed.&#34;&#41;&#10; iam_self_roles &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [data_overrides](variables.tf#L127) | Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`. | <code title="object&#40;&#123;&#10; billing_account &#61; optional&#40;string&#41;&#10; bucket &#61; optional&#40;object&#40;&#123;&#10; force_destroy &#61; optional&#40;bool&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; contacts &#61; optional&#40;map&#40;list&#40;string&#41;&#41;&#41;&#10; deletion_policy &#61; optional&#40;string&#41;&#10; factories_config &#61; optional&#40;object&#40;&#123;&#10; custom_roles &#61; optional&#40;string&#41;&#10; observability &#61; optional&#40;string&#41;&#10; org_policies &#61; optional&#40;string&#41;&#10; quotas &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; parent &#61; optional&#40;string&#41;&#10; prefix &#61; optional&#40;string&#41;&#10; service_encryption_key_ids &#61; optional&#40;map&#40;list&#40;string&#41;&#41;&#41;&#10; storage_location &#61; optional&#40;string&#41;&#10; tag_bindings &#61; optional&#40;map&#40;string&#41;&#41;&#10; services &#61; optional&#40;list&#40;string&#41;&#41;&#10; service_accounts &#61; optional&#40;map&#40;object&#40;&#123;&#10; display_name &#61; optional&#40;string, &#34;Terraform-managed.&#34;&#41;&#10; iam_self_roles &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#41;&#10; universe &#61; optional&#40;object&#40;&#123;&#10; prefix &#61; string&#10; unavailable_service_identities &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; unavailable_services &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;&#41;&#10; vpc_sc &#61; optional&#40;object&#40;&#123;&#10; perimeter_name &#61; string&#10; is_dry_run &#61; optional&#40;bool, false&#41;&#10; &#125;&#41;&#41;&#10; logging_data_access &#61; optional&#40;map&#40;object&#40;&#123;&#10; ADMIN_READ &#61; optional&#40;object&#40;&#123; exempted_members &#61; optional&#40;list&#40;string&#41;&#41; &#125;&#41;&#41;,&#10; DATA_READ &#61; optional&#40;object&#40;&#123; exempted_members &#61; optional&#40;list&#40;string&#41;&#41; &#125;&#41;&#41;,&#10; DATA_WRITE &#61; optional&#40;object&#40;&#123; exempted_members &#61; optional&#40;list&#40;string&#41;&#41; &#125;&#41;&#41;&#10; &#125;&#41;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [factories_config](variables.tf#L173) | Path to folder with YAML resource description data files. | <code title="object&#40;&#123;&#10; folders &#61; optional&#40;string, &#34;data&#47;folders&#34;&#41;&#10; projects &#61; optional&#40;string, &#34;data&#47;projects&#34;&#41;&#10; budgets &#61; optional&#40;object&#40;&#123;&#10; billing_account_id &#61; string&#10; data &#61; string&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [folder_ids](variables-fast.tf#L42) | Folders created in the bootstrap stage. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | <code>0-org-setup</code> |
| [host_project_ids](variables-fast.tf#L58) | Host project for the shared VPC. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | <code>2-networking</code> |
| [iam_principals](variables-fast.tf#L50) | IAM-format principals. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | <code>0-org-setup</code> |
@@ -373,8 +376,9 @@ automation:
| [perimeters](variables-fast.tf#L84) | Optional VPC-SC perimeter ids. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | <code>1-vpcsc</code> |
| [project_ids](variables-fast.tf#L102) | Projects created in the bootstrap stage. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | <code>0-org-setup</code> |
| [service_accounts](variables-fast.tf#L110) | Service accounts created in the bootstrap stage. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | <code>0-org-setup</code> |
| [stage_name](variables.tf#L55) | FAST stage name. Used to separate output files across different factories. | <code>string</code> | | <code>&#34;2-project-factory&#34;</code> | |
| [tag_values](variables-fast.tf#L118) | FAST-managed resource manager tag values. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | <code>0-org-setup</code> |
| [stage_name](variables.tf#L193) | FAST stage name. Used to separate output files across different factories. | <code>string</code> | | <code>&#34;2-project-factory&#34;</code> | |
| [subnet_self_links](variables-fast.tf#L118) | Shared VPC subnet IDs. | <code>map&#40;map&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | <code>2-networking</code> |
| [tag_values](variables-fast.tf#L126) | FAST-managed resource manager tag values. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | <code>0-org-setup</code> |
## Outputs

View File

@@ -16,9 +16,25 @@
# tfdoc:file:description Project factory.
locals {
subnet_self_links = flatten([
for net, subnets in var.subnet_self_links : [
for subnet_name, subnet_link in subnets : {
key = "${net}/${subnet_name}"
link = subnet_link
}
]
])
}
module "factory" {
source = "../../../modules/project-factory"
context = {
condition_vars = merge({
subnet_self_links = {
for v in local.subnet_self_links : v.key => v.link
}
}, var.context.condition_vars)
custom_roles = merge(
var.custom_roles, var.context.custom_roles
)
@@ -50,20 +66,23 @@ module "factory" {
var.perimeters, var.context.vpc_sc_perimeters
)
}
data_defaults = {
# more defaults are available, check the project factory variables
billing_account = var.billing_account.id
storage_location = var.locations.storage
}
data_merges = {
services = [
data_defaults = merge(var.data_defaults, {
billing_account = coalesce(
var.data_defaults.billing_account, var.billing_account.id
)
prefix = coalesce(var.data_defaults.prefix, var.prefix)
storage_location = coalesce(
var.data_defaults.storage_location, var.locations.storage
)
})
data_merges = merge(var.data_merges, {
services = length(var.data_merges.services) > 0 ? var.data_merges.services : [
"logging.googleapis.com",
"monitoring.googleapis.com"
]
}
data_overrides = {
prefix = var.prefix
}
}
)
data_overrides = var.data_overrides
factories_config = merge(var.factories_config, {
budgets = {
billing_account_id = try(

View File

@@ -115,6 +115,14 @@ variable "service_accounts" {
default = {}
}
variable "subnet_self_links" {
# tfdoc:variable:source 2-networking
description = "Shared VPC subnet IDs."
type = map(map(string))
nullable = false
default = {}
}
variable "tag_values" {
# tfdoc:variable:source 0-org-setup
description = "FAST-managed resource manager tag values."

View File

@@ -17,6 +17,7 @@
variable "context" {
description = "Context-specific interpolations."
type = object({
condition_vars = optional(map(map(string)), {})
custom_roles = optional(map(string), {})
folder_ids = optional(map(string), {})
iam_principals = optional(map(string), {})
@@ -32,6 +33,143 @@ variable "context" {
nullable = false
}
variable "data_defaults" {
description = "Optional default values used when corresponding project or folder data from files are missing."
type = object({
billing_account = optional(string)
bucket = optional(object({
force_destroy = optional(bool)
}), {})
contacts = optional(map(list(string)), {})
deletion_policy = optional(string)
factories_config = optional(object({
custom_roles = optional(string)
observability = optional(string)
org_policies = optional(string)
quotas = optional(string)
}), {})
labels = optional(map(string), {})
metric_scopes = optional(list(string), [])
parent = optional(string)
prefix = optional(string)
project_reuse = optional(object({
use_data_source = optional(bool, true)
attributes = optional(object({
name = string
number = number
services_enabled = optional(list(string), [])
}))
}))
service_encryption_key_ids = optional(map(list(string)), {})
services = optional(list(string), [])
shared_vpc_service_config = optional(object({
host_project = string
iam_bindings_additive = optional(map(object({
member = string
role = string
condition = optional(object({
expression = string
title = string
description = optional(string)
}))
})), {})
network_users = optional(list(string), [])
service_agent_iam = optional(map(list(string)), {})
service_agent_subnet_iam = optional(map(list(string)), {})
service_iam_grants = optional(list(string), [])
network_subnet_users = optional(map(list(string)), {})
}))
storage_location = optional(string)
tag_bindings = optional(map(string), {})
# non-project resources
service_accounts = optional(map(object({
display_name = optional(string, "Terraform-managed.")
iam_self_roles = optional(list(string))
})), {})
universe = optional(object({
prefix = string
unavailable_service_identities = optional(list(string), [])
unavailable_services = optional(list(string), [])
}))
vpc_sc = optional(object({
perimeter_name = string
is_dry_run = optional(bool, false)
}))
logging_data_access = optional(map(object({
ADMIN_READ = optional(object({ exempted_members = optional(list(string)) })),
DATA_READ = optional(object({ exempted_members = optional(list(string)) })),
DATA_WRITE = optional(object({ exempted_members = optional(list(string)) }))
})), {})
})
nullable = false
default = {}
}
variable "data_merges" {
description = "Optional values that will be merged with corresponding data from files. Combines with `data_defaults`, file data, and `data_overrides`."
type = object({
contacts = optional(map(list(string)), {})
labels = optional(map(string), {})
metric_scopes = optional(list(string), [])
service_encryption_key_ids = optional(map(list(string)), {})
services = optional(list(string), [])
tag_bindings = optional(map(string), {})
# non-project resources
service_accounts = optional(map(object({
display_name = optional(string, "Terraform-managed.")
iam_self_roles = optional(list(string))
})), {})
})
nullable = false
default = {}
}
variable "data_overrides" {
description = "Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`."
type = object({
# data overrides default to null to mark that they should not override
billing_account = optional(string)
bucket = optional(object({
force_destroy = optional(bool)
}), {})
contacts = optional(map(list(string)))
deletion_policy = optional(string)
factories_config = optional(object({
custom_roles = optional(string)
observability = optional(string)
org_policies = optional(string)
quotas = optional(string)
}), {})
parent = optional(string)
prefix = optional(string)
service_encryption_key_ids = optional(map(list(string)))
storage_location = optional(string)
tag_bindings = optional(map(string))
services = optional(list(string))
# non-project resources
service_accounts = optional(map(object({
display_name = optional(string, "Terraform-managed.")
iam_self_roles = optional(list(string))
})))
universe = optional(object({
prefix = string
unavailable_service_identities = optional(list(string), [])
unavailable_services = optional(list(string), [])
}))
vpc_sc = optional(object({
perimeter_name = string
is_dry_run = optional(bool, false)
}))
logging_data_access = optional(map(object({
ADMIN_READ = optional(object({ exempted_members = optional(list(string)) })),
DATA_READ = optional(object({ exempted_members = optional(list(string)) })),
DATA_WRITE = optional(object({ exempted_members = optional(list(string)) }))
})))
})
nullable = false
default = {}
}
variable "factories_config" {
description = "Path to folder with YAML resource description data files."
type = object({

View File

@@ -138,8 +138,12 @@ resource "google_org_policy_policy" "default" {
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)
allowed_values = try(rule.value.allow.values, null) == null ? null : [
for v in rule.value.allow.values : templatestring(v, var.context.condition_vars)
]
denied_values = try(rule.value.deny.values, null) == null ? null : [
for v in rule.value.deny.values : templatestring(v, var.context.condition_vars)
]
}
}
}
@@ -177,8 +181,12 @@ resource "google_org_policy_policy" "default" {
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)
allowed_values = try(rule.value.allow.values, null) == null ? null : [
for v in rule.value.allow.values : templatestring(v, var.context.condition_vars)
]
denied_values = try(rule.value.deny.values, null) == null ? null : [
for v in rule.value.deny.values : templatestring(v, var.context.condition_vars)
]
}
}
}

View File

@@ -138,8 +138,12 @@ resource "google_org_policy_policy" "default" {
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)
allowed_values = try(rule.value.allow.values, null) == null ? null : [
for v in rule.value.allow.values : templatestring(v, var.context.condition_vars)
]
denied_values = try(rule.value.deny.values, null) == null ? null : [
for v in rule.value.deny.values : templatestring(v, var.context.condition_vars)
]
}
}
}
@@ -177,8 +181,12 @@ resource "google_org_policy_policy" "default" {
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)
allowed_values = try(rule.value.allow.values, null) == null ? null : [
for v in rule.value.allow.values : templatestring(v, var.context.condition_vars)
]
denied_values = try(rule.value.deny.values, null) == null ? null : [
for v in rule.value.deny.values : templatestring(v, var.context.condition_vars)
]
}
}
}

View File

@@ -138,8 +138,12 @@ resource "google_org_policy_policy" "default" {
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)
allowed_values = try(rule.value.allow.values, null) == null ? null : [
for v in rule.value.allow.values : templatestring(v, var.context.condition_vars)
]
denied_values = try(rule.value.deny.values, null) == null ? null : [
for v in rule.value.deny.values : templatestring(v, var.context.condition_vars)
]
}
}
}
@@ -177,8 +181,12 @@ resource "google_org_policy_policy" "default" {
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)
allowed_values = try(rule.value.allow.values, null) == null ? null : [
for v in rule.value.allow.values : templatestring(v, var.context.condition_vars)
]
denied_values = try(rule.value.deny.values, null) == null ? null : [
for v in rule.value.deny.values : templatestring(v, var.context.condition_vars)
]
}
}
}

View File

@@ -54,7 +54,7 @@ def test_policy_implementation():
'- parent = local.folder_id\n',
'+ name = "${var.organization_id}/policies/${each.value}"\n',
'+ parent = var.organization_id\n',
'@@ -187,0 +188,9 @@\n',
'@@ -195,0 +196,9 @@\n',
'+ depends_on = [\n',
'+ google_organization_iam_binding.authoritative,\n',
'+ google_organization_iam_binding.bindings,\n',