Custom role factories for organization and project modules (#1912)

* backport custom role factories

* backport from fast ci/cd branch

* indent

* tfdoc

* fix module tests
This commit is contained in:
Ludovico Magnocavallo 2023-12-11 15:16:39 +01:00 committed by GitHub
parent 886734e1e9
commit bba814c091
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 438 additions and 272 deletions

View File

@ -71,13 +71,15 @@ locals {
}
module "folder" {
source = "../../../modules/folder"
folder_create = var.folder_config.folder_create != null
parent = try(var.folder_config.folder_create.parent, null)
name = try(var.folder_config.folder_create.display_name, null)
id = var.folder_config.folder_create != null ? null : var.folder_config.folder_id
group_iam = local.group_iam
org_policies_data_path = var.data_dir != null ? "${var.data_dir}/org-policies" : null
source = "../../../modules/folder"
folder_create = var.folder_config.folder_create != null
parent = try(var.folder_config.folder_create.parent, null)
name = try(var.folder_config.folder_create.display_name, null)
id = var.folder_config.folder_create != null ? null : var.folder_config.folder_id
group_iam = local.group_iam
factories_config = {
org_policies = var.data_dir != null ? "${var.data_dir}/org-policies" : null
}
logging_sinks = var.enable_features.log_sink ? {
for name, attrs in var.log_sinks : name => {
bq_partitioned_table = attrs.type == "bigquery"

View File

@ -155,21 +155,21 @@ Once the configuration is done just go through the usual `init/apply` cycle. On
|---|---|:---:|:---:|:---:|:---:|
| [automation](variables.tf#L20) | Automation resources created by the bootstrap stage. | <code title="object&#40;&#123;&#10; outputs_bucket &#61; string&#10; project_id &#61; string&#10; project_number &#61; string&#10; federated_identity_pools &#61; list&#40;string&#41;&#10; federated_identity_providers &#61; map&#40;object&#40;&#123;&#10; audiences &#61; list&#40;string&#41;&#10; issuer &#61; string&#10; issuer_uri &#61; string&#10; name &#61; string&#10; principal_tpl &#61; string&#10; principalset_tpl &#61; string&#10; &#125;&#41;&#41;&#10; service_accounts &#61; object&#40;&#123;&#10; networking &#61; string&#10; resman &#61; string&#10; security &#61; string&#10; dp-dev &#61; optional&#40;string&#41;&#10; dp-prod &#61; optional&#40;string&#41;&#10; gke-dev &#61; optional&#40;string&#41;&#10; gke-prod &#61; optional&#40;string&#41;&#10; pf-dev &#61; optional&#40;string&#41;&#10; pf-prod &#61; optional&#40;string&#41;&#10; sandbox &#61; optional&#40;string&#41;&#10; teams &#61; optional&#40;string&#41;&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [billing_account](variables.tf#L52) | Billing account id. If billing account is not part of the same org set `is_org_level` to `false`. To disable handling of billing IAM roles set `no_iam` to `true`. | <code title="object&#40;&#123;&#10; id &#61; string&#10; is_org_level &#61; optional&#40;bool, true&#41;&#10; no_iam &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [organization](variables.tf#L205) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [prefix](variables.tf#L227) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>0-bootstrap</code> |
| [root_node](variables.tf#L237) | Root folder node for the tenant, in folders/nnnnnn format. | <code>string</code> | ✓ | | |
| [short_name](variables.tf#L242) | Short name used to identify the tenant. | <code>string</code> | ✓ | | |
| [tags](variables.tf#L247) | Resource management tags. | <code title="object&#40;&#123;&#10; keys &#61; object&#40;&#123;&#10; context &#61; string&#10; environment &#61; string&#10; tenant &#61; string&#10; &#125;&#41;&#10; names &#61; object&#40;&#123;&#10; context &#61; string&#10; environment &#61; string&#10; tenant &#61; string&#10; &#125;&#41;&#10; values &#61; map&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | |
| [organization](variables.tf#L214) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [prefix](variables.tf#L230) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>0-bootstrap</code> |
| [root_node](variables.tf#L240) | Root folder node for the tenant, in folders/nnnnnn format. | <code>string</code> | ✓ | | |
| [short_name](variables.tf#L245) | Short name used to identify the tenant. | <code>string</code> | ✓ | | |
| [tags](variables.tf#L250) | Resource management tags. | <code title="object&#40;&#123;&#10; keys &#61; object&#40;&#123;&#10; context &#61; string&#10; environment &#61; string&#10; tenant &#61; string&#10; &#125;&#41;&#10; names &#61; object&#40;&#123;&#10; context &#61; string&#10; environment &#61; string&#10; tenant &#61; string&#10; &#125;&#41;&#10; values &#61; map&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | |
| [cicd_repositories](variables.tf#L63) | CI/CD repository configuration. Identity providers reference keys in the `automation.federated_identity_providers` variable. Set to null to disable, or set individual repositories to null if not needed. | <code title="object&#40;&#123;&#10; data_platform_dev &#61; object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#10; data_platform_prod &#61; object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#10; gke_dev &#61; object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#10; gke_prod &#61; object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#10; networking &#61; object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#10; project_factory_dev &#61; object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#10; project_factory_prod &#61; object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#10; security &#61; object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [custom_roles](variables.tf#L145) | Custom roles defined at the org level, in key => id format. | <code title="object&#40;&#123;&#10; service_project_network_admin &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>0-bootstrap</code> |
| [data_dir](variables.tf#L154) | Relative path for the folder storing configuration data. | <code>string</code> | | <code>&#34;data&#34;</code> | |
| [fast_features](variables.tf#L160) | Selective control for top-level FAST features. | <code title="object&#40;&#123;&#10; data_platform &#61; optional&#40;bool, false&#41;&#10; gke &#61; optional&#40;bool, false&#41;&#10; project_factory &#61; optional&#40;bool, false&#41;&#10; sandbox &#61; optional&#40;bool, false&#41;&#10; teams &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | <code>0-0-bootstrap</code> |
| [groups](variables.tf#L174) | Group names to grant organization-level permissions. | <code title="object&#40;&#123;&#10; gcp-devops &#61; optional&#40;string&#41;&#10; gcp-network-admins &#61; optional&#40;string&#41;&#10; gcp-security-admins &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | <code>0-bootstrap</code> |
| [locations](variables.tf#L187) | Optional locations for GCS, BigQuery, and logging buckets created here. | <code title="object&#40;&#123;&#10; bq &#61; string&#10; gcs &#61; string&#10; logging &#61; string&#10; pubsub &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; bq &#61; &#34;EU&#34;&#10; gcs &#61; &#34;EU&#34;&#10; logging &#61; &#34;global&#34;&#10; pubsub &#61; &#91;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> | <code>0-bootstrap</code> |
| [organization_policy_data_path](variables.tf#L215) | Path for the data folder used by the organization policies factory. | <code>string</code> | | <code>null</code> | |
| [outputs_location](variables.tf#L221) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | <code>string</code> | | <code>null</code> | |
| [team_folders](variables.tf#L265) | Team folders to be created. Format is described in a code comment. | <code title="map&#40;object&#40;&#123;&#10; descriptive_name &#61; string&#10; group_iam &#61; map&#40;list&#40;string&#41;&#41;&#10; impersonation_groups &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>null</code> | |
| [test_skip_data_sources](variables.tf#L275) | Used when testing to bypass data sources. | <code>bool</code> | | <code>false</code> | |
| [factories_config](variables.tf#L160) | Configuration for the organization policies factory. | <code title="object&#40;&#123;&#10; org_policy &#61; optional&#40;string, &#34;data&#47;org-policies&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [fast_features](variables.tf#L169) | Selective control for top-level FAST features. | <code title="object&#40;&#123;&#10; data_platform &#61; optional&#40;bool, false&#41;&#10; gke &#61; optional&#40;bool, false&#41;&#10; project_factory &#61; optional&#40;bool, false&#41;&#10; sandbox &#61; optional&#40;bool, false&#41;&#10; teams &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | <code>0-0-bootstrap</code> |
| [groups](variables.tf#L183) | Group names to grant organization-level permissions. | <code title="object&#40;&#123;&#10; gcp-devops &#61; optional&#40;string&#41;&#10; gcp-network-admins &#61; optional&#40;string&#41;&#10; gcp-security-admins &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | <code>0-bootstrap</code> |
| [locations](variables.tf#L196) | Optional locations for GCS, BigQuery, and logging buckets created here. | <code title="object&#40;&#123;&#10; bq &#61; string&#10; gcs &#61; string&#10; logging &#61; string&#10; pubsub &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; bq &#61; &#34;EU&#34;&#10; gcs &#61; &#34;EU&#34;&#10; logging &#61; &#34;global&#34;&#10; pubsub &#61; &#91;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> | <code>0-bootstrap</code> |
| [outputs_location](variables.tf#L224) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | <code>string</code> | | <code>null</code> | |
| [team_folders](variables.tf#L268) | Team folders to be created. Format is described in a code comment. | <code title="map&#40;object&#40;&#123;&#10; descriptive_name &#61; string&#10; group_iam &#61; map&#40;list&#40;string&#41;&#41;&#10; impersonation_groups &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>null</code> | |
| [test_skip_data_sources](variables.tf#L278) | Used when testing to bypass data sources. | <code>bool</code> | | <code>false</code> | |
## Outputs

View File

@ -26,6 +26,9 @@ module "root-folder" {
)
name = var.test_skip_data_sources ? "Test" : null
# end test attributes
factories_config = {
org_policy = var.factories_config.org_policy
}
iam_bindings_additive = {
sa_net_fw_policy_admin = {
member = local.automation_sas_iam.networking
@ -40,5 +43,4 @@ module "root-folder" {
role = "roles/accesscontextmanager.policyAdmin"
}
}
org_policies_data_path = var.organization_policy_data_path
}

View File

@ -157,6 +157,15 @@ variable "data_dir" {
default = "data"
}
variable "factories_config" {
description = "Configuration for the organization policies factory."
type = object({
org_policy = optional(string, "data/org-policies")
})
nullable = false
default = {}
}
variable "fast_features" {
# tfdoc:variable:source 0-0-bootstrap
description = "Selective control for top-level FAST features."
@ -212,12 +221,6 @@ variable "organization" {
})
}
variable "organization_policy_data_path" {
description = "Path for the data folder used by the organization policies factory."
type = string
default = null
}
variable "outputs_location" {
description = "Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable."
type = string

View File

@ -594,37 +594,36 @@ The `fast_features` variable consists of 4 toggles:
| name | description | type | required | default | producer |
|---|---|:---:|:---:|:---:|:---:|
| [billing_account](variables.tf#L17) | Billing account id. If billing account is not part of the same org set `is_org_level` to `false`. To disable handling of billing IAM roles set `no_iam` to `true`. | <code title="object&#40;&#123;&#10; id &#61; string&#10; is_org_level &#61; optional&#40;bool, true&#41;&#10; no_iam &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | |
| [organization](variables.tf#L248) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | |
| [prefix](variables.tf#L263) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | |
| [organization](variables.tf#L235) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | |
| [prefix](variables.tf#L250) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | |
| [bootstrap_user](variables.tf#L27) | Email of the nominal user running this stage for the first time. | <code>string</code> | | <code>null</code> | |
| [cicd_repositories](variables.tf#L33) | CI/CD repository configuration. Identity providers reference keys in the `federated_identity_providers` variable. Set to null to disable, or set individual repositories to null if not needed. | <code title="object&#40;&#123;&#10; bootstrap &#61; optional&#40;object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10; resman &#61; optional&#40;object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [custom_role_names](variables.tf#L79) | Names of custom roles defined at the org level. | <code title="object&#40;&#123;&#10; organization_iam_admin &#61; string&#10; service_project_network_admin &#61; string&#10; tenant_network_admin &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; organization_iam_admin &#61; &#34;organizationIamAdmin&#34;&#10; service_project_network_admin &#61; &#34;serviceProjectNetworkAdmin&#34;&#10; tenant_network_admin &#61; &#34;tenantNetworkAdmin&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [custom_roles](variables.tf#L93) | Map of role names => list of permissions to additionally create at the organization level. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [factories_config](variables.tf#L100) | Configuration for the organization policies factory. | <code title="object&#40;&#123;&#10; org_policy_data_path &#61; optional&#40;string, &#34;data&#47;org-policies&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [fast_features](variables.tf#L109) | Selective control for top-level FAST features. | <code title="object&#40;&#123;&#10; data_platform &#61; optional&#40;bool, false&#41;&#10; gke &#61; optional&#40;bool, false&#41;&#10; project_factory &#61; optional&#40;bool, false&#41;&#10; sandbox &#61; optional&#40;bool, false&#41;&#10; teams &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [federated_identity_providers](variables.tf#L122) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | <code title="map&#40;object&#40;&#123;&#10; attribute_condition &#61; optional&#40;string&#41;&#10; issuer &#61; string&#10; custom_settings &#61; optional&#40;object&#40;&#123;&#10; issuer_uri &#61; optional&#40;string&#41;&#10; audiences &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; jwks_json &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [group_iam](variables.tf#L142) | Organization-level authoritative IAM binding for groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [groups](variables.tf#L149) | Group names or emails to grant organization-level permissions. If just the name is provided, the default organization domain is assumed. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; gcp-billing-admins &#61; &#34;gcp-billing-admins&#34;,&#10; gcp-devops &#61; &#34;gcp-devops&#34;,&#10; gcp-network-admins &#61; &#34;gcp-network-admins&#34;&#10; gcp-organization-admins &#61; &#34;gcp-organization-admins&#34;&#10; gcp-security-admins &#61; &#34;gcp-security-admins&#34;&#10; gcp-support &#61; &#34;gcp-devops&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [iam](variables.tf#L167) | Organization-level custom IAM settings in role => [principal] format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [iam_bindings_additive](variables.tf#L174) | Organization-level custom additive IAM bindings. Keys are arbitrary. | <code title="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;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [locations](variables.tf#L189) | Optional locations for GCS, BigQuery, and logging buckets created here. | <code title="object&#40;&#123;&#10; bq &#61; optional&#40;string, &#34;EU&#34;&#41;&#10; gcs &#61; optional&#40;string, &#34;EU&#34;&#41;&#10; logging &#61; optional&#40;string, &#34;global&#34;&#41;&#10; pubsub &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [log_sinks](variables.tf#L203) | Org-level log sinks, in name => {type, filter} format. | <code title="map&#40;object&#40;&#123;&#10; filter &#61; string&#10; type &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; audit-logs &#61; &#123;&#10; filter &#61; &#34;logName:&#92;&#34;&#47;logs&#47;cloudaudit.googleapis.com&#37;2Factivity&#92;&#34; OR logName:&#92;&#34;&#47;logs&#47;cloudaudit.googleapis.com&#37;2Fsystem_event&#92;&#34; OR protoPayload.metadata.&#64;type&#61;&#92;&#34;type.googleapis.com&#47;google.cloud.audit.TransparencyLog&#92;&#34;&#34;&#10; type &#61; &#34;logging&#34;&#10; &#125;&#10; vpc-sc &#61; &#123;&#10; filter &#61; &#34;protoPayload.metadata.&#64;type&#61;&#92;&#34;type.googleapis.com&#47;google.cloud.audit.VpcServiceControlAuditMetadata&#92;&#34;&#34;&#10; type &#61; &#34;logging&#34;&#10; &#125;&#10; workspace-audit-logs &#61; &#123;&#10; filter &#61; &#34;logName:&#92;&#34;&#47;logs&#47;cloudaudit.googleapis.com&#37;2Fdata_access&#92;&#34; and protoPayload.serviceName:&#92;&#34;login.googleapis.com&#92;&#34;&#34;&#10; type &#61; &#34;logging&#34;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [org_policies_config](variables.tf#L232) | Organization policies customization. | <code title="object&#40;&#123;&#10; constraints &#61; optional&#40;object&#40;&#123;&#10; allowed_policy_member_domains &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; tag_name &#61; optional&#40;string, &#34;org-policies&#34;&#41;&#10; tag_values &#61; optional&#40;map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Managed by the Terraform organization module.&#34;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; id &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [outputs_location](variables.tf#L257) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | <code>string</code> | | <code>null</code> | |
| [project_parent_ids](variables.tf#L272) | Optional parents for projects created here in folders/nnnnnnn format. Null values will use the organization as parent. | <code title="object&#40;&#123;&#10; automation &#61; string&#10; billing &#61; string&#10; logging &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; automation &#61; null&#10; billing &#61; null&#10; logging &#61; null&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [custom_roles](variables.tf#L79) | Map of role names => list of permissions to additionally create at the organization level. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [factories_config](variables.tf#L86) | Configuration for the organization policies factory. | <code title="object&#40;&#123;&#10; custom_roles &#61; optional&#40;string, &#34;data&#47;custom-roles&#34;&#41;&#10; org_policy &#61; optional&#40;string, &#34;data&#47;org-policies&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [fast_features](variables.tf#L96) | Selective control for top-level FAST features. | <code title="object&#40;&#123;&#10; data_platform &#61; optional&#40;bool, false&#41;&#10; gke &#61; optional&#40;bool, false&#41;&#10; project_factory &#61; optional&#40;bool, false&#41;&#10; sandbox &#61; optional&#40;bool, false&#41;&#10; teams &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [federated_identity_providers](variables.tf#L109) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | <code title="map&#40;object&#40;&#123;&#10; attribute_condition &#61; optional&#40;string&#41;&#10; issuer &#61; string&#10; custom_settings &#61; optional&#40;object&#40;&#123;&#10; issuer_uri &#61; optional&#40;string&#41;&#10; audiences &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; jwks_json &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [group_iam](variables.tf#L129) | Organization-level authoritative IAM binding for groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [groups](variables.tf#L136) | Group names or emails to grant organization-level permissions. If just the name is provided, the default organization domain is assumed. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; gcp-billing-admins &#61; &#34;gcp-billing-admins&#34;,&#10; gcp-devops &#61; &#34;gcp-devops&#34;,&#10; gcp-network-admins &#61; &#34;gcp-network-admins&#34;&#10; gcp-organization-admins &#61; &#34;gcp-organization-admins&#34;&#10; gcp-security-admins &#61; &#34;gcp-security-admins&#34;&#10; gcp-support &#61; &#34;gcp-devops&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [iam](variables.tf#L154) | Organization-level custom IAM settings in role => [principal] format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [iam_bindings_additive](variables.tf#L161) | Organization-level custom additive IAM bindings. Keys are arbitrary. | <code title="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;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [locations](variables.tf#L176) | Optional locations for GCS, BigQuery, and logging buckets created here. | <code title="object&#40;&#123;&#10; bq &#61; optional&#40;string, &#34;EU&#34;&#41;&#10; gcs &#61; optional&#40;string, &#34;EU&#34;&#41;&#10; logging &#61; optional&#40;string, &#34;global&#34;&#41;&#10; pubsub &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [log_sinks](variables.tf#L190) | Org-level log sinks, in name => {type, filter} format. | <code title="map&#40;object&#40;&#123;&#10; filter &#61; string&#10; type &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; audit-logs &#61; &#123;&#10; filter &#61; &#34;logName:&#92;&#34;&#47;logs&#47;cloudaudit.googleapis.com&#37;2Factivity&#92;&#34; OR logName:&#92;&#34;&#47;logs&#47;cloudaudit.googleapis.com&#37;2Fsystem_event&#92;&#34; OR protoPayload.metadata.&#64;type&#61;&#92;&#34;type.googleapis.com&#47;google.cloud.audit.TransparencyLog&#92;&#34;&#34;&#10; type &#61; &#34;logging&#34;&#10; &#125;&#10; vpc-sc &#61; &#123;&#10; filter &#61; &#34;protoPayload.metadata.&#64;type&#61;&#92;&#34;type.googleapis.com&#47;google.cloud.audit.VpcServiceControlAuditMetadata&#92;&#34;&#34;&#10; type &#61; &#34;logging&#34;&#10; &#125;&#10; workspace-audit-logs &#61; &#123;&#10; filter &#61; &#34;logName:&#92;&#34;&#47;logs&#47;cloudaudit.googleapis.com&#37;2Fdata_access&#92;&#34; and protoPayload.serviceName:&#92;&#34;login.googleapis.com&#92;&#34;&#34;&#10; type &#61; &#34;logging&#34;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [org_policies_config](variables.tf#L219) | Organization policies customization. | <code title="object&#40;&#123;&#10; constraints &#61; optional&#40;object&#40;&#123;&#10; allowed_policy_member_domains &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; tag_name &#61; optional&#40;string, &#34;org-policies&#34;&#41;&#10; tag_values &#61; optional&#40;map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Managed by the Terraform organization module.&#34;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; id &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [outputs_location](variables.tf#L244) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | <code>string</code> | | <code>null</code> | |
| [project_parent_ids](variables.tf#L259) | Optional parents for projects created here in folders/nnnnnnn format. Null values will use the organization as parent. | <code title="object&#40;&#123;&#10; automation &#61; string&#10; billing &#61; string&#10; logging &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; automation &#61; null&#10; billing &#61; null&#10; logging &#61; null&#10;&#125;">&#123;&#8230;&#125;</code> | |
## Outputs
| name | description | sensitive | consumers |
|---|---|:---:|---|
| [automation](outputs.tf#L112) | Automation resources. | | |
| [billing_dataset](outputs.tf#L117) | BigQuery dataset prepared for billing export. | | |
| [cicd_repositories](outputs.tf#L122) | CI/CD repository configurations. | | |
| [custom_roles](outputs.tf#L134) | Organization-level custom roles. | | |
| [federated_identity](outputs.tf#L139) | Workload Identity Federation pool and providers. | | |
| [outputs_bucket](outputs.tf#L149) | GCS bucket where generated output files are stored. | | |
| [project_ids](outputs.tf#L154) | Projects created by this stage. | | |
| [providers](outputs.tf#L164) | Terraform provider files for this stage and dependent stages. | ✓ | <code>stage-01</code> |
| [service_accounts](outputs.tf#L171) | Automation service accounts created by this stage. | | |
| [tfvars](outputs.tf#L180) | Terraform variable files for the following stages. | ✓ | |
| [automation](outputs.tf#L102) | Automation resources. | | |
| [billing_dataset](outputs.tf#L107) | BigQuery dataset prepared for billing export. | | |
| [cicd_repositories](outputs.tf#L112) | CI/CD repository configurations. | | |
| [custom_roles](outputs.tf#L124) | Organization-level custom roles. | | |
| [federated_identity](outputs.tf#L129) | Workload Identity Federation pool and providers. | | |
| [outputs_bucket](outputs.tf#L139) | GCS bucket where generated output files are stored. | | |
| [project_ids](outputs.tf#L144) | Projects created by this stage. | | |
| [providers](outputs.tf#L154) | Terraform provider files for this stage and dependent stages. | ✓ | <code>stage-01</code> |
| [service_accounts](outputs.tf#L161) | Automation service accounts created by this stage. | | |
| [tfvars](outputs.tf#L170) | Terraform variable files for the following stages. | ✓ | |
<!-- END TFDOC -->

View File

@ -0,0 +1,20 @@
# Copyright 2023 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.
# this is needed for use in additive IAM bindings, to avoid conflicts
name: organizationIamAdmin
includedPermissions:
- resourcemanager.organizations.get
- resourcemanager.organizations.getIamPolicy
- resourcemanager.organizations.setIamPolicy

View File

@ -0,0 +1,31 @@
# Copyright 2023 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.
name: serviceProjectNetworkAdmin
includedPermissions:
- compute.globalOperations.get
# compute.networks.updatePeering and compute.networks.get are
# used by automation service accounts who manage service
# projects where peering creation might be needed (e.g. GKE). If
# you remove them your network administrators should create
# peerings for service projects
- compute.networks.updatePeering
- compute.networks.get
- compute.organizations.disableXpnResource
- compute.organizations.enableXpnResource
- compute.projects.get
- compute.subnetworks.getIamPolicy
- compute.subnetworks.setIamPolicy
- dns.networks.bindPrivateDNSZone
- resourcemanager.projects.get

View File

@ -0,0 +1,17 @@
# Copyright 2023 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.
name: tenantNetworkAdmin
includedPermissions:
- compute.globalOperations.get

View File

@ -95,7 +95,7 @@ module "organization" {
iam_bindings = {
organization_iam_admin_conditional = {
members = [module.automation-tf-resman-sa.iam_email]
role = module.organization.custom_role_id[var.custom_role_names.organization_iam_admin]
role = module.organization.custom_role_id["organization_iam_admin"]
condition = {
expression = format(
"api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly([%s])",
@ -106,7 +106,7 @@ module "organization" {
"roles/compute.xpnAdmin",
"roles/orgpolicy.policyAdmin",
"roles/resourcemanager.organizationViewer",
module.organization.custom_role_id[var.custom_role_names.tenant_network_admin]
module.organization.custom_role_id["tenant_network_admin"]
],
local.billing_mode == "org" ? [
"roles/billing.admin",
@ -120,34 +120,13 @@ module "organization" {
}
}
}
custom_roles = merge(var.custom_roles, {
# this is needed for use in additive IAM bindings, to avoid conflicts
(var.custom_role_names.organization_iam_admin) = [
"resourcemanager.organizations.get",
"resourcemanager.organizations.getIamPolicy",
"resourcemanager.organizations.setIamPolicy"
]
(var.custom_role_names.service_project_network_admin) = [
"compute.globalOperations.get",
# compute.networks.updatePeering and compute.networks.get are
# used by automation service accounts who manage service
# projects where peering creation might be needed (e.g. GKE). If
# you remove them your network administrators should create
# peerings for service projects
"compute.networks.updatePeering",
"compute.networks.get",
"compute.organizations.disableXpnResource",
"compute.organizations.enableXpnResource",
"compute.projects.get",
"compute.subnetworks.getIamPolicy",
"compute.subnetworks.setIamPolicy",
"dns.networks.bindPrivateDNSZone",
"resourcemanager.projects.get",
]
(var.custom_role_names.tenant_network_admin) = [
"compute.globalOperations.get",
]
})
custom_roles = var.custom_roles
factories_config = {
custom_roles = var.factories_config.custom_roles
org_policies = (
var.bootstrap_user != null ? null : var.factories_config.org_policy
)
}
logging_sinks = {
for name, attrs in var.log_sinks : name => {
bq_partitioned_table = attrs.type == "bigquery"
@ -156,11 +135,6 @@ module "organization" {
type = attrs.type
}
}
org_policies_data_path = (
var.bootstrap_user != null
? null
: var.factories_config.org_policy_data_path
)
org_policies = var.bootstrap_user != null ? {} : {
"iam.allowedPolicyMemberDomains" = {
rules = [

View File

@ -38,16 +38,6 @@ locals {
}
)
}
custom_roles = merge(
{
for k, v in var.custom_role_names :
k => try(module.organization.custom_role_id[v], "")
},
{
for k, v in var.custom_roles :
k => try(module.organization.custom_role_id[k], "")
}
)
providers = {
"0-bootstrap" = templatefile(local._tpl_providers, {
backend_extra = null
@ -82,7 +72,7 @@ locals {
project_id = module.automation-project.project_id
project_number = module.automation-project.number
}
custom_roles = local.custom_roles
custom_roles = module.organization.custom_role_id
logging = {
project_id = module.log-export-project.project_id
project_number = module.log-export-project.number
@ -133,7 +123,7 @@ output "cicd_repositories" {
output "custom_roles" {
description = "Organization-level custom roles."
value = local.custom_roles
value = module.organization.custom_role_id
}
output "federated_identity" {

View File

@ -76,20 +76,6 @@ variable "cicd_repositories" {
}
}
variable "custom_role_names" {
description = "Names of custom roles defined at the org level."
type = object({
organization_iam_admin = string
service_project_network_admin = string
tenant_network_admin = string
})
default = {
organization_iam_admin = "organizationIamAdmin"
service_project_network_admin = "serviceProjectNetworkAdmin"
tenant_network_admin = "tenantNetworkAdmin"
}
}
variable "custom_roles" {
description = "Map of role names => list of permissions to additionally create at the organization level."
type = map(list(string))
@ -100,7 +86,8 @@ variable "custom_roles" {
variable "factories_config" {
description = "Configuration for the organization policies factory."
type = object({
org_policy_data_path = optional(string, "data/org-policies")
custom_roles = optional(string, "data/custom-roles")
org_policy = optional(string, "data/org-policies")
})
nullable = false
default = {}

View File

@ -122,10 +122,12 @@ The example below deploys a few organization policies split between two YAML fil
```hcl
module "folder" {
source = "./fabric/modules/folder"
parent = var.folder_id
name = "Folder name"
org_policies_data_path = "configs/org-policies/"
source = "./fabric/modules/folder"
parent = var.folder_id
name = "Folder name"
factories_config = {
org_policies = "configs/org-policies/"
}
}
# tftest modules=1 resources=8 files=boolean,list inventory=org-policies.yaml e2e
```
@ -198,6 +200,7 @@ module "folder" {
}
# tftest modules=2 resources=3 e2e serial
```
## Log Sinks
```hcl
@ -340,21 +343,21 @@ module "folder" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [contacts](variables.tf#L17) | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [firewall_policy](variables.tf#L24) | Hierarchical firewall policy to associate to this folder. | <code title="object&#40;&#123;&#10; name &#61; string&#10; policy &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [folder_create](variables.tf#L33) | Create folder. When set to false, uses id to reference an existing folder. | <code>bool</code> | | <code>true</code> |
| [group_iam](variables.tf#L39) | Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam](variables.tf#L46) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings](variables.tf#L53) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | <code title="map&#40;object&#40;&#123;&#10; members &#61; list&#40;string&#41;&#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;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings_additive](variables.tf#L68) | Individual additive IAM bindings. Keys are arbitrary. | <code title="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;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [id](variables.tf#L83) | Folder ID in case you use folder_create=false. | <code>string</code> | | <code>null</code> |
| [logging_data_access](variables.tf#L89) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | <code>map&#40;map&#40;list&#40;string&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_exclusions](variables.tf#L104) | Logging exclusions for this folder in the form {NAME -> FILTER}. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_sinks](variables.tf#L111) | Logging sinks to create for the folder. | <code title="map&#40;object&#40;&#123;&#10; bq_partitioned_table &#61; optional&#40;bool&#41;&#10; description &#61; optional&#40;string&#41;&#10; destination &#61; string&#10; disabled &#61; optional&#40;bool, false&#41;&#10; exclusions &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; filter &#61; string&#10; iam &#61; optional&#40;bool, true&#41;&#10; include_children &#61; optional&#40;bool, true&#41;&#10; type &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [name](variables.tf#L142) | Folder name. | <code>string</code> | | <code>null</code> |
| [org_policies](variables.tf#L148) | Organization policies applied to this folder keyed by policy name. | <code title="map&#40;object&#40;&#123;&#10; inherit_from_parent &#61; optional&#40;bool&#41; &#35; for list policies only.&#10; reset &#61; optional&#40;bool&#41;&#10; rules &#61; optional&#40;list&#40;object&#40;&#123;&#10; allow &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; deny &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; enforce &#61; optional&#40;bool&#41; &#35; for boolean policies only.&#10; condition &#61; optional&#40;object&#40;&#123;&#10; description &#61; optional&#40;string&#41;&#10; expression &#61; optional&#40;string&#41;&#10; location &#61; optional&#40;string&#41;&#10; title &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [org_policies_data_path](variables.tf#L175) | Path containing org policies in YAML format. | <code>string</code> | | <code>null</code> |
| [parent](variables.tf#L181) | Parent in folders/folder_id or organizations/org_id format. | <code>string</code> | | <code>null</code> |
| [tag_bindings](variables.tf#L191) | Tag bindings for this folder, in key => tag value id format. | <code>map&#40;string&#41;</code> | | <code>null</code> |
| [factories_config](variables.tf#L24) | Paths to data files and folders that enable factory functionality. | <code title="object&#40;&#123;&#10; org_policies &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [firewall_policy](variables.tf#L33) | Hierarchical firewall policy to associate to this folder. | <code title="object&#40;&#123;&#10; name &#61; string&#10; policy &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [folder_create](variables.tf#L42) | Create folder. When set to false, uses id to reference an existing folder. | <code>bool</code> | | <code>true</code> |
| [group_iam](variables.tf#L48) | Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam](variables.tf#L55) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings](variables.tf#L62) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | <code title="map&#40;object&#40;&#123;&#10; members &#61; list&#40;string&#41;&#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;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings_additive](variables.tf#L77) | Individual additive IAM bindings. Keys are arbitrary. | <code title="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;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [id](variables.tf#L92) | Folder ID in case you use folder_create=false. | <code>string</code> | | <code>null</code> |
| [logging_data_access](variables.tf#L98) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | <code>map&#40;map&#40;list&#40;string&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_exclusions](variables.tf#L113) | Logging exclusions for this folder in the form {NAME -> FILTER}. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_sinks](variables.tf#L120) | Logging sinks to create for the folder. | <code title="map&#40;object&#40;&#123;&#10; bq_partitioned_table &#61; optional&#40;bool&#41;&#10; description &#61; optional&#40;string&#41;&#10; destination &#61; string&#10; disabled &#61; optional&#40;bool, false&#41;&#10; exclusions &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; filter &#61; string&#10; iam &#61; optional&#40;bool, true&#41;&#10; include_children &#61; optional&#40;bool, true&#41;&#10; type &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [name](variables.tf#L151) | Folder name. | <code>string</code> | | <code>null</code> |
| [org_policies](variables.tf#L157) | Organization policies applied to this folder keyed by policy name. | <code title="map&#40;object&#40;&#123;&#10; inherit_from_parent &#61; optional&#40;bool&#41; &#35; for list policies only.&#10; reset &#61; optional&#40;bool&#41;&#10; rules &#61; optional&#40;list&#40;object&#40;&#123;&#10; allow &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; deny &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; enforce &#61; optional&#40;bool&#41; &#35; for boolean policies only.&#10; condition &#61; optional&#40;object&#40;&#123;&#10; description &#61; optional&#40;string&#41;&#10; expression &#61; optional&#40;string&#41;&#10; location &#61; optional&#40;string&#41;&#10; title &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [parent](variables.tf#L184) | Parent in folders/folder_id or organizations/org_id format. | <code>string</code> | | <code>null</code> |
| [tag_bindings](variables.tf#L194) | Tag bindings for this folder, in key => tag value id format. | <code>map&#40;string&#41;</code> | | <code>null</code> |
## Outputs

View File

@ -18,10 +18,9 @@
locals {
_factory_data_raw = merge([
for f in try(fileset(var.org_policies_data_path, "*.yaml"), []) :
yamldecode(file("${var.org_policies_data_path}/${f}"))
for f in try(fileset(var.factories_config.org_policies, "*.yaml"), []) :
yamldecode(file("${var.factories_config.org_policies}/${f}"))
]...)
# simulate applying defaults to data coming from yaml files
_factory_data = {
for k, v in local._factory_data_raw :
@ -49,9 +48,7 @@ locals {
]
}
}
_org_policies = merge(local._factory_data, var.org_policies)
org_policies = {
for k, v in local._org_policies :
k => merge(v, {

View File

@ -21,6 +21,15 @@ variable "contacts" {
nullable = false
}
variable "factories_config" {
description = "Paths to data files and folders that enable factory functionality."
type = object({
org_policies = optional(string)
})
nullable = false
default = {}
}
variable "firewall_policy" {
description = "Hierarchical firewall policy to associate to this folder."
type = object({
@ -172,12 +181,6 @@ variable "org_policies" {
nullable = false
}
variable "org_policies_data_path" {
description = "Path containing org policies in YAML format."
type = string
default = null
}
variable "parent" {
description = "Parent in folders/folder_id or organizations/org_id format."
type = string

View File

@ -102,7 +102,6 @@ module "org" {
}
]
}
"compute.trustedImageProjects" = {
rules = [{
allow = {
@ -145,7 +144,6 @@ To manage organization policy custom constraints, the `orgpolicy.googleapis.com`
module "org" {
source = "./fabric/modules/organization"
organization_id = var.organization_id
org_policy_custom_constraints = {
"custom.gkeEnableAutoUpgrade" = {
resource_types = ["container.googleapis.com/NodePool"]
@ -156,7 +154,6 @@ module "org" {
description = "All node pools must have node auto-upgrade enabled."
}
}
# not necessarily to enforce on the org level, policy may be applied on folder/project levels
org_policies = {
"custom.gkeEnableAutoUpgrade" = {
@ -177,9 +174,11 @@ The example below deploys a few org policy custom constraints split between two
```hcl
module "org" {
source = "./fabric/modules/organization"
organization_id = var.organization_id
org_policy_custom_constraints_data_path = "configs/custom-constraints"
source = "./fabric/modules/organization"
organization_id = var.organization_id
factories_config = {
org_policy_custom_constraints = "configs/custom-constraints"
}
org_policies = {
"custom.gkeEnableAutoUpgrade" = {
rules = [{ enforce = true }]
@ -373,6 +372,41 @@ module "org" {
# tftest modules=1 resources=2 inventory=roles.yaml e2e serial
```
Custom roles can also be specified via a factory in a similar way to organization policies and policy constraints. Each file is mapped to a custom role, where
- the role name defaults to the file name but can be overridden via a `name` attribute in the yaml
- role permissions are defined in an `includedPermissions` map
Custom roles defined via the variable are merged with those coming from the factory, and override them in case of duplicate names.
```hcl
module "org" {
source = "./fabric/modules/organization"
organization_id = var.organization_id
factories_config = {
custom_roles = "data/custom_roles"
}
}
# tftest modules=1 resources=2 files=custom-role-1,custom-role-2 inventory=custom-roles.yaml
```
```yaml
# tftest-file id=custom-role-1 path=data/custom_roles/test_1.yaml
includedPermissions:
- compute.globalOperations.get
```
```yaml
# tftest-file id=custom-role-2 path=data/custom_roles/test_2.yaml
name: projectViewer
includedPermissions:
- resourcemanager.projects.get
- resourcemanager.projects.getIamPolicy
- resourcemanager.projects.list
```
## Tags
Refer to the [Creating and managing tags](https://cloud.google.com/resource-manager/docs/tags/tags-creating-and-managing) documentation for details on usage.
@ -453,24 +487,23 @@ module "org" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [organization_id](variables.tf#L212) | Organization id in organizations/nnnnnn format. | <code>string</code> | ✓ | |
| [organization_id](variables.tf#L211) | Organization id in organizations/nnnnnn format. | <code>string</code> | ✓ | |
| [contacts](variables.tf#L17) | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [custom_roles](variables.tf#L24) | Map of role name => list of permissions to create in this project. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [firewall_policy](variables.tf#L31) | Hierarchical firewall policies to associate to the organization. | <code title="object&#40;&#123;&#10; name &#61; string&#10; policy &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [group_iam](variables.tf#L40) | Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam](variables.tf#L47) | IAM bindings, in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings](variables.tf#L54) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | <code title="map&#40;object&#40;&#123;&#10; members &#61; list&#40;string&#41;&#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;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings_additive](variables.tf#L69) | Individual additive IAM bindings. Keys are arbitrary. | <code title="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;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_data_access](variables.tf#L84) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | <code>map&#40;map&#40;list&#40;string&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_exclusions](variables.tf#L99) | Logging exclusions for this organization in the form {NAME -> FILTER}. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_sinks](variables.tf#L106) | Logging sinks to create for the organization. | <code title="map&#40;object&#40;&#123;&#10; bq_partitioned_table &#61; optional&#40;bool&#41;&#10; description &#61; optional&#40;string&#41;&#10; destination &#61; string&#10; disabled &#61; optional&#40;bool, false&#41;&#10; exclusions &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; filter &#61; string&#10; iam &#61; optional&#40;bool, true&#41;&#10; include_children &#61; optional&#40;bool, true&#41;&#10; type &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [network_tags](variables.tf#L137) | Network tags by key name. If `id` is provided, key creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | <code title="map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Managed by the Terraform organization module.&#34;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; id &#61; optional&#40;string&#41;&#10; network &#61; string &#35; project_id&#47;vpc_name&#10; values &#61; optional&#40;map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Managed by the Terraform organization module.&#34;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [org_policies](variables.tf#L159) | Organization policies applied to this organization keyed by policy name. | <code title="map&#40;object&#40;&#123;&#10; inherit_from_parent &#61; optional&#40;bool&#41; &#35; for list policies only.&#10; reset &#61; optional&#40;bool&#41;&#10; rules &#61; optional&#40;list&#40;object&#40;&#123;&#10; allow &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; deny &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; enforce &#61; optional&#40;bool&#41; &#35; for boolean policies only.&#10; condition &#61; optional&#40;object&#40;&#123;&#10; description &#61; optional&#40;string&#41;&#10; expression &#61; optional&#40;string&#41;&#10; location &#61; optional&#40;string&#41;&#10; title &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [org_policies_data_path](variables.tf#L186) | Path containing org policies in YAML format. | <code>string</code> | | <code>null</code> |
| [org_policy_custom_constraints](variables.tf#L192) | Organization policy custom constraints keyed by constraint name. | <code title="map&#40;object&#40;&#123;&#10; display_name &#61; optional&#40;string&#41;&#10; description &#61; optional&#40;string&#41;&#10; action_type &#61; string&#10; condition &#61; string&#10; method_types &#61; list&#40;string&#41;&#10; resource_types &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [org_policy_custom_constraints_data_path](variables.tf#L206) | Path containing org policy custom constraints in YAML format. | <code>string</code> | | <code>null</code> |
| [tag_bindings](variables.tf#L221) | Tag bindings for this organization, in key => tag value id format. | <code>map&#40;string&#41;</code> | | <code>null</code> |
| [tags](variables.tf#L227) | 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 title="map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Managed by the Terraform organization module.&#34;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; id &#61; optional&#40;string&#41;&#10; values &#61; optional&#40;map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Managed by the Terraform organization module.&#34;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; id &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [factories_config](variables.tf#L31) | Paths to data files and folders that enable factory functionality. | <code title="object&#40;&#123;&#10; custom_roles &#61; optional&#40;string&#41;&#10; org_policies &#61; optional&#40;string&#41;&#10; org_policy_custom_constraints &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [firewall_policy](variables.tf#L42) | Hierarchical firewall policies to associate to the organization. | <code title="object&#40;&#123;&#10; name &#61; string&#10; policy &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [group_iam](variables.tf#L51) | Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam](variables.tf#L58) | IAM bindings, in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings](variables.tf#L65) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | <code title="map&#40;object&#40;&#123;&#10; members &#61; list&#40;string&#41;&#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;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings_additive](variables.tf#L80) | Individual additive IAM bindings. Keys are arbitrary. | <code title="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;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_data_access](variables.tf#L95) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | <code>map&#40;map&#40;list&#40;string&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_exclusions](variables.tf#L110) | Logging exclusions for this organization in the form {NAME -> FILTER}. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_sinks](variables.tf#L117) | Logging sinks to create for the organization. | <code title="map&#40;object&#40;&#123;&#10; bq_partitioned_table &#61; optional&#40;bool&#41;&#10; description &#61; optional&#40;string&#41;&#10; destination &#61; string&#10; disabled &#61; optional&#40;bool, false&#41;&#10; exclusions &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; filter &#61; string&#10; iam &#61; optional&#40;bool, true&#41;&#10; include_children &#61; optional&#40;bool, true&#41;&#10; type &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [network_tags](variables.tf#L148) | Network tags by key name. If `id` is provided, key creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | <code title="map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Managed by the Terraform organization module.&#34;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; id &#61; optional&#40;string&#41;&#10; network &#61; string &#35; project_id&#47;vpc_name&#10; values &#61; optional&#40;map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Managed by the Terraform organization module.&#34;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [org_policies](variables.tf#L170) | Organization policies applied to this organization keyed by policy name. | <code title="map&#40;object&#40;&#123;&#10; inherit_from_parent &#61; optional&#40;bool&#41; &#35; for list policies only.&#10; reset &#61; optional&#40;bool&#41;&#10; rules &#61; optional&#40;list&#40;object&#40;&#123;&#10; allow &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; deny &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; enforce &#61; optional&#40;bool&#41; &#35; for boolean policies only.&#10; condition &#61; optional&#40;object&#40;&#123;&#10; description &#61; optional&#40;string&#41;&#10; expression &#61; optional&#40;string&#41;&#10; location &#61; optional&#40;string&#41;&#10; title &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [org_policy_custom_constraints](variables.tf#L197) | Organization policy custom constraints keyed by constraint name. | <code title="map&#40;object&#40;&#123;&#10; display_name &#61; optional&#40;string&#41;&#10; description &#61; optional&#40;string&#41;&#10; action_type &#61; string&#10; condition &#61; string&#10; method_types &#61; list&#40;string&#41;&#10; resource_types &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [tag_bindings](variables.tf#L220) | Tag bindings for this organization, in key => tag value id format. | <code>map&#40;string&#41;</code> | | <code>null</code> |
| [tags](variables.tf#L226) | 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 title="map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Managed by the Terraform organization module.&#34;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; id &#61; optional&#40;string&#41;&#10; values &#61; optional&#40;map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Managed by the Terraform organization module.&#34;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; id &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
## Outputs
@ -478,12 +511,12 @@ module "org" {
|---|---|:---:|
| [custom_constraint_ids](outputs.tf#L17) | Map of CUSTOM_CONSTRAINTS => ID in the organization. | |
| [custom_role_id](outputs.tf#L22) | Map of custom role IDs created in the organization. | |
| [custom_roles](outputs.tf#L35) | Map of custom roles resources created in the organization. | |
| [id](outputs.tf#L40) | Fully qualified organization id. | |
| [network_tag_keys](outputs.tf#L57) | Tag key resources. | |
| [network_tag_values](outputs.tf#L66) | Tag value resources. | |
| [organization_id](outputs.tf#L76) | Organization id dependent on module resources. | |
| [sink_writer_identities](outputs.tf#L93) | Writer identities created for each sink. | |
| [tag_keys](outputs.tf#L101) | Tag key resources. | |
| [tag_values](outputs.tf#L110) | Tag value resources. | |
| [custom_roles](outputs.tf#L32) | Map of custom roles resources created in the organization. | |
| [id](outputs.tf#L37) | Fully qualified organization id. | |
| [network_tag_keys](outputs.tf#L54) | Tag key resources. | |
| [network_tag_values](outputs.tf#L63) | Tag value resources. | |
| [organization_id](outputs.tf#L73) | Organization id dependent on module resources. | |
| [sink_writer_identities](outputs.tf#L90) | Writer identities created for each sink. | |
| [tag_keys](outputs.tf#L98) | Tag key resources. | |
| [tag_values](outputs.tf#L107) | Tag value resources. | |
<!-- END TFDOC -->

View File

@ -17,12 +17,32 @@
# tfdoc:file:description IAM bindings, roles and audit logging resources.
locals {
_custom_roles = {
for f in try(fileset(var.factories_config.custom_roles, "*.yaml"), []) :
replace(f, ".yaml", "") => yamldecode(
file("${var.factories_config.custom_roles}/${f}")
)
}
_group_iam_roles = distinct(flatten(values(var.group_iam)))
_group_iam = {
for r in local._group_iam_roles : r => [
for k, v in var.group_iam : "group:${k}" if try(index(v, r), null) != null
]
}
custom_roles = merge(
{
for k, v in local._custom_roles : k => {
name = lookup(v, "name", k)
permissions = v["includedPermissions"]
}
},
{
for k, v in var.custom_roles : k => {
name = k
permissions = v
}
}
)
iam = {
for role in distinct(concat(keys(var.iam), keys(local._group_iam))) :
role => concat(
@ -32,13 +52,27 @@ locals {
}
}
# we use a different key for custom roles to allow referring to the role alias
# in Terraform, while still being able to define unique role names
check "custom_roles" {
assert {
condition = (
length(local.custom_roles) == length({
for k, v in local.custom_roles : v.name => null
})
)
error_message = "Duplicate role name in custom roles."
}
}
resource "google_organization_iam_custom_role" "roles" {
for_each = var.custom_roles
for_each = local.custom_roles
org_id = local.organization_id_numeric
role_id = each.key
title = "Custom role ${each.key}"
role_id = each.value.name
title = "Custom role ${each.value.name}"
description = "Terraform-managed."
permissions = each.value
permissions = each.value.permissions
}
resource "google_organization_iam_binding" "authoritative" {

View File

@ -16,11 +16,9 @@
locals {
_custom_constraints_factory_data_raw = merge([
for f in try(fileset(var.org_policy_custom_constraints_data_path, "*.yaml"), []) :
yamldecode(file("${var.org_policy_custom_constraints_data_path}/${f}"))
for f in try(fileset(var.factories_config.org_policy_custom_constraints, "*.yaml"), []) :
yamldecode(file("${var.factories_config.org_policy_custom_constraints}/${f}"))
]...)
_custom_constraints_factory_data = {
for k, v in local._custom_constraints_factory_data_raw :
k => {
@ -32,9 +30,10 @@ locals {
resource_types = v.resource_types
}
}
_custom_constraints = merge(local._custom_constraints_factory_data, var.org_policy_custom_constraints)
_custom_constraints = merge(
local._custom_constraints_factory_data,
var.org_policy_custom_constraints
)
custom_constraints = {
for k, v in local._custom_constraints :
k => merge(v, {

View File

@ -18,10 +18,9 @@
locals {
_factory_data_raw = merge([
for f in try(fileset(var.org_policies_data_path, "*.yaml"), []) :
yamldecode(file("${var.org_policies_data_path}/${f}"))
for f in try(fileset(var.factories_config.org_policies, "*.yaml"), []) :
yamldecode(file("${var.factories_config.org_policies}/${f}"))
]...)
# simulate applying defaults to data coming from yaml files
_factory_data = {
for k, v in local._factory_data_raw :
@ -49,9 +48,7 @@ locals {
]
}
}
_org_policies = merge(local._factory_data, var.org_policies)
org_policies = {
for k, v in local._org_policies :
k => merge(v, {

View File

@ -22,14 +22,11 @@ output "custom_constraint_ids" {
output "custom_role_id" {
description = "Map of custom role IDs created in the organization."
value = {
for role_id, role in google_organization_iam_custom_role.roles :
for k, v in google_organization_iam_custom_role.roles :
# build the string manually so that role IDs can be used as map
# keys (useful for folder/organization/project-level iam bindings)
(role_id) => "${var.organization_id}/roles/${role_id}"
(k) => "${var.organization_id}/roles/${local.custom_roles[k].name}"
}
depends_on = [
google_organization_iam_custom_role.roles
]
}
output "custom_roles" {

View File

@ -28,6 +28,17 @@ variable "custom_roles" {
nullable = false
}
variable "factories_config" {
description = "Paths to data files and folders that enable factory functionality."
type = object({
custom_roles = optional(string)
org_policies = optional(string)
org_policy_custom_constraints = optional(string)
})
nullable = false
default = {}
}
variable "firewall_policy" {
description = "Hierarchical firewall policies to associate to the organization."
type = object({
@ -183,12 +194,6 @@ variable "org_policies" {
nullable = false
}
variable "org_policies_data_path" {
description = "Path containing org policies in YAML format."
type = string
default = null
}
variable "org_policy_custom_constraints" {
description = "Organization policy custom constraints keyed by constraint name."
type = map(object({
@ -203,12 +208,6 @@ variable "org_policy_custom_constraints" {
nullable = false
}
variable "org_policy_custom_constraints_data_path" {
description = "Path containing org policy custom constraints in YAML format."
type = string
default = null
}
variable "organization_id" {
description = "Organization id in organizations/nnnnnn format."
type = string

View File

@ -421,12 +421,14 @@ The example below deploys a few organization policies split between two YAML fil
```hcl
module "project" {
source = "./fabric/modules/project"
billing_account = var.billing_account_id
name = "project"
parent = var.folder_id
prefix = var.prefix
org_policies_data_path = "configs/org-policies/"
source = "./fabric/modules/project"
billing_account = var.billing_account_id
name = "project"
parent = var.folder_id
prefix = var.prefix
factories_config = {
org_policies = "configs/org-policies/"
}
}
# tftest modules=1 resources=8 files=boolean,list inventory=org-policies.yaml e2e
```
@ -898,7 +900,7 @@ module "bucket" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [name](variables.tf#L186) | Project name and id suffix. | <code>string</code> | ✓ | |
| [name](variables.tf#L196) | Project name and id suffix. | <code>string</code> | ✓ | |
| [auto_create_network](variables.tf#L17) | Whether to create the default network for the project. | <code>bool</code> | | <code>false</code> |
| [billing_account](variables.tf#L23) | Billing account id. | <code>string</code> | | <code>null</code> |
| [compute_metadata](variables.tf#L29) | Optional compute metadata key/values. Only usable if compute API has been enabled. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
@ -906,41 +908,41 @@ module "bucket" {
| [custom_roles](variables.tf#L43) | Map of role name => list of permissions to create in this project. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [default_service_account](variables.tf#L50) | Project default service account setting: can be one of `delete`, `deprivilege`, `disable`, or `keep`. | <code>string</code> | | <code>&#34;keep&#34;</code> |
| [descriptive_name](variables.tf#L63) | Name of the project name. Used for project name instead of `name` variable. | <code>string</code> | | <code>null</code> |
| [group_iam](variables.tf#L69) | Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam](variables.tf#L76) | Authoritative IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings](variables.tf#L83) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | <code title="map&#40;object&#40;&#123;&#10; members &#61; list&#40;string&#41;&#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;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings_additive](variables.tf#L98) | Individual additive IAM bindings. Keys are arbitrary. | <code title="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;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [labels](variables.tf#L113) | Resource labels. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [lien_reason](variables.tf#L120) | If non-empty, creates a project lien with this description. | <code>string</code> | | <code>null</code> |
| [logging_data_access](variables.tf#L126) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | <code>map&#40;map&#40;list&#40;string&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_exclusions](variables.tf#L141) | Logging exclusions for this project in the form {NAME -> FILTER}. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_sinks](variables.tf#L148) | Logging sinks to create for this project. | <code title="map&#40;object&#40;&#123;&#10; bq_partitioned_table &#61; optional&#40;bool&#41;&#10; description &#61; optional&#40;string&#41;&#10; destination &#61; string&#10; disabled &#61; optional&#40;bool, false&#41;&#10; exclusions &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; filter &#61; string&#10; iam &#61; optional&#40;bool, true&#41;&#10; type &#61; string&#10; unique_writer &#61; optional&#40;bool, true&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [metric_scopes](variables.tf#L179) | List of projects that will act as metric scopes for this project. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [org_policies](variables.tf#L191) | Organization policies applied to this project keyed by policy name. | <code title="map&#40;object&#40;&#123;&#10; inherit_from_parent &#61; optional&#40;bool&#41; &#35; for list policies only.&#10; reset &#61; optional&#40;bool&#41;&#10; rules &#61; optional&#40;list&#40;object&#40;&#123;&#10; allow &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; deny &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; enforce &#61; optional&#40;bool&#41; &#35; for boolean policies only.&#10; condition &#61; optional&#40;object&#40;&#123;&#10; description &#61; optional&#40;string&#41;&#10; expression &#61; optional&#40;string&#41;&#10; location &#61; optional&#40;string&#41;&#10; title &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [org_policies_data_path](variables.tf#L218) | Path containing org policies in YAML format. | <code>string</code> | | <code>null</code> |
| [parent](variables.tf#L224) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | <code>string</code> | | <code>null</code> |
| [prefix](variables.tf#L234) | Optional prefix used to generate project id and name. | <code>string</code> | | <code>null</code> |
| [project_create](variables.tf#L244) | Create project. When set to false, uses a data source to reference existing project. | <code>bool</code> | | <code>true</code> |
| [service_config](variables.tf#L250) | Configure service API activation. | <code title="object&#40;&#123;&#10; disable_on_destroy &#61; bool&#10; disable_dependent_services &#61; bool&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; disable_on_destroy &#61; false&#10; disable_dependent_services &#61; false&#10;&#125;">&#123;&#8230;&#125;</code> |
| [service_encryption_key_ids](variables.tf#L262) | Cloud KMS encryption key in {SERVICE => [KEY_URL]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [service_perimeter_bridges](variables.tf#L269) | Name of VPC-SC Bridge perimeters to add project into. See comment in the variables file for format. | <code>list&#40;string&#41;</code> | | <code>null</code> |
| [service_perimeter_standard](variables.tf#L276) | Name of VPC-SC Standard perimeter to add project into. See comment in the variables file for format. | <code>string</code> | | <code>null</code> |
| [services](variables.tf#L282) | Service APIs to enable. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [shared_vpc_host_config](variables.tf#L288) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | <code title="object&#40;&#123;&#10; enabled &#61; bool&#10; service_projects &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [shared_vpc_service_config](variables.tf#L297) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | <code title="object&#40;&#123;&#10; host_project &#61; string&#10; service_identity_iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; service_identity_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;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; host_project &#61; null&#10;&#125;">&#123;&#8230;&#125;</code> |
| [skip_delete](variables.tf#L320) | Allows the underlying resources to be destroyed without destroying the project itself. | <code>bool</code> | | <code>false</code> |
| [tag_bindings](variables.tf#L326) | Tag bindings for this project, in key => tag value id format. | <code>map&#40;string&#41;</code> | | <code>null</code> |
| [factories_config](variables.tf#L69) | Paths to data files and folders that enable factory functionality. | <code title="object&#40;&#123;&#10; custom_roles &#61; optional&#40;string&#41;&#10; org_policies &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [group_iam](variables.tf#L79) | Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam](variables.tf#L86) | Authoritative IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings](variables.tf#L93) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | <code title="map&#40;object&#40;&#123;&#10; members &#61; list&#40;string&#41;&#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;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings_additive](variables.tf#L108) | Individual additive IAM bindings. Keys are arbitrary. | <code title="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;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [labels](variables.tf#L123) | Resource labels. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [lien_reason](variables.tf#L130) | If non-empty, creates a project lien with this description. | <code>string</code> | | <code>null</code> |
| [logging_data_access](variables.tf#L136) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | <code>map&#40;map&#40;list&#40;string&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_exclusions](variables.tf#L151) | Logging exclusions for this project in the form {NAME -> FILTER}. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_sinks](variables.tf#L158) | Logging sinks to create for this project. | <code title="map&#40;object&#40;&#123;&#10; bq_partitioned_table &#61; optional&#40;bool&#41;&#10; description &#61; optional&#40;string&#41;&#10; destination &#61; string&#10; disabled &#61; optional&#40;bool, false&#41;&#10; exclusions &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; filter &#61; string&#10; iam &#61; optional&#40;bool, true&#41;&#10; type &#61; string&#10; unique_writer &#61; optional&#40;bool, true&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [metric_scopes](variables.tf#L189) | List of projects that will act as metric scopes for this project. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [org_policies](variables.tf#L201) | Organization policies applied to this project keyed by policy name. | <code title="map&#40;object&#40;&#123;&#10; inherit_from_parent &#61; optional&#40;bool&#41; &#35; for list policies only.&#10; reset &#61; optional&#40;bool&#41;&#10; rules &#61; optional&#40;list&#40;object&#40;&#123;&#10; allow &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; deny &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; enforce &#61; optional&#40;bool&#41; &#35; for boolean policies only.&#10; condition &#61; optional&#40;object&#40;&#123;&#10; description &#61; optional&#40;string&#41;&#10; expression &#61; optional&#40;string&#41;&#10; location &#61; optional&#40;string&#41;&#10; title &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [parent](variables.tf#L228) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | <code>string</code> | | <code>null</code> |
| [prefix](variables.tf#L238) | Optional prefix used to generate project id and name. | <code>string</code> | | <code>null</code> |
| [project_create](variables.tf#L248) | Create project. When set to false, uses a data source to reference existing project. | <code>bool</code> | | <code>true</code> |
| [service_config](variables.tf#L254) | Configure service API activation. | <code title="object&#40;&#123;&#10; disable_on_destroy &#61; bool&#10; disable_dependent_services &#61; bool&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; disable_on_destroy &#61; false&#10; disable_dependent_services &#61; false&#10;&#125;">&#123;&#8230;&#125;</code> |
| [service_encryption_key_ids](variables.tf#L266) | Cloud KMS encryption key in {SERVICE => [KEY_URL]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [service_perimeter_bridges](variables.tf#L273) | Name of VPC-SC Bridge perimeters to add project into. See comment in the variables file for format. | <code>list&#40;string&#41;</code> | | <code>null</code> |
| [service_perimeter_standard](variables.tf#L280) | Name of VPC-SC Standard perimeter to add project into. See comment in the variables file for format. | <code>string</code> | | <code>null</code> |
| [services](variables.tf#L286) | Service APIs to enable. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [shared_vpc_host_config](variables.tf#L292) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | <code title="object&#40;&#123;&#10; enabled &#61; bool&#10; service_projects &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [shared_vpc_service_config](variables.tf#L301) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | <code title="object&#40;&#123;&#10; host_project &#61; string&#10; service_identity_iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; service_identity_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;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; host_project &#61; null&#10;&#125;">&#123;&#8230;&#125;</code> |
| [skip_delete](variables.tf#L324) | Allows the underlying resources to be destroyed without destroying the project itself. | <code>bool</code> | | <code>false</code> |
| [tag_bindings](variables.tf#L330) | Tag bindings for this project, in key => tag value id format. | <code>map&#40;string&#41;</code> | | <code>null</code> |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| [custom_roles](outputs.tf#L17) | Ids of the created custom roles. | |
| [id](outputs.tf#L25) | Project id. | |
| [name](outputs.tf#L44) | Project name. | |
| [number](outputs.tf#L56) | Project number. | |
| [project_id](outputs.tf#L75) | Project id. | |
| [service_accounts](outputs.tf#L94) | Product robot service accounts in project. | |
| [services](outputs.tf#L110) | Service APIs to enabled in the project. | |
| [sink_writer_identities](outputs.tf#L119) | Writer identities created for each sink. | |
| [custom_role_ids](outputs.tf#L17) | Map of custom role IDs created in the project. | |
| [id](outputs.tf#L27) | Project id. | |
| [name](outputs.tf#L46) | Project name. | |
| [number](outputs.tf#L58) | Project number. | |
| [project_id](outputs.tf#L77) | Project id. | |
| [service_accounts](outputs.tf#L96) | Product robot service accounts in project. | |
| [services](outputs.tf#L112) | Service APIs to enabled in the project. | |
| [sink_writer_identities](outputs.tf#L121) | Writer identities created for each sink. | |
<!-- END TFDOC -->

View File

@ -20,12 +20,32 @@
# - external users need to have accepted the invitation email to join
locals {
_custom_roles = {
for f in try(fileset(var.factories_config.custom_roles, "*.yaml"), []) :
replace(f, ".yaml", "") => yamldecode(
file("${var.factories_config.custom_roles}/${f}")
)
}
_group_iam_roles = distinct(flatten(values(var.group_iam)))
_group_iam = {
for r in local._group_iam_roles : r => [
for k, v in var.group_iam : "group:${k}" if try(index(v, r), null) != null
]
}
custom_roles = merge(
{
for k, v in local._custom_roles : k => {
name = lookup(v, "name", k)
permissions = v["includedPermissions"]
}
},
{
for k, v in var.custom_roles : k => {
name = k
permissions = v
}
}
)
iam = {
for role in distinct(concat(keys(var.iam), keys(local._group_iam))) :
role => concat(
@ -35,13 +55,27 @@ locals {
}
}
# we use a different key for custom roles to allow referring to the role alias
# in Terraform, while still being able to define unique role names
check "custom_roles" {
assert {
condition = (
length(local.custom_roles) == length({
for k, v in local.custom_roles : v.name => null
})
)
error_message = "Duplicate role name in custom roles."
}
}
resource "google_project_iam_custom_role" "roles" {
for_each = var.custom_roles
for_each = local.custom_roles
project = local.project.project_id
role_id = each.key
title = "Custom role ${each.key}"
role_id = each.value.name
title = "Custom role ${each.value.name}"
description = "Terraform-managed."
permissions = each.value
permissions = each.value.permissions
}
resource "google_project_iam_binding" "authoritative" {

View File

@ -18,10 +18,9 @@
locals {
_factory_data_raw = merge([
for f in try(fileset(var.org_policies_data_path, "*.yaml"), []) :
yamldecode(file("${var.org_policies_data_path}/${f}"))
for f in try(fileset(var.factories_config.org_policies, "*.yaml"), []) :
yamldecode(file("${var.factories_config.org_policies}/${f}"))
]...)
# simulate applying defaults to data coming from yaml files
_factory_data = {
for k, v in local._factory_data_raw :
@ -49,9 +48,7 @@ locals {
]
}
}
_org_policies = merge(local._factory_data, var.org_policies)
org_policies = {
for k, v in local._org_policies :
k => merge(v, {

View File

@ -14,11 +14,13 @@
* limitations under the License.
*/
output "custom_roles" {
description = "Ids of the created custom roles."
output "custom_role_ids" {
description = "Map of custom role IDs created in the project."
value = {
for name, role in google_project_iam_custom_role.roles :
name => role.id
for k, v in google_project_iam_custom_role.roles :
# build the string manually so that role IDs can be used as map
# keys (useful for folder/organization/project-level iam bindings)
(k) => "projects/${local.prefix}${var.name}/roles/${local.custom_roles[k].name}"
}
}

View File

@ -66,6 +66,16 @@ variable "descriptive_name" {
default = null
}
variable "factories_config" {
description = "Paths to data files and folders that enable factory functionality."
type = object({
custom_roles = optional(string)
org_policies = optional(string)
})
nullable = false
default = {}
}
variable "group_iam" {
description = "Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable."
type = map(list(string))
@ -215,12 +225,6 @@ variable "org_policies" {
nullable = false
}
variable "org_policies_data_path" {
description = "Path containing org policies in YAML format."
type = string
default = null
}
variable "parent" {
description = "Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format."
type = string

View File

@ -25,5 +25,5 @@ def test_policy_factory(plan_summary, tfvars_to_yaml, tmp_path, policy_type):
'modules/folder',
tf_var_files=['common.tfvars', f'org_policies_{policy_type}.tfvars'])
yaml_plan = plan_summary('modules/folder', tf_var_files=['common.tfvars'],
org_policies_data_path=f'{tmp_path}')
factories_config=f'{{org_policies="{tmp_path}"}}')
assert tfvars_plan.values == yaml_plan.values

View File

@ -0,0 +1,40 @@
# Copyright 2023 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.org.google_organization_iam_custom_role.roles["test_2"]:
description: Terraform-managed.
org_id: '1122334455'
permissions:
- resourcemanager.projects.get
- resourcemanager.projects.getIamPolicy
- resourcemanager.projects.list
role_id: projectViewer
stage: GA
title: Custom role projectViewer
module.org.google_organization_iam_custom_role.roles["test_1"]:
description: Terraform-managed.
org_id: '1122334455'
permissions:
- compute.globalOperations.get
role_id: test_1
stage: GA
title: Custom role test_1
counts:
google_organization_iam_custom_role: 2
modules: 1
resources: 2
outputs: {}

View File

@ -26,7 +26,7 @@ def test_policy_factory(plan_summary, tfvars_to_yaml, tmp_path, policy_type):
tf_var_files=['common.tfvars', f'org_policies_{policy_type}.tfvars'])
yaml_plan = plan_summary('modules/organization',
tf_var_files=['common.tfvars'],
org_policies_data_path=f'{tmp_path}')
factories_config=f'{{org_policies="{tmp_path}"}}')
assert tfvars_plan.values == yaml_plan.values
@ -39,5 +39,5 @@ def test_custom_constraint_factory(plan_summary, tfvars_to_yaml, tmp_path):
tf_var_files=['common.tfvars', f'org_policies_custom_constraints.tfvars'])
yaml_plan = plan_summary(
'modules/organization', tf_var_files=['common.tfvars'],
org_policy_custom_constraints_data_path=f'{tmp_path}')
factories_config=f'{{org_policy_custom_constraints="{tmp_path}"}}')
assert tfvars_plan.values == yaml_plan.values

View File

@ -34,7 +34,7 @@ def test_policy_implementation():
'@@ -17 +17 @@\n',
'-# tfdoc:file:description Project-level organization policies.\n',
'+# tfdoc:file:description Folder-level organization policies.\n',
'@@ -58,2 +58,2 @@\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.name}/policies/${k}"\n',
@ -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',
'@@ -58,2 +58,2 @@\n',
'@@ -55,2 +55,2 @@\n',
'- name = "${local.folder.name}/policies/${k}"\n',
'- parent = local.folder.name\n',
'+ name = "${var.organization_id}/policies/${k}"\n',
'+ parent = var.organization_id\n',
'@@ -116,0 +117,9 @@\n',
'@@ -113,0 +114,9 @@\n',
'+ depends_on = [\n',
'+ google_organization_iam_binding.authoritative,\n',
'+ google_organization_iam_binding.bindings,\n',

View File

@ -25,5 +25,5 @@ def test_policy_factory(plan_summary, tfvars_to_yaml, tmp_path, policy_type):
'modules/project',
tf_var_files=['common.tfvars', f'org_policies_{policy_type}.tfvars'])
yaml_plan = plan_summary('modules/project', tf_var_files=['common.tfvars'],
org_policies_data_path=f'{tmp_path}')
factories_config=f'{{org_policies="{tmp_path}"}}')
assert tfvars_plan.values == yaml_plan.values