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({