Implement support for VPC-SC perimeter membership from project factory (#3007)
* support project factory-level vpc-sc perimeter interpolation * fix ro role * add support for IAM on service accounts * fix typo
This commit is contained in:
committed by
GitHub
parent
25b6020a14
commit
69188fa9d9
@@ -494,7 +494,7 @@ service_accounts:
|
||||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [factories_config](variables.tf#L120) | Path to folder with YAML resource description data files. | <code title="object({ budgets = optional(object({ billing_account = string budgets_data_path = string notification_channels = optional(map(any), {}) })) context = optional(object({ folder_ids = optional(map(string), {}) iam_principals = optional(map(string), {}) tag_values = optional(map(string), {}) vpc_host_projects = optional(map(string), {}) notification_channels = optional(map(string), {}) }), {}) folders_data_path = optional(string) projects_data_path = optional(string) })">object({…})</code> | ✓ | |
|
||||
| [factories_config](variables.tf#L120) | Path to folder with YAML resource description data files. | <code title="object({ budgets = optional(object({ billing_account = string budgets_data_path = string notification_channels = optional(map(any), {}) })) context = optional(object({ folder_ids = optional(map(string), {}) iam_principals = optional(map(string), {}) perimeters = optional(map(string), {}) tag_values = optional(map(string), {}) vpc_host_projects = optional(map(string), {}) notification_channels = optional(map(string), {}) }), {}) folders_data_path = optional(string) projects_data_path = optional(string) })">object({…})</code> | ✓ | |
|
||||
| [data_defaults](variables.tf#L17) | Optional default values used when corresponding project data from files are missing. | <code title="object({ billing_account = optional(string) contacts = optional(map(list(string)), {}) factories_config = optional(object({ custom_roles = optional(string) observability = optional(string) org_policies = optional(string) quotas = optional(string) }), {}) labels = optional(map(string), {}) metric_scopes = optional(list(string), []) parent = optional(string) prefix = optional(string) service_encryption_key_ids = optional(map(list(string)), {}) services = optional(list(string), []) shared_vpc_service_config = optional(object({ host_project = string network_users = optional(list(string), []) service_agent_iam = optional(map(list(string)), {}) service_agent_subnet_iam = optional(map(list(string)), {}) service_iam_grants = optional(list(string), []) network_subnet_users = optional(map(list(string)), {}) }), { host_project = null }) storage_location = optional(string) tag_bindings = optional(map(string), {}) service_accounts = optional(map(object({ display_name = optional(string, "Terraform-managed.") iam_self_roles = optional(list(string)) })), {}) vpc_sc = optional(object({ perimeter_name = string perimeter_bridges = optional(list(string), []) is_dry_run = optional(bool, false) })) logging_data_access = optional(map(object({ ADMIN_READ = optional(object({ exempted_members = optional(list(string)) })), DATA_READ = optional(object({ exempted_members = optional(list(string)) })), DATA_WRITE = optional(object({ exempted_members = optional(list(string)) })) })), {}) })">object({…})</code> | | <code>{}</code> |
|
||||
| [data_merges](variables.tf#L64) | Optional values that will be merged with corresponding data from files. Combines with `data_defaults`, file data, and `data_overrides`. | <code title="object({ contacts = optional(map(list(string)), {}) labels = optional(map(string), {}) metric_scopes = optional(list(string), []) service_encryption_key_ids = optional(map(list(string)), {}) services = optional(list(string), []) tag_bindings = optional(map(string), {}) service_accounts = optional(map(object({ display_name = optional(string, "Terraform-managed.") iam_self_roles = optional(list(string)) })), {}) })">object({…})</code> | | <code>{}</code> |
|
||||
| [data_overrides](variables.tf#L83) | Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`. | <code title="object({ billing_account = optional(string) contacts = optional(map(list(string))) factories_config = optional(object({ custom_roles = optional(string) observability = optional(string) org_policies = optional(string) quotas = optional(string) }), {}) parent = optional(string) prefix = optional(string) service_encryption_key_ids = optional(map(list(string))) storage_location = optional(string) tag_bindings = optional(map(string)) services = optional(list(string)) service_accounts = optional(map(object({ display_name = optional(string, "Terraform-managed.") iam_self_roles = optional(list(string)) }))) vpc_sc = optional(object({ perimeter_name = string perimeter_bridges = optional(list(string), []) is_dry_run = optional(bool, false) })) logging_data_access = optional(map(object({ ADMIN_READ = optional(object({ exempted_members = optional(list(string)) })), DATA_READ = optional(object({ exempted_members = optional(list(string)) })), DATA_WRITE = optional(object({ exempted_members = optional(list(string)) })) })), {}) })">object({…})</code> | | <code>{}</code> |
|
||||
|
||||
@@ -97,6 +97,7 @@ locals {
|
||||
try(var.data_defaults.service_accounts.display_name, null),
|
||||
"Terraform-managed."
|
||||
)
|
||||
iam = try(opts.iam, {})
|
||||
iam_billing_roles = try(opts.iam_billing_roles, {})
|
||||
iam_organization_roles = try(opts.iam_organization_roles, {})
|
||||
iam_sa_roles = try(opts.iam_sa_roles, {})
|
||||
|
||||
@@ -87,8 +87,20 @@ module "projects" {
|
||||
for k, v in merge(each.value.tag_bindings, var.data_merges.tag_bindings) :
|
||||
k => lookup(var.factories_config.context.tag_values, v, v)
|
||||
}
|
||||
tags = each.value.tags
|
||||
vpc_sc = each.value.vpc_sc
|
||||
tags = each.value.tags
|
||||
vpc_sc = each.value.vpc_sc == null ? null : {
|
||||
perimeter_name = (
|
||||
each.value.vpc_sc.perimeter_name == null
|
||||
? null
|
||||
: lookup(
|
||||
var.factories_config.context.perimeters,
|
||||
each.value.vpc_sc.perimeter_name,
|
||||
each.value.vpc_sc.perimeter_name
|
||||
)
|
||||
)
|
||||
perimeter_bridges = each.value.vpc_sc.perimeter_bridges
|
||||
is_dry_run = each.value.vpc_sc.is_dry_run
|
||||
}
|
||||
}
|
||||
|
||||
module "projects-iam" {
|
||||
@@ -313,6 +325,24 @@ module "service-accounts" {
|
||||
project_id = module.projects[each.value.project].project_id
|
||||
name = each.value.name
|
||||
display_name = each.value.display_name
|
||||
iam = {
|
||||
for k, v in lookup(each.value, "iam", {}) : k => [
|
||||
for vv in v : try(
|
||||
# automation service account (rw)
|
||||
local.context.iam_principals["${each.key}/automation/${vv}"],
|
||||
# automation service account (automation/rw)
|
||||
local.context.iam_principals["${each.key}/${vv}"],
|
||||
# other automation service account (project/automation/rw)
|
||||
local.context.iam_principals[vv],
|
||||
# passthrough + error handling using tonumber until Terraform gets fail/raise function
|
||||
(
|
||||
strcontains(vv, ":")
|
||||
? vv
|
||||
: tonumber("[Error] Invalid member: '${vv}' in project '${each.key}'")
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
||||
iam_project_roles = merge(
|
||||
{
|
||||
for k, v in each.value.iam_project_roles :
|
||||
|
||||
@@ -200,6 +200,9 @@
|
||||
"display_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"iam": {
|
||||
"$ref": "#/$defs/iam"
|
||||
},
|
||||
"iam_self_roles": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -618,4 +621,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,6 +130,7 @@ variable "factories_config" {
|
||||
# TODO: add KMS keys
|
||||
folder_ids = optional(map(string), {})
|
||||
iam_principals = optional(map(string), {})
|
||||
perimeters = optional(map(string), {})
|
||||
tag_values = optional(map(string), {})
|
||||
vpc_host_projects = optional(map(string), {})
|
||||
notification_channels = optional(map(string), {})
|
||||
|
||||
Reference in New Issue
Block a user