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:
committed by
GitHub
parent
a34cdd5597
commit
a8c84357f4
@@ -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({ id = string is_org_level = optional(bool, true) no_iam = optional(bool, false) })">object({…})</code> | ✓ | | |
|
||||
| [organization](variables.tf#L235) | Organization details. | <code title="object({ domain = string id = number customer_id = string })">object({…})</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({ domain = string id = number customer_id = string })">object({…})</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({ bootstrap = optional(object({ name = string type = string branch = optional(string) identity_provider = optional(string) })) resman = optional(object({ name = string type = string branch = optional(string) identity_provider = optional(string) })) })">object({…})</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(list(string))</code> | | <code>{}</code> | |
|
||||
| [factories_config](variables.tf#L86) | Configuration for the organization policies factory. | <code title="object({ custom_roles = optional(string, "data/custom-roles") org_policy = optional(string, "data/org-policies") })">object({…})</code> | | <code>{}</code> | |
|
||||
| [fast_features](variables.tf#L96) | Selective control for top-level FAST features. | <code title="object({ data_platform = optional(bool, false) gke = optional(bool, false) project_factory = optional(bool, false) sandbox = optional(bool, false) teams = optional(bool, false) })">object({…})</code> | | <code>{}</code> | |
|
||||
| [federated_identity_providers](variables.tf#L109) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | <code title="map(object({ attribute_condition = optional(string) issuer = string custom_settings = optional(object({ issuer_uri = optional(string) audiences = optional(list(string), []) jwks_json = optional(string) }), {}) }))">map(object({…}))</code> | | <code>{}</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(list(string))</code> | | <code>{}</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(string)</code> | | <code title="{ 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-devops" }">{…}</code> | |
|
||||
| [iam](variables.tf#L154) | Organization-level custom IAM settings in role => [principal] format. | <code>map(list(string))</code> | | <code>{}</code> | |
|
||||
| [iam_bindings_additive](variables.tf#L161) | Organization-level custom additive IAM bindings. Keys are arbitrary. | <code title="map(object({ member = string role = string condition = optional(object({ expression = string title = string description = optional(string) })) }))">map(object({…}))</code> | | <code>{}</code> | |
|
||||
| [locations](variables.tf#L176) | Optional locations for GCS, BigQuery, and logging buckets created here. | <code title="object({ bq = optional(string, "EU") gcs = optional(string, "EU") logging = optional(string, "global") pubsub = optional(list(string), []) })">object({…})</code> | | <code>{}</code> | |
|
||||
| [log_sinks](variables.tf#L190) | Org-level log sinks, in name => {type, filter} format. | <code title="map(object({ filter = string type = string }))">map(object({…}))</code> | | <code title="{ audit-logs = { filter = "logName:\"/logs/cloudaudit.googleapis.com%2Factivity\" OR logName:\"/logs/cloudaudit.googleapis.com%2Fsystem_event\" OR protoPayload.metadata.@type=\"type.googleapis.com/google.cloud.audit.TransparencyLog\"" type = "logging" } vpc-sc = { filter = "protoPayload.metadata.@type=\"type.googleapis.com/google.cloud.audit.VpcServiceControlAuditMetadata\"" type = "logging" } workspace-audit-logs = { filter = "logName:\"/logs/cloudaudit.googleapis.com%2Fdata_access\" and protoPayload.serviceName:\"login.googleapis.com\"" type = "logging" } }">{…}</code> | |
|
||||
| [org_policies_config](variables.tf#L219) | Organization policies customization. | <code title="object({ constraints = optional(object({ allowed_policy_member_domains = optional(list(string), []) }), {}) tag_name = optional(string, "org-policies") tag_values = optional(map(object({ description = optional(string, "Managed by the Terraform organization module.") iam = optional(map(list(string)), {}) id = optional(string) })), {}) })">object({…})</code> | | <code>{}</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({ automation = string billing = string logging = string })">object({…})</code> | | <code title="{ automation = null billing = null logging = null }">{…}</code> | |
|
||||
| [factories_config](variables.tf#L86) | Configuration for the resource factories or external data. | <code title="object({ checklist_data = optional(string) checklist_org_iam = optional(string) custom_roles = optional(string, "data/custom-roles") org_policy = optional(string, "data/org-policies") })">object({…})</code> | | <code>{}</code> | |
|
||||
| [fast_features](variables.tf#L98) | Selective control for top-level FAST features. | <code title="object({ data_platform = optional(bool, false) gke = optional(bool, false) project_factory = optional(bool, false) sandbox = optional(bool, false) teams = optional(bool, false) })">object({…})</code> | | <code>{}</code> | |
|
||||
| [federated_identity_providers](variables.tf#L111) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | <code title="map(object({ attribute_condition = optional(string) issuer = string custom_settings = optional(object({ issuer_uri = optional(string) audiences = optional(list(string), []) jwks_json = optional(string) }), {}) }))">map(object({…}))</code> | | <code>{}</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(list(string))</code> | | <code>{}</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({ gcp-billing-admins = string gcp-devops = string gcp-network-admins = string gcp-organization-admins = string gcp-security-admins = string gcp-support = string })">object({…})</code> | | <code title="{ 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-devops" }">{…}</code> | |
|
||||
| [iam](variables.tf#L163) | Organization-level custom IAM settings in role => [principal] format. | <code>map(list(string))</code> | | <code>{}</code> | |
|
||||
| [iam_bindings_additive](variables.tf#L170) | Organization-level custom additive IAM bindings. Keys are arbitrary. | <code title="map(object({ member = string role = string condition = optional(object({ expression = string title = string description = optional(string) })) }))">map(object({…}))</code> | | <code>{}</code> | |
|
||||
| [locations](variables.tf#L185) | Optional locations for GCS, BigQuery, and logging buckets created here. | <code title="object({ bq = optional(string, "EU") gcs = optional(string, "EU") logging = optional(string, "global") pubsub = optional(list(string), []) })">object({…})</code> | | <code>{}</code> | |
|
||||
| [log_sinks](variables.tf#L199) | Org-level log sinks, in name => {type, filter} format. | <code title="map(object({ filter = string type = string }))">map(object({…}))</code> | | <code title="{ audit-logs = { filter = "logName:\"/logs/cloudaudit.googleapis.com%2Factivity\" OR logName:\"/logs/cloudaudit.googleapis.com%2Fsystem_event\" OR protoPayload.metadata.@type=\"type.googleapis.com/google.cloud.audit.TransparencyLog\"" type = "logging" } vpc-sc = { filter = "protoPayload.metadata.@type=\"type.googleapis.com/google.cloud.audit.VpcServiceControlAuditMetadata\"" type = "logging" } workspace-audit-logs = { filter = "logName:\"/logs/cloudaudit.googleapis.com%2Fdata_access\" and protoPayload.serviceName:\"login.googleapis.com\"" type = "logging" } }">{…}</code> | |
|
||||
| [org_policies_config](variables.tf#L228) | Organization policies customization. | <code title="object({ constraints = optional(object({ allowed_policy_member_domains = optional(list(string), []) }), {}) tag_name = optional(string, "org-policies") tag_values = optional(map(object({ description = optional(string, "Managed by the Terraform organization module.") iam = optional(map(list(string)), {}) id = optional(string) })), {}) })">object({…})</code> | | <code>{}</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({ automation = string billing = string logging = string })">object({…})</code> | | <code title="{ automation = null billing = null logging = null }">{…}</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 -->
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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
|
||||
|
||||
122
fast/stages/0-bootstrap/checklist.tf
Normal file
122
fast/stages/0-bootstrap/checklist.tf
Normal 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."
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"]))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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({ outputs_bucket = string project_id = string project_number = string federated_identity_pool = string federated_identity_providers = map(object({ audiences = list(string) issuer = string issuer_uri = string name = string principal_branch = string principal_repo = string })) service_accounts = object({ resman-r = string }) })">object({…})</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({ id = string is_org_level = optional(bool, true) no_iam = optional(bool, false) })">object({…})</code> | ✓ | | <code>0-bootstrap</code> |
|
||||
| [organization](variables.tf#L202) | Organization details. | <code title="object({ domain = string id = number customer_id = string })">object({…})</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({ domain = string id = number customer_id = string })">object({…})</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({ data_platform_dev = optional(object({ name = string type = string branch = optional(string) identity_provider = optional(string) })) data_platform_prod = optional(object({ name = string type = string branch = optional(string) identity_provider = optional(string) })) gke_dev = optional(object({ name = string type = string branch = optional(string) identity_provider = optional(string) })) gke_prod = optional(object({ name = string type = string branch = optional(string) identity_provider = optional(string) })) networking = optional(object({ name = string type = string branch = optional(string) identity_provider = optional(string) })) project_factory_dev = optional(object({ name = string type = string branch = optional(string) identity_provider = optional(string) })) project_factory_prod = optional(object({ name = string type = string branch = optional(string) identity_provider = optional(string) })) security = optional(object({ name = string type = string branch = optional(string) identity_provider = optional(string) })) })">object({…})</code> | | <code>null</code> | |
|
||||
| [custom_roles](variables.tf#L135) | Custom roles defined at the org level, in key => id format. | <code title="object({ service_project_network_admin = string storage_viewer = string })">object({…})</code> | | <code>null</code> | <code>0-bootstrap</code> |
|
||||
| [fast_features](variables.tf#L145) | Selective control for top-level FAST features. | <code title="object({ data_platform = optional(bool, false) gke = optional(bool, false) project_factory = optional(bool, false) sandbox = optional(bool, false) teams = optional(bool, false) })">object({…})</code> | | <code>{}</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({ gcp-devops = optional(string) gcp-network-admins = optional(string) gcp-security-admins = optional(string) })">object({…})</code> | | <code>{}</code> | <code>0-bootstrap</code> |
|
||||
| [locations](variables.tf#L172) | Optional locations for GCS, BigQuery, and logging buckets created here. | <code title="object({ bq = string gcs = string logging = string pubsub = list(string) })">object({…})</code> | | <code title="{ bq = "EU" gcs = "EU" logging = "global" pubsub = [] }">{…}</code> | <code>0-bootstrap</code> |
|
||||
| [org_policy_tags](variables.tf#L190) | Resource management tags for organization policy exceptions. | <code title="object({ key_id = optional(string) key_name = optional(string) values = optional(map(string), {}) })">object({…})</code> | | <code>{}</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({ context = optional(string, "context") environment = optional(string, "environment") tenant = optional(string, "tenant") })">object({…})</code> | | <code>{}</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(object({ description = optional(string, "Managed by the Terraform organization module.") iam = optional(map(list(string)), {}) values = optional(map(object({ description = optional(string, "Managed by the Terraform organization module.") iam = optional(map(list(string)), {}) id = optional(string) })), {}) }))">map(object({…}))</code> | | <code>{}</code> | |
|
||||
| [team_folders](variables.tf#L265) | Team folders to be created. Format is described in a code comment. | <code title="map(object({ descriptive_name = string group_iam = map(list(string)) impersonation_groups = list(string) cicd = optional(object({ branch = string identity_provider = string name = string type = string })) }))">map(object({…}))</code> | | <code>null</code> | |
|
||||
| [tenants](variables.tf#L281) | Lightweight tenant definitions. | <code title="map(object({ admin_group_email = string descriptive_name = string billing_account = optional(string) organization = optional(object({ customer_id = string domain = string id = number })) }))">map(object({…}))</code> | | <code>{}</code> | |
|
||||
| [tenants_config](variables.tf#L297) | Lightweight tenants shared configuration. Roles will be assigned to tenant admin group and service accounts. | <code title="object({ core_folder_roles = optional(list(string), []) tenant_folder_roles = optional(list(string), []) top_folder_roles = optional(list(string), []) })">object({…})</code> | | <code>{}</code> | |
|
||||
| [factories_config](variables.tf#L145) | Configuration for the resource factories or external data. | <code title="object({ checklist_data = optional(string) })">object({…})</code> | | <code>{}</code> | |
|
||||
| [fast_features](variables.tf#L154) | Selective control for top-level FAST features. | <code title="object({ data_platform = optional(bool, false) gke = optional(bool, false) project_factory = optional(bool, false) sandbox = optional(bool, false) teams = optional(bool, false) })">object({…})</code> | | <code>{}</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({ gcp-billing-admins = optional(string) gcp-devops = optional(string) gcp-network-admins = optional(string) gcp-organization-admins = optional(string) gcp-security-admins = optional(string) })">object({…})</code> | | <code>{}</code> | <code>0-bootstrap</code> |
|
||||
| [locations](variables.tf#L183) | Optional locations for GCS, BigQuery, and logging buckets created here. | <code title="object({ bq = string gcs = string logging = string pubsub = list(string) })">object({…})</code> | | <code title="{ bq = "EU" gcs = "EU" logging = "global" pubsub = [] }">{…}</code> | <code>0-bootstrap</code> |
|
||||
| [org_policy_tags](variables.tf#L201) | Resource management tags for organization policy exceptions. | <code title="object({ key_id = optional(string) key_name = optional(string) values = optional(map(string), {}) })">object({…})</code> | | <code>{}</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({ context = optional(string, "context") environment = optional(string, "environment") tenant = optional(string, "tenant") })">object({…})</code> | | <code>{}</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(object({ description = optional(string, "Managed by the Terraform organization module.") iam = optional(map(list(string)), {}) values = optional(map(object({ description = optional(string, "Managed by the Terraform organization module.") iam = optional(map(list(string)), {}) id = optional(string) })), {}) }))">map(object({…}))</code> | | <code>{}</code> | |
|
||||
| [team_folders](variables.tf#L276) | Team folders to be created. Format is described in a code comment. | <code title="map(object({ descriptive_name = string group_iam = map(list(string)) impersonation_groups = list(string) cicd = optional(object({ branch = string identity_provider = string name = string type = string })) }))">map(object({…}))</code> | | <code>null</code> | |
|
||||
| [tenants](variables.tf#L292) | Lightweight tenant definitions. | <code title="map(object({ admin_group_email = string descriptive_name = string billing_account = optional(string) organization = optional(object({ customer_id = string domain = string id = number })) }))">map(object({…}))</code> | | <code>{}</code> | |
|
||||
| [tenants_config](variables.tf#L308) | Lightweight tenants shared configuration. Roles will be assigned to tenant admin group and service accounts. | <code title="object({ core_folder_roles = optional(list(string), []) tenant_folder_roles = optional(list(string), []) top_folder_roles = optional(list(string), []) })">object({…})</code> | | <code>{}</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 -->
|
||||
|
||||
111
fast/stages/1-resman/checklist.tf
Normal file
111
fast/stages/1-resman/checklist.tf
Normal 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...
|
||||
}
|
||||
}
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
13
tests/fast/stages/s0_bootstrap/checklist.tfvars
Normal file
13
tests/fast/stages/s0_bootstrap/checklist.tfvars
Normal 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"
|
||||
98
tests/fast/stages/s0_bootstrap/checklist.yaml
Normal file
98
tests/fast/stages/s0_bootstrap/checklist.yaml
Normal 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
|
||||
311
tests/fast/stages/s0_bootstrap/data/checklist-data.json
Normal file
311
tests/fast/stages/s0_bootstrap/data/checklist-data.json
Normal 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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
112
tests/fast/stages/s0_bootstrap/data/checklist-org-iam.json
Normal file
112
tests/fast/stages/s0_bootstrap/data/checklist-org-iam.json
Normal 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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
|
||||
35
tests/fast/stages/s1_resman/checklist.tfvars
Normal file
35
tests/fast/stages/s1_resman/checklist.tfvars
Normal 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"
|
||||
134
tests/fast/stages/s1_resman/checklist.yaml
Normal file
134
tests/fast/stages/s1_resman/checklist.yaml
Normal 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
|
||||
30
tests/fast/stages/s1_resman/simple.yaml
Normal file
30
tests/fast/stages/s1_resman/simple.yaml
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user