From d111502f0b99ef15df1b5e8c8c58327f66298321 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Thu, 21 Jul 2022 14:42:32 +0200 Subject: [PATCH 1/4] Fix #748 --- examples/factories/project-factory/README.md | 28 ++++++++++++------- examples/factories/project-factory/main.tf | 1 + .../factories/project-factory/variables.tf | 10 ++++++- fast/assets/schemas/project.schema.yaml | 1 + .../project_factory/fixture/variables.tf | 13 +++++++++ .../factories/project_factory/test_plan.py | 20 ++++++++++++- 6 files changed, 61 insertions(+), 12 deletions(-) diff --git a/examples/factories/project-factory/README.md b/examples/factories/project-factory/README.md index 0b8b5183dd..8af7b15aeb 100644 --- a/examples/factories/project-factory/README.md +++ b/examples/factories/project-factory/README.md @@ -13,14 +13,14 @@ The Project Factory is meant to be executed by a Service Account (or a regular u * `"dns.networks.bindPrivateDNSZone"` * and role `"roles/orgpolicy.policyAdmin"` * **on each folder** where projects will be created - * `"roles/logging.admin"` - * `"roles/owner"` - * `"roles/resourcemanager.folderAdmin"` + * `"roles/logging.admin"` + * `"roles/owner"` + * `"roles/resourcemanager.folderAdmin"` * `"roles/resourcemanager.projectCreator"` * **on the host project** for the Shared VPC/s - * `"roles/browser"` + * `"roles/browser"` * `"roles/compute.viewer"` - * `"roles/dns.admin"` + * `"roles/dns.admin"` ## Example @@ -176,6 +176,13 @@ service_accounts: my-service-account: - roles/compute.admin +# [opt] IAM bindings on the service account resources. +# in name => {role => [members]} format +service_accounts_iam: + another-service-account: + - roles/iam.serviceAccountTokenCreator: + - group: app-team-1@example.com + # [opt] APIs to enable on the project. services: - storage.googleapis.com @@ -209,7 +216,7 @@ vpc: subnets_iam: europe-west1/prod-default-ew1: [] - user:foobar@example.com - - serviceAccount:service-account1 + - serviceAccount:service-account1@my-project.iam.gserviceaccount.com ``` @@ -230,10 +237,11 @@ vpc: | [labels](variables.tf#L92) | Labels to be assigned at project level. | map(string) | | {} | | [org_policies](variables.tf#L98) | Org-policy overrides at project level. | object({…}) | | null | | [prefix](variables.tf#L112) | Prefix used for the project id. | string | | null | -| [service_accounts](variables.tf#L123) | Service accounts to be created, and roles to assign them. | map(list(string)) | | {} | -| [service_identities_iam](variables.tf#L136) | Custom IAM settings for service identities in service => [role] format. | map(list(string)) | | {} | -| [services](variables.tf#L129) | Services to be enabled for the project. | list(string) | | [] | -| [vpc](variables.tf#L143) | VPC configuration for the project. | object({…}) | | null | +| [service_accounts](variables.tf#L123) | Service accounts to be created, and roles assigned them on the project. | map(list(string)) | | {} | +| [service_accounts_iam](variables.tf#L129) | IAM bindings on service account resources. Format is KEY => {ROLE => [MEMBERS]} | map(map(list(string))) | | {} | +| [service_identities_iam](variables.tf#L143) | Custom IAM settings for service identities in service => [role] format. | map(list(string)) | | {} | +| [services](variables.tf#L136) | Services to be enabled for the project. | list(string) | | [] | +| [vpc](variables.tf#L150) | VPC configuration for the project. | object({…}) | | null | ## Outputs diff --git a/examples/factories/project-factory/main.tf b/examples/factories/project-factory/main.tf index b9f64361c2..f34fe4f59d 100644 --- a/examples/factories/project-factory/main.tf +++ b/examples/factories/project-factory/main.tf @@ -183,6 +183,7 @@ module "service-accounts" { for_each = var.service_accounts name = each.key project_id = module.project.project_id + iam = lookup(var.service_accounts_iam, each.key, null) } resource "google_compute_subnetwork_iam_member" "default" { diff --git a/examples/factories/project-factory/variables.tf b/examples/factories/project-factory/variables.tf index 7f4a20f7d0..6154c032c1 100644 --- a/examples/factories/project-factory/variables.tf +++ b/examples/factories/project-factory/variables.tf @@ -69,6 +69,7 @@ variable "essential_contacts" { variable "folder_id" { description = "Folder ID for the folder where the project will be created." type = string + default = null } variable "group_iam" { @@ -121,11 +122,18 @@ variable "project_id" { } variable "service_accounts" { - description = "Service accounts to be created, and roles to assign them." + description = "Service accounts to be created, and roles assigned them on the project." 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))) + default = {} + nullable = false +} + variable "services" { description = "Services to be enabled for the project." type = list(string) diff --git a/fast/assets/schemas/project.schema.yaml b/fast/assets/schemas/project.schema.yaml index 49e4bf8956..2155c71a29 100644 --- a/fast/assets/schemas/project.schema.yaml +++ b/fast/assets/schemas/project.schema.yaml @@ -24,6 +24,7 @@ labels: map(str(), key=str(), required=False) org_policies: include('org_policies', required=False) secrets: map(list(str()), key=str(), required=False) service_accounts: map(list(str()), required=False) +service_accounts_iam: map(map(list(str())), required=False) services: list(str(matches='^[a-z-]*\.googleapis\.com$'), required=False) service_identities_iam: map(list(str()), key=str(), required=False) vpc: include('vpc', required=False) diff --git a/tests/examples/factories/project_factory/fixture/variables.tf b/tests/examples/factories/project_factory/fixture/variables.tf index 0662bf78dd..4d2fd9c1d7 100644 --- a/tests/examples/factories/project_factory/fixture/variables.tf +++ b/tests/examples/factories/project_factory/fixture/variables.tf @@ -38,6 +38,19 @@ variable "defaults_file" { default = "./defaults.yaml" } +variable "service_accounts" { + description = "Service accounts to be created, and roles assigned them on the project." + 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))) + default = {} + nullable = false +} + variable "shared_vpc_self_link" { description = "Self link for the shared VPC." type = string diff --git a/tests/examples/factories/project_factory/test_plan.py b/tests/examples/factories/project_factory/test_plan.py index f609b214b3..4c8e86412c 100644 --- a/tests/examples/factories/project_factory/test_plan.py +++ b/tests/examples/factories/project_factory/test_plan.py @@ -12,7 +12,25 @@ # See the License for the specific language governing permissions and # limitations under the License. -def test_counts(e2e_plan_runner): + +def test_plan(e2e_plan_runner): "Check for a clean plan" modules, resources = e2e_plan_runner() assert len(modules) > 0 and len(resources) > 0 + + +def test_plan_service_accounts(e2e_plan_runner): + "Check for a clean plan" + service_accounts = '''{ + sa-001 = [] + sa-002 = ["roles/owner"] + }''' + service_accounts_iam = '''{ + sa-002 = { + "roles/iam.serviceAccountTokenCreator" = ["group:team-1@example.com"] + } + }''' + modules, resources = e2e_plan_runner( + service_accounts=service_accounts, + service_accounts_iam=service_accounts_iam) + assert len(modules) > 0 and len(resources) > 0 From fd6e1d68ee3a1658b430ec202dbb8817945685f6 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Thu, 21 Jul 2022 14:48:57 +0200 Subject: [PATCH 2/4] fix linting --- examples/factories/project-factory/README.md | 26 ++++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/examples/factories/project-factory/README.md b/examples/factories/project-factory/README.md index 8af7b15aeb..9e49171d66 100644 --- a/examples/factories/project-factory/README.md +++ b/examples/factories/project-factory/README.md @@ -225,23 +225,23 @@ vpc: | name | description | type | required | default | |---|---|:---:|:---:|:---:| | [billing_account_id](variables.tf#L17) | Billing account id. | string | ✓ | | -| [folder_id](variables.tf#L69) | Folder ID for the folder where the project will be created. | string | ✓ | | -| [project_id](variables.tf#L118) | Project id. | string | ✓ | | +| [project_id](variables.tf#L119) | 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) | | [] | -| [group_iam](variables.tf#L74) | Custom IAM settings in group => [role] format. | map(list(string)) | | {} | -| [iam](variables.tf#L80) | Custom IAM settings in role => [principal] format. | map(list(string)) | | {} | -| [kms_service_agents](variables.tf#L86) | KMS IAM configuration in as service => [key]. | map(list(string)) | | {} | -| [labels](variables.tf#L92) | Labels to be assigned at project level. | map(string) | | {} | -| [org_policies](variables.tf#L98) | Org-policy overrides at project level. | object({…}) | | null | -| [prefix](variables.tf#L112) | Prefix used for the project id. | string | | null | -| [service_accounts](variables.tf#L123) | Service accounts to be created, and roles assigned them on the project. | map(list(string)) | | {} | -| [service_accounts_iam](variables.tf#L129) | IAM bindings on service account resources. Format is KEY => {ROLE => [MEMBERS]} | map(map(list(string))) | | {} | -| [service_identities_iam](variables.tf#L143) | Custom IAM settings for service identities in service => [role] format. | map(list(string)) | | {} | -| [services](variables.tf#L136) | Services to be enabled for the project. | list(string) | | [] | -| [vpc](variables.tf#L150) | VPC configuration for the project. | object({…}) | | null | +| [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. | object({…}) | | null | +| [prefix](variables.tf#L113) | Prefix used for the project id. | string | | null | +| [service_accounts](variables.tf#L124) | Service accounts to be created, and roles assigned them on the project. | map(list(string)) | | {} | +| [service_accounts_iam](variables.tf#L130) | IAM bindings on service account resources. Format is KEY => {ROLE => [MEMBERS]} | map(map(list(string))) | | {} | +| [service_identities_iam](variables.tf#L144) | Custom IAM settings for service identities in service => [role] format. | map(list(string)) | | {} | +| [services](variables.tf#L137) | Services to be enabled for the project. | list(string) | | [] | +| [vpc](variables.tf#L151) | VPC configuration for the project. | object({…}) | | null | ## Outputs From bf9b76a7ca93676ad990f15b5b5d9a8611ad7c65 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Thu, 21 Jul 2022 14:52:24 +0200 Subject: [PATCH 3/4] remove trailing whitespace --- examples/factories/project-factory/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/factories/project-factory/README.md b/examples/factories/project-factory/README.md index 9e49171d66..cbce8a0fc8 100644 --- a/examples/factories/project-factory/README.md +++ b/examples/factories/project-factory/README.md @@ -178,7 +178,7 @@ service_accounts: # [opt] IAM bindings on the service account resources. # in name => {role => [members]} format -service_accounts_iam: +service_accounts_iam: another-service-account: - roles/iam.serviceAccountTokenCreator: - group: app-team-1@example.com From 0633447b83e6934678671c1e548883dd7eb607d7 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Thu, 21 Jul 2022 15:02:23 +0200 Subject: [PATCH 4/4] update FAST pf --- fast/stages/03-project-factory/dev/main.tf | 1 + 1 file changed, 1 insertion(+) diff --git a/fast/stages/03-project-factory/dev/main.tf b/fast/stages/03-project-factory/dev/main.tf index 120b835908..318cd0e07d 100644 --- a/fast/stages/03-project-factory/dev/main.tf +++ b/fast/stages/03-project-factory/dev/main.tf @@ -49,6 +49,7 @@ module "projects" { org_policies = try(each.value.org_policies, null) prefix = var.prefix service_accounts = try(each.value.service_accounts, {}) + service_accounts_iam = try(each.value.service_accounts_iam, {}) services = try(each.value.services, []) service_identities_iam = try(each.value.service_identities_iam, {}) vpc = try(each.value.vpc, null)