add support for service account IAM variables to pf (#2130)

This commit is contained in:
Ludovico Magnocavallo 2024-03-05 13:13:02 +01:00 committed by GitHub
parent 81cf47c785
commit 39139e2fa1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 144 additions and 64 deletions

View File

@ -43,18 +43,21 @@ Some examples on where to use each of the three sets are [provided below](#examp
Service accounts can be managed as part of each project's YAML configuration. This allows creation of default service accounts used for GCE instances, in firewall rules, or for application-level credentials without resorting to a separate Terraform configuration. Service accounts can be managed as part of each project's YAML configuration. This allows creation of default service accounts used for GCE instances, in firewall rules, or for application-level credentials without resorting to a separate Terraform configuration.
Each service account is represented by one key and a set of optional key/value pairs in the `service_accounts` top-level YAML map, like in this example: Each service account is represented by one key and a set of optional key/value pairs in the `service_accounts` top-level YAML map, which expose most of the variables available in the `iam-service-account` module:
```yaml ```yaml
service_accounts: service_accounts:
be-0: {} be-0: {}
fe-1: fe-1:
display_name: GCE frontend service account. display_name: GCE frontend service account.
iam_self_roles:
- roles/storage.objectViewer
iam_project_roles: iam_project_roles:
- roles/storage.objectViewer my-host-project:
- roles/compute.networkUser
``` ```
Both the `display_name` and `iam_project_roles` attributes are optional. Both the `display_name` and `iam_self_roles` attributes are optional.
### Billing budgets ### Billing budgets
@ -122,7 +125,7 @@ module "project-factory" {
projects_data_path = "data/projects" projects_data_path = "data/projects"
} }
} }
# tftest modules=8 resources=35 files=prj-app-1,prj-app-2,prj-app-3,budget-test-100 inventory=example.yaml # tftest modules=8 resources=37 files=prj-app-1,prj-app-2,prj-app-3,budget-test-100
``` ```
```yaml ```yaml
@ -140,11 +143,17 @@ services:
- storage.googleapis.com - storage.googleapis.com
service_accounts: service_accounts:
app-1-be: app-1-be:
iam_project_roles: iam_self_roles:
- roles/logging.logWriter - roles/logging.logWriter
- roles/monitoring.metricWriter - roles/monitoring.metricWriter
iam_project_roles:
my-host-project:
- roles/compute.networkUser
app-1-fe: app-1-fe:
display_name: "Test app 1 frontend." display_name: "Test app 1 frontend."
iam_project_roles:
my-host-project:
- roles/compute.networkUser
billing_budgets: billing_budgets:
- test-100 - test-100
# tftest-file id=prj-app-1 path=data/projects/prj-app-1.yaml # tftest-file id=prj-app-1 path=data/projects/prj-app-1.yaml
@ -223,9 +232,9 @@ update_rules:
| name | description | type | required | default | | name | description | type | required | default |
|---|---|:---:|:---:|:---:| |---|---|:---:|:---:|:---:|
| [factories_config](variables.tf#L91) | Path to folder with YAML resource description data files. | <code title="object&#40;&#123;&#10; projects_data_path &#61; string&#10; budgets &#61; optional&#40;object&#40;&#123;&#10; billing_account &#61; string&#10; budgets_data_path &#61; string&#10; notification_channels &#61; optional&#40;map&#40;any&#41;, &#123;&#125;&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | | [factories_config](variables.tf#L91) | Path to folder with YAML resource description data files. | <code title="object&#40;&#123;&#10; projects_data_path &#61; string&#10; budgets &#61; optional&#40;object&#40;&#123;&#10; billing_account &#61; string&#10; budgets_data_path &#61; string&#10; notification_channels &#61; optional&#40;map&#40;any&#41;, &#123;&#125;&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | |
| [data_defaults](variables.tf#L17) | Optional default values used when corresponding project data from files are missing. | <code title="object&#40;&#123;&#10; billing_account &#61; optional&#40;string&#41;&#10; contacts &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; metric_scopes &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; parent &#61; optional&#40;string&#41;&#10; prefix &#61; optional&#40;string&#41;&#10; service_encryption_key_ids &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; service_perimeter_bridges &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; service_perimeter_standard &#61; optional&#40;string&#41;&#10; services &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; shared_vpc_service_config &#61; optional&#40;object&#40;&#123;&#10; host_project &#61; string&#10; network_users &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; service_identity_iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; service_identity_subnet_iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; service_iam_grants &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; network_subnet_users &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; &#125;&#41;, &#123; host_project &#61; null &#125;&#41;&#10; tag_bindings &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; service_accounts &#61; optional&#40;map&#40;object&#40;&#123;&#10; display_name &#61; optional&#40;string, &#34;Terraform-managed.&#34;&#41;&#10; iam_project_roles &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | | [data_defaults](variables.tf#L17) | Optional default values used when corresponding project data from files are missing. | <code title="object&#40;&#123;&#10; billing_account &#61; optional&#40;string&#41;&#10; contacts &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; metric_scopes &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; parent &#61; optional&#40;string&#41;&#10; prefix &#61; optional&#40;string&#41;&#10; service_encryption_key_ids &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; service_perimeter_bridges &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; service_perimeter_standard &#61; optional&#40;string&#41;&#10; services &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; shared_vpc_service_config &#61; optional&#40;object&#40;&#123;&#10; host_project &#61; string&#10; network_users &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; service_identity_iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; service_identity_subnet_iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; service_iam_grants &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; network_subnet_users &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; &#125;&#41;, &#123; host_project &#61; null &#125;&#41;&#10; tag_bindings &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; service_accounts &#61; optional&#40;map&#40;object&#40;&#123;&#10; display_name &#61; optional&#40;string, &#34;Terraform-managed.&#34;&#41;&#10; iam_self_roles &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [data_merges](variables.tf#L49) | Optional values that will be merged with corresponding data from files. Combines with `data_defaults`, file data, and `data_overrides`. | <code title="object&#40;&#123;&#10; contacts &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; metric_scopes &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; service_encryption_key_ids &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; service_perimeter_bridges &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; services &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; tag_bindings &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; service_accounts &#61; optional&#40;map&#40;object&#40;&#123;&#10; display_name &#61; optional&#40;string, &#34;Terraform-managed.&#34;&#41;&#10; iam_project_roles &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | | [data_merges](variables.tf#L49) | Optional values that will be merged with corresponding data from files. Combines with `data_defaults`, file data, and `data_overrides`. | <code title="object&#40;&#123;&#10; contacts &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; metric_scopes &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; service_encryption_key_ids &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; service_perimeter_bridges &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; services &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; tag_bindings &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; service_accounts &#61; optional&#40;map&#40;object&#40;&#123;&#10; display_name &#61; optional&#40;string, &#34;Terraform-managed.&#34;&#41;&#10; iam_self_roles &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [data_overrides](variables.tf#L69) | Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`. | <code title="object&#40;&#123;&#10; billing_account &#61; optional&#40;string&#41;&#10; contacts &#61; optional&#40;map&#40;list&#40;string&#41;&#41;&#41;&#10; parent &#61; optional&#40;string&#41;&#10; prefix &#61; optional&#40;string&#41;&#10; service_encryption_key_ids &#61; optional&#40;map&#40;list&#40;string&#41;&#41;&#41;&#10; service_perimeter_bridges &#61; optional&#40;list&#40;string&#41;&#41;&#10; service_perimeter_standard &#61; optional&#40;string&#41;&#10; tag_bindings &#61; optional&#40;map&#40;string&#41;&#41;&#10; services &#61; optional&#40;list&#40;string&#41;&#41;&#10; service_accounts &#61; optional&#40;map&#40;object&#40;&#123;&#10; display_name &#61; optional&#40;string, &#34;Terraform-managed.&#34;&#41;&#10; iam_project_roles &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | | [data_overrides](variables.tf#L69) | Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`. | <code title="object&#40;&#123;&#10; billing_account &#61; optional&#40;string&#41;&#10; contacts &#61; optional&#40;map&#40;list&#40;string&#41;&#41;&#41;&#10; parent &#61; optional&#40;string&#41;&#10; prefix &#61; optional&#40;string&#41;&#10; service_encryption_key_ids &#61; optional&#40;map&#40;list&#40;string&#41;&#41;&#41;&#10; service_perimeter_bridges &#61; optional&#40;list&#40;string&#41;&#41;&#10; service_perimeter_standard &#61; optional&#40;string&#41;&#10; tag_bindings &#61; optional&#40;map&#40;string&#41;&#41;&#10; services &#61; optional&#40;list&#40;string&#41;&#41;&#10; service_accounts &#61; optional&#40;map&#40;object&#40;&#123;&#10; display_name &#61; optional&#40;string, &#34;Terraform-managed.&#34;&#41;&#10; iam_self_roles &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
## Outputs ## Outputs

View File

@ -103,20 +103,31 @@ locals {
var.data_defaults.tag_bindings var.data_defaults.tag_bindings
) )
# non-project resources # non-project resources
service_accounts = coalesce( service_accounts = try(v.service_accounts, {})
var.data_overrides.service_accounts,
try(v.service_accounts, null),
var.data_defaults.service_accounts
)
}) })
} }
service_accounts = flatten([ service_accounts = flatten([
for k, v in local.projects : [ for k, v in local.projects : [
for name, opts in v.service_accounts : { for name, opts in v.service_accounts : {
project = k project = k
name = name name = name
display_name = try(opts.display_name, "Terraform-managed.") display_name = coalesce(
iam_project_roles = try(opts.iam_project_roles, null) try(var.data_overrides.service_accounts.display_name, null),
try(opts.display_name, null),
try(var.data_defaults.service_accounts.display_name, null),
"Terraform-managed."
)
iam_billing_roles = try(opts.iam_billing_roles, {})
iam_organization_roles = try(opts.iam_organization_roles, {})
iam_sa_roles = try(opts.iam_sa_roles, {})
iam_project_roles = try(opts.iam_project_roles, {})
iam_self_roles = distinct(concat(
try(var.data_overrides.service_accounts.iam_self_roles, []),
try(opts.iam_self_roles, []),
try(var.data_defaults.service_accounts.iam_self_roles, []),
))
iam_storage_roles = try(opts.iam_storage_roles, {})
opts = opts
} }
] ]
]) ])

