diff --git a/fast/addons/1-resman-tenants/README.md b/fast/addons/1-resman-tenants/README.md
index d40422072..4a16731da 100644
--- a/fast/addons/1-resman-tenants/README.md
+++ b/fast/addons/1-resman-tenants/README.md
@@ -328,14 +328,14 @@ gcloud storage cp gs://{prefix}-{tenant-shortname}-prod-iac-core-0/tfvars/0-boot
|---|---|:---:|:---:|:---:|:---:|
| [automation](variables-fast.tf#L19) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap |
| [billing_account](variables-fast.tf#L42) | 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`. | object({…}) | ✓ | | 0-bootstrap |
-| [environments](variables-fast.tf#L69) | Environment names. | map(object({…})) | ✓ | | 0-globals |
-| [logging](variables-fast.tf#L115) | Logging resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap |
-| [org_policy_tags](variables-fast.tf#L134) | Organization policy tags. | object({…}) | ✓ | | 0-bootstrap |
-| [organization](variables-fast.tf#L124) | Organization details. | object({…}) | ✓ | | 0-bootstrap |
-| [prefix](variables-fast.tf#L151) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap |
-| [custom_roles](variables-fast.tf#L53) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap |
-| [groups](variables-fast.tf#L87) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | 0-bootstrap |
-| [locations](variables-fast.tf#L102) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | 0-bootstrap |
+| [environments](variables-fast.tf#L75) | Environment names. | map(object({…})) | ✓ | | 0-globals |
+| [logging](variables-fast.tf#L121) | Logging resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap |
+| [org_policy_tags](variables-fast.tf#L140) | Organization policy tags. | object({…}) | ✓ | | 0-bootstrap |
+| [organization](variables-fast.tf#L130) | Organization details. | object({…}) | ✓ | | 0-bootstrap |
+| [prefix](variables-fast.tf#L157) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap |
+| [custom_roles](variables-fast.tf#L53) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap |
+| [groups](variables-fast.tf#L93) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | 0-bootstrap |
+| [locations](variables-fast.tf#L108) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | 0-bootstrap |
| [names](variables.tf#L18) | Configuration for names used for resources and output files. | object({…}) | | {} | |
| [outputs_location](variables.tf#L28) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | |
| [root_node](variables.tf#L34) | Root folder under which tenants are created, in folders/nnnn format. Defaults to the organization if null. | string | | null | |
@@ -346,5 +346,5 @@ gcloud storage cp gs://{prefix}-{tenant-shortname}-prod-iac-core-0/tfvars/0-boot
| name | description | sensitive | consumers |
|---|---|:---:|---|
-| [tenants](outputs.tf#L130) | Tenant base configuration. | | |
+| [tenants](outputs.tf#L139) | Tenant base configuration. | | |
diff --git a/fast/addons/1-resman-tenants/outputs.tf b/fast/addons/1-resman-tenants/outputs.tf
index 71ec64f67..bb0c75d7b 100644
--- a/fast/addons/1-resman-tenants/outputs.tf
+++ b/fast/addons/1-resman-tenants/outputs.tf
@@ -71,9 +71,18 @@ locals {
for k, v in local.fast_tenants : k => {
billing_account = v.billing_account
groups = v.principals
- locations = v.locations
- organization = v.organization
- prefix = v.prefix
+ environments = {
+ for k, v in var.environments : k => {
+ is_default = v.is_default
+ key = k
+ name = v.name
+ short_name = v.short_name != null ? v.short_name : k
+ tag_name = v.tag_name != null ? v.tag_name : lower(v.name)
+ }
+ }
+ locations = v.locations
+ organization = v.organization
+ prefix = v.prefix
}
}
tenant_tfvars = {
diff --git a/fast/addons/1-resman-tenants/variables-fast.tf b/fast/addons/1-resman-tenants/variables-fast.tf
index ed246957f..81c68ef2b 100644
--- a/fast/addons/1-resman-tenants/variables-fast.tf
+++ b/fast/addons/1-resman-tenants/variables-fast.tf
@@ -54,13 +54,19 @@ variable "custom_roles" {
# tfdoc:variable:source 0-bootstrap
description = "Custom roles defined at the org level, in key => id format."
type = object({
- gcve_network_admin = string
- network_firewall_policies_admin = string
- ngfw_enterprise_admin = optional(string)
- ngfw_enterprise_viewer = optional(string)
+ billing_viewer = string
+ dns_zone_binder = string
+ kms_key_encryption_admin = string
+ kms_key_viewer = string
organization_admin_viewer = string
+ project_iam_viewer = string
service_project_network_admin = string
storage_viewer = string
+ gcve_network_admin = optional(string)
+ gcve_network_viewer = optional(string)
+ network_firewall_policies_admin = optional(string)
+ ngfw_enterprise_admin = optional(string)
+ ngfw_enterprise_viewer = optional(string)
tenant_network_admin = string
})
default = null
diff --git a/fast/stages/1-resman/tenant-root.tf b/fast/stages/1-resman/tenant-root.tf
index 38403ff54..bbb30a16a 100644
--- a/fast/stages/1-resman/tenant-root.tf
+++ b/fast/stages/1-resman/tenant-root.tf
@@ -20,7 +20,13 @@ module "root-folder" {
id = var.root_node
folder_create = false
# additive bindings via delegated IAM grant set in stage 0
- iam_bindings_additive = local.iam_bindings_additive
+ iam_bindings_additive = {
+ for k, v in local.iam_bindings_additive : k => {
+ role = lookup(var.custom_roles, v.role, v.role)
+ member = lookup(local.principals_iam, v.member, v.member)
+ condition = lookup(v, "condition", null)
+ }
+ }
logging_sinks = {
for name, attrs in local.log_sinks : name => {
bq_partitioned_table = attrs.type == "bigquery"
diff --git a/tests/fast/addons/a1_resman_tenants/simple.tfvars b/tests/fast/addons/a1_resman_tenants/simple.tfvars
index a1a588c70..4ccc1e002 100644
--- a/tests/fast/addons/a1_resman_tenants/simple.tfvars
+++ b/tests/fast/addons/a1_resman_tenants/simple.tfvars
@@ -15,10 +15,17 @@ billing_account = {
}
custom_roles = {
# organization_iam_admin = "organizations/123456789012/roles/organizationIamAdmin",
+ billing_viewer = "organizations/123456789012/roles/billingViewer"
+ dns_zone_binder = "organizations/123456789012/roles/dnsZoneBinder"
gcve_network_admin = "organizations/123456789012/roles/gcveNetworkAdmin"
+ gcve_network_viewer = "organizations/123456789012/roles/gcveNetworkViewer"
+ kms_key_encryption_admin = "organizations/123456789012/roles/kmsKeyEncryptionAdmin"
+ kms_key_viewer = "organizations/123456789012/roles/kmsKeyViewer"
network_firewall_policies_admin = "organizations/123456789012/roles/networkFirewallPoliciesAdmin"
ngfw_enterprise_admin = "organizations/123456789012/roles/ngfwEnterpriseAdmin"
+ ngfw_enterprise_viewer = "organizations/123456789012/roles/ngfwEnterpriseViewer"
organization_admin_viewer = "organizations/123456789012/roles/organizationAdminViewer"
+ project_iam_viewer = "organizations/123456789012/roles/projectIamViewer"
service_project_network_admin = "organizations/123456789012/roles/xpnServiceAdmin"
storage_viewer = "organizations/123456789012/roles/storageViewer"
tenant_network_admin = "organizations/123456789012/roles/tenantNetworkAdmin"