Support service_agents_config.skip_iam in project-factory and fast stages (#4007)

* Support service_agents_config.skip_iam in project-factory and fast stages

* Fix inventories

* Change service-agent creation/iam order
This commit is contained in:
Julio Castillo
2026-06-01 12:04:54 +02:00
committed by GitHub
parent e3e261442f
commit 008a3719ad
22 changed files with 303 additions and 37 deletions

View File

@@ -898,11 +898,11 @@ compute.disableSerialPortAccess:
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [factories_config](variables.tf#L194) | Path to folder with YAML resource description data files. Exclusions match the start of file paths, relative to their containing folder. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | |
| [factories_config](variables.tf#L200) | Path to folder with YAML resource description data files. Exclusions match the start of file paths, relative to their containing folder. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | |
| [context](variables.tf#L17) | Context-specific interpolations. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [data_defaults](variables.tf#L47) | Optional default values used when corresponding project or folder data from files are missing. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [data_merges](variables.tf#L124) | Optional values that will be merged with corresponding data from files. Combines with `data_defaults`, file data, and `data_overrides`. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [data_overrides](variables.tf#L143) | Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [data_merges](variables.tf#L130) | Optional values that will be merged with corresponding data from files. Combines with `data_defaults`, file data, and `data_overrides`. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [data_overrides](variables.tf#L149) | Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [folders](variables-folders.tf#L17) | Folders data merged with factory data. | <code>map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [notification_channels](variables-billing.tf#L17) | Notification channels used by budget alerts. | <code>map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [projects](variables-projects.tf#L17) | Projects data merged with factory data. | <code>map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |

View File

@@ -158,6 +158,19 @@ locals {
try(v.service_encryption_key_ids, null),
local.data_defaults.defaults.service_encryption_key_ids
)
service_agents_config = (
try(v.service_agents_config, null) != null
? merge(
{
create_primary_agents = true
grant_default_roles = true
grant_service_agent_editor = true
skip_iam = []
},
v.service_agents_config
)
: local.data_defaults.defaults.service_agents_config
)
services = coalesce( # type: list(string)
local.data_defaults.overrides.services,
try(v.services, null),
@@ -291,6 +304,15 @@ locals {
}
)
)
service_agents_config = merge(
{
create_primary_agents = true
grant_default_roles = true
grant_service_agent_editor = true
skip_iam = []
},
try(local._data_defaults.defaults.service_agents_config, {})
)
service_encryption_key_ids = {}
services = []
shared_vpc_service_config = {

View File

@@ -162,6 +162,16 @@ module "projects" {
logging_sinks = try(each.value.logging_sinks, {})
notification_channels = try(each.value.notification_channels, null)
quotas = each.value.quotas
# Most service agent permissions must be granted in this first pass
# to ensure dependencies (like CMEK or Shared VPC) work correctly.
# We disable grant_service_agent_editor here because the authoritative
# IAM editor role is managed in the second pass (projects-iam).
service_agents_config = {
create_primary_agents = each.value.service_agents_config.create_primary_agents
grant_default_roles = each.value.service_agents_config.grant_default_roles
grant_service_agent_editor = false
skip_iam = each.value.service_agents_config.skip_iam
}
services = distinct(concat(
each.value.services,
var.data_merges.services
@@ -243,9 +253,13 @@ module "projects-iam" {
each.value.metric_scopes, var.data_merges.metric_scopes
))
pam_entitlements = try(each.value.pam_entitlements, {})
# The second pass handles the authoritative cloudservices editor binding.
# We disable primary agents creation and default roles here because they
# are already handled in the first pass, avoiding duplicate resource errors.
service_agents_config = {
create_primary_agents = false
grant_default_roles = false
create_primary_agents = false
grant_default_roles = false
grant_service_agent_editor = each.value.service_agents_config.grant_service_agent_editor
}
service_encryption_key_ids = merge(
each.value.service_encryption_key_ids,

View File

@@ -819,6 +819,27 @@
}
}
},
"service_agents_config": {
"type": "object",
"additionalProperties": false,
"properties": {
"create_primary_agents": {
"type": "boolean"
},
"grant_default_roles": {
"type": "boolean"
},
"grant_service_agent_editor": {
"type": "boolean"
},
"skip_iam": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"service_encryption_key_ids": {
"type": "object",
"additionalProperties": false,

View File

@@ -253,6 +253,13 @@
- **iam_project_roles**: *reference([iam_project_roles](#refs-iam_project_roles))*
- **iam_sa_roles**: *reference([iam_sa_roles](#refs-iam_sa_roles))*
- **tag_bindings**: *reference([tag_bindings](#refs-tag_bindings))*
- **service_agents_config**: *object*
<br>*additional properties: false*
- **create_primary_agents**: *boolean*
- **grant_default_roles**: *boolean*
- **grant_service_agent_editor**: *boolean*
- **skip_iam**: *array*
- items: *string*
- **service_encryption_key_ids**: *object*
<br>*additional properties: false*
- **`^[a-z-]+\.googleapis\.com$`**: *array*

View File

@@ -490,6 +490,12 @@ variable "projects" {
iam_self_roles = optional(list(string), [])
iam_project_roles = optional(map(list(string)), {})
})), {})
service_agents_config = optional(object({
create_primary_agents = optional(bool, true)
grant_default_roles = optional(bool, true)
grant_service_agent_editor = optional(bool, true)
skip_iam = optional(set(string), [])
}), {})
service_encryption_key_ids = optional(map(list(string)), {})
services = optional(list(string), [])
shared_vpc_host_config = optional(object({

View File

@@ -86,6 +86,12 @@ variable "data_defaults" {
display_name = optional(string, "Terraform-managed.")
iam_self_roles = optional(list(string))
})), {})
service_agents_config = optional(object({
create_primary_agents = optional(bool, true)
grant_default_roles = optional(bool, true)
grant_service_agent_editor = optional(bool, true)
skip_iam = optional(set(string), [])
}), {})
service_encryption_key_ids = optional(map(list(string)), {})
services = optional(list(string), [])
shared_vpc_service_config = optional(object({