/** * Copyright 2024 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. */ # tfdoc:file:description Organization-level IAM. locals { # reassemble logical bindings into the formats expected by the module _iam_bindings = merge( local.iam_domain_bindings, local.iam_sa_bindings, local.iam_user_bootstrap_bindings, { for k, v in local.iam_principal_bindings : k => { authoritative = [] additive = v.additive } } ) _iam_bindings_auth = flatten([ for member, data in local._iam_bindings : [ for role in data.authoritative : { member = member role = role } ] ]) _iam_bindings_add = flatten([ for member, data in local._iam_bindings : [ for role in data.additive : { member = member role = role } ] ]) drs_domains = concat( 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}" fast_custom_roles = [ "organization_admin_viewer", "organization_iam_admin", "service_project_network_admin", "storage_viewer", "tag_viewer", "tenant_network_admin", ] # intermediate values before we merge in what comes from the checklist _iam_principals = { for k, v in local.iam_principal_bindings : k => v.authoritative } _iam = merge( { for r in local.iam_delete_roles : r => [] }, { for b in local._iam_bindings_auth : b.role => b.member... } ) _iam_bindings_additive = { for b in local._iam_bindings_add : "${b.role}-${b.member}" => { member = b.member role = b.role } } # final values combining all sources iam_principals = { for k, v in local._iam_principals : k => distinct(concat( v, try(local.checklist.iam_principals[k], []) )) } iam = { for k, v in local._iam : k => distinct(concat( v, try(local.checklist.iam[k].authoritative, []) )) } iam_bindings_additive = merge( local._iam_bindings_additive, { for k, v in try(local.checklist.iam_bindings, {}) : v.key => v if lookup(local._iam_bindings_additive, v.key, null) == null } ) # compute authoritative and additive roles for use by add-ons (checklist, etc.) iam_roles_authoritative = distinct(concat( flatten(values(local._iam_principals)), keys(local._iam) )) iam_roles_additive = distinct([ for k, v in local._iam_bindings_additive : v.role ]) } # 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 = ( !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] } module "organization" { source = "../../../modules/organization" organization_id = "organizations/${var.organization.id}" # human (groups) IAM bindings iam_by_principals = { for k, v in local.iam_principals : k => distinct(concat(v, lookup(var.iam_by_principals, k, []))) } # machine (service accounts) IAM bindings iam = merge( { for k, v in local.iam : k => distinct(concat(v, lookup(var.iam, k, []))) }, { for k, v in var.iam : k => v if lookup(local.iam, k, null) == null } ) # additive bindings, used for roles co-managed by different stages iam_bindings_additive = merge( local.iam_bindings_additive, var.iam_bindings_additive ) # delegated role grant for resource manager service account 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"] ])) ) 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." } } } ) 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" destination = local.log_sink_destinations[name].id filter = attrs.filter type = attrs.type } } org_policies = var.bootstrap_user != null ? {} : { "iam.allowedPolicyMemberDomains" = { rules = [ { allow = { values = local.drs_domains } condition = { expression = ( "!resource.matchTag('${local.drs_tag_name}', 'allowed-policy-member-domains-all')" ) } }, { allow = { all = true } condition = { expression = ( "resource.matchTag('${local.drs_tag_name}', 'allowed-policy-member-domains-all')" ) title = "allow-all" } }, ] } # "gcp.resourceLocations" = {} # "iam.workloadIdentityPoolProviders" = {} } tags = { (var.org_policies_config.tag_name) = { description = "Organization policy conditions." iam = {} values = merge( { allowed-policy-member-domains-all = {} }, var.org_policies_config.tag_values ) } } }