cloud-foundation-fabric/fast/stages/0-bootstrap/organization.tf

231 lines
7.9 KiB
HCL

/**
* Copyright 2022 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 {
# organization authoritative IAM bindings, in an easy to edit format before
# they are combined with var.iam a bit further in locals
_iam = {
"roles/browser" = [
"domain:${var.organization.domain}"
]
"roles/logging.admin" = concat(
[
module.automation-tf-bootstrap-sa.iam_email,
module.automation-tf-resman-sa.iam_email
],
local._iam_bootstrap_user
)
"roles/owner" = local._iam_bootstrap_user
"roles/resourcemanager.folderAdmin" = [
module.automation-tf-resman-sa.iam_email
]
"roles/resourcemanager.organizationAdmin" = concat(
[module.automation-tf-bootstrap-sa.iam_email],
local._iam_bootstrap_user
)
"roles/resourcemanager.projectCreator" = concat(
[
module.automation-tf-bootstrap-sa.iam_email,
module.automation-tf-resman-sa.iam_email
],
local._iam_bootstrap_user
)
"roles/resourcemanager.projectMover" = [
module.automation-tf-bootstrap-sa.iam_email
]
"roles/resourcemanager.tagAdmin" = [
module.automation-tf-resman-sa.iam_email
]
"roles/resourcemanager.tagUser" = [
module.automation-tf-resman-sa.iam_email
]
}
# organization additive IAM bindings, in an easy to edit format before
# they are combined with var.iam_additive a bit further in locals
_iam_additive = merge(
{
"roles/accesscontextmanager.policyAdmin" = [
local.groups_iam.gcp-security-admins
]
"roles/compute.orgFirewallPolicyAdmin" = [
local.groups_iam.gcp-network-admins
]
"roles/compute.xpnAdmin" = [
local.groups_iam.gcp-network-admins
]
# use additive to support cross-org roles for billing
"roles/iam.organizationRoleAdmin" = [
# uncomment if roles/owner is removed to organization admins
# local.groups.gcp-organization-admins,
local.groups_iam.gcp-security-admins,
module.automation-tf-bootstrap-sa.iam_email
]
"roles/orgpolicy.policyAdmin" = [
local.groups_iam.gcp-organization-admins,
local.groups_iam.gcp-security-admins,
module.automation-tf-resman-sa.iam_email
]
# the following is useful if roles/browser is not desirable
# "roles/resourcemanager.organizationViewer" = [
# "domain:${var.organization.domain}"
# ]
},
var.billing_account.is_org_level ? {
"roles/billing.admin" = [
local.groups_iam.gcp-billing-admins,
local.groups_iam.gcp-organization-admins,
module.automation-tf-bootstrap-sa.iam_email,
module.automation-tf-resman-sa.iam_email
],
"roles/billing.costsManager" = [
local.groups_iam.gcp-billing-admins,
local.groups_iam.gcp-organization-admins,
module.automation-tf-bootstrap-sa.iam_email,
module.automation-tf-resman-sa.iam_email
]
} : {}
)
_iam_bootstrap_user = (
var.bootstrap_user == null ? [] : ["user:${var.bootstrap_user}"]
)
iam = {
for role in local.iam_roles : role => distinct(concat(
try(sort(local._iam[role]), []),
try(sort(var.iam[role]), [])
))
}
iam_additive = {
for role in local.iam_roles_additive : role => distinct(concat(
try(sort(local._iam_additive[role]), []),
try(sort(var.iam_additive[role]), [])
))
}
iam_roles = distinct(concat(
keys(local._iam), keys(var.iam)
))
iam_roles_additive = distinct(concat(
keys(local._iam_additive), keys(var.iam_additive)
))
}
module "organization" {
source = "../../../modules/organization"
organization_id = "organizations/${var.organization.id}"
# human (groups) IAM bindings
group_iam = {
(local.groups.gcp-organization-admins) = [
"roles/cloudasset.owner",
"roles/cloudsupport.admin",
"roles/compute.osAdminLogin",
"roles/compute.osLoginExternalUser",
"roles/owner",
# granted via additive roles
# roles/iam.organizationRoleAdmin
# roles/orgpolicy.policyAdmin
"roles/resourcemanager.folderAdmin",
"roles/resourcemanager.organizationAdmin",
"roles/resourcemanager.projectCreator",
],
(local.groups.gcp-network-admins) = [
"roles/cloudasset.owner",
"roles/cloudsupport.techSupportEditor",
]
(local.groups.gcp-security-admins) = [
"roles/cloudasset.owner",
"roles/cloudsupport.techSupportEditor",
"roles/iam.securityReviewer",
"roles/logging.admin",
"roles/securitycenter.admin",
],
(local.groups.gcp-support) = [
"roles/cloudsupport.techSupportEditor",
"roles/logging.viewer",
"roles/monitoring.viewer",
]
}
# machine (service accounts) IAM bindings
iam = local.iam
# additive bindings, used for roles co-managed by different stages
iam_additive = local.iam_additive
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",
]
}
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
}
}
}
# assign the custom restricted Organization Admin role to the relevant service
# accounts, with a condition that only enables granting specific roles;
# these roles use additive bindings everywhere to avoid conflicts / permadiffs
resource "google_organization_iam_binding" "org_admin_delegated" {
org_id = var.organization.id
role = module.organization.custom_role_id[var.custom_role_names.organization_iam_admin]
members = [module.automation-tf-resman-sa.iam_email]
condition {
title = "automation_sa_delegated_grants"
description = "Automation service account delegated grants."
expression = format(
"api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly([%s])",
join(",", formatlist("'%s'", concat(
[
"roles/accesscontextmanager.policyAdmin",
"roles/compute.orgFirewallPolicyAdmin",
"roles/compute.xpnAdmin",
"roles/orgpolicy.policyAdmin",
"roles/resourcemanager.organizationViewer",
],
var.billing_account.is_org_level ? [
"roles/billing.admin",
"roles/billing.costsManager",
"roles/billing.user",
] : []
)))
)
}
depends_on = [module.organization]
}