Integrate checklist data in FAST (#1969)

* add locals for additive and authoritative org iam roles

* first shot at IAM and logging location

* tfdoc

* use locals for locations

* fix file parsing, resman stubs

* initial resman implementation

* remove unneeded code

* fix data file

* replace dumb yamldecode

* fix wrong type in organization additive bindings try

* simplify logging local

* Use check asserts for version and org id

* Checks on checklist for resman

* refactor checks, ignore checklist files on wrong org id

* stage 0 tests

* fix checklist checks

* stage 1 tests

---------

Co-authored-by: Wiktor Niesiobędzki <wiktorn@google.com>
This commit is contained in:
Ludovico Magnocavallo
2024-01-18 05:45:29 +01:00
committed by GitHub
parent a34cdd5597
commit a8c84357f4
24 changed files with 1145 additions and 77 deletions

View File

@@ -578,6 +578,7 @@ The `fast_features` variable consists of 4 toggles:
|---|---|---|---|
| [automation.tf](./automation.tf) | Automation project and resources. | <code>gcs</code> · <code>iam-service-account</code> · <code>project</code> | |
| [billing.tf](./billing.tf) | Billing export project and dataset. | <code>bigquery-dataset</code> · <code>project</code> | <code>google_billing_account_iam_member</code> |
| [checklist.tf](./checklist.tf) | None | | |
| [cicd.tf](./cicd.tf) | Workload Identity Federation configurations for CI/CD. | <code>iam-service-account</code> · <code>source-repository</code> | |
| [identity-providers.tf](./identity-providers.tf) | Workload Identity Federation provider definitions. | | <code>google_iam_workload_identity_pool</code> · <code>google_iam_workload_identity_pool_provider</code> |
| [log-export.tf](./log-export.tf) | Audit log project and sink. | <code>bigquery-dataset</code> · <code>gcs</code> · <code>logging-bucket</code> · <code>project</code> · <code>pubsub</code> | |
@@ -594,23 +595,23 @@ The `fast_features` variable consists of 4 toggles:
| name | description | type | required | default | producer |
|---|---|:---:|:---:|:---:|:---:|
| [billing_account](variables.tf#L17) | Billing account id. If billing account is not part of the same org set `is_org_level` to `false`. To disable handling of billing IAM roles set `no_iam` to `true`. | <code title="object&#40;&#123;&#10; id &#61; string&#10; is_org_level &#61; optional&#40;bool, true&#41;&#10; no_iam &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | |
| [organization](variables.tf#L235) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | |
| [prefix](variables.tf#L250) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | |
| [organization](variables.tf#L244) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | |
| [prefix](variables.tf#L259) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | |
| [bootstrap_user](variables.tf#L27) | Email of the nominal user running this stage for the first time. | <code>string</code> | | <code>null</code> | |
| [cicd_repositories](variables.tf#L33) | CI/CD repository configuration. Identity providers reference keys in the `federated_identity_providers` variable. Set to null to disable, or set individual repositories to null if not needed. | <code title="object&#40;&#123;&#10; bootstrap &#61; optional&#40;object&#40;&#123;&#10; name &#61; string&#10; type &#61; string&#10; branch &#61; optional&#40;string&#41;&#10; identity_provider &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; resman &#61; optional&#40;object&#40;&#123;&#10; name &#61; string&#10; type &#61; string&#10; branch &#61; optional&#40;string&#41;&#10; identity_provider &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [custom_roles](variables.tf#L79) | Map of role names => list of permissions to additionally create at the organization level. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [factories_config](variables.tf#L86) | Configuration for the organization policies factory. | <code title="object&#40;&#123;&#10; custom_roles &#61; optional&#40;string, &#34;data&#47;custom-roles&#34;&#41;&#10; org_policy &#61; optional&#40;string, &#34;data&#47;org-policies&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [fast_features](variables.tf#L96) | Selective control for top-level FAST features. | <code title="object&#40;&#123;&#10; data_platform &#61; optional&#40;bool, false&#41;&#10; gke &#61; optional&#40;bool, false&#41;&#10; project_factory &#61; optional&#40;bool, false&#41;&#10; sandbox &#61; optional&#40;bool, false&#41;&#10; teams &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [federated_identity_providers](variables.tf#L109) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | <code title="map&#40;object&#40;&#123;&#10; attribute_condition &#61; optional&#40;string&#41;&#10; issuer &#61; string&#10; custom_settings &#61; optional&#40;object&#40;&#123;&#10; issuer_uri &#61; optional&#40;string&#41;&#10; audiences &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; jwks_json &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [group_iam](variables.tf#L129) | Organization-level authoritative IAM binding for groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [groups](variables.tf#L136) | Group names or emails to grant organization-level permissions. If just the name is provided, the default organization domain is assumed. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; gcp-billing-admins &#61; &#34;gcp-billing-admins&#34;,&#10; gcp-devops &#61; &#34;gcp-devops&#34;,&#10; gcp-network-admins &#61; &#34;gcp-network-admins&#34;&#10; gcp-organization-admins &#61; &#34;gcp-organization-admins&#34;&#10; gcp-security-admins &#61; &#34;gcp-security-admins&#34;&#10; gcp-support &#61; &#34;gcp-devops&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [iam](variables.tf#L154) | Organization-level custom IAM settings in role => [principal] format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [iam_bindings_additive](variables.tf#L161) | Organization-level custom additive IAM bindings. Keys are arbitrary. | <code title="map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [locations](variables.tf#L176) | Optional locations for GCS, BigQuery, and logging buckets created here. | <code title="object&#40;&#123;&#10; bq &#61; optional&#40;string, &#34;EU&#34;&#41;&#10; gcs &#61; optional&#40;string, &#34;EU&#34;&#41;&#10; logging &#61; optional&#40;string, &#34;global&#34;&#41;&#10; pubsub &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [log_sinks](variables.tf#L190) | Org-level log sinks, in name => {type, filter} format. | <code title="map&#40;object&#40;&#123;&#10; filter &#61; string&#10; type &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; audit-logs &#61; &#123;&#10; filter &#61; &#34;logName:&#92;&#34;&#47;logs&#47;cloudaudit.googleapis.com&#37;2Factivity&#92;&#34; OR logName:&#92;&#34;&#47;logs&#47;cloudaudit.googleapis.com&#37;2Fsystem_event&#92;&#34; OR protoPayload.metadata.&#64;type&#61;&#92;&#34;type.googleapis.com&#47;google.cloud.audit.TransparencyLog&#92;&#34;&#34;&#10; type &#61; &#34;logging&#34;&#10; &#125;&#10; vpc-sc &#61; &#123;&#10; filter &#61; &#34;protoPayload.metadata.&#64;type&#61;&#92;&#34;type.googleapis.com&#47;google.cloud.audit.VpcServiceControlAuditMetadata&#92;&#34;&#34;&#10; type &#61; &#34;logging&#34;&#10; &#125;&#10; workspace-audit-logs &#61; &#123;&#10; filter &#61; &#34;logName:&#92;&#34;&#47;logs&#47;cloudaudit.googleapis.com&#37;2Fdata_access&#92;&#34; and protoPayload.serviceName:&#92;&#34;login.googleapis.com&#92;&#34;&#34;&#10; type &#61; &#34;logging&#34;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [org_policies_config](variables.tf#L219) | Organization policies customization. | <code title="object&#40;&#123;&#10; constraints &#61; optional&#40;object&#40;&#123;&#10; allowed_policy_member_domains &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; tag_name &#61; optional&#40;string, &#34;org-policies&#34;&#41;&#10; tag_values &#61; optional&#40;map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Managed by the Terraform organization module.&#34;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; id &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [outputs_location](variables.tf#L244) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | <code>string</code> | | <code>null</code> | |
| [project_parent_ids](variables.tf#L259) | Optional parents for projects created here in folders/nnnnnnn format. Null values will use the organization as parent. | <code title="object&#40;&#123;&#10; automation &#61; string&#10; billing &#61; string&#10; logging &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; automation &#61; null&#10; billing &#61; null&#10; logging &#61; null&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [factories_config](variables.tf#L86) | Configuration for the resource factories or external data. | <code title="object&#40;&#123;&#10; checklist_data &#61; optional&#40;string&#41;&#10; checklist_org_iam &#61; optional&#40;string&#41;&#10; custom_roles &#61; optional&#40;string, &#34;data&#47;custom-roles&#34;&#41;&#10; org_policy &#61; optional&#40;string, &#34;data&#47;org-policies&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [fast_features](variables.tf#L98) | Selective control for top-level FAST features. | <code title="object&#40;&#123;&#10; data_platform &#61; optional&#40;bool, false&#41;&#10; gke &#61; optional&#40;bool, false&#41;&#10; project_factory &#61; optional&#40;bool, false&#41;&#10; sandbox &#61; optional&#40;bool, false&#41;&#10; teams &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [federated_identity_providers](variables.tf#L111) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | <code title="map&#40;object&#40;&#123;&#10; attribute_condition &#61; optional&#40;string&#41;&#10; issuer &#61; string&#10; custom_settings &#61; optional&#40;object&#40;&#123;&#10; issuer_uri &#61; optional&#40;string&#41;&#10; audiences &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; jwks_json &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [group_iam](variables.tf#L131) | Organization-level authoritative IAM binding for groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [groups](variables.tf#L138) | Group names or emails to grant organization-level permissions. If just the name is provided, the default organization domain is assumed. | <code title="object&#40;&#123;&#10; gcp-billing-admins &#61; string&#10; gcp-devops &#61; string&#10; gcp-network-admins &#61; string&#10; gcp-organization-admins &#61; string&#10; gcp-security-admins &#61; string&#10; gcp-support &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; gcp-billing-admins &#61; &#34;gcp-billing-admins&#34;&#10; gcp-devops &#61; &#34;gcp-devops&#34;&#10; gcp-network-admins &#61; &#34;gcp-network-admins&#34;&#10; gcp-organization-admins &#61; &#34;gcp-organization-admins&#34;&#10; gcp-security-admins &#61; &#34;gcp-security-admins&#34;&#10; gcp-support &#61; &#34;gcp-devops&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [iam](variables.tf#L163) | Organization-level custom IAM settings in role => [principal] format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [iam_bindings_additive](variables.tf#L170) | Organization-level custom additive IAM bindings. Keys are arbitrary. | <code title="map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [locations](variables.tf#L185) | Optional locations for GCS, BigQuery, and logging buckets created here. | <code title="object&#40;&#123;&#10; bq &#61; optional&#40;string, &#34;EU&#34;&#41;&#10; gcs &#61; optional&#40;string, &#34;EU&#34;&#41;&#10; logging &#61; optional&#40;string, &#34;global&#34;&#41;&#10; pubsub &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [log_sinks](variables.tf#L199) | Org-level log sinks, in name => {type, filter} format. | <code title="map&#40;object&#40;&#123;&#10; filter &#61; string&#10; type &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; audit-logs &#61; &#123;&#10; filter &#61; &#34;logName:&#92;&#34;&#47;logs&#47;cloudaudit.googleapis.com&#37;2Factivity&#92;&#34; OR logName:&#92;&#34;&#47;logs&#47;cloudaudit.googleapis.com&#37;2Fsystem_event&#92;&#34; OR protoPayload.metadata.&#64;type&#61;&#92;&#34;type.googleapis.com&#47;google.cloud.audit.TransparencyLog&#92;&#34;&#34;&#10; type &#61; &#34;logging&#34;&#10; &#125;&#10; vpc-sc &#61; &#123;&#10; filter &#61; &#34;protoPayload.metadata.&#64;type&#61;&#92;&#34;type.googleapis.com&#47;google.cloud.audit.VpcServiceControlAuditMetadata&#92;&#34;&#34;&#10; type &#61; &#34;logging&#34;&#10; &#125;&#10; workspace-audit-logs &#61; &#123;&#10; filter &#61; &#34;logName:&#92;&#34;&#47;logs&#47;cloudaudit.googleapis.com&#37;2Fdata_access&#92;&#34; and protoPayload.serviceName:&#92;&#34;login.googleapis.com&#92;&#34;&#34;&#10; type &#61; &#34;logging&#34;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [org_policies_config](variables.tf#L228) | Organization policies customization. | <code title="object&#40;&#123;&#10; constraints &#61; optional&#40;object&#40;&#123;&#10; allowed_policy_member_domains &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; tag_name &#61; optional&#40;string, &#34;org-policies&#34;&#41;&#10; tag_values &#61; optional&#40;map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Managed by the Terraform organization module.&#34;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; id &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [outputs_location](variables.tf#L253) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | <code>string</code> | | <code>null</code> | |
| [project_parent_ids](variables.tf#L268) | Optional parents for projects created here in folders/nnnnnnn format. Null values will use the organization as parent. | <code title="object&#40;&#123;&#10; automation &#61; string&#10; billing &#61; string&#10; logging &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; automation &#61; null&#10; billing &#61; null&#10; logging &#61; null&#10;&#125;">&#123;&#8230;&#125;</code> | |
## Outputs
@@ -625,5 +626,5 @@ The `fast_features` variable consists of 4 toggles:
| [project_ids](outputs.tf#L166) | Projects created by this stage. | | |
| [providers](outputs.tf#L176) | Terraform provider files for this stage and dependent stages. | ✓ | <code>stage-01</code> |
| [service_accounts](outputs.tf#L183) | Automation service accounts created by this stage. | | |
| [tfvars](outputs.tf#L192) | Terraform variable files for the following stages. | ✓ | |
| [tfvars](outputs.tf#L201) | Terraform variable files for the following stages. | ✓ | |
<!-- END TFDOC -->

View File

@@ -143,7 +143,7 @@ module "automation-tf-output-gcs" {
project_id = module.automation-project.project_id
name = "iac-core-outputs-0"
prefix = local.prefix
location = var.locations.gcs
location = local.locations.gcs
storage_class = local.gcs_storage_class
versioning = true
depends_on = [module.organization]
@@ -156,7 +156,7 @@ module "automation-tf-bootstrap-gcs" {
project_id = module.automation-project.project_id
name = "iac-core-bootstrap-0"
prefix = local.prefix
location = var.locations.gcs
location = local.locations.gcs
storage_class = local.gcs_storage_class
versioning = true
depends_on = [module.organization]
@@ -211,7 +211,7 @@ module "automation-tf-resman-gcs" {
project_id = module.automation-project.project_id
name = "iac-core-resman-0"
prefix = local.prefix
location = var.locations.gcs
location = local.locations.gcs
storage_class = local.gcs_storage_class
versioning = true
iam = {

View File

@@ -66,7 +66,7 @@ module "billing-export-dataset" {
project_id = module.billing-export-project.0.project_id
id = "billing_export"
friendly_name = "Billing export."
location = var.locations.bq
location = local.locations.bq
}
# standalone billing account

View File

@@ -0,0 +1,122 @@
/**
* 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.
*/
locals {
# group mapping from checklist to ours
_cl_groups = {
BILLING_ADMINS = local.groups.gcp-billing-admins
DEVOPS = local.groups.gcp-devops
# LOGGING_ADMINS
# MONITORING_ADMINS
NETWORK_ADMINS = local.groups.gcp-network-admins
ORG_ADMINS = local.groups.gcp-organization-admins
SECURITY_ADMINS = local.groups.gcp-security-admins
}
# parse raw data from JSON files if they exist
_cl_data_raw = (
var.factories_config.checklist_data == null
? null
: jsondecode(file(pathexpand(var.factories_config.checklist_data)))
)
_cl_org_raw = (
var.factories_config.checklist_org_iam == null
? null
: jsondecode(file(pathexpand(var.factories_config.checklist_org_iam)))
)
# check that files are for the correct organization and ignore them if not
_cl_data = (
try(local._cl_data_raw.organization.id, null) != tostring(var.organization.id)
? null
: local._cl_data_raw
)
_cl_org = (
try(local._cl_org_raw.organization.id, null) != tostring(var.organization.id)
? null
: local._cl_org_raw
)
# do a first pass on IAM bindings to identify groups and normalize
_cl_org_iam_bindings = {
for b in try(local._cl_org.iam_bindings, []) :
lookup(local._cl_groups, b.group_id, b.principal) => {
additive = [
for r in b.role : r if !contains(local.iam_roles_authoritative, r)
]
authoritative = [
for r in b.role : r if contains(local.iam_roles_authoritative, r)
]
roles = b.role
is_group = lookup(local._cl_groups, b.group_id, null) != null
}
}
# compile the final data structure we will consume from various places
checklist = {
billing_account = try(local._cl_data.billing_account, null)
group_iam = {
for k, v in local._cl_org_iam_bindings :
k => v.authoritative if v.is_group && length(v.authoritative) > 0
}
iam = {
for k, v in local._cl_org_iam_bindings :
k => v.authoritative if !v.is_group && length(v.authoritative) > 0
}
iam_bindings = concat(flatten([
for k, v in local._cl_org_iam_bindings : [
for r in v.additive : [
{
key = v.is_group ? "${r}-group:${k}" : "${r}-${k}"
member = v.is_group ? "group:${k}" : k
role = r
}
]
]
]))
location = try(local._cl_data.logging.sinks[0].destination.location, null)
}
}
check "checklist" {
# checklist data files don't need to be both present so we check independently
# version mismatch might be ok, we just alert users
assert {
condition = (
var.factories_config.checklist_data == null ||
try(local._cl_data_raw.version, null) == "0.1.0"
)
error_message = "Checklist data version mismatch."
}
assert {
condition = (
var.factories_config.checklist_org_iam == null ||
try(local._cl_org_raw.version, null) == "0.1.0"
)
error_message = "Checklist org IAM version mismatch."
}
# wrong org id forces us to ignore the files, but we also alert users
assert {
condition = (
var.factories_config.checklist_data == null ||
try(local._cl_data_raw.organization.id, null) == tostring(var.organization.id)
)
error_message = "Checklist data organization id mismatch, file ignored."
}
assert {
condition = (
var.factories_config.checklist_org_iam == null ||
try(local._cl_org_raw.organization.id, null) == tostring(var.organization.id)
)
error_message = "Checklist org IAM organization id mismatch, file ignored."
}
}

View File

@@ -19,9 +19,15 @@
locals {
log_sink_destinations = merge(
# use the same dataset for all sinks with `bigquery` as destination
{ for k, v in var.log_sinks : k => module.log-export-dataset.0 if v.type == "bigquery" },
{
for k, v in var.log_sinks :
k => module.log-export-dataset.0 if v.type == "bigquery"
},
# use the same gcs bucket for all sinks with `storage` as destination
{ for k, v in var.log_sinks : k => module.log-export-gcs.0 if v.type == "storage" },
{
for k, v in var.log_sinks :
k => module.log-export-gcs.0 if v.type == "storage"
},
# use separate pubsub topics and logging buckets for sinks with
# destination `pubsub` and `logging`
module.log-export-pubsub,
@@ -60,7 +66,7 @@ module "log-export-dataset" {
project_id = module.log-export-project.project_id
id = "audit_export"
friendly_name = "Audit logs export."
location = var.locations.bq
location = local.locations.bq
}
module "log-export-gcs" {
@@ -69,7 +75,7 @@ module "log-export-gcs" {
project_id = module.log-export-project.project_id
name = "audit-logs-0"
prefix = local.prefix
location = var.locations.gcs
location = local.locations.gcs
storage_class = local.gcs_storage_class
}
@@ -79,7 +85,7 @@ module "log-export-logbucket" {
parent_type = "project"
parent = module.log-export-project.project_id
id = "audit-logs-${each.key}"
location = var.locations.logging
location = local.locations.logging
}
module "log-export-pubsub" {
@@ -87,5 +93,5 @@ module "log-export-pubsub" {
for_each = toset([for k, v in var.log_sinks : k if v.type == "pubsub"])
project_id = module.log-export-project.project_id
name = "audit-logs-${each.key}"
regions = var.locations.pubsub
regions = local.locations.pubsub
}

View File

@@ -16,7 +16,7 @@
locals {
gcs_storage_class = (
length(split("-", var.locations.gcs)) < 2
length(split("-", local.locations.gcs)) < 2
? "MULTI_REGIONAL"
: "REGIONAL"
)
@@ -28,6 +28,12 @@ locals {
for k, v in local.groups :
k => "group:${v}"
}
locations = {
bq = var.locations.bq
gcs = var.locations.gcs
logging = coalesce(try(local.checklist.location, null), var.locations.logging)
pubsub = var.locations.pubsub
}
# naming: environment used in most resource names
prefix = join("-", compact([var.prefix, "prod"]))
}

View File

@@ -58,10 +58,11 @@ locals {
"tag_viewer",
"tenant_network_admin",
]
group_iam = {
# intermediate values before we merge in what comes from the checklist
_group_iam = {
for k, v in local.iam_group_bindings : k => v.authoritative
}
iam = merge(
_iam = merge(
{
for r in local.iam_delete_roles : r => []
},
@@ -69,12 +70,40 @@ locals {
for b in local._iam_bindings_auth : b.role => b.member...
}
)
iam_bindings_additive = {
_iam_bindings_additive = {
for b in local._iam_bindings_add : "${b.role}-${b.member}" => {
member = b.member
role = b.role
}
}
# final values combining all sources
group_iam = {
for k, v in local._group_iam : k => distinct(concat(
v,
try(local.checklist.group_iam[k], [])
))
}
iam = {
for k, v in local._iam : k => distinct(concat(
v,
try(local.checklist.iam[k].authoritative, [])
))
}
iam_bindings_additive = merge(
local._iam_bindings_additive,
{
for k, v in try(local.checklist.iam_bindings, {}) :
v.key => v if lookup(local._iam_bindings_additive, v.key, null) == null
}
)
# compute authoritative and additive roles for use by add-ons (checklist, etc.)
iam_roles_authoritative = distinct(concat(
flatten(values(local._group_iam)),
keys(local._iam)
))
iam_roles_additive = distinct([
for k, v in local._iam_bindings_additive : v.role
])
}
# TODO: add a check block to ensure our custom roles exist in the factory files

View File

@@ -115,7 +115,7 @@ locals {
billing_account = var.billing_account
fast_features = var.fast_features
groups = var.groups
locations = var.locations
locations = local.locations
organization = var.organization
prefix = var.prefix
}
@@ -188,6 +188,15 @@ output "service_accounts" {
}
}
# output "test" {
# value = {
# checklist = local.checklist
# iam_roles_authoritative = local.iam_roles_authoritative
# iam_roles_additive = local.iam_roles_additive
# test = local.checklist
# }
# }
# ready to use variable values for subsequent stages
output "tfvars" {
description = "Terraform variable files for the following stages."

View File

@@ -84,10 +84,12 @@ variable "custom_roles" {
}
variable "factories_config" {
description = "Configuration for the organization policies factory."
description = "Configuration for the resource factories or external data."
type = object({
custom_roles = optional(string, "data/custom-roles")
org_policy = optional(string, "data/org-policies")
checklist_data = optional(string)
checklist_org_iam = optional(string)
custom_roles = optional(string, "data/custom-roles")
org_policy = optional(string, "data/org-policies")
})
nullable = false
default = {}
@@ -136,10 +138,17 @@ variable "group_iam" {
variable "groups" {
# https://cloud.google.com/docs/enterprise/setup-checklist
description = "Group names or emails to grant organization-level permissions. If just the name is provided, the default organization domain is assumed."
type = map(string)
type = object({
gcp-billing-admins = string
gcp-devops = string
gcp-network-admins = string
gcp-organization-admins = string
gcp-security-admins = string
gcp-support = string
})
default = {
gcp-billing-admins = "gcp-billing-admins",
gcp-devops = "gcp-devops",
gcp-billing-admins = "gcp-billing-admins"
gcp-devops = "gcp-devops"
gcp-network-admins = "gcp-network-admins"
gcp-organization-admins = "gcp-organization-admins"
gcp-security-admins = "gcp-security-admins"

View File

@@ -334,6 +334,7 @@ Due to its simplicity, this stage lends itself easily to customizations: adding
| [branch-security.tf](./branch-security.tf) | Security stage resources. | <code>folder</code> · <code>gcs</code> · <code>iam-service-account</code> | |
| [branch-teams.tf](./branch-teams.tf) | Team stage resources. | <code>folder</code> · <code>gcs</code> · <code>iam-service-account</code> | |
| [branch-tenants.tf](./branch-tenants.tf) | Lightweight tenant resources. | <code>folder</code> · <code>gcs</code> · <code>iam-service-account</code> · <code>project</code> | |
| [checklist.tf](./checklist.tf) | None | <code>folder</code> | |
| [cicd-data-platform.tf](./cicd-data-platform.tf) | CI/CD resources for the data platform branch. | <code>iam-service-account</code> · <code>source-repository</code> | |
| [cicd-gke.tf](./cicd-gke.tf) | CI/CD resources for the GKE multitenant branch. | <code>iam-service-account</code> · <code>source-repository</code> | |
| [cicd-networking.tf](./cicd-networking.tf) | CI/CD resources for the networking branch. | <code>iam-service-account</code> · <code>source-repository</code> | |
@@ -355,34 +356,35 @@ Due to its simplicity, this stage lends itself easily to customizations: adding
|---|---|:---:|:---:|:---:|:---:|
| [automation](variables.tf#L20) | Automation resources created by the bootstrap stage. | <code title="object&#40;&#123;&#10; outputs_bucket &#61; string&#10; project_id &#61; string&#10; project_number &#61; string&#10; federated_identity_pool &#61; string&#10; federated_identity_providers &#61; map&#40;object&#40;&#123;&#10; audiences &#61; list&#40;string&#41;&#10; issuer &#61; string&#10; issuer_uri &#61; string&#10; name &#61; string&#10; principal_branch &#61; string&#10; principal_repo &#61; string&#10; &#125;&#41;&#41;&#10; service_accounts &#61; object&#40;&#123;&#10; resman-r &#61; string&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [billing_account](variables.tf#L42) | Billing account id. If billing account is not part of the same org set `is_org_level` to `false`. To disable handling of billing IAM roles set `no_iam` to `true`. | <code title="object&#40;&#123;&#10; id &#61; string&#10; is_org_level &#61; optional&#40;bool, true&#41;&#10; no_iam &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [organization](variables.tf#L202) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [prefix](variables.tf#L218) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>0-bootstrap</code> |
| [organization](variables.tf#L213) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [prefix](variables.tf#L229) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>0-bootstrap</code> |
| [cicd_repositories](variables.tf#L53) | CI/CD repository configuration. Identity providers reference keys in the `automation.federated_identity_providers` variable. Set to null to disable, or set individual repositories to null if not needed. | <code title="object&#40;&#123;&#10; data_platform_dev &#61; optional&#40;object&#40;&#123;&#10; name &#61; string&#10; type &#61; string&#10; branch &#61; optional&#40;string&#41;&#10; identity_provider &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; data_platform_prod &#61; optional&#40;object&#40;&#123;&#10; name &#61; string&#10; type &#61; string&#10; branch &#61; optional&#40;string&#41;&#10; identity_provider &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; gke_dev &#61; optional&#40;object&#40;&#123;&#10; name &#61; string&#10; type &#61; string&#10; branch &#61; optional&#40;string&#41;&#10; identity_provider &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; gke_prod &#61; optional&#40;object&#40;&#123;&#10; name &#61; string&#10; type &#61; string&#10; branch &#61; optional&#40;string&#41;&#10; identity_provider &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; networking &#61; optional&#40;object&#40;&#123;&#10; name &#61; string&#10; type &#61; string&#10; branch &#61; optional&#40;string&#41;&#10; identity_provider &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; project_factory_dev &#61; optional&#40;object&#40;&#123;&#10; name &#61; string&#10; type &#61; string&#10; branch &#61; optional&#40;string&#41;&#10; identity_provider &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; project_factory_prod &#61; optional&#40;object&#40;&#123;&#10; name &#61; string&#10; type &#61; string&#10; branch &#61; optional&#40;string&#41;&#10; identity_provider &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; security &#61; optional&#40;object&#40;&#123;&#10; name &#61; string&#10; type &#61; string&#10; branch &#61; optional&#40;string&#41;&#10; identity_provider &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [custom_roles](variables.tf#L135) | Custom roles defined at the org level, in key => id format. | <code title="object&#40;&#123;&#10; service_project_network_admin &#61; string&#10; storage_viewer &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>0-bootstrap</code> |
| [fast_features](variables.tf#L145) | Selective control for top-level FAST features. | <code title="object&#40;&#123;&#10; data_platform &#61; optional&#40;bool, false&#41;&#10; gke &#61; optional&#40;bool, false&#41;&#10; project_factory &#61; optional&#40;bool, false&#41;&#10; sandbox &#61; optional&#40;bool, false&#41;&#10; teams &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | <code>0-0-bootstrap</code> |
| [groups](variables.tf#L159) | Group names or emails to grant organization-level permissions. If just the name is provided, the default organization domain is assumed. | <code title="object&#40;&#123;&#10; gcp-devops &#61; optional&#40;string&#41;&#10; gcp-network-admins &#61; optional&#40;string&#41;&#10; gcp-security-admins &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | <code>0-bootstrap</code> |
| [locations](variables.tf#L172) | Optional locations for GCS, BigQuery, and logging buckets created here. | <code title="object&#40;&#123;&#10; bq &#61; string&#10; gcs &#61; string&#10; logging &#61; string&#10; pubsub &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; bq &#61; &#34;EU&#34;&#10; gcs &#61; &#34;EU&#34;&#10; logging &#61; &#34;global&#34;&#10; pubsub &#61; &#91;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> | <code>0-bootstrap</code> |
| [org_policy_tags](variables.tf#L190) | Resource management tags for organization policy exceptions. | <code title="object&#40;&#123;&#10; key_id &#61; optional&#40;string&#41;&#10; key_name &#61; optional&#40;string&#41;&#10; values &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | <code>0-bootstrap</code> |
| [outputs_location](variables.tf#L212) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | <code>string</code> | | <code>null</code> | |
| [tag_names](variables.tf#L229) | Customized names for resource management tags. | <code title="object&#40;&#123;&#10; context &#61; optional&#40;string, &#34;context&#34;&#41;&#10; environment &#61; optional&#40;string, &#34;environment&#34;&#41;&#10; tenant &#61; optional&#40;string, &#34;tenant&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [tags](variables.tf#L244) | Custome secure tags by key name. The `iam` attribute behaves like the similarly named one at module level. | <code title="map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Managed by the Terraform organization module.&#34;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; values &#61; optional&#40;map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Managed by the Terraform organization module.&#34;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; id &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [team_folders](variables.tf#L265) | Team folders to be created. Format is described in a code comment. | <code title="map&#40;object&#40;&#123;&#10; descriptive_name &#61; string&#10; group_iam &#61; map&#40;list&#40;string&#41;&#41;&#10; impersonation_groups &#61; list&#40;string&#41;&#10; cicd &#61; optional&#40;object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>null</code> | |
| [tenants](variables.tf#L281) | Lightweight tenant definitions. | <code title="map&#40;object&#40;&#123;&#10; admin_group_email &#61; string&#10; descriptive_name &#61; string&#10; billing_account &#61; optional&#40;string&#41;&#10; organization &#61; optional&#40;object&#40;&#123;&#10; customer_id &#61; string&#10; domain &#61; string&#10; id &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [tenants_config](variables.tf#L297) | Lightweight tenants shared configuration. Roles will be assigned to tenant admin group and service accounts. | <code title="object&#40;&#123;&#10; core_folder_roles &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; tenant_folder_roles &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; top_folder_roles &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [factories_config](variables.tf#L145) | Configuration for the resource factories or external data. | <code title="object&#40;&#123;&#10; checklist_data &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [fast_features](variables.tf#L154) | Selective control for top-level FAST features. | <code title="object&#40;&#123;&#10; data_platform &#61; optional&#40;bool, false&#41;&#10; gke &#61; optional&#40;bool, false&#41;&#10; project_factory &#61; optional&#40;bool, false&#41;&#10; sandbox &#61; optional&#40;bool, false&#41;&#10; teams &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | <code>0-0-bootstrap</code> |
| [groups](variables.tf#L168) | Group names or emails to grant organization-level permissions. If just the name is provided, the default organization domain is assumed. | <code title="object&#40;&#123;&#10; gcp-billing-admins &#61; optional&#40;string&#41;&#10; gcp-devops &#61; optional&#40;string&#41;&#10; gcp-network-admins &#61; optional&#40;string&#41;&#10; gcp-organization-admins &#61; optional&#40;string&#41;&#10; gcp-security-admins &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | <code>0-bootstrap</code> |
| [locations](variables.tf#L183) | Optional locations for GCS, BigQuery, and logging buckets created here. | <code title="object&#40;&#123;&#10; bq &#61; string&#10; gcs &#61; string&#10; logging &#61; string&#10; pubsub &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; bq &#61; &#34;EU&#34;&#10; gcs &#61; &#34;EU&#34;&#10; logging &#61; &#34;global&#34;&#10; pubsub &#61; &#91;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> | <code>0-bootstrap</code> |
| [org_policy_tags](variables.tf#L201) | Resource management tags for organization policy exceptions. | <code title="object&#40;&#123;&#10; key_id &#61; optional&#40;string&#41;&#10; key_name &#61; optional&#40;string&#41;&#10; values &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | <code>0-bootstrap</code> |
| [outputs_location](variables.tf#L223) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | <code>string</code> | | <code>null</code> | |
| [tag_names](variables.tf#L240) | Customized names for resource management tags. | <code title="object&#40;&#123;&#10; context &#61; optional&#40;string, &#34;context&#34;&#41;&#10; environment &#61; optional&#40;string, &#34;environment&#34;&#41;&#10; tenant &#61; optional&#40;string, &#34;tenant&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [tags](variables.tf#L255) | Custome secure tags by key name. The `iam` attribute behaves like the similarly named one at module level. | <code title="map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Managed by the Terraform organization module.&#34;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; values &#61; optional&#40;map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Managed by the Terraform organization module.&#34;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; id &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [team_folders](variables.tf#L276) | Team folders to be created. Format is described in a code comment. | <code title="map&#40;object&#40;&#123;&#10; descriptive_name &#61; string&#10; group_iam &#61; map&#40;list&#40;string&#41;&#41;&#10; impersonation_groups &#61; list&#40;string&#41;&#10; cicd &#61; optional&#40;object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>null</code> | |
| [tenants](variables.tf#L292) | Lightweight tenant definitions. | <code title="map&#40;object&#40;&#123;&#10; admin_group_email &#61; string&#10; descriptive_name &#61; string&#10; billing_account &#61; optional&#40;string&#41;&#10; organization &#61; optional&#40;object&#40;&#123;&#10; customer_id &#61; string&#10; domain &#61; string&#10; id &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [tenants_config](variables.tf#L308) | Lightweight tenants shared configuration. Roles will be assigned to tenant admin group and service accounts. | <code title="object&#40;&#123;&#10; core_folder_roles &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; tenant_folder_roles &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; top_folder_roles &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
## Outputs
| name | description | sensitive | consumers |
|---|---|:---:|---|
| [cicd_repositories](outputs.tf#L336) | WIF configuration for CI/CD repositories. | | |
| [dataplatform](outputs.tf#L350) | Data for the Data Platform stage. | | |
| [gke_multitenant](outputs.tf#L366) | Data for the GKE multitenant stage. | | <code>03-gke-multitenant</code> |
| [networking](outputs.tf#L387) | Data for the networking stage. | | |
| [project_factories](outputs.tf#L396) | Data for the project factories stage. | | |
| [providers](outputs.tf#L411) | Terraform provider files for this stage and dependent stages. | ✓ | <code>02-networking</code> · <code>02-security</code> · <code>03-dataplatform</code> · <code>xx-sandbox</code> · <code>xx-teams</code> |
| [sandbox](outputs.tf#L418) | Data for the sandbox stage. | | <code>xx-sandbox</code> |
| [security](outputs.tf#L432) | Data for the networking stage. | | <code>02-security</code> |
| [team_cicd_repositories](outputs.tf#L442) | WIF configuration for Team CI/CD repositories. | | |
| [teams](outputs.tf#L456) | Data for the teams stage. | | |
| [tfvars](outputs.tf#L468) | Terraform variable files for the following stages. | ✓ | |
| [cicd_repositories](outputs.tf#L337) | WIF configuration for CI/CD repositories. | | |
| [dataplatform](outputs.tf#L351) | Data for the Data Platform stage. | | |
| [gke_multitenant](outputs.tf#L367) | Data for the GKE multitenant stage. | | <code>03-gke-multitenant</code> |
| [networking](outputs.tf#L388) | Data for the networking stage. | | |
| [project_factories](outputs.tf#L397) | Data for the project factories stage. | | |
| [providers](outputs.tf#L412) | Terraform provider files for this stage and dependent stages. | ✓ | <code>02-networking</code> · <code>02-security</code> · <code>03-dataplatform</code> · <code>xx-sandbox</code> · <code>xx-teams</code> |
| [sandbox](outputs.tf#L419) | Data for the sandbox stage. | | <code>xx-sandbox</code> |
| [security](outputs.tf#L433) | Data for the networking stage. | | <code>02-security</code> |
| [team_cicd_repositories](outputs.tf#L443) | WIF configuration for Team CI/CD repositories. | | |
| [teams](outputs.tf#L457) | Data for the teams stage. | | |
| [tfvars](outputs.tf#L469) | Terraform variable files for the following stages. | ✓ | |
<!-- END TFDOC -->

View File

@@ -0,0 +1,111 @@
/**
* 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.
*/
locals {
# parse raw data from JSON files if they exist
_cl_data_raw = (
var.factories_config.checklist_data == null
? null
: jsondecode(file(pathexpand(var.factories_config.checklist_data)))
)
# check that files are for the correct organization and ignore them if not
_cl_data = (
try(local._cl_data_raw.organization.id, null) != tostring(var.organization.id)
? null
: local._cl_data_raw
)
# normalized IAM bindings one element per binding
_cl_iam = local._cl_data == null ? [] : flatten([
for v in try(local._cl_data.access_control, []) : [
for r in v.role : {
principal = v.principal
resource_id = v.resource.id
role = r
} if v.resource.type == "FOLDER"
]
])
# compile the final data structure we will consume from various places
checklist = {
hierarchy = local._cl_data == null ? {} : {
for v in try(local._cl_data.folders, []) : v.reference_id => {
level = length(split("/", v.reference_id))
name = v.display_name
parent_id = v.parent
}
}
iam = {
for v in local._cl_iam : v.resource_id => v...
}
}
}
check "checklist" {
# version mismatch might be ok, we just alert users
assert {
condition = (
var.factories_config.checklist_data == null ||
try(local._cl_data_raw.version, null) == "0.1.0"
)
error_message = "Checklist data version mismatch."
}
# wrong org id forces us to ignore the files, but we also alert users
assert {
condition = (
var.factories_config.checklist_data == null ||
try(local._cl_data_raw.organization.id, null) == tostring(var.organization.id)
)
error_message = "Checklist data organization id mismatch, file ignored."
}
}
module "checklist-folder-1" {
source = "../../../modules/folder"
for_each = {
for k, v in local.checklist.hierarchy : k => v if v.level == 1
}
parent = "organizations/${var.organization.id}"
name = each.value.name
iam = {
for v in try(local.checklist.iam[each.key], []) :
v.role => v.principal...
}
}
module "checklist-folder-2" {
source = "../../../modules/folder"
for_each = {
for k, v in local.checklist.hierarchy : k => v if v.level == 2
}
parent = module.checklist-folder-1[each.value.parent_id].id
name = each.value.name
iam = {
for v in try(local.checklist.iam[each.key], []) :
v.role => v.principal...
}
}
module "checklist-folder-3" {
source = "../../../modules/folder"
for_each = {
for k, v in local.checklist.hierarchy : k => v if v.level == 3
}
parent = module.checklist-folder-2[each.value.parent_id].id
name = each.value.name
iam = {
for v in try(local.checklist.iam[each.key], []) :
v.role => v.principal...
}
}

View File

@@ -325,11 +325,12 @@ locals {
}
}
tfvars = {
folder_ids = local.folder_ids
service_accounts = local.service_accounts
tag_keys = { for k, v in try(module.organization.tag_keys, {}) : k => v.id }
tag_names = var.tag_names
tag_values = { for k, v in try(module.organization.tag_values, {}) : k => v.id }
checklist_hierarchy = local.checklist.hierarchy
folder_ids = local.folder_ids
service_accounts = local.service_accounts
tag_keys = { for k, v in try(module.organization.tag_keys, {}) : k => v.id }
tag_names = var.tag_names
tag_values = { for k, v in try(module.organization.tag_values, {}) : k => v.id }
}
}

View File

@@ -142,6 +142,15 @@ variable "custom_roles" {
default = null
}
variable "factories_config" {
description = "Configuration for the resource factories or external data."
type = object({
checklist_data = optional(string)
})
nullable = false
default = {}
}
variable "fast_features" {
# tfdoc:variable:source 0-0-bootstrap
description = "Selective control for top-level FAST features."
@@ -161,9 +170,11 @@ variable "groups" {
# https://cloud.google.com/docs/enterprise/setup-checklist
description = "Group names or emails to grant organization-level permissions. If just the name is provided, the default organization domain is assumed."
type = object({
gcp-devops = optional(string)
gcp-network-admins = optional(string)
gcp-security-admins = optional(string)
gcp-billing-admins = optional(string)
gcp-devops = optional(string)
gcp-network-admins = optional(string)
gcp-organization-admins = optional(string)
gcp-security-admins = optional(string)
})
default = {}
nullable = false

View File

@@ -0,0 +1,13 @@
organization = {
domain = "fast.example.com"
id = 123456789012
customer_id = "C00000000"
}
billing_account = {
id = "000000-111111-222222"
}
factories_config = {
checklist_data = "checklist-data.json"
checklist_org_iam = "checklist-org-iam.json"
}
prefix = "fast"

View File

@@ -0,0 +1,98 @@
# 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.
values:
module.log-export-logbucket["audit-logs"].google_logging_project_bucket_config.bucket[0]:
bucket_id: audit-logs-audit-logs
cmek_settings: []
enable_analytics: false
index_configs: []
location: europe-west1
locked: null
project: fast-prod-audit-logs-0
retention_days: 30
module.log-export-logbucket["vpc-sc"].google_logging_project_bucket_config.bucket[0]:
bucket_id: audit-logs-vpc-sc
cmek_settings: []
enable_analytics: false
index_configs: []
location: europe-west1
locked: null
project: fast-prod-audit-logs-0
retention_days: 30
module.log-export-logbucket["workspace-audit-logs"].google_logging_project_bucket_config.bucket[0]:
bucket_id: audit-logs-workspace-audit-logs
cmek_settings: []
enable_analytics: false
index_configs: []
location: europe-west1
locked: null
project: fast-prod-audit-logs-0
retention_days: 30
module.organization.google_organization_iam_binding.authoritative["roles/securitycenter.admin"]:
condition: []
members:
- group:gcp-organization-admins@fast.example.com
- group:gcp-security-admins@fast.example.com
org_id: '123456789012'
role: roles/securitycenter.admin
module.organization.google_organization_iam_binding.authoritative["roles/resourcemanager.folderViewer"]:
condition: []
members:
- group:gcp-devops@fast.example.com
- group:gcp-network-admins@fast.example.com
- serviceAccount:fast-prod-bootstrap-0r@fast-prod-iac-core-0.iam.gserviceaccount.com
- serviceAccount:fast-prod-resman-0r@fast-prod-iac-core-0.iam.gserviceaccount.com
org_id: '123456789012'
role: roles/resourcemanager.folderViewer
? module.organization.google_organization_iam_member.bindings["roles/compute.viewer-group:gcp-security-admins@fast.example.com"]
: condition: []
member: group:gcp-security-admins@fast.example.com
org_id: '123456789012'
role: roles/compute.viewer
? module.organization.google_organization_iam_member.bindings["roles/container.viewer-group:gcp-security-admins@fast.example.com"]
: condition: []
member: group:gcp-security-admins@fast.example.com
org_id: '123456789012'
role: roles/container.viewer
? module.organization.google_organization_iam_member.bindings["roles/monitoring.admin-group:gcp-monitoring-admins@fast.example.com"]
: condition: []
member: group:gcp-monitoring-admins@fast.example.com
org_id: '123456789012'
role: roles/monitoring.admin
counts:
google_bigquery_dataset: 1
google_bigquery_default_service_account: 3
google_logging_organization_sink: 3
google_logging_project_bucket_config: 3
google_org_policy_policy: 13
google_organization_iam_binding: 23
google_organization_iam_custom_role: 6
google_organization_iam_member: 35
google_project: 3
google_project_iam_binding: 19
google_project_iam_member: 6
google_project_service: 29
google_project_service_identity: 3
google_service_account: 4
google_service_account_iam_binding: 2
google_storage_bucket: 3
google_storage_bucket_iam_binding: 2
google_storage_bucket_iam_member: 4
google_storage_bucket_object: 7
google_storage_project_service_account: 3
google_tags_tag_key: 1
google_tags_tag_value: 1
modules: 15
resources: 174

View File

@@ -0,0 +1,311 @@
{
"version": "0.1.0",
"organization": {
"id": "123456789012",
"name": "fast.example.com"
},
"billing_account": {},
"resource_hierarchy": {
"template": "TEAM_ENV",
"environments": [
{
"name": "Production",
"recommendation": "ENV_REC_PROD"
},
{
"name": "Non-Production",
"recommendation": "ENV_REC_NONPROD"
},
{
"name": "Development",
"recommendation": "ENV_REC_DEV"
}
],
"business_units": [
{
"name": "Department 1",
"teams": [
{
"name": "Team 1"
},
{
"name": "Team 2"
},
{
"name": "Team 3"
},
{
"name": "Team 4"
}
]
},
{
"name": "Department 2",
"teams": [
{
"name": "Team 1"
},
{
"name": "Team 2"
},
{
"name": "Team 3"
},
{
"name": "Team 4"
}
]
},
{
"name": "Department 3",
"teams": [
{
"name": "Team 1"
},
{
"name": "Team 2"
},
{
"name": "Team 3"
},
{
"name": "Team 4"
}
]
}
],
"top_level_teams": [
{
"name": "Team 1"
},
{
"name": "Team 2"
},
{
"name": "Team 3"
}
]
},
"folders": [
{
"reference_id": "Common",
"parent": "ROOT",
"display_name": "Common"
},
{
"reference_id": "Team 1",
"parent": "ROOT",
"display_name": "Team 1"
},
{
"reference_id": "Team 1/Production",
"parent": "Team 1",
"display_name": "Production"
},
{
"reference_id": "Team 1/Non-Production",
"parent": "Team 1",
"display_name": "Non-Production"
},
{
"reference_id": "Team 1/Development",
"parent": "Team 1",
"display_name": "Development"
},
{
"reference_id": "Team 2",
"parent": "ROOT",
"display_name": "Team 2"
},
{
"reference_id": "Team 2/Production",
"parent": "Team 2",
"display_name": "Production"
},
{
"reference_id": "Team 2/Non-Production",
"parent": "Team 2",
"display_name": "Non-Production"
},
{
"reference_id": "Team 2/Development",
"parent": "Team 2",
"display_name": "Development"
},
{
"reference_id": "Team 3",
"parent": "ROOT",
"display_name": "Team 3"
},
{
"reference_id": "Team 3/Production",
"parent": "Team 3",
"display_name": "Production"
},
{
"reference_id": "Team 3/Non-Production",
"parent": "Team 3",
"display_name": "Non-Production"
},
{
"reference_id": "Team 3/Development",
"parent": "Team 3",
"display_name": "Development"
}
],
"projects": [
{
"id": "vpc-host-prod-eh785-eh349",
"name": "vpc-host-prod",
"parent": "Common",
"recommendation": "PROJ_REC_VPC_HOST_PROD"
},
{
"id": "vpc-host-nonprod-eh785-eh349",
"name": "vpc-host-nonprod",
"parent": "Common",
"recommendation": "PROJ_REC_VPC_HOST_NONPROD"
},
{
"id": "logging-eh785-eh349",
"name": "logging",
"parent": "Common",
"recommendation": "PROJ_REC_LOGGING"
},
{
"id": "monitoring-prod-eh785-eh349",
"name": "monitoring-prod",
"parent": "Common",
"recommendation": "PROJ_REC_MONITORING_PROD"
},
{
"id": "monitoring-nonprod-eh785-eh349",
"name": "monitoring-nonprod",
"parent": "Common",
"recommendation": "PROJ_REC_MONITORING_NONPROD"
},
{
"id": "monitoring-dev-eh785-eh349",
"name": "monitoring-dev",
"parent": "Common",
"recommendation": "PROJ_REC_MONITORING_DEV"
},
{
"id": "m2-elevated-oven-410710",
"name": "Test 0",
"parent": "Common",
"recommendation": "PROJ_REC_NONE"
}
],
"logging": {
"sinks": [
{
"destination": {
"project_id": "logging-eh785-eh349",
"name": "checklist-test.joonix-logging",
"location": "europe-west1",
"retention_period_seconds": "2592000"
},
"role": "SINK_LOG_BUCKET"
}
]
},
"access_control": [
{
"principal": "group:gcp-developers@fast.example.com",
"group_id": "DEVELOPERS",
"role": [
"roles/compute.instanceAdmin.v1",
"roles/container.admin"
],
"resource": {
"type": "FOLDER",
"id": "Team 1/Non-Production"
}
},
{
"principal": "group:gcp-developers@fast.example.com",
"group_id": "DEVELOPERS",
"role": [
"roles/compute.instanceAdmin.v1",
"roles/container.admin"
],
"resource": {
"type": "FOLDER",
"id": "Team 2/Non-Production"
}
},
{
"principal": "group:gcp-developers@fast.example.com",
"group_id": "DEVELOPERS",
"role": [
"roles/compute.instanceAdmin.v1",
"roles/container.admin"
],
"resource": {
"type": "FOLDER",
"id": "Team 3/Non-Production"
}
},
{
"principal": "group:gcp-developers@fast.example.com",
"group_id": "DEVELOPERS",
"role": [
"roles/compute.instanceAdmin.v1",
"roles/container.admin"
],
"resource": {
"type": "FOLDER",
"id": "Team 1/Development"
}
},
{
"principal": "group:gcp-developers@fast.example.com",
"group_id": "DEVELOPERS",
"role": [
"roles/compute.instanceAdmin.v1",
"roles/container.admin"
],
"resource": {
"type": "FOLDER",
"id": "Team 2/Development"
}
},
{
"principal": "group:gcp-developers@fast.example.com",
"group_id": "DEVELOPERS",
"role": [
"roles/compute.instanceAdmin.v1",
"roles/container.admin"
],
"resource": {
"type": "FOLDER",
"id": "Team 3/Development"
}
},
{
"principal": "group:gcp-logging-viewers@fast.example.com",
"group_id": "LOGGING_VIEWERS",
"role": [
"roles/logging.viewer",
"roles/logging.privateLogViewer",
"roles/bigquery.dataViewer"
],
"resource": {
"type": "PROJECT",
"id": "logging-eh785-eh349"
}
},
{
"principal": "group:gcp-security-admins@fast.example.com",
"group_id": "SECURITY_ADMINS",
"role": [
"roles/bigquery.dataViewer"
],
"resource": {
"type": "PROJECT",
"id": "logging-eh785-eh349"
}
}
]
}

View File

@@ -0,0 +1,112 @@
{
"version": "0.1.0",
"organization": {
"id": "123456789012",
"name": "fast.example.com"
},
"iam_bindings": [
{
"principal": "group:gcp-organization-admins@fast.example.com",
"group_id": "ORG_ADMINS",
"role": [
"roles/resourcemanager.organizationAdmin",
"roles/resourcemanager.folderAdmin",
"roles/resourcemanager.projectCreator",
"roles/billing.user",
"roles/iam.organizationRoleAdmin",
"roles/orgpolicy.policyAdmin",
"roles/securitycenter.admin",
"roles/cloudsupport.admin",
"roles/owner",
"roles/cloudasset.owner",
"roles/compute.osAdminLogin",
"roles/compute.osLoginExternalUser",
"roles/resourcemanager.tagAdmin",
"roles/compute.xpnAdmin"
],
"resource": {
"type": "ORGANIZATION",
"id": "123456789012"
}
},
{
"principal": "group:gcp-billing-admins@fast.example.com",
"group_id": "BILLING_ADMINS",
"role": [
"roles/billing.admin",
"roles/billing.creator",
"roles/resourcemanager.organizationViewer"
],
"resource": {
"type": "ORGANIZATION",
"id": "123456789012"
}
},
{
"principal": "group:gcp-network-admins@fast.example.com",
"group_id": "NETWORK_ADMINS",
"role": [
"roles/compute.networkAdmin",
"roles/compute.xpnAdmin",
"roles/compute.securityAdmin",
"roles/resourcemanager.folderViewer"
],
"resource": {
"type": "ORGANIZATION",
"id": "123456789012"
}
},
{
"principal": "group:gcp-logging-admins@fast.example.com",
"group_id": "LOGGING_ADMINS",
"role": [
"roles/logging.admin"
],
"resource": {
"type": "ORGANIZATION",
"id": "123456789012"
}
},
{
"principal": "group:gcp-monitoring-admins@fast.example.com",
"group_id": "MONITORING_ADMINS",
"role": [
"roles/monitoring.admin"
],
"resource": {
"type": "ORGANIZATION",
"id": "123456789012"
}
},
{
"principal": "group:gcp-security-admins@fast.example.com",
"group_id": "SECURITY_ADMINS",
"role": [
"roles/orgpolicy.policyAdmin",
"roles/iam.securityReviewer",
"roles/iam.organizationRoleViewer",
"roles/securitycenter.admin",
"roles/resourcemanager.folderIamAdmin",
"roles/logging.privateLogViewer",
"roles/logging.configWriter",
"roles/container.viewer",
"roles/compute.viewer"
],
"resource": {
"type": "ORGANIZATION",
"id": "123456789012"
}
},
{
"principal": "group:gcp-devops@fast.example.com",
"group_id": "DEVOPS",
"role": [
"roles/resourcemanager.folderViewer"
],
"resource": {
"type": "ORGANIZATION",
"id": "123456789012"
}
}
]
}

View File

@@ -1,8 +1,29 @@
# skip boilerplate check
# 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.
module: fast/stages/0-bootstrap
tests:
checklist:
extra_files:
- ../../../tests/fast/stages/s0_bootstrap/data/checklist-data.json
- ../../../tests/fast/stages/s0_bootstrap/data/checklist-org-iam.json
tfvars:
- checklist.tfvars
inventory:
- checklist.yaml
simple:
tfvars:
- simple.tfvars

View File

@@ -0,0 +1,35 @@
automation = {
federated_identity_pool = null
federated_identity_providers = null
project_id = "fast-prod-automation"
project_number = 123456
outputs_bucket = "test"
service_accounts = {
resman-r = "ldj-prod-resman-0r@fast2-prod-iac-core-0.iam.gserviceaccount.com"
}
}
billing_account = {
id = "000000-111111-222222"
}
custom_roles = {
# organization_iam_admin = "organizations/123456789012/roles/organizationIamAdmin",
service_project_network_admin = "organizations/123456789012/roles/xpnServiceAdmin"
storage_viewer = "organizations/123456789012/roles/storageViewer"
}
factories_config = {
checklist_data = "checklist-data.json"
}
groups = {
gcp-billing-admins = "gcp-billing-admins",
gcp-devops = "gcp-devops",
gcp-network-admins = "gcp-network-admins",
gcp-organization-admins = "gcp-organization-admins",
gcp-security-admins = "gcp-security-admins",
gcp-support = "gcp-support"
}
organization = {
domain = "fast.example.com"
id = 123456789012
customer_id = "C00000000"
}
prefix = "fast2"

View File

@@ -0,0 +1,134 @@
# 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.
values:
module.checklist-folder-1["Common"].google_folder.folder[0]:
display_name: Common
parent: organizations/123456789012
timeouts: null
module.checklist-folder-1["Team 1"].google_folder.folder[0]:
display_name: Team 1
parent: organizations/123456789012
timeouts: null
module.checklist-folder-1["Team 2"].google_folder.folder[0]:
display_name: Team 2
parent: organizations/123456789012
timeouts: null
module.checklist-folder-1["Team 3"].google_folder.folder[0]:
display_name: Team 3
parent: organizations/123456789012
timeouts: null
module.checklist-folder-2["Team 1/Development"].google_folder.folder[0]:
display_name: Development
timeouts: null
module.checklist-folder-2["Team 1/Development"].google_folder_iam_binding.authoritative["roles/compute.instanceAdmin.v1"]:
condition: []
members:
- group:gcp-developers@fast.example.com
role: roles/compute.instanceAdmin.v1
module.checklist-folder-2["Team 1/Development"].google_folder_iam_binding.authoritative["roles/container.admin"]:
condition: []
members:
- group:gcp-developers@fast.example.com
role: roles/container.admin
module.checklist-folder-2["Team 1/Non-Production"].google_folder.folder[0]:
display_name: Non-Production
timeouts: null
? module.checklist-folder-2["Team 1/Non-Production"].google_folder_iam_binding.authoritative["roles/compute.instanceAdmin.v1"]
: condition: []
members:
- group:gcp-developers@fast.example.com
role: roles/compute.instanceAdmin.v1
module.checklist-folder-2["Team 1/Non-Production"].google_folder_iam_binding.authoritative["roles/container.admin"]:
condition: []
members:
- group:gcp-developers@fast.example.com
role: roles/container.admin
module.checklist-folder-2["Team 1/Production"].google_folder.folder[0]:
display_name: Production
timeouts: null
module.checklist-folder-2["Team 2/Development"].google_folder.folder[0]:
display_name: Development
timeouts: null
module.checklist-folder-2["Team 2/Development"].google_folder_iam_binding.authoritative["roles/compute.instanceAdmin.v1"]:
condition: []
members:
- group:gcp-developers@fast.example.com
role: roles/compute.instanceAdmin.v1
module.checklist-folder-2["Team 2/Development"].google_folder_iam_binding.authoritative["roles/container.admin"]:
condition: []
members:
- group:gcp-developers@fast.example.com
role: roles/container.admin
module.checklist-folder-2["Team 2/Non-Production"].google_folder.folder[0]:
display_name: Non-Production
timeouts: null
? module.checklist-folder-2["Team 2/Non-Production"].google_folder_iam_binding.authoritative["roles/compute.instanceAdmin.v1"]
: condition: []
members:
- group:gcp-developers@fast.example.com
role: roles/compute.instanceAdmin.v1
module.checklist-folder-2["Team 2/Non-Production"].google_folder_iam_binding.authoritative["roles/container.admin"]:
condition: []
members:
- group:gcp-developers@fast.example.com
role: roles/container.admin
module.checklist-folder-2["Team 2/Production"].google_folder.folder[0]:
display_name: Production
timeouts: null
module.checklist-folder-2["Team 3/Development"].google_folder.folder[0]:
display_name: Development
timeouts: null
module.checklist-folder-2["Team 3/Development"].google_folder_iam_binding.authoritative["roles/compute.instanceAdmin.v1"]:
condition: []
members:
- group:gcp-developers@fast.example.com
role: roles/compute.instanceAdmin.v1
module.checklist-folder-2["Team 3/Development"].google_folder_iam_binding.authoritative["roles/container.admin"]:
condition: []
members:
- group:gcp-developers@fast.example.com
role: roles/container.admin
module.checklist-folder-2["Team 3/Non-Production"].google_folder.folder[0]:
display_name: Non-Production
timeouts: null
? module.checklist-folder-2["Team 3/Non-Production"].google_folder_iam_binding.authoritative["roles/compute.instanceAdmin.v1"]
: condition: []
members:
- group:gcp-developers@fast.example.com
role: roles/compute.instanceAdmin.v1
module.checklist-folder-2["Team 3/Non-Production"].google_folder_iam_binding.authoritative["roles/container.admin"]:
condition: []
members:
- group:gcp-developers@fast.example.com
role: roles/container.admin
module.checklist-folder-2["Team 3/Production"].google_folder.folder[0]:
display_name: Production
timeouts: null
counts:
google_folder: 18
google_folder_iam_binding: 31
google_organization_iam_member: 5
google_project_iam_member: 4
google_service_account: 4
google_service_account_iam_binding: 4
google_storage_bucket: 2
google_storage_bucket_iam_binding: 4
google_storage_bucket_iam_member: 4
google_storage_bucket_object: 5
google_tags_tag_binding: 5
google_tags_tag_key: 3
google_tags_tag_value: 9
modules: 25
resources: 98

View File

@@ -0,0 +1,30 @@
# 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.
counts:
google_folder: 5
google_folder_iam_binding: 19
google_organization_iam_member: 5
google_project_iam_member: 4
google_service_account: 4
google_service_account_iam_binding: 4
google_storage_bucket: 2
google_storage_bucket_iam_binding: 4
google_storage_bucket_iam_member: 4
google_storage_bucket_object: 5
google_tags_tag_binding: 5
google_tags_tag_key: 3
google_tags_tag_value: 9
modules: 12
resources: 73

View File

@@ -1,4 +1,4 @@
# Copyright 2023 Google LLC
# 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.
@@ -12,10 +12,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
module: fast/stages/1-resman
def test_counts(plan_summary):
"Test stage."
summary = plan_summary("fast/stages/1-resman",
tf_var_files=["common.tfvars"])
assert summary.counts["modules"] > 0
assert summary.counts["resources"] > 0
tests:
checklist:
extra_files:
- ../../../tests/fast/stages/s0_bootstrap/data/checklist-data.json
tfvars:
- checklist.tfvars
inventory:
- checklist.yaml
simple:
tfvars:
- simple.tfvars
inventory:
- simple.yaml

View File

@@ -127,12 +127,10 @@ def plan_summary(module_path, basedir, tf_var_files=None, extra_files=None,
q = collections.deque([plan.root_module])
while q:
e = q.popleft()
if 'type' in e:
counts[e['type']] += 1
if 'values' in e:
values[e['address']] = e['values']
for x in e.get('resources', []):
counts['resources'] += 1
q.append(x)
@@ -198,6 +196,8 @@ def plan_validator(module_path, inventory_paths, basedir, tf_var_files=None,
# - put the values coming from user's inventory the right
# side of any comparison operators.
# - include a descriptive error message to the assert
# print(yaml.dump({'values': summary.values}))
# print(yaml.dump({'counts': summary.counts}))
if 'values' in inventory:
validate_plan_object(inventory['values'], summary.values, relative_path,
@@ -234,7 +234,6 @@ def plan_validator(module_path, inventory_paths, basedir, tf_var_files=None,
if _buffer:
print(yaml.dump(_buffer))
raise
return summary