Support domainless orgs in FAST (#2086)
* bootstrap * align org policies to domainless enforced ones * fix #2073 * fix tests * fix team admin attribute in resman stage
This commit is contained in:
parent
bee3072568
commit
eb23bb62d2
|
@ -626,7 +626,7 @@ The `fast_features` variable consists of 4 toggles:
|
||||||
| name | description | type | required | default | producer |
|
| 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({ id = string is_org_level = optional(bool, true) no_iam = optional(bool, false) })">object({…})</code> | ✓ | | |
|
| [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({ id = string is_org_level = optional(bool, true) no_iam = optional(bool, false) })">object({…})</code> | ✓ | | |
|
||||||
| [organization](variables.tf#L229) | Organization details. | <code title="object({ domain = string id = number customer_id = string })">object({…})</code> | ✓ | | |
|
| [organization](variables.tf#L229) | Organization details. | <code title="object({ id = number domain = optional(string) customer_id = optional(string) })">object({…})</code> | ✓ | | |
|
||||||
| [prefix](variables.tf#L244) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | |
|
| [prefix](variables.tf#L244) | 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> | |
|
| [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({ bootstrap = optional(object({ name = string type = string branch = optional(string) identity_provider = optional(string) })) resman = optional(object({ name = string type = string branch = optional(string) identity_provider = optional(string) })) })">object({…})</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({ bootstrap = optional(object({ name = string type = string branch = optional(string) identity_provider = optional(string) })) resman = optional(object({ name = string type = string branch = optional(string) identity_provider = optional(string) })) })">object({…})</code> | | <code>null</code> | |
|
||||||
|
|
|
@ -34,22 +34,25 @@ compute.trustedImageProjects:
|
||||||
rules:
|
rules:
|
||||||
- allow:
|
- allow:
|
||||||
values:
|
values:
|
||||||
- "projects/centos-cloud"
|
- "is:projects/centos-cloud"
|
||||||
- "projects/cos-cloud"
|
- "is:projects/cos-cloud"
|
||||||
- "projects/debian-cloud"
|
- "is:projects/debian-cloud"
|
||||||
- "projects/fedora-cloud"
|
- "is:projects/fedora-cloud"
|
||||||
- "projects/fedora-coreos-cloud"
|
- "is:projects/fedora-coreos-cloud"
|
||||||
- "projects/opensuse-cloud"
|
- "is:projects/opensuse-cloud"
|
||||||
- "projects/rhel-cloud"
|
- "is:projects/rhel-cloud"
|
||||||
- "projects/rhel-sap-cloud"
|
- "is:projects/rhel-sap-cloud"
|
||||||
- "projects/rocky-linux-cloud"
|
- "is:projects/rocky-linux-cloud"
|
||||||
- "projects/suse-cloud"
|
- "is:projects/suse-cloud"
|
||||||
- "projects/suse-byos-cloud"
|
- "is:projects/suse-sap-cloud"
|
||||||
- "projects/suse-sap-cloud"
|
- "is:projects/ubuntu-os-cloud"
|
||||||
- "projects/ubuntu-os-cloud"
|
- "is:projects/ubuntu-os-pro-cloud"
|
||||||
- "projects/ubuntu-os-pro-cloud"
|
- "is:projects/windows-cloud"
|
||||||
- "projects/windows-cloud"
|
- "is:projects/windows-sql-cloud"
|
||||||
- "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:
|
# compute.disableInternetNetworkEndpointGroup:
|
||||||
# rules:
|
# rules:
|
||||||
|
|
|
@ -10,7 +10,6 @@ run.allowedIngress:
|
||||||
rules:
|
rules:
|
||||||
- allow:
|
- allow:
|
||||||
values:
|
values:
|
||||||
- is:internal
|
|
||||||
- is:internal-and-cloud-load-balancing
|
- is:internal-and-cloud-load-balancing
|
||||||
# run.allowedVPCEgress:
|
# run.allowedVPCEgress:
|
||||||
# rules:
|
# rules:
|
||||||
|
|
|
@ -22,7 +22,7 @@ locals {
|
||||||
"roles/billing.creator"
|
"roles/billing.creator"
|
||||||
]
|
]
|
||||||
# domain IAM bindings
|
# domain IAM bindings
|
||||||
iam_domain_bindings = {
|
iam_domain_bindings = var.organization.domain == null ? {} : {
|
||||||
"domain:${var.organization.domain}" = {
|
"domain:${var.organization.domain}" = {
|
||||||
authoritative = ["roles/browser"]
|
authoritative = ["roles/browser"]
|
||||||
additive = []
|
additive = []
|
||||||
|
|
|
@ -46,7 +46,7 @@ locals {
|
||||||
]
|
]
|
||||||
])
|
])
|
||||||
drs_domains = concat(
|
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
|
var.org_policies_config.constraints.allowed_policy_member_domains
|
||||||
)
|
)
|
||||||
drs_tag_name = "${var.organization.id}/${var.org_policies_config.tag_name}"
|
drs_tag_name = "${var.organization.id}/${var.org_policies_config.tag_name}"
|
||||||
|
@ -104,26 +104,28 @@ locals {
|
||||||
iam_roles_additive = distinct([
|
iam_roles_additive = distinct([
|
||||||
for k, v in local._iam_bindings_additive : v.role
|
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
|
# 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 org policy constraints enabled by default in new orgs since February 2024
|
||||||
import {
|
import {
|
||||||
for_each = !local.import_org_policies ? toset([]) : toset([
|
for_each = (
|
||||||
"compute.requireOsLogin",
|
!var.org_policies_config.import_defaults || var.bootstrap_user != null
|
||||||
"compute.skipDefaultNetworkCreation",
|
? toset([])
|
||||||
"compute.vmExternalIpAccess",
|
: toset([
|
||||||
"iam.allowedPolicyMemberDomains",
|
"compute.requireOsLogin",
|
||||||
"iam.automaticIamGrantsForDefaultServiceAccounts",
|
"compute.skipDefaultNetworkCreation",
|
||||||
"iam.disableServiceAccountKeyCreation",
|
"compute.vmExternalIpAccess",
|
||||||
"iam.disableServiceAccountKeyUpload",
|
"iam.allowedPolicyMemberDomains",
|
||||||
"sql.restrictAuthorizedNetworks",
|
"iam.automaticIamGrantsForDefaultServiceAccounts",
|
||||||
"sql.restrictPublicIp",
|
"iam.disableServiceAccountKeyCreation",
|
||||||
"storage.uniformBucketLevelAccess",
|
"iam.disableServiceAccountKeyUpload",
|
||||||
])
|
"sql.restrictAuthorizedNetworks",
|
||||||
|
"sql.restrictPublicIp",
|
||||||
|
"storage.uniformBucketLevelAccess",
|
||||||
|
])
|
||||||
|
)
|
||||||
id = "organizations/${var.organization.id}/policies/${each.key}"
|
id = "organizations/${var.organization.id}/policies/${each.key}"
|
||||||
to = module.organization.google_org_policy_policy.default[each.key]
|
to = module.organization.google_org_policy_policy.default[each.key]
|
||||||
}
|
}
|
||||||
|
@ -151,34 +153,48 @@ module "organization" {
|
||||||
var.iam_bindings_additive
|
var.iam_bindings_additive
|
||||||
)
|
)
|
||||||
# delegated role grant for resource manager service account
|
# delegated role grant for resource manager service account
|
||||||
iam_bindings = {
|
iam_bindings = merge(
|
||||||
organization_iam_admin_conditional = {
|
{
|
||||||
members = [module.automation-tf-resman-sa.iam_email]
|
organization_iam_admin_conditional = {
|
||||||
role = module.organization.custom_role_id["organization_iam_admin"]
|
members = [module.automation-tf-resman-sa.iam_email]
|
||||||
condition = {
|
role = module.organization.custom_role_id["organization_iam_admin"]
|
||||||
expression = format(
|
condition = {
|
||||||
"api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly([%s])",
|
expression = format(
|
||||||
join(",", formatlist("'%s'", concat(
|
"api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly([%s])",
|
||||||
[
|
join(",", formatlist("'%s'", [
|
||||||
"roles/accesscontextmanager.policyAdmin",
|
"roles/accesscontextmanager.policyAdmin",
|
||||||
"roles/compute.orgFirewallPolicyAdmin",
|
"roles/compute.orgFirewallPolicyAdmin",
|
||||||
"roles/compute.xpnAdmin",
|
"roles/compute.xpnAdmin",
|
||||||
"roles/orgpolicy.policyAdmin",
|
"roles/orgpolicy.policyAdmin",
|
||||||
|
"roles/orgpolicy.policyViewer",
|
||||||
"roles/resourcemanager.organizationViewer",
|
"roles/resourcemanager.organizationViewer",
|
||||||
module.organization.custom_role_id["tenant_network_admin"]
|
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.admin",
|
||||||
"roles/billing.costsManager",
|
"roles/billing.costsManager",
|
||||||
"roles/billing.user",
|
"roles/billing.user",
|
||||||
] : []
|
]))
|
||||||
)))
|
)
|
||||||
)
|
title = "automation_sa_delegated_grants"
|
||||||
title = "automation_sa_delegated_grants"
|
description = "Automation service account delegated grants."
|
||||||
description = "Automation service account delegated grants."
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
custom_roles = var.custom_roles
|
custom_roles = var.custom_roles
|
||||||
factories_config = {
|
factories_config = {
|
||||||
custom_roles = var.factories_config.custom_roles
|
custom_roles = var.factories_config.custom_roles
|
||||||
|
|
|
@ -229,9 +229,9 @@ variable "org_policies_config" {
|
||||||
variable "organization" {
|
variable "organization" {
|
||||||
description = "Organization details."
|
description = "Organization details."
|
||||||
type = object({
|
type = object({
|
||||||
domain = string
|
|
||||||
id = number
|
id = number
|
||||||
customer_id = string
|
domain = optional(string)
|
||||||
|
customer_id = optional(string)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -254,7 +254,7 @@ This is an example that shows how to populate the relevant variables.
|
||||||
```tfvars
|
```tfvars
|
||||||
tenants = {
|
tenants = {
|
||||||
tn0 = {
|
tn0 = {
|
||||||
admin_group_email = "tn-0-admins@tenant.example.org"
|
admin_principal = "group:tn-0-admins@tenant.example.org"
|
||||||
descriptive_name = "Tenant 0"
|
descriptive_name = "Tenant 0"
|
||||||
# an optional billing account and org can be specified for the tenant
|
# an optional billing account and org can be specified for the tenant
|
||||||
organization = {
|
organization = {
|
||||||
|
@ -264,7 +264,7 @@ tenants = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tnq = {
|
tnq = {
|
||||||
admin_group_email = "tn-1-admins@example.org"
|
admin_principal = "group:tn-1-admins@example.org"
|
||||||
descriptive_name = "Tenant 1"
|
descriptive_name = "Tenant 1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,8 +34,8 @@ module "branch-security-folder" {
|
||||||
"roles/resourcemanager.folderAdmin" = [module.branch-security-sa.iam_email]
|
"roles/resourcemanager.folderAdmin" = [module.branch-security-sa.iam_email]
|
||||||
"roles/resourcemanager.projectCreator" = [module.branch-security-sa.iam_email]
|
"roles/resourcemanager.projectCreator" = [module.branch-security-sa.iam_email]
|
||||||
# read-only (plan) automation service account
|
# read-only (plan) automation service account
|
||||||
"roles/viewer" = [module.branch-network-r-sa.iam_email]
|
"roles/viewer" = [module.branch-security-r-sa.iam_email]
|
||||||
"roles/resourcemanager.folderViewer" = [module.branch-network-r-sa.iam_email]
|
"roles/resourcemanager.folderViewer" = [module.branch-security-r-sa.iam_email]
|
||||||
}
|
}
|
||||||
tag_bindings = {
|
tag_bindings = {
|
||||||
context = try(
|
context = try(
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
locals {
|
locals {
|
||||||
tenant_iam = {
|
tenant_iam = {
|
||||||
for k, v in var.tenants : k => [
|
for k, v in var.tenants : k => [
|
||||||
"group:${v.admin_group_email}",
|
v.admin_principal,
|
||||||
module.tenant-self-iac-sa[k].iam_email
|
module.tenant-self-iac-sa[k].iam_email
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,13 +127,13 @@ locals {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
for k, v in var.tenants : "org-viewer-tenant_${k}_admin" => {
|
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"
|
role = "roles/resourcemanager.organizationViewer"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
local.billing_mode != "org" ? {} : {
|
local.billing_mode != "org" ? {} : {
|
||||||
for k, v in var.tenants : "billing_user-tenant_${k}_billing_admin" => {
|
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"
|
role = "roles/billing.user"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -194,7 +194,7 @@ values:
|
||||||
module.organization.google_organization_iam_binding.bindings["organization_iam_admin_conditional"]:
|
module.organization.google_organization_iam_binding.bindings["organization_iam_admin_conditional"]:
|
||||||
condition:
|
condition:
|
||||||
- description: Automation service account delegated grants.
|
- 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
|
title: automation_sa_delegated_grants
|
||||||
members:
|
members:
|
||||||
- serviceAccount:fast-prod-resman-0@fast-prod-iac-core-0.iam.gserviceaccount.com
|
- 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_organization_sink: 3
|
||||||
google_logging_project_bucket_config: 3
|
google_logging_project_bucket_config: 3
|
||||||
google_org_policy_policy: 20
|
google_org_policy_policy: 20
|
||||||
google_organization_iam_binding: 26
|
google_organization_iam_binding: 27
|
||||||
google_organization_iam_custom_role: 6
|
google_organization_iam_custom_role: 6
|
||||||
google_organization_iam_member: 35
|
google_organization_iam_member: 35
|
||||||
google_project: 3
|
google_project: 3
|
||||||
|
@ -381,4 +381,4 @@ counts:
|
||||||
google_tags_tag_key: 1
|
google_tags_tag_key: 1
|
||||||
google_tags_tag_value: 1
|
google_tags_tag_value: 1
|
||||||
modules: 16
|
modules: 16
|
||||||
resources: 190
|
resources: 191
|
||||||
|
|
|
@ -42,7 +42,7 @@ counts:
|
||||||
google_logging_organization_sink: 3
|
google_logging_organization_sink: 3
|
||||||
google_logging_project_bucket_config: 3
|
google_logging_project_bucket_config: 3
|
||||||
google_org_policy_policy: 20
|
google_org_policy_policy: 20
|
||||||
google_organization_iam_binding: 26
|
google_organization_iam_binding: 27
|
||||||
google_organization_iam_custom_role: 6
|
google_organization_iam_custom_role: 6
|
||||||
google_organization_iam_member: 22
|
google_organization_iam_member: 22
|
||||||
google_project: 3
|
google_project: 3
|
||||||
|
@ -61,7 +61,7 @@ counts:
|
||||||
google_tags_tag_value: 1
|
google_tags_tag_value: 1
|
||||||
local_file: 7
|
local_file: 7
|
||||||
modules: 15
|
modules: 15
|
||||||
resources: 181
|
resources: 182
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
custom_roles:
|
custom_roles:
|
||||||
|
|
Loading…
Reference in New Issue