From d7e0af75b26c9539cbebf2d64dffe57d990fbb54 Mon Sep 17 00:00:00 2001 From: Malet <358505+Malet@users.noreply.github.com> Date: Mon, 31 Oct 2022 17:24:25 +0000 Subject: [PATCH] feat(project-factory): introduce additive iam bindings to project-factory (#932) --- .../factories/project-factory/README.md | 27 +++++++----- blueprints/factories/project-factory/main.tf | 42 ++++++++++++++++++- .../factories/project-factory/variables.tf | 39 +++++++++++++++-- 3 files changed, 92 insertions(+), 16 deletions(-) diff --git a/blueprints/factories/project-factory/README.md b/blueprints/factories/project-factory/README.md index c7bb0132f..595467c05 100644 --- a/blueprints/factories/project-factory/README.md +++ b/blueprints/factories/project-factory/README.md @@ -221,23 +221,28 @@ vpc: | name | description | type | required | default | |---|---|:---:|:---:|:---:| | [billing_account_id](variables.tf#L17) | Billing account id. | string | ✓ | | -| [project_id](variables.tf#L145) | Project id. | string | ✓ | | +| [project_id](variables.tf#L157) | Project id. | string | ✓ | | | [billing_alert](variables.tf#L22) | Billing alert configuration. | object({…}) | | null | | [defaults](variables.tf#L35) | Project factory default values. | object({…}) | | null | | [dns_zones](variables.tf#L57) | DNS private zones to create as child of var.defaults.environment_dns_zone. | list(string) | | [] | | [essential_contacts](variables.tf#L63) | Email contacts to be used for billing and GCP notifications. | list(string) | | [] | | [folder_id](variables.tf#L69) | Folder ID for the folder where the project will be created. | string | | null | | [group_iam](variables.tf#L75) | Custom IAM settings in group => [role] format. | map(list(string)) | | {} | -| [iam](variables.tf#L81) | Custom IAM settings in role => [principal] format. | map(list(string)) | | {} | -| [kms_service_agents](variables.tf#L87) | KMS IAM configuration in as service => [key]. | map(list(string)) | | {} | -| [labels](variables.tf#L93) | Labels to be assigned at project level. | map(string) | | {} | -| [org_policies](variables.tf#L99) | Org-policy overrides at project level. | map(object({…})) | | {} | -| [prefix](variables.tf#L139) | Prefix used for the project id. | string | | null | -| [service_accounts](variables.tf#L150) | Service accounts to be created, and roles assigned them on the project. | map(list(string)) | | {} | -| [service_accounts_iam](variables.tf#L156) | IAM bindings on service account resources. Format is KEY => {ROLE => [MEMBERS]} | map(map(list(string))) | | {} | -| [service_identities_iam](variables.tf#L164) | Custom IAM settings for service identities in service => [role] format. | map(list(string)) | | {} | -| [services](variables.tf#L171) | Services to be enabled for the project. | list(string) | | [] | -| [vpc](variables.tf#L178) | VPC configuration for the project. | object({…}) | | null | +| [group_iam_additive](variables.tf#L81) | Custom additive IAM settings in group => [role] format. | map(list(string)) | | {} | +| [iam](variables.tf#L87) | Custom IAM settings in role => [principal] format. | map(list(string)) | | {} | +| [iam_additive](variables.tf#L93) | Custom additive IAM settings in role => [principal] format. | map(list(string)) | | {} | +| [kms_service_agents](variables.tf#L99) | KMS IAM configuration in as service => [key]. | map(list(string)) | | {} | +| [labels](variables.tf#L105) | Labels to be assigned at project level. | map(string) | | {} | +| [org_policies](variables.tf#L111) | Org-policy overrides at project level. | map(object({…})) | | {} | +| [prefix](variables.tf#L151) | Prefix used for the project id. | string | | null | +| [service_accounts](variables.tf#L162) | Service accounts to be created, and roles assigned them on the project. | map(list(string)) | | {} | +| [service_accounts_additive](variables.tf#L168) | Service accounts to be created, and roles assigned them on the project additively. | map(list(string)) | | {} | +| [service_accounts_iam](variables.tf#L174) | IAM bindings on service account resources. Format is KEY => {ROLE => [MEMBERS]} | map(map(list(string))) | | {} | +| [service_accounts_iam_additive](variables.tf#L181) | IAM additive bindings on service account resources. Format is KEY => {ROLE => [MEMBERS]} | map(map(list(string))) | | {} | +| [service_identities_iam](variables.tf#L195) | Custom IAM settings for service identities in service => [role] format. | map(list(string)) | | {} | +| [service_identities_iam_additive](variables.tf#L202) | Custom additive IAM settings for service identities in service => [role] format. | map(list(string)) | | {} | +| [services](variables.tf#L188) | Services to be enabled for the project. | list(string) | | [] | +| [vpc](variables.tf#L209) | VPC configuration for the project. | object({…}) | | null | ## Outputs diff --git a/blueprints/factories/project-factory/main.tf b/blueprints/factories/project-factory/main.tf index db80810b9..effcfd73e 100644 --- a/blueprints/factories/project-factory/main.tf +++ b/blueprints/factories/project-factory/main.tf @@ -21,7 +21,14 @@ locals { "group:${k}" if try(index(v, r), null) != null ] } - _group_iam_bindings = distinct(flatten(values(var.group_iam))) + _group_iam_additive = { + for r in local._group_iam_additive_bindings : r => [ + for k, v in var.group_iam_additive : + "group:${k}" if try(index(v, r), null) != null + ] + } + _group_iam_bindings = distinct(flatten(values(var.group_iam))) + _group_iam_additive_bindings = distinct(flatten(values(var.group_iam_additive))) _project_id = ( var.prefix == null || var.prefix == "" ? var.project_id @@ -37,6 +44,16 @@ locals { _service_accounts_iam_bindings = distinct(flatten( values(var.service_accounts) )) + _service_accounts_iam_additive = { + for r in local._service_accounts_iam_additive_bindings : r => [ + for k, v in var.service_accounts_additive : + module.service-accounts[k].iam_email + if try(index(v, r), null) != null + ] + } + _service_accounts_iam_additive_bindings = distinct(flatten( + values(var.service_accounts_additive) + )) _services = concat([ "billingbudgets.googleapis.com", "essentialcontacts.googleapis.com" @@ -53,6 +70,14 @@ locals { if contains(roles, role) ] } + _service_identities_roles_additive = distinct(flatten(values(var.service_identities_iam_additive))) + _service_identities_iam_additive = { + for role in local._service_identities_roles_additive : role => [ + for service, roles in var.service_identities_iam_additive : + "serviceAccount:${module.project.service_accounts.robots[service]}" + if contains(roles, role) + ] + } _vpc_subnet_bindings = ( local.vpc.subnets_iam == null || local.vpc.host_project == null ? [] @@ -91,6 +116,20 @@ locals { try(local._service_identities_iam[role], []), ) } + iam_additive = { + for role in distinct(concat( + keys(var.iam_additive), + keys(local._group_iam_additive), + keys(local._service_accounts_iam_additive), + keys(local._service_identities_iam_additive), + )) : + role => concat( + try(var.iam_additive[role], []), + try(local._group_iam_additive[role], []), + try(local._service_accounts_iam_additive[role], []), + try(local._service_identities_iam_additive[role], []), + ) + } labels = merge( coalesce(var.labels, {}), coalesce(try(var.defaults.labels, {}), {}) ) @@ -147,6 +186,7 @@ module "project" { prefix = var.prefix contacts = { for c in local.essential_contacts : c => ["ALL"] } iam = local.iam + iam_additive = local.iam_additive labels = local.labels org_policies = try(var.org_policies, {}) parent = var.folder_id diff --git a/blueprints/factories/project-factory/variables.tf b/blueprints/factories/project-factory/variables.tf index cf5d8fb34..8efc0bc1e 100644 --- a/blueprints/factories/project-factory/variables.tf +++ b/blueprints/factories/project-factory/variables.tf @@ -78,12 +78,24 @@ variable "group_iam" { default = {} } +variable "group_iam_additive" { + description = "Custom additive IAM settings in group => [role] format." + type = map(list(string)) + default = {} +} + variable "iam" { description = "Custom IAM settings in role => [principal] format." type = map(list(string)) default = {} } +variable "iam_additive" { + description = "Custom additive IAM settings in role => [principal] format." + type = map(list(string)) + default = {} +} + variable "kms_service_agents" { description = "KMS IAM configuration in as service => [key]." type = map(list(string)) @@ -153,6 +165,12 @@ variable "service_accounts" { default = {} } +variable "service_accounts_additive" { + description = "Service accounts to be created, and roles assigned them on the project additively." + type = map(list(string)) + default = {} +} + variable "service_accounts_iam" { description = "IAM bindings on service account resources. Format is KEY => {ROLE => [MEMBERS]}" type = map(map(list(string))) @@ -160,10 +178,9 @@ variable "service_accounts_iam" { nullable = false } - -variable "service_identities_iam" { - description = "Custom IAM settings for service identities in service => [role] format." - type = map(list(string)) +variable "service_accounts_iam_additive" { + description = "IAM additive bindings on service account resources. Format is KEY => {ROLE => [MEMBERS]}" + type = map(map(list(string))) default = {} nullable = false } @@ -175,6 +192,20 @@ variable "services" { nullable = false } +variable "service_identities_iam" { + description = "Custom IAM settings for service identities in service => [role] format." + type = map(list(string)) + default = {} + nullable = false +} + +variable "service_identities_iam_additive" { + description = "Custom additive IAM settings for service identities in service => [role] format." + type = map(list(string)) + default = {} + nullable = false +} + variable "vpc" { description = "VPC configuration for the project." type = object({