View File

@ -67,14 +67,17 @@ module "projects" {
module "service-accounts" { module "service-accounts" {
source = "../iam-service-account" source = "../iam-service-account"
for_each = { for_each = {
for k in local.service_accounts : "${k.project}-${k.name}" => k for k in local.service_accounts : "${k.project}/${k.name}" => k
} }
project_id = module.projects[each.value.project].project_id project_id = module.projects[each.value.project].project_id
name = each.value.name name = each.value.name
display_name = each.value.display_name display_name = each.value.display_name
iam_project_roles = each.value.iam_project_roles == null ? {} : { iam_project_roles = merge(
(module.projects[each.value.project].project_id) = each.value.iam_project_roles each.value.iam_project_roles,
} each.value.iam_self_roles == null ? {} : {
(module.projects[each.value.project].project_id) = each.value.iam_self_roles
}
)
} }
module "billing-account" { module "billing-account" {

View File

@ -21,7 +21,6 @@ output "projects" {
output "service_accounts" { output "service_accounts" {
description = "Service account emails." description = "Service account emails."
# TODO: group by project
value = { value = {
for k, v in module.service-accounts : k => v.email for k, v in module.service-accounts : k => v.email
} }

View File

@ -38,8 +38,8 @@ variable "data_defaults" {
tag_bindings = optional(map(string), {}) tag_bindings = optional(map(string), {})
# non-project resources # non-project resources
service_accounts = optional(map(object({ service_accounts = optional(map(object({
display_name = optional(string, "Terraform-managed.") display_name = optional(string, "Terraform-managed.")
iam_project_roles = optional(list(string)) iam_self_roles = optional(list(string))
})), {}) })), {})
}) })
nullable = false nullable = false
@ -58,8 +58,8 @@ variable "data_merges" {
tag_bindings = optional(map(string), {}) tag_bindings = optional(map(string), {})
# non-project resources # non-project resources
service_accounts = optional(map(object({ service_accounts = optional(map(object({
display_name = optional(string, "Terraform-managed.") display_name = optional(string, "Terraform-managed.")
iam_project_roles = optional(list(string)) iam_self_roles = optional(list(string))
})), {}) })), {})
}) })
nullable = false nullable = false
@ -80,8 +80,8 @@ variable "data_overrides" {
services = optional(list(string)) services = optional(list(string))
# non-project resources # non-project resources
service_accounts = optional(map(object({ service_accounts = optional(map(object({
display_name = optional(string, "Terraform-managed.") display_name = optional(string, "Terraform-managed.")
iam_project_roles = optional(list(string)) iam_self_roles = optional(list(string))
}))) })))
}) })
nullable = false nullable = false

