diff --git a/fast/stages/0-bootstrap/README.md b/fast/stages/0-bootstrap/README.md index 1ad86d0d..51e59a8a 100644 --- a/fast/stages/0-bootstrap/README.md +++ b/fast/stages/0-bootstrap/README.md @@ -626,7 +626,7 @@ 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`. | object({…}) | ✓ | | | -| [organization](variables.tf#L229) | Organization details. | object({…}) | ✓ | | | +| [organization](variables.tf#L229) | Organization details. | object({…}) | ✓ | | | | [prefix](variables.tf#L244) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | | | [bootstrap_user](variables.tf#L27) | Email of the nominal user running this stage for the first time. | string | | null | | | [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. | object({…}) | | null | | diff --git a/fast/stages/0-bootstrap/data/org-policies/compute.yaml b/fast/stages/0-bootstrap/data/org-policies/compute.yaml index 652ac645..98fa6c65 100644 --- a/fast/stages/0-bootstrap/data/org-policies/compute.yaml +++ b/fast/stages/0-bootstrap/data/org-policies/compute.yaml @@ -34,22 +34,25 @@ compute.trustedImageProjects: rules: - allow: values: - - "projects/centos-cloud" - - "projects/cos-cloud" - - "projects/debian-cloud" - - "projects/fedora-cloud" - - "projects/fedora-coreos-cloud" - - "projects/opensuse-cloud" - - "projects/rhel-cloud" - - "projects/rhel-sap-cloud" - - "projects/rocky-linux-cloud" - - "projects/suse-cloud" - - "projects/suse-byos-cloud" - - "projects/suse-sap-cloud" - - "projects/ubuntu-os-cloud" - - "projects/ubuntu-os-pro-cloud" - - "projects/windows-cloud" - - "projects/windows-sql-cloud" + - "is:projects/centos-cloud" + - "is:projects/cos-cloud" + - "is:projects/debian-cloud" + - "is:projects/fedora-cloud" + - "is:projects/fedora-coreos-cloud" + - "is:projects/opensuse-cloud" + - "is:projects/rhel-cloud" + - "is:projects/rhel-sap-cloud" + - "is:projects/rocky-linux-cloud" + - "is:projects/suse-cloud" + - "is:projects/suse-sap-cloud" + - "is:projects/ubuntu-os-cloud" + - "is:projects/ubuntu-os-pro-cloud" + - "is:projects/windows-cloud" + - "is:projects/windows-sql-cloud" + - "is:projects/confidential-vm-images" + - "is:projects/backupdr-images" + - "is:projects/deeplearning-platform-release" + - "is:projects/serverless-vpc-access-images" # compute.disableInternetNetworkEndpointGroup: # rules: diff --git a/fast/stages/0-bootstrap/data/org-policies/serverless.yaml b/fast/stages/0-bootstrap/data/org-policies/serverless.yaml index 1fce1a9b..1c07905a 100644 --- a/fast/stages/0-bootstrap/data/org-policies/serverless.yaml +++ b/fast/stages/0-bootstrap/data/org-policies/serverless.yaml @@ -10,7 +10,6 @@ run.allowedIngress: rules: - allow: values: - - is:internal - is:internal-and-cloud-load-balancing # run.allowedVPCEgress: # rules: diff --git a/fast/stages/0-bootstrap/organization-iam.tf b/fast/stages/0-bootstrap/organization-iam.tf index 514d9af3..6ca44a24 100644 --- a/fast/stages/0-bootstrap/organization-iam.tf +++ b/fast/stages/0-bootstrap/organization-iam.tf @@ -22,7 +22,7 @@ locals { "roles/billing.creator" ] # domain IAM bindings - iam_domain_bindings = { + iam_domain_bindings = var.organization.domain == null ? {} : { "domain:${var.organization.domain}" = { authoritative = ["roles/browser"] additive = [] diff --git a/fast/stages/0-bootstrap/organization.tf b/fast/stages/0-bootstrap/organization.tf index 8cdb70b2..44bd78c2 100644 --- a/fast/stages/0-bootstrap/organization.tf +++ b/fast/stages/0-bootstrap/organization.tf @@ -46,7 +46,7 @@ locals { ] ]) drs_domains = concat( - [var.organization.customer_id], + var.organization.customer_id == null ? [] : [var.organization.customer_id], var.org_policies_config.constraints.allowed_policy_member_domains ) drs_tag_name = "${var.organization.id}/${var.org_policies_config.tag_name}" @@ -104,26 +104,28 @@ locals { iam_roles_additive = distinct([ for k, v in local._iam_bindings_additive : v.role ]) - # import org policies only when not using bootstrap user - import_org_policies = var.org_policies_config.import_defaults && var.bootstrap_user != null } # TODO: add a check block to ensure our custom roles exist in the factory files # import org policy constraints enabled by default in new orgs since February 2024 import { - for_each = !local.import_org_policies ? toset([]) : toset([ - "compute.requireOsLogin", - "compute.skipDefaultNetworkCreation", - "compute.vmExternalIpAccess", - "iam.allowedPolicyMemberDomains", - "iam.automaticIamGrantsForDefaultServiceAccounts", - "iam.disableServiceAccountKeyCreation", - "iam.disableServiceAccountKeyUpload", - "sql.restrictAuthorizedNetworks", - "sql.restrictPublicIp", - "storage.uniformBucketLevelAccess", - ]) + for_each = ( + !var.org_policies_config.import_defaults || var.bootstrap_user != null + ? toset([]) + : toset([ + "compute.requireOsLogin", + "compute.skipDefaultNetworkCreation", + "compute.vmExternalIpAccess", + "iam.allowedPolicyMemberDomains", + "iam.automaticIamGrantsForDefaultServiceAccounts", + "iam.disableServiceAccountKeyCreation", + "iam.disableServiceAccountKeyUpload", + "sql.restrictAuthorizedNetworks", + "sql.restrictPublicIp", + "storage.uniformBucketLevelAccess", + ]) + ) id = "organizations/${var.organization.id}/policies/${each.key}" to = module.organization.google_org_policy_policy.default[each.key] } @@ -151,34 +153,48 @@ module "organization" { var.iam_bindings_additive ) # delegated role grant for resource manager service account - iam_bindings = { - organization_iam_admin_conditional = { - members = [module.automation-tf-resman-sa.iam_email] - role = module.organization.custom_role_id["organization_iam_admin"] - condition = { - expression = format( - "api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly([%s])", - join(",", formatlist("'%s'", concat( - [ + iam_bindings = merge( + { + organization_iam_admin_conditional = { + members = [module.automation-tf-resman-sa.iam_email] + role = module.organization.custom_role_id["organization_iam_admin"] + condition = { + expression = format( + "api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly([%s])", + join(",", formatlist("'%s'", [ "roles/accesscontextmanager.policyAdmin", "roles/compute.orgFirewallPolicyAdmin", "roles/compute.xpnAdmin", "roles/orgpolicy.policyAdmin", + "roles/orgpolicy.policyViewer", "roles/resourcemanager.organizationViewer", module.organization.custom_role_id["tenant_network_admin"] - ], - local.billing_mode == "org" ? [ + ])) + ) + title = "automation_sa_delegated_grants" + description = "Automation service account delegated grants." + } + } + }, + local.billing_mode != "org" ? {} : { + organization_billing_conditional = { + members = [module.automation-tf-resman-sa.iam_email] + role = module.organization.custom_role_id["organization_iam_admin"] + condition = { + expression = format( + "api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly([%s])", + join(",", formatlist("'%s'", [ "roles/billing.admin", "roles/billing.costsManager", "roles/billing.user", - ] : [] - ))) - ) - title = "automation_sa_delegated_grants" - description = "Automation service account delegated grants." + ])) + ) + title = "automation_sa_delegated_grants" + description = "Automation service account delegated grants." + } } } - } + ) custom_roles = var.custom_roles factories_config = { custom_roles = var.factories_config.custom_roles diff --git a/fast/stages/0-bootstrap/variables.tf b/fast/stages/0-bootstrap/variables.tf index a3d0bb53..d2d47a1f 100644 --- a/fast/stages/0-bootstrap/variables.tf +++ b/fast/stages/0-bootstrap/variables.tf @@ -229,9 +229,9 @@ variable "org_policies_config" { variable "organization" { description = "Organization details." type = object({ - domain = string id = number - customer_id = string + domain = optional(string) + customer_id = optional(string) }) } diff --git a/fast/stages/1-resman/README.md b/fast/stages/1-resman/README.md index a28451a1..a84c704f 100644 --- a/fast/stages/1-resman/README.md +++ b/fast/stages/1-resman/README.md @@ -254,7 +254,7 @@ This is an example that shows how to populate the relevant variables. ```tfvars tenants = { tn0 = { - admin_group_email = "tn-0-admins@tenant.example.org" + admin_principal = "group:tn-0-admins@tenant.example.org" descriptive_name = "Tenant 0" # an optional billing account and org can be specified for the tenant organization = { @@ -264,7 +264,7 @@ tenants = { } } tnq = { - admin_group_email = "tn-1-admins@example.org" + admin_principal = "group:tn-1-admins@example.org" descriptive_name = "Tenant 1" } } diff --git a/fast/stages/1-resman/branch-security.tf b/fast/stages/1-resman/branch-security.tf index b688d935..5e828812 100644 --- a/fast/stages/1-resman/branch-security.tf +++ b/fast/stages/1-resman/branch-security.tf @@ -34,8 +34,8 @@ module "branch-security-folder" { "roles/resourcemanager.folderAdmin" = [module.branch-security-sa.iam_email] "roles/resourcemanager.projectCreator" = [module.branch-security-sa.iam_email] # read-only (plan) automation service account - "roles/viewer" = [module.branch-network-r-sa.iam_email] - "roles/resourcemanager.folderViewer" = [module.branch-network-r-sa.iam_email] + "roles/viewer" = [module.branch-security-r-sa.iam_email] + "roles/resourcemanager.folderViewer" = [module.branch-security-r-sa.iam_email] } tag_bindings = { context = try( diff --git a/fast/stages/1-resman/branch-tenants.tf b/fast/stages/1-resman/branch-tenants.tf index aab02c0d..150f8eee 100644 --- a/fast/stages/1-resman/branch-tenants.tf +++ b/fast/stages/1-resman/branch-tenants.tf @@ -21,7 +21,7 @@ locals { tenant_iam = { for k, v in var.tenants : k => [ - "group:${v.admin_group_email}", + v.admin_principal, module.tenant-self-iac-sa[k].iam_email ] } diff --git a/fast/stages/1-resman/organization-iam.tf b/fast/stages/1-resman/organization-iam.tf index 28764a25..c5c17b90 100644 --- a/fast/stages/1-resman/organization-iam.tf +++ b/fast/stages/1-resman/organization-iam.tf @@ -127,13 +127,13 @@ locals { }, { for k, v in var.tenants : "org-viewer-tenant_${k}_admin" => { - member = "group:${v.admin_group_email}" + member = v.admin_principal role = "roles/resourcemanager.organizationViewer" } }, local.billing_mode != "org" ? {} : { for k, v in var.tenants : "billing_user-tenant_${k}_billing_admin" => { - member = "group:${v.admin_group_email}" + member = v.admin_principal role = "roles/billing.user" } }, diff --git a/tests/fast/stages/s0_bootstrap/checklist.yaml b/tests/fast/stages/s0_bootstrap/checklist.yaml index a1f92cb0..a111eb02 100644 --- a/tests/fast/stages/s0_bootstrap/checklist.yaml +++ b/tests/fast/stages/s0_bootstrap/checklist.yaml @@ -194,7 +194,7 @@ values: module.organization.google_organization_iam_binding.bindings["organization_iam_admin_conditional"]: condition: - description: Automation service account delegated grants. - expression: api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly(['roles/accesscontextmanager.policyAdmin','roles/compute.orgFirewallPolicyAdmin','roles/compute.xpnAdmin','roles/orgpolicy.policyAdmin','roles/resourcemanager.organizationViewer','organizations/123456789012/roles/tenantNetworkAdmin','roles/billing.admin','roles/billing.costsManager','roles/billing.user']) + expression: api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly(['roles/accesscontextmanager.policyAdmin','roles/compute.orgFirewallPolicyAdmin','roles/compute.xpnAdmin','roles/orgpolicy.policyAdmin','roles/orgpolicy.policyViewer','roles/resourcemanager.organizationViewer','organizations/123456789012/roles/tenantNetworkAdmin']) title: automation_sa_delegated_grants members: - serviceAccount:fast-prod-resman-0@fast-prod-iac-core-0.iam.gserviceaccount.com @@ -363,7 +363,7 @@ counts: google_logging_organization_sink: 3 google_logging_project_bucket_config: 3 google_org_policy_policy: 20 - google_organization_iam_binding: 26 + google_organization_iam_binding: 27 google_organization_iam_custom_role: 6 google_organization_iam_member: 35 google_project: 3 @@ -381,4 +381,4 @@ counts: google_tags_tag_key: 1 google_tags_tag_value: 1 modules: 16 - resources: 190 + resources: 191 diff --git a/tests/fast/stages/s0_bootstrap/simple.yaml b/tests/fast/stages/s0_bootstrap/simple.yaml index 0a1e628a..2180aa45 100644 --- a/tests/fast/stages/s0_bootstrap/simple.yaml +++ b/tests/fast/stages/s0_bootstrap/simple.yaml @@ -42,7 +42,7 @@ counts: google_logging_organization_sink: 3 google_logging_project_bucket_config: 3 google_org_policy_policy: 20 - google_organization_iam_binding: 26 + google_organization_iam_binding: 27 google_organization_iam_custom_role: 6 google_organization_iam_member: 22 google_project: 3 @@ -61,7 +61,7 @@ counts: google_tags_tag_value: 1 local_file: 7 modules: 15 - resources: 181 + resources: 182 outputs: custom_roles: