Support automation/controlling projects and resources in project factory (#2162)
* initial implementation not tested * project factory automation project support
This commit is contained in:
parent
11b9319043
commit
7f8d2834b3
|
@ -23,10 +23,12 @@ The code is meant to be executed by a high level service accounts with powerful
|
|||
<!-- BEGIN TOC -->
|
||||
- [Folder hierarchy](#folder-hierarchy)
|
||||
- [Projects](#projects)
|
||||
- [Leveraging project defaults, merges, optionals](#leveraging-project-defaults-merges-optionals)
|
||||
- [Factory-wide project defaults, merges, optionals](#factory-wide-project-defaults-merges-optionals)
|
||||
- [Service accounts](#service-accounts)
|
||||
- [Automation project and resources](#automation-project-and-resources)
|
||||
- [Billing budgets](#billing-budgets)
|
||||
- [Example](#example)
|
||||
- [Files](#files)
|
||||
- [Variables](#variables)
|
||||
- [Outputs](#outputs)
|
||||
- [Tests](#tests)
|
||||
|
@ -51,7 +53,7 @@ Refer to the [example](#example) below for actual examples of the YAML definitio
|
|||
|
||||
The project factory is configured via the `factories_config.projects_data_path` variable, and project files are also read from the hierarchy describe in the previous section when enabled. The YAML format mirrors the project module, refer to the [example](#example) below for actual examples of the YAML definitions.
|
||||
|
||||
### Leveraging project defaults, merges, optionals
|
||||
### Factory-wide project defaults, merges, optionals
|
||||
|
||||
In addition to the YAML-based project configurations, the factory accepts three additional sets of inputs via Terraform variables:
|
||||
|
||||
|
@ -81,6 +83,54 @@ service_accounts:
|
|||
|
||||
Both the `display_name` and `iam_self_roles` attributes are optional.
|
||||
|
||||
### Automation project and resources
|
||||
|
||||
Project configurations also support defining service accounts and storage buckets to support automation, created in a separate controlling project so as to be outside of the sphere of control of the managed project.
|
||||
|
||||
Automation resources are defined via the `automation` attribute in project configurations, which supports:
|
||||
|
||||
- a mandatory `project` attribute to define the external controlling project
|
||||
- an optional `service_accounts` list where each element will define a service account in the controlling project
|
||||
- an optional `buckets` map where each key will define a bucket in the controlling project, and the map of roles/principals in the corresponding value assigned on the created bucket; principals can refer to the created service accounts by key
|
||||
|
||||
Service accounts and buckets will be prefixed with the project name, and use the key specified in the YAML file as a suffix.
|
||||
|
||||
```yaml
|
||||
# file name: prod-app-example-0
|
||||
# prefix via factory defaults: foo
|
||||
# project id: foo-prod-app-example-0
|
||||
billing_account: 012345-67890A-BCDEF0
|
||||
parent: folders/12345678
|
||||
services:
|
||||
- compute.googleapis.com
|
||||
- stackdriver.googleapis.com
|
||||
iam:
|
||||
roles/owner:
|
||||
- rw
|
||||
roles/viewer:
|
||||
- ro
|
||||
automation:
|
||||
project: foo-prod-iac-core-0
|
||||
service_accounts:
|
||||
# sa name: foo-prod-app-example-0-rw
|
||||
rw:
|
||||
description: Read/write automation sa for app example 0.
|
||||
# sa name: foo-prod-app-example-0-ro
|
||||
ro:
|
||||
description: Read-only automation sa for app example 0.
|
||||
buckets:
|
||||
# bucket name: foo-prod-app-example-0-state
|
||||
state:
|
||||
description: Terraform state bucket for app example 0.
|
||||
iam:
|
||||
roles/storage.objectCreator:
|
||||
- rw
|
||||
roles/storage.objectViewer:
|
||||
- rw
|
||||
- ro
|
||||
- group:devops@example.org
|
||||
```
|
||||
|
||||
## Billing budgets
|
||||
|
||||
The billing budgets factory integrates the `[`billing-account`](../billing-account/) module functionality, and adds support for easy referencing budgets in project files.
|
||||
|
@ -102,7 +152,7 @@ billing_budgets:
|
|||
- test-100
|
||||
```
|
||||
|
||||
The example below shows how to use the billing budgets factory.
|
||||
A simple billing budget example is show in the [example](#example) below.
|
||||
|
||||
## Example
|
||||
|
||||
|
@ -155,7 +205,7 @@ module "project-factory" {
|
|||
projects_data_path = "data/projects"
|
||||
}
|
||||
}
|
||||
# tftest modules=13 resources=48 files=prj-app-1,prj-app-2,prj-app-3,budget-test-100,h-0-0,h-1-0,h-0-1,h-1-1,h-1-1-p0 inventory=example.yaml
|
||||
# tftest modules=16 resources=55 files=prj-app-1,prj-app-2,prj-app-3,budget-test-100,h-0-0,h-1-0,h-0-1,h-1-1,h-1-1-p0 inventory=example.yaml
|
||||
```
|
||||
|
||||
A simple hierarchy of folders:
|
||||
|
@ -191,7 +241,7 @@ billing_account: 012345-67890A-BCDEF0
|
|||
services:
|
||||
- container.googleapis.com
|
||||
- storage.googleapis.com
|
||||
# tftest-file id=h-1-1-p0 path=data/hierarchy/bar/baz/bar-baz-0.yaml
|
||||
# tftest-file id=h-1-1-p0 path=data/hierarchy/bar/baz/bar-baz-iac-0.yaml
|
||||
```
|
||||
|
||||
More traditional project definitions via the project factory data:
|
||||
|
@ -264,12 +314,36 @@ shared_vpc_service_config:
|
|||
# tftest-file id=prj-app-2 path=data/projects/prj-app-2.yaml
|
||||
```
|
||||
|
||||
This project uses a reference to a hierarchy folder, and defines a controlling project via the `automation` attributes:
|
||||
|
||||
```yaml
|
||||
# project app-3
|
||||
parent: folders/12345678
|
||||
parent: bar/baz
|
||||
services:
|
||||
- run.googleapis.com
|
||||
- storage.googleapis.com
|
||||
iam:
|
||||
"roles/owner":
|
||||
- rw
|
||||
"roles/viewer":
|
||||
- ro
|
||||
automation:
|
||||
project: bar-baz-iac-0
|
||||
service_accounts:
|
||||
rw:
|
||||
description: Read/write automation sa for app example 0.
|
||||
ro:
|
||||
description: Read-only automation sa for app example 0.
|
||||
buckets:
|
||||
state:
|
||||
description: Terraform state bucket for app example 0.
|
||||
iam:
|
||||
roles/storage.objectCreator:
|
||||
- rw
|
||||
roles/storage.objectViewer:
|
||||
- rw
|
||||
- ro
|
||||
- group:devops@example.org
|
||||
|
||||
|
||||
# tftest-file id=prj-app-3 path=data/projects/prj-app-3.yaml
|
||||
```
|
||||
|
@ -297,7 +371,21 @@ update_rules:
|
|||
# tftest-file id=budget-test-100 path=data/budgets/test-100.yaml
|
||||
```
|
||||
|
||||
<!-- TFDOC OPTS files:1 -->
|
||||
<!-- BEGIN TFDOC -->
|
||||
## Files
|
||||
|
||||
| name | description | modules |
|
||||
|---|---|---|
|
||||
| [automation.tf](./automation.tf) | Automation projects locals and resources. | <code>gcs</code> · <code>iam-service-account</code> |
|
||||
| [factory-budgets.tf](./factory-budgets.tf) | Billing budget factory locals. | |
|
||||
| [factory-folders.tf](./factory-folders.tf) | Folder hierarchy factory locals. | |
|
||||
| [factory-projects.tf](./factory-projects.tf) | Projects factory locals. | |
|
||||
| [folders.tf](./folders.tf) | Folder hierarchy factory resources. | <code>folder</code> |
|
||||
| [main.tf](./main.tf) | Projects and billing budgets factory resources. | <code>billing-account</code> · <code>iam-service-account</code> · <code>project</code> |
|
||||
| [outputs.tf](./outputs.tf) | Module outputs. | |
|
||||
| [variables.tf](./variables.tf) | Module variables. | |
|
||||
|
||||
## Variables
|
||||
|
||||
| name | description | type | required | default |
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
/**
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
# tfdoc:file:description Automation projects locals and resources.
|
||||
|
||||
locals {
|
||||
automation_buckets = flatten([
|
||||
for k, v in local.projects : [
|
||||
for ks, kv in try(v.automation.buckets, {}) : merge(kv, {
|
||||
automation_project = v.automation.project
|
||||
name = ks
|
||||
prefix = v.prefix
|
||||
project = k
|
||||
})
|
||||
]
|
||||
])
|
||||
automation_sa = flatten([
|
||||
for k, v in local.projects : [
|
||||
for ks, kv in try(v.automation.service_accounts, {}) : merge(kv, {
|
||||
automation_project = v.automation.project
|
||||
name = ks
|
||||
prefix = v.prefix
|
||||
project = k
|
||||
})
|
||||
]
|
||||
])
|
||||
}
|
||||
|
||||
module "automation-buckets" {
|
||||
source = "../gcs"
|
||||
for_each = {
|
||||
for k in local.automation_buckets : "${k.project}/${k.name}" => k
|
||||
}
|
||||
project_id = each.value.automation_project
|
||||
prefix = each.value.prefix
|
||||
name = "${each.value.project}-${each.value.name}"
|
||||
encryption_key = lookup(each.value, "encryption_key", null)
|
||||
# try interpolating service accounts by key in principals
|
||||
iam = {
|
||||
for k, v in lookup(each.value, "iam", {}) : k => [
|
||||
for vv in v : try(
|
||||
module.automation-service-accounts["${each.value.project}/${vv}"].iam_email,
|
||||
vv
|
||||
)
|
||||
]
|
||||
}
|
||||
iam_bindings = {
|
||||
for k, v in lookup(each.value, "iam_bindings", {}) : k => merge(v, {
|
||||
members = [
|
||||
for vv in v.members : try(
|
||||
module.automation-service-accounts["${each.value.project}/${vv}"].iam_email,
|
||||
vv
|
||||
)
|
||||
]
|
||||
})
|
||||
}
|
||||
iam_bindings_additive = {
|
||||
for k, v in lookup(each.value, "iam_bindings_additive", {}) : k => merge(v, {
|
||||
member = try(
|
||||
module.automation-service-accounts["${each.value.project}/${v.member}"].iam_email,
|
||||
v.member
|
||||
)
|
||||
})
|
||||
}
|
||||
labels = lookup(each.value, "labels", {})
|
||||
location = lookup(each.value, "location", "EU")
|
||||
storage_class = lookup(each.value, "storage_class", "MULTI_REGIONAL")
|
||||
uniform_bucket_level_access = lookup(each.value, "uniform_bucket_level_access", true)
|
||||
versioning = lookup(each.value, "versioning", false)
|
||||
}
|
||||
|
||||
module "automation-service-accounts" {
|
||||
source = "../iam-service-account"
|
||||
for_each = {
|
||||
for k in local.automation_sa : "${k.project}/${k.name}" => k
|
||||
}
|
||||
project_id = each.value.automation_project
|
||||
prefix = each.value.prefix
|
||||
name = "${each.value.project}-${each.value.name}"
|
||||
description = lookup(each.value, "description", null)
|
||||
display_name = lookup(
|
||||
each.value,
|
||||
"display_name",
|
||||
"Service account ${each.value.name} for ${each.value.project}."
|
||||
)
|
||||
iam = lookup(each.value, "iam", {})
|
||||
iam_bindings = lookup(each.value, "iam_bindings", {})
|
||||
iam_bindings_additive = lookup(each.value, "iam_bindings_additive", {})
|
||||
iam_billing_roles = lookup(each.value, "iam_billing_roles", {})
|
||||
iam_folder_roles = lookup(each.value, "iam_folder_roles", {})
|
||||
iam_organization_roles = lookup(each.value, "iam_organization_roles", {})
|
||||
iam_project_roles = lookup(each.value, "iam_project_roles", {})
|
||||
iam_sa_roles = lookup(each.value, "iam_sa_roles", {})
|
||||
# we don't interpolate buckets here as we can't use a dynamic key
|
||||
iam_storage_roles = lookup(each.value, "iam_storage_roles", {})
|
||||
}
|
|
@ -14,6 +14,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
# tfdoc:file:description Billing budget factory locals.
|
||||
|
||||
locals {
|
||||
# reimplement the billing account factory here to interpolate projects
|
||||
_budget_path = try(pathexpand(var.factories_config.budgets.budgets_data_path), null)
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
# tfdoc:file:description Folder hierarchy factory locals.
|
||||
|
||||
locals {
|
||||
_folders_path = try(
|
||||
pathexpand(var.factories_config.hierarchy.folders_data_path), null
|
||||
|
@ -51,53 +53,3 @@ check "hierarchy-data" {
|
|||
error_message = "No default set for hierarchy parent ids."
|
||||
}
|
||||
}
|
||||
|
||||
module "hierarchy-folder-lvl-1" {
|
||||
source = "../folder"
|
||||
for_each = { for k, v in local.folders : k => v if v.level == 1 }
|
||||
parent = try(
|
||||
# allow the YAML data to set the parent for this level
|
||||
lookup(
|
||||
var.factories_config.hierarchy.parent_ids,
|
||||
each.value.parent,
|
||||
# use the value as is if it's not in the parents map
|
||||
each.value.parent
|
||||
),
|
||||
# use the default value in the initial parents map
|
||||
var.factories_config.hierarchy.parent_ids.default
|
||||
# fail if we don't have an explicit parent
|
||||
)
|
||||
name = each.value.name
|
||||
iam = lookup(each.value, "iam", {})
|
||||
iam_bindings = lookup(each.value, "iam_bindings", {})
|
||||
iam_bindings_additive = lookup(each.value, "iam_bindings_additive", {})
|
||||
iam_by_principals = lookup(each.value, "iam_by_principals", {})
|
||||
org_policies = lookup(each.value, "org_policies", {})
|
||||
tag_bindings = lookup(each.value, "tag_bindings", {})
|
||||
}
|
||||
|
||||
module "hierarchy-folder-lvl-2" {
|
||||
source = "../folder"
|
||||
for_each = { for k, v in local.folders : k => v if v.level == 2 }
|
||||
parent = module.hierarchy-folder-lvl-1[each.value.parent_key].id
|
||||
name = each.value.name
|
||||
iam = lookup(each.value, "iam", {})
|
||||
iam_bindings = lookup(each.value, "iam_bindings", {})
|
||||
iam_bindings_additive = lookup(each.value, "iam_bindings_additive", {})
|
||||
iam_by_principals = lookup(each.value, "iam_by_principals", {})
|
||||
org_policies = lookup(each.value, "org_policies", {})
|
||||
tag_bindings = lookup(each.value, "tag_bindings", {})
|
||||
}
|
||||
|
||||
module "hierarchy-folder-lvl-3" {
|
||||
source = "../folder"
|
||||
for_each = { for k, v in local.folders : k => v if v.level == 3 }
|
||||
parent = module.hierarchy-folder-lvl-2[each.value.parent_key].id
|
||||
name = each.value.name
|
||||
iam = lookup(each.value, "iam", {})
|
||||
iam_bindings = lookup(each.value, "iam_bindings", {})
|
||||
iam_bindings_additive = lookup(each.value, "iam_bindings_additive", {})
|
||||
iam_by_principals = lookup(each.value, "iam_by_principals", {})
|
||||
org_policies = lookup(each.value, "org_policies", {})
|
||||
tag_bindings = lookup(each.value, "tag_bindings", {})
|
||||
}
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
# tfdoc:file:description Projects factory locals.
|
||||
|
||||
locals {
|
||||
_hierarchy_projects = (
|
||||
{
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
# tfdoc:file:description Folder hierarchy factory resources.
|
||||
|
||||
module "hierarchy-folder-lvl-1" {
|
||||
source = "../folder"
|
||||
for_each = { for k, v in local.folders : k => v if v.level == 1 }
|
||||
parent = try(
|
||||
# allow the YAML data to set the parent for this level
|
||||
lookup(
|
||||
var.factories_config.hierarchy.parent_ids,
|
||||
each.value.parent,
|
||||
# use the value as is if it's not in the parents map
|
||||
each.value.parent
|
||||
),
|
||||
# use the default value in the initial parents map
|
||||
var.factories_config.hierarchy.parent_ids.default
|
||||
# fail if we don't have an explicit parent
|
||||
)
|
||||
name = each.value.name
|
||||
iam = lookup(each.value, "iam", {})
|
||||
iam_bindings = lookup(each.value, "iam_bindings", {})
|
||||
iam_bindings_additive = lookup(each.value, "iam_bindings_additive", {})
|
||||
iam_by_principals = lookup(each.value, "iam_by_principals", {})
|
||||
org_policies = lookup(each.value, "org_policies", {})
|
||||
tag_bindings = lookup(each.value, "tag_bindings", {})
|
||||
}
|
||||
|
||||
module "hierarchy-folder-lvl-2" {
|
||||
source = "../folder"
|
||||
for_each = { for k, v in local.folders : k => v if v.level == 2 }
|
||||
parent = module.hierarchy-folder-lvl-1[each.value.parent_key].id
|
||||
name = each.value.name
|
||||
iam = lookup(each.value, "iam", {})
|
||||
iam_bindings = lookup(each.value, "iam_bindings", {})
|
||||
iam_bindings_additive = lookup(each.value, "iam_bindings_additive", {})
|
||||
iam_by_principals = lookup(each.value, "iam_by_principals", {})
|
||||
org_policies = lookup(each.value, "org_policies", {})
|
||||
tag_bindings = lookup(each.value, "tag_bindings", {})
|
||||
}
|
||||
|
||||
module "hierarchy-folder-lvl-3" {
|
||||
source = "../folder"
|
||||
for_each = { for k, v in local.folders : k => v if v.level == 3 }
|
||||
parent = module.hierarchy-folder-lvl-2[each.value.parent_key].id
|
||||
name = each.value.name
|
||||
iam = lookup(each.value, "iam", {})
|
||||
iam_bindings = lookup(each.value, "iam_bindings", {})
|
||||
iam_bindings_additive = lookup(each.value, "iam_bindings_additive", {})
|
||||
iam_by_principals = lookup(each.value, "iam_by_principals", {})
|
||||
org_policies = lookup(each.value, "org_policies", {})
|
||||
tag_bindings = lookup(each.value, "tag_bindings", {})
|
||||
}
|
|
@ -14,6 +14,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
# tfdoc:file:description Projects and billing budgets factory resources.
|
||||
|
||||
module "projects" {
|
||||
source = "../project"
|
||||
for_each = local.projects
|
||||
|
@ -31,10 +33,35 @@ module "projects" {
|
|||
)
|
||||
default_service_account = try(each.value.default_service_account, "keep")
|
||||
descriptive_name = try(each.value.descriptive_name, null)
|
||||
iam = try(each.value.iam, {})
|
||||
iam_bindings = try(each.value.iam_bindings, {})
|
||||
iam_bindings_additive = try(each.value.iam_bindings_additive, {})
|
||||
iam_by_principals = try(each.value.iam_by_principals, {})
|
||||
# IAM interpolates automation service accounts
|
||||
iam = {
|
||||
for k, v in lookup(each.value, "iam", {}) : k => [
|
||||
for vv in v : try(
|
||||
module.automation-service-accounts["${each.key}/${vv}"].iam_email,
|
||||
vv
|
||||
)
|
||||
]
|
||||
}
|
||||
iam_bindings = {
|
||||
for k, v in lookup(each.value, "iam_bindings", {}) : k => merge(v, {
|
||||
members = [
|
||||
for vv in v.members : try(
|
||||
module.automation-service-accounts["${each.key}/${vv}"].iam_email,
|
||||
vv
|
||||
)
|
||||
]
|
||||
})
|
||||
}
|
||||
iam_bindings_additive = {
|
||||
for k, v in lookup(each.value, "iam_bindings_additive", {}) : k => merge(v, {
|
||||
member = try(
|
||||
module.automation-service-accounts["${each.key}/${v.member}"].iam_email,
|
||||
v.member
|
||||
)
|
||||
})
|
||||
}
|
||||
# IAM principals would trigger dynamic key errors so we don't interpolate
|
||||
iam_by_principals = try(each.value.iam_by_principals, {})
|
||||
labels = merge(
|
||||
each.value.labels, var.data_merges.labels
|
||||
)
|
||||
|
|
|
@ -12,6 +12,107 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
values:
|
||||
module.project-factory.module.automation-buckets["prj-app-3/state"].google_storage_bucket.bucket:
|
||||
autoclass:
|
||||
- enabled: false
|
||||
cors: []
|
||||
custom_placement_config: []
|
||||
default_event_based_hold: null
|
||||
enable_object_retention: null
|
||||
encryption: []
|
||||
force_destroy: false
|
||||
labels: null
|
||||
lifecycle_rule: []
|
||||
location: EU
|
||||
logging: []
|
||||
name: test-pf-prj-app-3-state
|
||||
project: bar-baz-iac-0
|
||||
requester_pays: null
|
||||
retention_policy: []
|
||||
storage_class: MULTI_REGIONAL
|
||||
timeouts: null
|
||||
uniform_bucket_level_access: true
|
||||
versioning:
|
||||
- enabled: false
|
||||
? module.project-factory.module.automation-buckets["prj-app-3/state"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectCreator"]
|
||||
: bucket: test-pf-prj-app-3-state
|
||||
condition: []
|
||||
members:
|
||||
- serviceAccount:test-pf-prj-app-3-rw@bar-baz-iac-0.iam.gserviceaccount.com
|
||||
role: roles/storage.objectCreator
|
||||
? module.project-factory.module.automation-buckets["prj-app-3/state"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectViewer"]
|
||||
: bucket: test-pf-prj-app-3-state
|
||||
condition: []
|
||||
members:
|
||||
- group:devops@example.org
|
||||
- serviceAccount:test-pf-prj-app-3-ro@bar-baz-iac-0.iam.gserviceaccount.com
|
||||
- serviceAccount:test-pf-prj-app-3-rw@bar-baz-iac-0.iam.gserviceaccount.com
|
||||
role: roles/storage.objectViewer
|
||||
module.project-factory.module.automation-service-accounts["prj-app-3/ro"].google_service_account.service_account[0]:
|
||||
account_id: test-pf-prj-app-3-ro
|
||||
create_ignore_already_exists: null
|
||||
description: Read-only automation sa for app example 0.
|
||||
disabled: false
|
||||
display_name: Service account ro for prj-app-3.
|
||||
project: bar-baz-iac-0
|
||||
timeouts: null
|
||||
module.project-factory.module.automation-service-accounts["prj-app-3/rw"].google_service_account.service_account[0]:
|
||||
account_id: test-pf-prj-app-3-rw
|
||||
create_ignore_already_exists: null
|
||||
description: Read/write automation sa for app example 0.
|
||||
disabled: false
|
||||
display_name: Service account rw for prj-app-3.
|
||||
project: bar-baz-iac-0
|
||||
timeouts: null
|
||||
module.project-factory.module.hierarchy-folder-lvl-1["bar"].google_folder.folder[0]:
|
||||
display_name: Bar (level 1)
|
||||
parent: folders/4567890
|
||||
timeouts: null
|
||||
module.project-factory.module.hierarchy-folder-lvl-1["foo"].google_folder.folder[0]:
|
||||
display_name: Foo (level 1)
|
||||
parent: folders/12345678
|
||||
timeouts: null
|
||||
module.project-factory.module.hierarchy-folder-lvl-1["foo"].google_folder_iam_binding.authoritative["roles/viewer"]:
|
||||
condition: []
|
||||
members:
|
||||
- group:a@example.com
|
||||
role: roles/viewer
|
||||
module.project-factory.module.hierarchy-folder-lvl-2["bar/baz"].google_folder.folder[0]:
|
||||
display_name: Bar Baz (level 2)
|
||||
timeouts: null
|
||||
module.project-factory.module.hierarchy-folder-lvl-2["foo/baz"].google_folder.folder[0]:
|
||||
display_name: Foo Baz (level 2)
|
||||
timeouts: null
|
||||
module.project-factory.module.projects["bar-baz-iac-0"].data.google_storage_project_service_account.gcs_sa[0]:
|
||||
project: test-pf-bar-baz-iac-0
|
||||
user_project: null
|
||||
module.project-factory.module.projects["prj-app-3"].google_project.project[0]:
|
||||
auto_create_network: false
|
||||
billing_account: 123456-123456-123456
|
||||
effective_labels:
|
||||
environment: test
|
||||
labels:
|
||||
environment: test
|
||||
name: test-pf-prj-app-3
|
||||
project_id: test-pf-prj-app-3
|
||||
skip_delete: false
|
||||
terraform_labels:
|
||||
environment: test
|
||||
timeouts: null
|
||||
module.project-factory.module.projects["prj-app-3"].google_project_iam_binding.authoritative["roles/owner"]:
|
||||
condition: []
|
||||
members:
|
||||
- serviceAccount:test-pf-prj-app-3-rw@bar-baz-iac-0.iam.gserviceaccount.com
|
||||
project: test-pf-prj-app-3
|
||||
role: roles/owner
|
||||
module.project-factory.module.projects["prj-app-3"].google_project_iam_binding.authoritative["roles/viewer"]:
|
||||
condition: []
|
||||
members:
|
||||
- serviceAccount:test-pf-prj-app-3-ro@bar-baz-iac-0.iam.gserviceaccount.com
|
||||
project: test-pf-prj-app-3
|
||||
role: roles/viewer
|
||||
|
||||
counts:
|
||||
google_billing_budget: 1
|
||||
google_compute_shared_vpc_service_project: 1
|
||||
|
@ -23,9 +124,12 @@ counts:
|
|||
google_monitoring_notification_channel: 1
|
||||
google_org_policy_policy: 1
|
||||
google_project: 4
|
||||
google_project_iam_binding: 2
|
||||
google_project_iam_member: 6
|
||||
google_project_service: 14
|
||||
google_service_account: 3
|
||||
google_service_account: 5
|
||||
google_storage_bucket: 1
|
||||
google_storage_bucket_iam_binding: 2
|
||||
google_storage_project_service_account: 4
|
||||
modules: 13
|
||||
resources: 48
|
||||
modules: 16
|
||||
resources: 55
|
||||
|
|
Loading…
Reference in New Issue