View File

@ -13,6 +13,44 @@
# limitations under the License. # limitations under the License.
values: values:
module.project-factory.module.billing-account[0].google_billing_budget.default["test-100"]:
all_updates_rule:
- disable_default_iam_recipients: true
pubsub_topic: null
schema_version: '1.0'
amount:
- last_period_amount: null
specified_amount:
- nanos: null
units: '100'
billing_account: 123456-123456-123456
budget_filter:
- calendar_period: null
credit_types_treatment: INCLUDE_ALL_CREDITS
custom_period: []
projects:
- projects/test-pf-prj-app-1
resource_ancestors:
- folders/1234567890
display_name: 100 dollars in current spend
threshold_rules:
- spend_basis: CURRENT_SPEND
threshold_percent: 0.5
- spend_basis: CURRENT_SPEND
threshold_percent: 0.75
timeouts: null
module.project-factory.module.billing-account[0].google_monitoring_notification_channel.default["billing-default"]:
description: null
display_name: Budget email notification billing-default.
enabled: true
force_delete: false
labels:
email_address: gcp-billing-admins@example.com
project: foo-billing-audit
sensitive_labels: []
timeouts: null
type: email
user_labels: null
module.project-factory.module.projects["prj-app-1"].data.google_storage_project_service_account.gcs_sa[0]: module.project-factory.module.projects["prj-app-1"].data.google_storage_project_service_account.gcs_sa[0]:
project: test-pf-prj-app-1 project: test-pf-prj-app-1
user_project: null user_project: null
@ -74,6 +112,25 @@ values:
host_project: foo-host host_project: foo-host
service_project: test-pf-prj-app-2 service_project: test-pf-prj-app-2
timeouts: null timeouts: null
? module.project-factory.module.projects["prj-app-2"].google_compute_subnetwork_iam_member.shared_vpc_host_robots["europe-west1:prod-default-ew1:cloudservices"]
: condition: []
project: foo-host
region: europe-west1
role: roles/compute.networkUser
subnetwork: prod-default-ew1
? module.project-factory.module.projects["prj-app-2"].google_compute_subnetwork_iam_member.shared_vpc_host_robots["europe-west1:prod-default-ew1:container-engine"]
: condition: []
project: foo-host
region: europe-west1
role: roles/compute.networkUser
subnetwork: prod-default-ew1
? module.project-factory.module.projects["prj-app-2"].google_compute_subnetwork_iam_member.shared_vpc_host_subnets_iam["europe-west1:prod-default-ew1:group:team-1@example.com"]
: condition: []
member: group:team-1@example.com
project: foo-host
region: europe-west1
role: roles/compute.networkUser
subnetwork: prod-default-ew1
module.project-factory.module.projects["prj-app-2"].google_essential_contacts_contact.contact["admin@example.com"]: module.project-factory.module.projects["prj-app-2"].google_essential_contacts_contact.contact["admin@example.com"]:
email: admin@example.com email: admin@example.com
language_tag: en language_tag: en
@ -81,6 +138,23 @@ values:
- ALL - ALL
parent: projects/test-pf-prj-app-2 parent: projects/test-pf-prj-app-2
timeouts: null timeouts: null
? module.project-factory.module.projects["prj-app-2"].google_org_policy_policy.default["compute.restrictSharedVpcSubnetworks"]
: dry_run_spec: []
name: projects/test-pf-prj-app-2/policies/compute.restrictSharedVpcSubnetworks
parent: projects/test-pf-prj-app-2
spec:
- inherit_from_parent: null
reset: null
rules:
- allow_all: null
condition: []
deny_all: null
enforce: null
values:
- allowed_values:
- projects/foo-host/regions/europe-west1/subnetworks/prod-default-ew1
denied_values: null
timeouts: null
module.project-factory.module.projects["prj-app-2"].google_project.project[0]: module.project-factory.module.projects["prj-app-2"].google_project.project[0]:
auto_create_network: false auto_create_network: false
billing_account: 123456-123456-123456 billing_account: 123456-123456-123456
@ -110,18 +184,6 @@ values:
: condition: [] : condition: []
project: foo-host project: foo-host
role: roles/vpcaccess.user role: roles/vpcaccess.user
? module.project-factory.module.projects["prj-app-2"].google_compute_subnetwork_iam_member.shared_vpc_host_robots["europe-west1:prod-default-ew1:cloudservices"]
: condition: [ ]
project: foo-host
role: roles/compute.networkUser
? module.project-factory.module.projects["prj-app-2"].google_compute_subnetwork_iam_member.shared_vpc_host_robots["europe-west1:prod-default-ew1:container-engine"]
: condition: [ ]
project: foo-host
role: roles/compute.networkUser
? module.project-factory.module.projects["prj-app-2"].google_compute_subnetwork_iam_member.shared_vpc_host_subnets_iam["europe-west1:prod-default-ew1:group:team-1@example.com"]
: condition: [ ]
project: foo-host
role: roles/compute.networkUser
module.project-factory.module.projects["prj-app-2"].google_project_service.project_services["compute.googleapis.com"]: module.project-factory.module.projects["prj-app-2"].google_project_service.project_services["compute.googleapis.com"]:
disable_dependent_services: false disable_dependent_services: false
disable_on_destroy: false disable_on_destroy: false
@ -152,21 +214,6 @@ values:
project: test-pf-prj-app-2 project: test-pf-prj-app-2
service: storage.googleapis.com service: storage.googleapis.com
timeouts: null timeouts: null
module.project-factory.module.projects["prj-app-2"].google_org_policy_policy.default["compute.restrictSharedVpcSubnetworks"]:
name: projects/test-pf-prj-app-2/policies/compute.restrictSharedVpcSubnetworks
parent: projects/test-pf-prj-app-2
spec:
- inherit_from_parent: null
reset: null
rules:
- allow_all: null
condition: [ ]
deny_all: null
enforce: null
values:
- allowed_values:
- projects/foo-host/regions/europe-west1/subnetworks/prod-default-ew1
denied_values: null
module.project-factory.module.projects["prj-app-3"].data.google_storage_project_service_account.gcs_sa[0]: module.project-factory.module.projects["prj-app-3"].data.google_storage_project_service_account.gcs_sa[0]:
project: test-pf-prj-app-3 project: test-pf-prj-app-3
user_project: null user_project: null
@ -210,33 +257,44 @@ values:
project: test-pf-prj-app-3 project: test-pf-prj-app-3
service: storage.googleapis.com service: storage.googleapis.com
timeouts: null timeouts: null
? module.project-factory.module.service-accounts["prj-app-1-app-1-be"].google_project_iam_member.project-roles["test-pf-prj-app-1-roles/logging.logWriter"] ? module.project-factory.module.service-accounts["prj-app-1/app-1-be"].google_project_iam_member.project-roles["my-host-project-roles/compute.networkUser"]
: condition: []
project: my-host-project
role: roles/compute.networkUser
? module.project-factory.module.service-accounts["prj-app-1/app-1-be"].google_project_iam_member.project-roles["test-pf-prj-app-1-roles/logging.logWriter"]
: condition: [] : condition: []
project: test-pf-prj-app-1 project: test-pf-prj-app-1
role: roles/logging.logWriter role: roles/logging.logWriter
? module.project-factory.module.service-accounts["prj-app-1-app-1-be"].google_project_iam_member.project-roles["test-pf-prj-app-1-roles/monitoring.metricWriter"] ? module.project-factory.module.service-accounts["prj-app-1/app-1-be"].google_project_iam_member.project-roles["test-pf-prj-app-1-roles/monitoring.metricWriter"]
: condition: [] : condition: []
project: test-pf-prj-app-1 project: test-pf-prj-app-1
role: roles/monitoring.metricWriter role: roles/monitoring.metricWriter
module.project-factory.module.service-accounts["prj-app-1-app-1-be"].google_service_account.service_account[0]: module.project-factory.module.service-accounts["prj-app-1/app-1-be"].google_service_account.service_account[0]:
account_id: app-1-be account_id: app-1-be
create_ignore_already_exists: null
description: null description: null
disabled: false disabled: false
display_name: null display_name: Terraform-managed.
project: test-pf-prj-app-1 project: test-pf-prj-app-1
timeouts: null timeouts: null
module.project-factory.module.service-accounts["prj-app-1-app-1-fe"].google_service_account.service_account[0]: ? module.project-factory.module.service-accounts["prj-app-1/app-1-fe"].google_project_iam_member.project-roles["my-host-project-roles/compute.networkUser"]
: condition: []
project: my-host-project
role: roles/compute.networkUser
module.project-factory.module.service-accounts["prj-app-1/app-1-fe"].google_service_account.service_account[0]:
account_id: app-1-fe account_id: app-1-fe
create_ignore_already_exists: null
description: null description: null
disabled: false disabled: false
display_name: Test app 1 frontend. display_name: Test app 1 frontend.
project: test-pf-prj-app-1 project: test-pf-prj-app-1
timeouts: null timeouts: null
module.project-factory.module.service-accounts["prj-app-2-app-2-be"].google_service_account.service_account[0]: module.project-factory.module.service-accounts["prj-app-2/app-2-be"].google_service_account.service_account[0]:
account_id: app-2-be account_id: app-2-be
create_ignore_already_exists: null
description: null description: null
disabled: false disabled: false
display_name: null display_name: Terraform-managed.
project: test-pf-prj-app-2 project: test-pf-prj-app-2
timeouts: null timeouts: null
@ -249,11 +307,11 @@ counts:
google_monitoring_notification_channel: 1 google_monitoring_notification_channel: 1
google_org_policy_policy: 1 google_org_policy_policy: 1
google_project: 3 google_project: 3
google_project_iam_member: 4 google_project_iam_member: 6
google_project_service: 11 google_project_service: 11
google_service_account: 3 google_service_account: 3
google_storage_project_service_account: 3 google_storage_project_service_account: 3
modules: 8 modules: 8
resources: 35 resources: 37
outputs: {} outputs: {}