Add skip_iam option to project service agents config (#4005)

This commit is contained in:
Julio Castillo
2026-06-01 06:36:07 +02:00
committed by GitHub
parent 982717188d
commit e3e261442f
5 changed files with 112 additions and 12 deletions

View File

@@ -13,6 +13,7 @@ This module implements the creation and management of one GCP project including
- [Additive IAM](#additive-iam)
- [Service Agents](#service-agents)
- [Cloudservices Editor Role](#cloudservices-editor-role)
- [Skipping Service Agent IAM Grants](#skipping-service-agent-iam-grants)
- [Service Agent Aliases](#service-agent-aliases)
- [Shared VPC](#shared-vpc)
- [Organization Policies](#organization-policies)
@@ -270,6 +271,28 @@ The complete list of Google Cloud service agents, including their names, default
The `cloudservices` service agent is granted `roles/editor` by default, making it easy to accidentally remove this binding when managing the editor role authoritatively. In those cases, the module auto-injects the `cloudservices` service agent to preserve the binding. This behaviour is disabled when the `service_agents_config.grant_service_agent_editor` variable is set to `false`.
#### Skipping Service Agent IAM Grants
In some cases, you might want to prevent the module from automatically granting default roles to specific service agents (for example, if the service agent is created lazily by GCP and does not exist yet). You can do this by listing the agent names in `service_agents_config.skip_iam`:
```hcl
module "project" {
source = "./fabric/modules/project"
billing_account = var.billing_account_id
name = "project"
parent = var.folder_id
prefix = var.prefix
services = [
"container.googleapis.com",
"run.googleapis.com"
]
service_agents_config = {
skip_iam = ["serverless-robot-prod"]
}
}
# tftest modules=1 resources=7 inventory=service-agents-skip.yaml
```
#### Service Agent Aliases
Consider the code below:
@@ -2380,17 +2403,17 @@ module "project" {
| [scc_mute_configs](variables-scc.tf#L17) | SCC mute configurations keyed by name. | <code>map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [scc_sha_custom_modules](variables-scc.tf#L28) | SCC custom modules keyed by module name. | <code>map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [service_agents_config](variables.tf#L329) | Automatic service agent configuration options. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [service_config](variables.tf#L340) | Configure service API activation. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#8230;&#125;</code> |
| [service_encryption_key_ids](variables.tf#L352) | Service Agents to be granted encryption/decryption permissions over Cloud KMS encryption keys. Format {SERVICE_AGENT => [KEY_ID]}. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [services](variables.tf#L359) | Service APIs to enable. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [shared_vpc_host_config](variables.tf#L365) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [shared_vpc_service_config](variables.tf#L375) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#8230;&#125;</code> |
| [skip_delete](variables.tf#L412) | Deprecated. Use deletion_policy. | <code>bool</code> | | <code>null</code> |
| [service_config](variables.tf#L341) | Configure service API activation. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#8230;&#125;</code> |
| [service_encryption_key_ids](variables.tf#L353) | Service Agents to be granted encryption/decryption permissions over Cloud KMS encryption keys. Format {SERVICE_AGENT => [KEY_ID]}. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [services](variables.tf#L360) | Service APIs to enable. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [shared_vpc_host_config](variables.tf#L366) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [shared_vpc_service_config](variables.tf#L376) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#8230;&#125;</code> |
| [skip_delete](variables.tf#L413) | Deprecated. Use deletion_policy. | <code>bool</code> | | <code>null</code> |
| [tag_bindings](variables-tags.tf#L89) | Tag bindings for this project, in key => tag value id format. | <code>map&#40;string&#41;</code> | | <code>null</code> |
| [tags](variables-tags.tf#L96) | Tags by key name. If `id` is provided, key or value creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | <code>map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [tags_config](variables-tags.tf#L171) | Fine-grained control on tag resource and IAM creation. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [universe](variables.tf#L424) | GCP universe where to deploy the project. The prefix will be prepended to the project id. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [vpc_sc](variables.tf#L435) | VPC-SC configuration for the project, use when `ignore_changes` for resources is set in the VPC-SC module. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [universe](variables.tf#L425) | GCP universe where to deploy the project. The prefix will be prepended to the project id. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [vpc_sc](variables.tf#L436) | VPC-SC configuration for the project, use when `ignore_changes` for resources is set in the VPC-SC module. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [workload_identity_pools](variables-identity-providers.tf#L17) | Workload Identity Federation pools and providers. | <code>map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
## Outputs

View File

@@ -116,7 +116,12 @@ locals {
} if alltrue([
var.service_agents_config.grant_default_roles,
agent.role != null,
!agent.skip_iam
# 1. Static Skip (Global): Managed via tools/build_service_agents.py
# Filters out known lazy agents that always fail on API enablement.
!agent.skip_iam,
# 2. Dynamic Skip (Runtime/Project-level): Managed by the user via skip_iam
# Filters out project-specific overrides or newly introduced lazy agents.
!contains(var.service_agents_config.skip_iam, agent.name)
])
}
services = [

View File

@@ -332,6 +332,7 @@ variable "service_agents_config" {
create_primary_agents = optional(bool, true)
grant_default_roles = optional(bool, true)
grant_service_agent_editor = optional(bool, true)
skip_iam = optional(set(string), [])
})
default = {}
nullable = false

View File

@@ -0,0 +1,70 @@
# Copyright 2026 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
#
# https://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_project.project[0]:
auto_create_network: false
billing_account: 123456-123456-123456
deletion_policy: DELETE
effective_labels:
goog-terraform-provisioned: 'true'
folder_id: '1122334455'
labels: null
name: test-project
org_id: null
project_id: test-project
tags: null
terraform_labels:
goog-terraform-provisioned: 'true'
timeouts: null
module.project.google_project_iam_member.service_agents["container-engine-robot"]:
condition: []
project: test-project
role: roles/container.serviceAgent
module.project.google_project_iam_member.service_agents["gkenode"]:
condition: []
project: test-project
role: roles/container.defaultNodeServiceAgent
module.project.google_project_service.project_services["container.googleapis.com"]:
deletion_policy: DELETE
disable_dependent_services: false
disable_on_destroy: false
project: test-project
service: container.googleapis.com
timeouts: null
module.project.google_project_service.project_services["run.googleapis.com"]:
deletion_policy: DELETE
disable_dependent_services: false
disable_on_destroy: false
project: test-project
service: run.googleapis.com
timeouts: null
module.project.google_project_service_identity.default["container.googleapis.com"]:
project: test-project
service: container.googleapis.com
timeouts: null
module.project.google_project_service_identity.default["run.googleapis.com"]:
project: test-project
service: run.googleapis.com
timeouts: null
counts:
google_project: 1
google_project_iam_member: 2
google_project_service: 2
google_project_service_identity: 2
modules: 1
resources: 7
outputs: {}

View File

@@ -56,10 +56,11 @@ ALIASES = {
IGNORED_AGENTS = []
# SKIP_IAM_AGENTS defines the GLOBAL/STATIC skip list.
# These service agents are known to be created lazily by GCP and will ALWAYS
# fail on API enablement if Fabric tries to grant default roles automatically.
# Running this script marks them with `skip_iam: true` in `service-agents.yaml`.
SKIP_IAM_AGENTS = [
# skips IAM role grants to the non-primary agents listed below as
# it's failing, possibly because the agents don't exist after API
# activation
'service-PROJECT_NUMBER@gcp-sa-apigateway-mgmt.iam.gserviceaccount.com',
'service-PROJECT_NUMBER@gcp-sa-apigateway.iam.gserviceaccount.com',
'service-PROJECT_NUMBER@gcp-sa-bigqueryspark.iam.gserviceaccount.com',