diff --git a/examples/factories/project-factory/README.md b/examples/factories/project-factory/README.md
index 0b8b5183dd..cbce8a0fc8 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
```
@@ -218,22 +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 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
|
+| [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
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/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)
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