diff --git a/fast/README.md b/fast/README.md index 9da748db2..57bb02f20 100644 --- a/fast/README.md +++ b/fast/README.md @@ -38,9 +38,11 @@ FAST uses YAML-based factories to deploy subnets and firewall rules and, as its One of our objectives with FAST is to provide a lightweight reference design for the IaC repositories, and a built-in implementation for running our code in automated pipelines. Our CI/CD approach leverages [Workload Identity Federation](https://cloud.google.com/iam/docs/workload-identity-federation), and provides sample workflow configurations for several major providers. Refer to the [CI/CD section in the bootstrap stage](./stages/0-bootstrap/README.md#cicd) for more details. We also provide separate [optional small stages](./extras/) to help you configure your CI/CD provider. + + ### Multitenant organizations -FAST has built-in support for multitenancy implemented in [an optional stage 1](./stages/1-tenant-factory/). Tenants can optionally be created with FAST compatibility, allowing them independent use of stages 1+ in their own context. +FAST has built-in support for multitenancy implemented in [an add-on stage](./addons/1-resman-tenants/). Tenants can optionally be created with FAST compatibility, allowing them independent use of stages 1+ in their own context. The following diagram is a high-level overview of stages used with multitenancy. @@ -64,12 +66,3 @@ Since we expect users to customize FAST to their specific needs, we strive to ma We also recognize that FAST users don't need all of its features. Therefore, you don't need to use our project factory or our GKE implementation if you don't want to. Instead, remove those stages or pieces of code and keep what suits you. Those familiar with Python will note that FAST follows many of the maxims in the [Zen of Python](https://www.python.org/dev/peps/pep-0020/#id2). - -## Roadmap - -Besides the features already described, FAST also includes: - -- Stage to deploy environment-specific multitenant GKE clusters following Google's best practices -- Stage to deploy a fully featured data platform -- Reference implementation to use FAST in CI/CD pipelines -- Static policy enforcement (planned) diff --git a/fast/stages/1-tenant-factory/.fast-stage.env b/fast/addons/1-resman-tenants/.fast-stage.env similarity index 100% rename from fast/stages/1-tenant-factory/.fast-stage.env rename to fast/addons/1-resman-tenants/.fast-stage.env diff --git a/fast/stages/1-tenant-factory/README.md b/fast/addons/1-resman-tenants/README.md similarity index 88% rename from fast/stages/1-tenant-factory/README.md rename to fast/addons/1-resman-tenants/README.md index adb91c678..d40422072 100644 --- a/fast/stages/1-tenant-factory/README.md +++ b/fast/addons/1-resman-tenants/README.md @@ -1,6 +1,6 @@ -# Tenant Factory +# Tenant Factory Resource Manager Add-on -This optional stage implements multitenancy, where a limited number of tenants need a high degree of autonomy over their slice of the shared organization, while still being subject to a measure of central control. +This add-on implements multitenancy on top of the resource management stage, where a limited number of tenants need a high degree of autonomy over their slice of the shared organization, while still being subject to a measure of central control. Typical use cases include large organizations managing a single Cloud subscription for multiple semi-independent entities (governments, state-wide associations), multinational groups with different local subsidiaries, or even business units who own their cloud presence while still consuming centralized resources or services. @@ -83,13 +83,13 @@ Once a FAST-enabled tenant is created, the admin principal for the tenant has ac ## How to run this stage -This stage uses a similar configuration to the [resource management](../1-resman/README.md) stage, with the only differences being the backend used, and the configuration of the specific variables that drive tenant creation. +This stage is designed as an add-on to the [resource management](../../stages/1-resman/README.md) stage, and reuses its IaC resources and IAM configurations. -The only real prerequisite is having fully deployed the [bootstrap](../0-bootstrap) stage, but there's no need to run [resource management](../1-resman/) before creating tenants unless a top-level "Tenants" folder is needed (and even that can be created by hand removing the dependency on stage 1). +Once the bootstrap and resource management stages are applied, configure the bootstrap stage `fast_addon` variable to enable this stage, as explained in the [add-ons documentation](../README.md). ### Provider and Terraform variables -As all other FAST stages, the [mechanism used to pass variable values and pre-built provider files from one stage to the next](../0-bootstrap/README.md#output-files-and-cross-stage-variables) is also leveraged here. +As all other FAST stages, the [mechanism used to pass variable values and pre-built provider files from one stage to the next](../../stages/0-bootstrap/README.md#output-files-and-cross-stage-variables) is also leveraged here. The commands to link or copy the provider and terraform variable files can be easily derived from the `fast-links.sh` script in the FAST stages folder, passing it a single argument with the local output files folder (if configured) or the GCS output bucket in the automation project (derived from stage 0 outputs). The following examples demonstrate both cases, and the resulting commands that then need to be copy/pasted and run. @@ -145,7 +145,7 @@ Note that the `outputs_location` variable is disabled by default, if you want ou outputs_location = "~/fast-config" ``` -For additional details on output files and how they are used, refer to the [bootstrap stage documentation](../0-bootstrap/README.md#output-files-and-cross-stage-variables). +For additional details on output files and how they are used, refer to the [bootstrap stage documentation](../../stages/0-bootstrap/README.md#output-files-and-cross-stage-variables). ### Running the stage @@ -218,7 +218,7 @@ tenant_configs = { FAST compatibility is enabled for a tenant by defining the `fast_config` attribute in their configuration, in addition to the attributes outlined above. -The `fast_config` attributes control the FAST bootstrap emulation for a tenant, and behave in a similar way to the corresponding variables that control the [bootstrap stage](../0-bootstrap/README.md#variables). They are all optional, and their behaviour is explained in the bootstrap stage documentation. +The `fast_config` attributes control the FAST bootstrap emulation for a tenant, and behave in a similar way to the corresponding variables that control the [bootstrap stage](../../stages/0-bootstrap/README.md#variables). They are all optional, and their behaviour is explained in the bootstrap stage documentation. This is an example of two FAST-enabled tenants: @@ -328,17 +328,19 @@ gcloud storage cp gs://{prefix}-{tenant-shortname}-prod-iac-core-0/tfvars/0-boot |---|---|:---:|:---:|:---:|:---:| | [automation](variables-fast.tf#L19) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | | [billing_account](variables-fast.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`. | object({…}) | ✓ | | 0-bootstrap | -| [logging](variables-fast.tf#L97) | Logging resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | -| [org_policy_tags](variables-fast.tf#L116) | Organization policy tags. | object({…}) | ✓ | | 0-bootstrap | -| [organization](variables-fast.tf#L106) | Organization details. | object({…}) | ✓ | | 0-bootstrap | -| [prefix](variables-fast.tf#L133) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | +| [environments](variables-fast.tf#L69) | Environment names. | map(object({…})) | ✓ | | 0-globals | +| [logging](variables-fast.tf#L115) | Logging resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | +| [org_policy_tags](variables-fast.tf#L134) | Organization policy tags. | object({…}) | ✓ | | 0-bootstrap | +| [organization](variables-fast.tf#L124) | Organization details. | object({…}) | ✓ | | 0-bootstrap | +| [prefix](variables-fast.tf#L151) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | | [custom_roles](variables-fast.tf#L53) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | -| [groups](variables-fast.tf#L69) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | 0-bootstrap | -| [locations](variables-fast.tf#L84) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | 0-bootstrap | -| [outputs_location](variables.tf#L17) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | -| [root_node](variables.tf#L23) | Root folder under which tenants are created, in folders/nnnn format. Defaults to the organization if null. | string | | null | | -| [tag_names](variables.tf#L36) | Customized names for resource management tags. | object({…}) | | {} | | -| [tenant_configs](variables.tf#L49) | Tenant configurations. Keys are the short names used for naming resources and should not be changed once defined. | map(object({…})) | | {} | | +| [groups](variables-fast.tf#L87) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | 0-bootstrap | +| [locations](variables-fast.tf#L102) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | 0-bootstrap | +| [names](variables.tf#L18) | Configuration for names used for resources and output files. | object({…}) | | {} | | +| [outputs_location](variables.tf#L28) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| [root_node](variables.tf#L34) | Root folder under which tenants are created, in folders/nnnn format. Defaults to the organization if null. | string | | null | | +| [tag_names](variables.tf#L47) | Customized names for resource management tags. | object({…}) | | {} | | +| [tenant_configs](variables.tf#L60) | Tenant configurations. Keys are the short names used for naming resources and should not be changed once defined. | map(object({…})) | | {} | | ## Outputs diff --git a/fast/stages/1-tenant-factory/diagram-flow.png b/fast/addons/1-resman-tenants/diagram-flow.png similarity index 100% rename from fast/stages/1-tenant-factory/diagram-flow.png rename to fast/addons/1-resman-tenants/diagram-flow.png diff --git a/fast/stages/1-tenant-factory/diagram.png b/fast/addons/1-resman-tenants/diagram.png similarity index 100% rename from fast/stages/1-tenant-factory/diagram.png rename to fast/addons/1-resman-tenants/diagram.png diff --git a/fast/stages/1-tenant-factory/identity-providers-defs.tf b/fast/addons/1-resman-tenants/identity-providers-defs.tf similarity index 100% rename from fast/stages/1-tenant-factory/identity-providers-defs.tf rename to fast/addons/1-resman-tenants/identity-providers-defs.tf diff --git a/fast/stages/1-tenant-factory/main.tf b/fast/addons/1-resman-tenants/main.tf similarity index 94% rename from fast/stages/1-tenant-factory/main.tf rename to fast/addons/1-resman-tenants/main.tf index 4d07c34af..89ecb32fe 100644 --- a/fast/stages/1-tenant-factory/main.tf +++ b/fast/addons/1-resman-tenants/main.tf @@ -15,6 +15,9 @@ */ locals { + default_environment = [ + for k, v in var.environments : v if v.is_default == true + ][0] tenants = { for k, v in var.tenant_configs : k => merge(v, { billing_account = merge(v.billing_account, { diff --git a/fast/stages/1-tenant-factory/outputs-files.tf b/fast/addons/1-resman-tenants/outputs-files.tf similarity index 83% rename from fast/stages/1-tenant-factory/outputs-files.tf rename to fast/addons/1-resman-tenants/outputs-files.tf index 6734c934a..047831acd 100644 --- a/fast/stages/1-tenant-factory/outputs-files.tf +++ b/fast/addons/1-resman-tenants/outputs-files.tf @@ -21,7 +21,7 @@ resource "local_file" "providers-simple" { for k, v in local.tenants : k => local.tenant_data[k] } file_permission = "0644" - filename = "${try(pathexpand(var.outputs_location), "")}/providers/tenant-${each.key}.tf" + filename = "${try(pathexpand(var.outputs_location), "")}/providers/${var.names.output_files_prefix}-${each.key}.tf" content = templatefile(local._tpl_providers, { backend_extra = null bucket = each.value.gcs_bucket @@ -35,21 +35,21 @@ resource "local_file" "tfvars-simple" { for k, v in local.tenants : k => local.tenant_data[k] } file_permission = "0644" - filename = "${try(pathexpand(var.outputs_location), "")}/tfvars/tenant-${each.key}.auto.tfvars.json" + filename = "${try(pathexpand(var.outputs_location), "")}/tfvars/${var.names.output_files_prefix}-${each.key}.auto.tfvars.json" content = jsonencode(each.value) } resource "local_file" "providers" { for_each = var.outputs_location == null ? {} : local.tenant_providers file_permission = "0644" - filename = "${try(pathexpand(var.outputs_location), "")}/tenants/${each.key}/providers/1-resman-providers.tf" + filename = "${try(pathexpand(var.outputs_location), "")}/${var.names.output_files_prefix}/${each.key}/providers/1-resman-providers.tf" content = try(each.value, null) } resource "local_file" "providers-r" { for_each = var.outputs_location == null ? {} : local.tenant_providers_r file_permission = "0644" - filename = "${try(pathexpand(var.outputs_location), "")}/tenants/${each.key}/providers/1-resman-r-providers.tf" + filename = "${try(pathexpand(var.outputs_location), "")}/${var.names.output_files_prefix}/${each.key}/providers/1-resman-r-providers.tf" content = try(each.value, null) } @@ -59,20 +59,20 @@ resource "local_file" "tfvars" { for k, v in local.tenant_tfvars : k => v if var.outputs_location != null } file_permission = "0644" - filename = "${try(pathexpand(var.outputs_location), "")}/tenants/${each.key}/tfvars/0-bootstrap.auto.tfvars.json" + filename = "${try(pathexpand(var.outputs_location), "")}/${var.names.output_files_prefix}/${each.key}/tfvars/0-bootstrap.auto.tfvars.json" content = jsonencode(each.value) } resource "local_file" "tfvars_globals" { for_each = var.outputs_location == null ? {} : local.tenant_globals file_permission = "0644" - filename = "${try(pathexpand(var.outputs_location), "")}/tenants/${each.key}/tfvars/0-globals.auto.tfvars.json" + filename = "${try(pathexpand(var.outputs_location), "")}/${var.names.output_files_prefix}/${each.key}/tfvars/0-globals.auto.tfvars.json" content = jsonencode(each.value) } resource "local_file" "workflows" { for_each = var.outputs_location == null ? {} : local.tenant_cicd_workflows file_permission = "0644" - filename = "${try(pathexpand(var.outputs_location), "")}/tenants/${each.key}/workflows/1-resman-workflow.yaml" + filename = "${try(pathexpand(var.outputs_location), "")}/${var.names.output_files_prefix}/${each.key}/workflows/1-resman-workflow.yaml" content = try(each.value, null) } diff --git a/fast/stages/1-tenant-factory/outputs-gcs.tf b/fast/addons/1-resman-tenants/outputs-gcs.tf similarity index 94% rename from fast/stages/1-tenant-factory/outputs-gcs.tf rename to fast/addons/1-resman-tenants/outputs-gcs.tf index 7c20876d2..23dc34292 100644 --- a/fast/stages/1-tenant-factory/outputs-gcs.tf +++ b/fast/addons/1-resman-tenants/outputs-gcs.tf @@ -21,7 +21,7 @@ resource "google_storage_bucket_object" "providers-simple" { for k, v in local.tenants : k => local.tenant_data[k] } bucket = var.automation.outputs_bucket - name = "providers/tenant-${each.key}.tf" + name = "providers/${var.names.output_files_prefix}-${each.key}.tf" content = templatefile(local._tpl_providers, { backend_extra = null bucket = each.value.gcs_bucket @@ -35,7 +35,7 @@ resource "google_storage_bucket_object" "tfvars-simple" { for k, v in local.tenants : k => local.tenant_data[k] } bucket = var.automation.outputs_bucket - name = "tfvars/tenant-${each.key}.auto.tfvars.json" + name = "tfvars/${var.names.output_files_prefix}-${each.key}.auto.tfvars.json" content = jsonencode(each.value) } diff --git a/fast/stages/1-tenant-factory/outputs.tf b/fast/addons/1-resman-tenants/outputs.tf similarity index 100% rename from fast/stages/1-tenant-factory/outputs.tf rename to fast/addons/1-resman-tenants/outputs.tf diff --git a/fast/stages/1-tenant-factory/templates/providers.tf.tpl b/fast/addons/1-resman-tenants/templates/providers.tf.tpl similarity index 100% rename from fast/stages/1-tenant-factory/templates/providers.tf.tpl rename to fast/addons/1-resman-tenants/templates/providers.tf.tpl diff --git a/fast/stages/1-tenant-factory/templates/providers_terraform.tf.tpl b/fast/addons/1-resman-tenants/templates/providers_terraform.tf.tpl similarity index 100% rename from fast/stages/1-tenant-factory/templates/providers_terraform.tf.tpl rename to fast/addons/1-resman-tenants/templates/providers_terraform.tf.tpl diff --git a/fast/assets/templates/workflow-github.yaml b/fast/addons/1-resman-tenants/templates/workflow-github.yaml similarity index 100% rename from fast/assets/templates/workflow-github.yaml rename to fast/addons/1-resman-tenants/templates/workflow-github.yaml diff --git a/fast/assets/templates/workflow-gitlab.yaml b/fast/addons/1-resman-tenants/templates/workflow-gitlab.yaml similarity index 100% rename from fast/assets/templates/workflow-gitlab.yaml rename to fast/addons/1-resman-tenants/templates/workflow-gitlab.yaml diff --git a/fast/stages/1-tenant-factory/tenant-billing-iam.tf b/fast/addons/1-resman-tenants/tenant-billing-iam.tf similarity index 100% rename from fast/stages/1-tenant-factory/tenant-billing-iam.tf rename to fast/addons/1-resman-tenants/tenant-billing-iam.tf diff --git a/fast/stages/1-tenant-factory/tenant-core.tf b/fast/addons/1-resman-tenants/tenant-core.tf similarity index 94% rename from fast/stages/1-tenant-factory/tenant-core.tf rename to fast/addons/1-resman-tenants/tenant-core.tf index c4d3086c9..994cdf059 100644 --- a/fast/stages/1-tenant-factory/tenant-core.tf +++ b/fast/addons/1-resman-tenants/tenant-core.tf @@ -25,7 +25,7 @@ module "tenant-core-logbucket" { for_each = local.tenants parent_type = "project" parent = var.logging.project_id - id = "tn-${each.key}-audit" + id = "${var.names.resource_short_name}-${each.key}-audit" location = var.locations.logging log_analytics = { enable = true } } @@ -36,7 +36,7 @@ module "tenant-core-folder" { parent = local.root_node name = "${each.value.descriptive_name} Core" logging_sinks = { - "tn-${each.key}-audit" = { + "${var.names.resource_short_name}-${each.key}-audit" = { destination = module.tenant-core-logbucket[each.key].id filter = <<-FILTER log_id("cloudaudit.googleapis.com/activity") OR diff --git a/fast/stages/1-tenant-factory/tenant-fast-automation.tf b/fast/addons/1-resman-tenants/tenant-fast-automation.tf similarity index 99% rename from fast/stages/1-tenant-factory/tenant-fast-automation.tf rename to fast/addons/1-resman-tenants/tenant-fast-automation.tf index 8873f9cff..bf37dbdc2 100644 --- a/fast/stages/1-tenant-factory/tenant-fast-automation.tf +++ b/fast/addons/1-resman-tenants/tenant-fast-automation.tf @@ -26,7 +26,7 @@ locals { } fast_tenants = { for k, v in local._fast_tenants : k => merge(v, { - stage_0_prefix = "${v.prefix}-prod" + stage_0_prefix = "${v.prefix}-${local.default_environment.short_name}" principals = { for gk, gv in v.groups : gk => ( can(regex("^[a-zA-Z]+:", gv)) diff --git a/fast/stages/1-tenant-factory/tenant-fast-cicd.tf b/fast/addons/1-resman-tenants/tenant-fast-cicd.tf similarity index 99% rename from fast/stages/1-tenant-factory/tenant-fast-cicd.tf rename to fast/addons/1-resman-tenants/tenant-fast-cicd.tf index 099083177..8a1d68905 100644 --- a/fast/stages/1-tenant-factory/tenant-fast-cicd.tf +++ b/fast/addons/1-resman-tenants/tenant-fast-cicd.tf @@ -131,7 +131,7 @@ module "automation-tf-cicd-r-sa" { source = "../../../modules/iam-service-account" for_each = local.cicd_repositories project_id = var.automation.project_id - name = "tenant-${each.key}-1r" + name = "${each.key}-1r" display_name = "Terraform CI/CD ${each.key} service account (read-only)." prefix = var.prefix iam = { diff --git a/fast/stages/1-tenant-factory/tenant-fast-identity-providers.tf b/fast/addons/1-resman-tenants/tenant-fast-identity-providers.tf similarity index 100% rename from fast/stages/1-tenant-factory/tenant-fast-identity-providers.tf rename to fast/addons/1-resman-tenants/tenant-fast-identity-providers.tf diff --git a/fast/stages/1-tenant-factory/tenant-fast-logging.tf b/fast/addons/1-resman-tenants/tenant-fast-logging.tf similarity index 100% rename from fast/stages/1-tenant-factory/tenant-fast-logging.tf rename to fast/addons/1-resman-tenants/tenant-fast-logging.tf diff --git a/fast/stages/1-tenant-factory/tenant-fast-vpcsc.tf b/fast/addons/1-resman-tenants/tenant-fast-vpcsc.tf similarity index 100% rename from fast/stages/1-tenant-factory/tenant-fast-vpcsc.tf rename to fast/addons/1-resman-tenants/tenant-fast-vpcsc.tf diff --git a/fast/stages/1-tenant-factory/tenant.tf b/fast/addons/1-resman-tenants/tenant.tf similarity index 96% rename from fast/stages/1-tenant-factory/tenant.tf rename to fast/addons/1-resman-tenants/tenant.tf index b143639f2..f53fb0338 100644 --- a/fast/stages/1-tenant-factory/tenant.tf +++ b/fast/addons/1-resman-tenants/tenant.tf @@ -97,7 +97,7 @@ module "tenant-sa" { source = "../../../modules/iam-service-account" for_each = local.tenants project_id = var.automation.project_id - name = "tn-${each.key}-0" + name = "${var.names.resource_short_name}-${each.key}-0" display_name = "Terraform tenant ${each.key} service account." prefix = var.prefix iam = { @@ -114,7 +114,7 @@ module "tenant-gcs" { source = "../../../modules/gcs" for_each = local.tenants project_id = var.automation.project_id - name = "tn-${each.key}-0" + name = "${var.names.resource_short_name}-${each.key}-0" prefix = var.prefix location = each.value.locations.gcs versioning = true diff --git a/fast/stages/1-tenant-factory/variables-fast.tf b/fast/addons/1-resman-tenants/variables-fast.tf similarity index 90% rename from fast/stages/1-tenant-factory/variables-fast.tf rename to fast/addons/1-resman-tenants/variables-fast.tf index e8179a2bb..ed246957f 100644 --- a/fast/stages/1-tenant-factory/variables-fast.tf +++ b/fast/addons/1-resman-tenants/variables-fast.tf @@ -66,6 +66,24 @@ variable "custom_roles" { default = null } +variable "environments" { + # tfdoc:variable:source 0-globals + description = "Environment names." + type = map(object({ + name = string + short_name = string + tag_name = string + is_default = optional(bool, false) + })) + nullable = false + validation { + condition = anytrue([ + for k, v in var.environments : v.is_default == true + ]) + error_message = "At least one environment should be marked as default." + } +} + variable "groups" { # tfdoc:variable:source 0-bootstrap # https://cloud.google.com/docs/enterprise/setup-checklist diff --git a/fast/stages/1-tenant-factory/variables.tf b/fast/addons/1-resman-tenants/variables.tf similarity index 92% rename from fast/stages/1-tenant-factory/variables.tf rename to fast/addons/1-resman-tenants/variables.tf index 210635f67..d9af1de91 100644 --- a/fast/stages/1-tenant-factory/variables.tf +++ b/fast/addons/1-resman-tenants/variables.tf @@ -14,6 +14,17 @@ * limitations under the License. */ +# TODO: backport names variable from resman stage +variable "names" { + description = "Configuration for names used for resources and output files." + type = object({ + output_files_prefix = optional(string, "2-resman-tenants") + resource_short_name = optional(string, "tn") + }) + nullable = false + default = {} +} + variable "outputs_location" { description = "Path where providers and tfvars files for the following stages are written. Leave empty to disable." type = string diff --git a/fast/addons/2-networking-ngfw/.fast-stage.env b/fast/addons/2-networking-ngfw/.fast-stage.env new file mode 100644 index 000000000..ae26e9fcb --- /dev/null +++ b/fast/addons/2-networking-ngfw/.fast-stage.env @@ -0,0 +1,5 @@ +FAST_STAGE_DESCRIPTION="NGFW Enterprise networking add-on" +FAST_STAGE_LEVEL=2 +FAST_STAGE_NAME=networking-ngfw +FAST_STAGE_DEPS="0-globals 0-bootstrap 1-resman 2-networking" +FAST_STAGE_OPTIONAL="2-security" \ No newline at end of file diff --git a/fast/addons/2-networking-ngfw/README.md b/fast/addons/2-networking-ngfw/README.md new file mode 100644 index 000000000..0cbcf7ead --- /dev/null +++ b/fast/addons/2-networking-ngfw/README.md @@ -0,0 +1,225 @@ +# NGFW Enterprise Networking Add-on + +This add-on includes all configurations and resources required to activate [Cloud Next Generation Firewall](https://cloud.google.com/firewall/docs/about-firewalls), and associate its endpoints to an arbitrary number of VPC networks. + +This diagram shows the resources used by this add-on, and their relationships with its networking parent stage. + +

+ Network security NGFW diagram +

+ + +- [Design overview and choices](#design-overview-and-choices) +- [How to run this stage](#how-to-run-this-stage) + - [Provider and Terraform variables](#provider-and-terraform-variables) + - [Impersonating the automation service account](#impersonating-the-automation-service-account) + - [Variable configuration](#variable-configuration) + - [Running the stage](#running-the-stage) + - [Using add-on resources from the networking stage](#using-add-on-resources-from-the-networking-stage) +- [Files](#files) +- [Variables](#variables) + + +## Design overview and choices + +This add-on is intentionally self-contained to allow directly using it to implement different designs, via a single instance or multiple instances. + +All project-level resources in this stage with the exception of VPC associations are created in the same project, so that dependencies and IAM configurations are kept as simple as possible, and everything is within the same span of control. + +The controlling project is usually one of those already created and managed by the networking stage: the landing host project, or a shared environment project if that exists. Alternatively, a dedicated project can be created and used here provided the necessary IAM and organization policies configurations are also defined. + +## How to run this stage + +Once the main networking stage has been configured and applied, the following configuration is added the the resource management `fast_addon` variable to create the add-on provider files, and its optional CI/CD resources if those are also required. The add-on name (`networking-ngfw`) is customizable, in case the add-on needs to be run multiple times for example to create different sets of endpoints and NGFW configurations per environment. + +```hcl +fast_addon = { + networking-ngfw = { + parent_stage = "2-networking" + # cicd_config = { + # identity_provider = "github-test" + # repository = { + # name = "test/ngfw" + # type = "github" + # branch = "main" + # } + # } + } +} +``` + +### Provider and Terraform variables + +As all other FAST stages, the [mechanism used to pass variable values and pre-built provider files from one stage to the next](../../stages/0-bootstrap/README.md#output-files-and-cross-stage-variables) is also leveraged here. + +The commands to link or copy the provider and terraform variable files can be easily derived from the `fast-links.sh` script in the FAST stages folder, passing it a single argument with the local output files folder (if configured) or the GCS output bucket in the automation project (derived from stage 0 outputs). The following example uses local files but GCS behaves identically. + +```bash +../../stages/fast-links.sh ~/fast-config +# File linking commands for NGFW Enterprise networking add-on stage + +# provider file +ln -s ~/fast-config/providers/2-networking-ngfw-providers.tf ./ + +# input files from other stages +ln -s ~/fast-config/tfvars/0-globals.auto.tfvars.json ./ +ln -s ~/fast-config/tfvars/0-bootstrap.auto.tfvars.json ./ +ln -s ~/fast-config/tfvars/1-resman.auto.tfvars.json ./ +ln -s ~/fast-config/tfvars/2-networking.auto.tfvars.json ./ + +# conventional place for stage tfvars (manually created) +ln -s ~/fast-config/2-networking-ngfw.auto.tfvars ./ + +# optional files +ln -s ~/fast-config/tfvars/2-security.auto.tfvars.json ./ +``` + +### Impersonating the automation service account + +The preconfigured provider file uses impersonation to run with this stage's automation service account's credentials. The `gcp-devops` and `organization-admins` groups have the necessary IAM bindings in place to do that, so make sure the current user is a member of one of those groups. + +### Variable configuration + +Variables in this stage -- like most other FAST stages -- are broadly divided into three separate sets: + +- variables which refer to global values for the whole organization (org id, billing account id, prefix, etc.), which are pre-populated via the `0-globals.auto.tfvars.json` file linked or copied above +- variables which refer to resources managed by previous stages, which are prepopulated here via the `0-bootstrap.auto.tfvars.json`, `1-resman.auto.tfvars.json` and `2-networking.auto.tfvars.json` files linked or copied above +- and finally variables that optionally control this stage's behaviour and customizations, and can to be set in a custom `terraform.tfvars` file + +The first two sets are defined in the `variables-fast.tf` file, the latter set in the `variables.tf` file. The full list of variables can be found in the [Variables](#variables) table at the bottom of this document. + +Note that the `outputs_location` variable is disabled by default, you need to explicitly set it in your `terraform.tfvars` file if you want output files to be generated by this stage. This is a sample `terraform.tfvars` that configures it, refer to the [bootstrap stage documentation](../../stages/0-bootstrap/README.md#output-files-and-cross-stage-variables) for more details: + +```tfvars +outputs_location = "~/fast-config" +``` + +Once output files are in place, define your addon configuration in a tfvars file. This is an example of configuring this addon, with optional variable attributes filled in for illustration purposes. + +```hcl +certificate_authorities = { + # if CA pools defined in the security stage are used this is optional + ngfw-0 = { + location = "europe-west8" + ca_configs = { + ca-0 = { + deletion_protection = false + subject = { + common_name = "example.org" + organization = "Test Organization" + } + } + } + ca_pool_config = { + authz_nsec_sa = true + name = "ca-pool-0" + } + } +} +ngfw_config = { + name = "ngfw-0" + endpoint_zones = ["europe-west8-b"] + network_associations = { + prod = { + # VPC ids defined in the network stage can be referred to via short name + # vpc_id = "prod-spoke-0" + vpc_id = "projects/xxx-prod-net-spoke-0/global/networks/prod-spoke-0" + tls_inspection_policy = "ngfw-0" + } + } +} +outputs_location = "~/fast-config" +project_id = "xxx-prod-net-landing-0" +security_profiles = { + ngfw-0 = { + # these are optional and shown here for convenience + threat_prevention_profile = { + severity_overrides = { + informational-allow = { + action = "ALLOW" + severity = "INFORMATIONAL" + } + } + threat_overrides = { + allow-280647 = { + action = "ALLOW" + threat_id = "280647" + } + } + } + } +} +tls_inspection_policies = { + ngfw-0 = { + # reference the pool defined above, or an external one + # CA pools defined in the security stage can be referred to via short name + ca_pool_id = "ngfw-0" + location = "europe-west8" + trust_config = "ngfw-0" + } +} +trust_configs = { + ngfw-0 = { + location = "europe-west8" + allowlisted_certificates = { + server-0 = "~/fast-config/data/2-networking-ngfw/server-0.cert.pem" + } + trust_stores = { + ludo-joonix = { + intermediate_cas = { + issuing-ca-1 = "~/fast-config/data/2-networking-ngfw/intermediate.cert.pem" + } + trust_anchors = { + root-ca-1 = "~/fast-config/data/2-networking-ngfw/ca.cert.pem" + } + } + } + } +} +``` + +### Running the stage + +Once provider and variable values are in place and the correct user is configured, the stage can be run: + +```bash +terraform init +terraform apply +``` + +### Using add-on resources from the networking stage + +Security profiles group defined here are exported via output variable file, and can be consumed in the firewall policies defined in the networking stage. + + + +## Files + +| name | description | modules | resources | +|---|---|---|---| +| [main.tf](./main.tf) | Module-level locals and resources. | project | | +| [ngfw.tf](./ngfw.tf) | NGFW Enteprise resources. | | google_network_security_firewall_endpoint · google_network_security_firewall_endpoint_association | +| [outputs.tf](./outputs.tf) | Module outputs. | | google_storage_bucket_object · local_file | +| [security-profiles.tf](./security-profiles.tf) | Organization-level network security profiles. | | google_network_security_security_profile · google_network_security_security_profile_group | +| [tls-inspection.tf](./tls-inspection.tf) | TLS inspection policies and supporting resources. | certificate-authority-service | google_certificate_manager_trust_config · google_network_security_tls_inspection_policy | +| [variables-fast.tf](./variables-fast.tf) | FAST stage interface. | | | +| [variables.tf](./variables.tf) | Module variables. | | | + +## Variables + +| name | description | type | required | default | producer | +|---|---|:---:|:---:|:---:|:---:| +| [automation](variables-fast.tf#L28) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | +| [ngfw_config](variables.tf#L102) | Configuration for NGFW Enterprise endpoints. Billing project defaults to the automation project. Network and TLS inspection policy ids support interpolation. | object({…}) | ✓ | | | +| [organization](variables-fast.tf#L48) | Organization details. | object({…}) | ✓ | | 0-globals | +| [project_id](variables.tf#L123) | Project where the network security resources will be created. | string | ✓ | | | +| [_fast_debug](variables-fast.tf#L19) | Internal FAST variable used for testing and debugging. Do not use. | object({…}) | | {} | | +| [certificate_authorities](variables.tf#L17) | Certificate Authority Service pool and CAs. If host project ids is null identical pools and CAs are created in every host project. | map(object({…})) | | {} | | +| [certificate_authority_pools](variables-fast.tf#L36) | Certificate authority pools. | map(object({…})) | | {} | 2-security | +| [names](variables.tf#L93) | Configuration for names used for output files. | object({…}) | | {} | | +| [outputs_location](variables.tf#L117) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| [security_profiles](variables.tf#L129) | Security profile groups for Layer 7 inspection. Null environment list means all environments. | map(object({…})) | | {…} | | +| [tls_inspection_policies](variables.tf#L171) | TLS inspection policies configuration. CA pools, trust configs and host project ids support interpolation. | map(object({…})) | | {} | | +| [trust_configs](variables.tf#L213) | Certificate Manager trust configurations for TLS inspection policies. Project ids and region can reference keys in the relevant FAST variables. | map(object({…})) | | {…} | | +| [vpc_self_links](variables-fast.tf#L58) | VPC network self links. | map(string) | | {} | 2-networking | + diff --git a/fast/addons/2-networking-ngfw/diagram.excalidraw.gz b/fast/addons/2-networking-ngfw/diagram.excalidraw.gz new file mode 100644 index 000000000..77a1dbaea Binary files /dev/null and b/fast/addons/2-networking-ngfw/diagram.excalidraw.gz differ diff --git a/fast/addons/2-networking-ngfw/diagram.png b/fast/addons/2-networking-ngfw/diagram.png new file mode 100644 index 000000000..907693e7d Binary files /dev/null and b/fast/addons/2-networking-ngfw/diagram.png differ diff --git a/fast/assets/templates/providers.tf.tpl b/fast/addons/2-networking-ngfw/main.tf similarity index 55% rename from fast/assets/templates/providers.tf.tpl rename to fast/addons/2-networking-ngfw/main.tf index 7f0ce142f..606ac210f 100644 --- a/fast/assets/templates/providers.tf.tpl +++ b/fast/addons/2-networking-ngfw/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2022 Google LLC + * Copyright 2025 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,17 +14,19 @@ * limitations under the License. */ -terraform { - backend "gcs" { - bucket = "${bucket}" - impersonate_service_account = "${sa}" - } -} -provider "google" { - impersonate_service_account = "${sa}" -} -provider "google-beta" { - impersonate_service_account = "${sa}" +locals { + project_id = try(module.project[0].project_id, var.project_id) } -# end provider.tf for ${name} +module "project" { + source = "../../../modules/project" + count = var._fast_debug.skip_datasources == true ? 0 : 1 + name = var.project_id + project_create = false + services = [ + "certificatemanager.googleapis.com", + "networkmanagement.googleapis.com", + "networksecurity.googleapis.com", + "privateca.googleapis.com" + ] +} diff --git a/fast/addons/2-networking-ngfw/ngfw.tf b/fast/addons/2-networking-ngfw/ngfw.tf new file mode 100644 index 000000000..7586d432c --- /dev/null +++ b/fast/addons/2-networking-ngfw/ngfw.tf @@ -0,0 +1,56 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description NGFW Enteprise resources. + +locals { + ngfw_associations = flatten([ + for k, v in var.ngfw_config.network_associations : [ + for z in coalesce(v.zones, var.ngfw_config.endpoint_zones) : merge(v, { + key = "${z}-${k}" + location = z + vpc_id = lookup(var.vpc_self_links, v.vpc_id, v.vpc_id) + }) + ] + ]) + tls_inspection_policies = { + for k, v in google_network_security_tls_inspection_policy.default : + k => v.id + } +} + +resource "google_network_security_firewall_endpoint" "default" { + for_each = toset(var.ngfw_config.endpoint_zones) + name = var.ngfw_config.name + parent = "organizations/${var.organization.id}" + location = each.key + billing_project_id = local.project_id +} + +resource "google_network_security_firewall_endpoint_association" "default" { + for_each = { for k in local.ngfw_associations : k.key => k } + name = "${var.ngfw_config.name}-${each.value.key}" + parent = "projects/${regex("projects/([^/]+)/", each.value.vpc_id)[0]}" + location = each.value.location + network = each.value.vpc_id + firewall_endpoint = ( + google_network_security_firewall_endpoint.default[each.value.location].id + ) + # If TLS inspection is enabled, link the regional TLS inspection policy + tls_inspection_policy = try( + local.tls_inspection_policies[each.value.tls_inspection_policy], null + ) +} diff --git a/fast/addons/2-networking-ngfw/outputs.tf b/fast/addons/2-networking-ngfw/outputs.tf new file mode 100644 index 000000000..d2646e6f1 --- /dev/null +++ b/fast/addons/2-networking-ngfw/outputs.tf @@ -0,0 +1,47 @@ +/** + * Copyright 2025 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 { + security_profile_group_ids = { + for k, v in google_network_security_security_profile_group.default : + k => "//networksecurity.googleapis.com/${v.id}" + } + tfvars = { + ngfw = { + associations = { + for k, v in google_network_security_firewall_endpoint_association.default : + k => v.id + } + endpoints = { + for k, v in google_network_security_firewall_endpoint.default : k => v.id + } + } + security_profile_groups = local.security_profile_group_ids + } +} + +resource "local_file" "tfvars" { + for_each = var.outputs_location == null ? {} : { 1 = 1 } + file_permission = "0644" + filename = "${try(pathexpand(var.outputs_location), "")}/tfvars/${var.names.output_files_prefix}.auto.tfvars.json" + content = jsonencode(local.tfvars) +} + +resource "google_storage_bucket_object" "tfvars" { + bucket = var.automation.outputs_bucket + name = "tfvars/${var.names.output_files_prefix}.auto.tfvars.json" + content = jsonencode(local.tfvars) +} diff --git a/fast/addons/2-networking-ngfw/security-profiles.tf b/fast/addons/2-networking-ngfw/security-profiles.tf new file mode 100644 index 000000000..5549a4163 --- /dev/null +++ b/fast/addons/2-networking-ngfw/security-profiles.tf @@ -0,0 +1,66 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Organization-level network security profiles. + +locals { + security_profiles = { + for k, v in var.security_profiles : k => merge(v, { + has_profiles = ( + v.threat_prevention_profile.severity_overrides != null || + v.threat_prevention_profile.threat_overrides != null + ) + }) + } +} + +resource "google_network_security_security_profile" "default" { + for_each = local.security_profiles + name = each.key + description = each.value.description + parent = "organizations/${var.organization.id}" + location = "global" + type = "THREAT_PREVENTION" + dynamic "threat_prevention_profile" { + for_each = each.value.has_profiles ? [""] : [] + iterator = profiles + content { + dynamic "severity_overrides" { + for_each = coalesce(each.value.threat_prevention_profile.severity_overrides, {}) + content { + action = severity_overrides.value.action + severity = severity_overrides.value.severity + } + } + dynamic "threat_overrides" { + for_each = coalesce(each.value.threat_prevention_profile.threat_overrides, {}) + content { + action = threat_overrides.value.action + threat_id = threat_overrides.value.threat_id + } + } + } + } +} + +resource "google_network_security_security_profile_group" "default" { + for_each = var.security_profiles + name = each.key + description = each.value.description + parent = "organizations/${var.organization.id}" + location = "global" + threat_prevention_profile = google_network_security_security_profile.default[each.key].id +} diff --git a/fast/stages/2-security/certs.tf b/fast/addons/2-networking-ngfw/tls-inspection.tf similarity index 57% rename from fast/stages/2-security/certs.tf rename to fast/addons/2-networking-ngfw/tls-inspection.tf index 7e6111679..11aa11b7d 100644 --- a/fast/stages/2-security/certs.tf +++ b/fast/addons/2-networking-ngfw/tls-inspection.tf @@ -1,5 +1,5 @@ /** - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,54 +14,43 @@ * limitations under the License. */ -# tfdoc:file:description Per-environment certificate resources. +# tfdoc:file:description TLS inspection policies and supporting resources. locals { - cas = flatten([ - for k, v in var.certificate_authorities : [ - for e in coalesce(v.environments, keys(var.environments)) : merge(v, { - environment = e - key = "${e}-${k}" - name = k - netsec_iam = contains(var.trust_configs.keys.dev.cas, k) - }) - ] - ]) - trust_configs = flatten([ - for k, v in var.trust_configs : [ - for e in coalesce(v.environments, keys(var.environments)) : merge(v, { - environment = e - key = "${e}-${k}" - name = k - }) - ] - ]) + ca_pool_ids = merge( + { for k, v in var.certificate_authority_pools : k => v.id }, + { for k, v in module.cas : k => v.ca_pool_id } + ) + trust_config_ids = { + for k, v in google_certificate_manager_trust_config.default : k => v.id + } } module "cas" { source = "../../../modules/certificate-authority-service" - for_each = { for k in local.cas : k.key => k } - project_id = module.project[each.value.environment].project_id + for_each = var.certificate_authorities + project_id = local.project_id ca_configs = each.value.ca_configs ca_pool_config = each.value.ca_pool_config iam = each.value.iam iam_bindings = each.value.iam_bindings iam_bindings_additive = merge( each.value.iam_bindings_additive, - !each.value.netsec_iam ? {} : { + var._fast_debug.skip_datasources == true ? {} : { nsec_agent = { - member = module.project[each.value.environment].service_agents["networksecurity"].iam_email + member = module.project[0].service_agents["networksecurity"].iam_email role = "roles/privateca.certificateManager" } - }) + } + ) iam_by_principals = each.value.iam_by_principals location = each.value.location } resource "google_certificate_manager_trust_config" "default" { - for_each = { for k in local.trust_configs : k.key => k } - name = each.value.name - project = module.project[each.value.environment].project_id + for_each = var.trust_configs + project = local.project_id + name = each.key description = each.value.description location = each.value.location dynamic "allowlisted_certificates" { @@ -88,3 +77,20 @@ resource "google_certificate_manager_trust_config" "default" { } } } + +resource "google_network_security_tls_inspection_policy" "default" { + for_each = var.tls_inspection_policies + project = local.project_id + name = each.key + location = each.value.location + exclude_public_ca_set = each.value.exclude_public_ca_set + ca_pool = lookup( + local.ca_pool_ids, each.value.ca_pool_id, each.value.ca_pool_id + ) + trust_config = lookup( + local.trust_config_ids, each.value.trust_config, each.value.trust_config + ) + custom_tls_features = each.value.tls.custom_features + tls_feature_profile = each.value.tls.feature_profile + min_tls_version = each.value.tls.min_version +} diff --git a/fast/addons/2-networking-ngfw/variables-fast.tf b/fast/addons/2-networking-ngfw/variables-fast.tf new file mode 100644 index 000000000..0d940305a --- /dev/null +++ b/fast/addons/2-networking-ngfw/variables-fast.tf @@ -0,0 +1,65 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description FAST stage interface. + +variable "_fast_debug" { + description = "Internal FAST variable used for testing and debugging. Do not use." + type = object({ + skip_datasources = optional(bool, false) + }) + nullable = false + default = {} +} + +variable "automation" { + # tfdoc:variable:source 0-bootstrap + description = "Automation resources created by the bootstrap stage." + type = object({ + outputs_bucket = string + }) +} + +variable "certificate_authority_pools" { + # tfdoc:variable:source 2-security + description = "Certificate authority pools." + type = map(object({ + id = string + ca_ids = map(string) + location = string + })) + nullable = false + default = {} +} + +variable "organization" { + # tfdoc:variable:source 0-globals + description = "Organization details." + type = object({ + domain = string + id = number + customer_id = string + }) +} + +variable "vpc_self_links" { + # tfdoc:variable:source 2-networking + description = "VPC network self links." + type = map(string) + nullable = false + default = {} +} + diff --git a/fast/addons/2-networking-ngfw/variables.tf b/fast/addons/2-networking-ngfw/variables.tf new file mode 100644 index 000000000..186c3691a --- /dev/null +++ b/fast/addons/2-networking-ngfw/variables.tf @@ -0,0 +1,245 @@ +/** + * 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. + */ + +variable "certificate_authorities" { + description = "Certificate Authority Service pool and CAs. If host project ids is null identical pools and CAs are created in every host project." + type = map(object({ + location = string + iam = optional(map(list(string)), {}) + iam_bindings = optional(map(any), {}) + iam_bindings_additive = optional(map(any), {}) + iam_by_principals = optional(map(list(string)), {}) + ca_configs = map(object({ + deletion_protection = optional(string, true) + type = optional(string, "SELF_SIGNED") + is_ca = optional(bool, true) + lifetime = optional(string, null) + pem_ca_certificate = optional(string, null) + ignore_active_certificates_on_deletion = optional(bool, false) + skip_grace_period = optional(bool, true) + labels = optional(map(string), null) + gcs_bucket = optional(string, null) + key_spec = optional(object({ + algorithm = optional(string, "RSA_PKCS1_2048_SHA256") + kms_key_id = optional(string, null) + }), {}) + key_usage = optional(object({ + cert_sign = optional(bool, true) + client_auth = optional(bool, false) + code_signing = optional(bool, false) + content_commitment = optional(bool, false) + crl_sign = optional(bool, true) + data_encipherment = optional(bool, false) + decipher_only = optional(bool, false) + digital_signature = optional(bool, false) + email_protection = optional(bool, false) + encipher_only = optional(bool, false) + key_agreement = optional(bool, false) + key_encipherment = optional(bool, true) + ocsp_signing = optional(bool, false) + server_auth = optional(bool, true) + time_stamping = optional(bool, false) + }), {}) + subject = optional( + object({ + common_name = string + organization = string + country_code = optional(string) + locality = optional(string) + organizational_unit = optional(string) + postal_code = optional(string) + province = optional(string) + street_address = optional(string) + }), + { + common_name = "test.example.com" + organization = "Test Example" + } + ) + subject_alt_name = optional(object({ + dns_names = optional(list(string), null) + email_addresses = optional(list(string), null) + ip_addresses = optional(list(string), null) + uris = optional(list(string), null) + }), null) + subordinate_config = optional(object({ + root_ca_id = optional(string) + pem_issuer_certificates = optional(list(string)) + }), null) + })) + ca_pool_config = object({ + ca_pool_id = optional(string, null) + name = optional(string, null) + tier = optional(string, "DEVOPS") + }) + })) + nullable = false + default = {} +} + +variable "names" { + description = "Configuration for names used for output files." + type = object({ + output_files_prefix = optional(string, "2-networking-ngfw") + }) + nullable = false + default = {} +} + +variable "ngfw_config" { + description = "Configuration for NGFW Enterprise endpoints. Billing project defaults to the automation project. Network and TLS inspection policy ids support interpolation." + type = object({ + endpoint_zones = list(string) + name = optional(string, "ngfw-0") + network_associations = optional(map(object({ + vpc_id = string + disabled = optional(bool) + tls_inspection_policy = optional(string) + zones = optional(list(string)) + })), {}) + }) + nullable = false +} + +variable "outputs_location" { + description = "Path where providers and tfvars files for the following stages are written. Leave empty to disable." + type = string + default = null +} + +variable "project_id" { + description = "Project where the network security resources will be created." + type = string + nullable = false +} + +variable "security_profiles" { + description = "Security profile groups for Layer 7 inspection. Null environment list means all environments." + type = map(object({ + description = optional(string) + threat_prevention_profile = optional(object({ + severity_overrides = optional(map(object({ + action = string + severity = string + }))) + threat_overrides = optional(map(object({ + action = string + threat_id = string + }))) + }), {}) + })) + nullable = false + default = { + ngfw-default = {} + } + validation { + condition = alltrue(flatten([ + for _, v in var.security_profiles : [ + for _, sv in coalesce(v.threat_prevention_profile.severity_overrides, {}) : ( + contains(["ALERT", "ALLOW", "DEFAULT_ACTION", "DENY"], sv.action) && + contains(["CRITICAL", "HIGH", "INFORMATIONAL", "LOW", "MEDIUM"], sv.severity) + ) + ] + ])) + error_message = "Incorrect severity override token." + } + validation { + condition = alltrue(flatten([ + for _, v in var.security_profiles : [ + for _, sv in coalesce(v.threat_prevention_profile.threat_overrides, {}) : ( + contains(["ALERT", "ALLOW", "DEFAULT_ACTION", "DENY"], sv.action) + ) + ] + ])) + error_message = "Incorrect threat override token." + } +} + +variable "tls_inspection_policies" { + description = "TLS inspection policies configuration. CA pools, trust configs and host project ids support interpolation." + type = map(object({ + ca_pool_id = string + location = string + exclude_public_ca_set = optional(bool) + trust_config = optional(string) + tls = optional(object({ + custom_features = optional(list(string)) + feature_profile = optional(string) + min_version = optional(string) + }), {}) + })) + nullable = false + default = {} + validation { + condition = alltrue([ + for k, v in var.tls_inspection_policies : v.tls.min_version == null || contains( + ["TLS_VERSION_UNSPECIFIED", "TLS_1_0", "TLS_1_1", "TLS_1_2", "TLS_1_3"], + coalesce(v.tls.min_version, "-") + ) + ]) + error_message = "Invalid min TLS version." + } + validation { + condition = alltrue([ + for k, v in var.tls_inspection_policies : v.tls.feature_profile == null || contains( + ["PROFILE_UNSPECIFIED", "PROFILE_COMPATIBLE", "PROFILE_MODERN", "PROFILE_RESTRICTED", "PROFILE_CUSTOM"], + coalesce(v.tls.feature_profile, "-") + ) + ]) + error_message = "Invalid TLS feature profile." + } + validation { + condition = alltrue([ + for k, v in var.tls_inspection_policies : + v.tls.custom_features == null || v.tls.feature_profile == "PROFILE_CUSTOM" + ]) + error_message = "TLS custom features can only be used with custom profile." + } +} + +variable "trust_configs" { + description = "Certificate Manager trust configurations for TLS inspection policies. Project ids and region can reference keys in the relevant FAST variables." + type = map(object({ + location = string + description = optional(string) + allowlisted_certificates = optional(map(string)) + trust_stores = optional(map(object({ + intermediate_cas = optional(map(string)) + trust_anchors = optional(map(string)) + }))) + })) + nullable = false + default = { + # dev-ngfw-default = { + # location = "primary" + # project_id = "dev-spoke-0" + # } + # prod-ngfw-default = { + # location = "primary" + # project_id = "prod-spoke-0" + # } + } + validation { + condition = alltrue([ + for k, v in var.trust_configs : ( + v.allowlisted_certificates != null || + try(v.trust_stores.intermediate_cas, null) != null || + try(v.trust_stores.trust_anchors, null) != null + ) + ]) + error_message = "a trust configuration needs at least one set of allowlisted certificates, or a valid trust store." + } +} diff --git a/fast/addons/README.md b/fast/addons/README.md new file mode 100644 index 000000000..aadb6960c --- /dev/null +++ b/fast/addons/README.md @@ -0,0 +1,42 @@ +# FAST add-ons + +Each of the folders contained here is a separate "add-on" that can be used to add extra functionality to a specific stage. + +Add-ons can be thought of as additional thin layers on top of a stage, that reuse its IaC resources and leverage the same IAM configuration: the same service accounts are used to run the add-on, and state configuration is stored in the same bucket as their "parent stage" under a different prefix. + +The only dedicated resources that can be optionally defined for add-ons are to enable CI/CD functionality, so that a dedicated repository can be used to host the add-on code and pipeline. + +Add-ons are currently only implemented for stage 1 (resource management and VPC-SC), and stage 2 (networking, project factory, security). + +## Add-on configuration + +To configure an add-on: + +- its "parent stage" (the stage the add-on augments) needs to be enabled and applied, so that the IaC and IAM configurations the add-on uses are present +- the `fast_addon` variable in the stage controlling the "parent stage" (boostrap for a stage 1 add-on, resource management for a stage 2 add-on) is configured and the stage applied, so that the add-on provider and optional CI/CD resources are created +- the provider and relevant FAST output variable files are linked or copied in the add-on folder (e.g. via the `fast-links.sh` script) + +At this point the add-on can be run, and operate on the same folders, projects and resources controlled by its "parent stage". + +Add-ons typically generate their own FAST output variable files, which can be optionally consumed by downstream stages. + +This is an example configuration of the `fast_addon` variable in the resource management stage, to enable running the NGFW networking add-on. The CI/CD configuration block is optional, and commented out here. + +```hcl +fast_addon = { + networking-ngfw = { + parent_stage = "2-networking" + # cicd_config = { + # identity_provider = "github-test" + # repository = { + # name = "test/ngfw" + # type = "github" + # branch = "main" + # } + # } + } +} +``` + +This configuration will create `tfvars/2-networking-ngfw-providers.tf` and +`tfvars/2-networking-ngfw-r-providers.tf` provider files in the GCS output bucket and local folder (if configured). diff --git a/fast/extras/0-cicd-github/main.tf b/fast/extras/0-cicd-github/main.tf index e0657233a..25b16e397 100644 --- a/fast/extras/0-cicd-github/main.tf +++ b/fast/extras/0-cicd-github/main.tf @@ -42,33 +42,13 @@ locals { for k, v in var.repositories : k => v.create_options == null ? k : github_repository.default[k].name } - repository_files = merge( - { - for k in local._repository_files : - "${k.repository}/${k.name}" => k - if !endswith(k.name, ".tf") || ( - !startswith(k.name, "0") && k.name != "globals.tf" - ) - }, - { - for k, v in var.repositories : - "${k}/templates/providers.tf.tpl" => { - repository = k - file = "../../assets/templates/providers.tf.tpl" - name = "templates/providers.tf.tpl" - } - if v.populate_from != null - }, - { - for k, v in var.repositories : - "${k}/templates/workflow-github.yaml" => { - repository = k - file = "../../assets/templates/workflow-github.yaml" - name = "templates/workflow-github.yaml" - } - if v.populate_from != null - } - ) + repository_files = { + for k in local._repository_files : + "${k.repository}/${k.name}" => k + if !endswith(k.name, ".tf") || ( + !startswith(k.name, "0") && k.name != "globals.tf" + ) + } } resource "github_repository" "default" { diff --git a/fast/plugins/2-networking-serverless-connector/README.md b/fast/plugins/2-networking-serverless-connector/README.md deleted file mode 100644 index b955d138c..000000000 --- a/fast/plugins/2-networking-serverless-connector/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# VPC Serverless Connector - -This FAST plugin adds centralized [Serverless VPC Access Connectors](https://cloud.google.com/vpc/docs/serverless-vpc-access) to network stages. - -This plugin does not manage - -- IAM bindings for the connectors, which should be added via the stage project-level variables -- firewall rules for the connectors, which should be added via the stage factory - -The plugin only requires a specific configuration if the defaults it uses need to be changed: - -- the connector-specific subnets default to the `10.255.255.0` range -- the machine type, number of instances and throughput use the API defaults - -To enable the plugin, simply copy or link its files in the networking stage. - - - - -## Files - -| name | description | modules | resources | -|---|---|---|---| -| [local-serverless-connector-outputs.tf](./local-serverless-connector-outputs.tf) | Serverless Connector outputs. | | google_storage_bucket_object · local_file | -| [local-serverless-connector-variables.tf](./local-serverless-connector-variables.tf) | Serverless Connector variables. | | | -| [local-serverless-connector.tf](./local-serverless-connector.tf) | Serverless Connector resources. | net-vpc | google_vpc_access_connector | - -## Variables - -| name | description | type | required | default | producer | -|---|---|:---:|:---:|:---:|:---:| -| [serverless_connector_config](local-serverless-connector-variables.tf#L19) | VPC Access Serverless Connectors configuration. | object({…}) | | {…} | | - -## Outputs - -| name | description | sensitive | consumers | -|---|---|:---:|---| -| [plugin_sc_connectors](local-serverless-connector-outputs.tf#L47) | VPC Access Connectors. | | | - - diff --git a/fast/plugins/2-networking-serverless-connector/local-serverless-connector-outputs.tf b/fast/plugins/2-networking-serverless-connector/local-serverless-connector-outputs.tf deleted file mode 100644 index ef562508b..000000000 --- a/fast/plugins/2-networking-serverless-connector/local-serverless-connector-outputs.tf +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -# tfdoc:file:description Serverless Connector outputs. - -locals { - plugin_sc_tfvars = { - dev = google_vpc_access_connector.dev-primary[0].id - prod = google_vpc_access_connector.prod-primary[0].id - } -} - -# generate tfvars file for subsequent stages - -resource "local_file" "plugin_sc_tfvars" { - for_each = var.outputs_location == null ? {} : { 1 = 1 } - file_permission = "0644" - filename = "${try(pathexpand(var.outputs_location), "")}/tfvars/2-networking-serverless-connnector.auto.tfvars.json" - content = jsonencode({ - vpc_connectors = local.plugin_sc_tfvars - }) -} - -resource "google_storage_bucket_object" "plugin_sc_tfvars" { - bucket = var.automation.outputs_bucket - name = "tfvars/2-networking-serverless-connnector.auto.tfvars.json" - content = jsonencode({ - vpc_connectors = local.plugin_sc_tfvars - }) -} - -# outputs - -output "plugin_sc_connectors" { - description = "VPC Access Connectors." - value = local.plugin_sc_tfvars -} diff --git a/fast/plugins/2-networking-serverless-connector/local-serverless-connector-variables.tf b/fast/plugins/2-networking-serverless-connector/local-serverless-connector-variables.tf deleted file mode 100644 index 05733c467..000000000 --- a/fast/plugins/2-networking-serverless-connector/local-serverless-connector-variables.tf +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -# tfdoc:file:description Serverless Connector variables. - -variable "serverless_connector_config" { - description = "VPC Access Serverless Connectors configuration." - type = object({ - dev-primary = object({ - ip_cidr_range = optional(string, "10.255.255.128/28") - machine_type = optional(string) - instances = optional(object({ - max = optional(number) - min = optional(number) - }), {}) - throughput = optional(object({ - max = optional(number) - min = optional(number) - }), {}) - }) - prod-primary = object({ - ip_cidr_range = optional(string, "10.255.255.0/28") - machine_type = optional(string) - instances = optional(object({ - max = optional(number) - min = optional(number) - }), {}) - throughput = optional(object({ - max = optional(number) - min = optional(number) - }), {}) - }) - }) - default = { - dev-primary = {} - prod-primary = {} - } - nullable = false -} diff --git a/fast/plugins/2-networking-serverless-connector/local-serverless-connector.tf b/fast/plugins/2-networking-serverless-connector/local-serverless-connector.tf deleted file mode 100644 index e70de806c..000000000 --- a/fast/plugins/2-networking-serverless-connector/local-serverless-connector.tf +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -# tfdoc:file:description Serverless Connector resources. - -locals { - plugin_sc_subnets = { - dev = { - for k, v in module.dev-spoke-vpc-serverless.subnets : k => v.name - } - prod = { - for k, v in module.prod-spoke-vpc-serverless.subnets : k => v.name - } - } -} - -module "dev-spoke-vpc-serverless" { - source = "../../../modules/net-vpc" - project_id = module.dev-spoke-project.project_id - name = module.dev-spoke-vpc.name - vpc_create = false - subnets = [{ - name = "access-connector" - description = "VPC Serverless Connector for the primary region." - ip_cidr_range = var.serverless_connector_config.dev-primary.ip_cidr_range - region = var.regions.primary - }] - # these should be create from the main VPC - create_googleapis_routes = null -} - -module "prod-spoke-vpc-serverless" { - source = "../../../modules/net-vpc" - project_id = module.prod-spoke-project.project_id - name = module.prod-spoke-vpc.name - vpc_create = false - subnets = [{ - name = "access-connector" - description = "VPC Serverless Connector for the primary region." - ip_cidr_range = var.serverless_connector_config.prod-primary.ip_cidr_range - region = var.regions.primary - }] - # these should be create from the main VPC - create_googleapis_routes = null -} - -resource "google_vpc_access_connector" "dev-primary" { - count = var.serverless_connector_config.dev-primary == null ? 0 : 1 - project = module.dev-spoke-project.project_id - region = var.regions.primary - name = "access-connector-${local.region_shortnames[var.regions.primary]}" - subnet { - name = local.plugin_sc_subnets.dev["${var.regions.primary}/access-connector"] - } - machine_type = var.serverless_connector_config.dev-primary.machine_type - max_instances = var.serverless_connector_config.dev-primary.instances.max - max_throughput = var.serverless_connector_config.dev-primary.throughput.max - min_instances = var.serverless_connector_config.dev-primary.instances.min - min_throughput = var.serverless_connector_config.dev-primary.throughput.min -} - -resource "google_vpc_access_connector" "prod-primary" { - count = var.serverless_connector_config.prod-primary == null ? 0 : 1 - project = module.prod-spoke-project.project_id - region = var.regions.primary - name = "access-connector-${local.region_shortnames[var.regions.primary]}" - subnet { - name = local.plugin_sc_subnets.prod["${var.regions.primary}/access-connector"] - } - machine_type = var.serverless_connector_config.prod-primary.machine_type - max_instances = var.serverless_connector_config.prod-primary.instances.max - max_throughput = var.serverless_connector_config.prod-primary.throughput.max - min_instances = var.serverless_connector_config.prod-primary.instances.min - min_throughput = var.serverless_connector_config.prod-primary.throughput.min -} diff --git a/fast/plugins/README.md b/fast/plugins/README.md deleted file mode 100644 index d4e7ce787..000000000 --- a/fast/plugins/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# FAST plugin system - -This folders details a simple mechanism that can be used to add extra functionality to FAST stages, and a few examples that implement simple plugins that can be used as-is. - -## Available plugins - -### Networking - -- [Serverless VPC Access Connector](./2-networking-serverless-connector/) - -## Anatomy of a plugin - -FAST plugins are much simpler and easier to code than full-blown stages: each plugin is meant to add a single feature using a small set of resources, and interacting directly with stage modules and variables. - -A simple plugin might be composed of a single file with one resource, and grow up to the canonical set of one "main" (resources), one variables, and outputs file. - -Plugin file names start with the `local-` prefix which is purposefully excluded in FAST stages via Git ignore, so that plugins are not accidentally committed to stages during development and staying aligned with our master branch is possible. - -Plugins are structured here as individual folders, organized in top-level folders according to the FAST stage they are designed to work with. - -As an example, the [`2-networking/serverless-connector` plugin](./2-networking-serverless-connector/) implements centralized [Serverless VPC Access Connectors](https://cloud.google.com/vpc/docs/serverless-vpc-access) for our networking stages, and is composed of three files: - -- [`local-serverless-connector.tf`](./2-networking-serverless-connector/local-serverless-connector.tf) managing resources including the subnets needed in each VPC and the connectors themselves -- [`local-serverless-connector-outputs.tf`](./2-networking-serverless-connector/local-serverless-connector-outputs.tf) defining a single `serverless_connectors` output for the plugin, and optional output files -- [`local-serverless-connector-variables.tf`](./2-networking-serverless-connector/local-serverless-connector-variables.tf) defining a single `serverless_connector_config` variable used to configure the plugin diff --git a/fast/stages/0-bootstrap/README.md b/fast/stages/0-bootstrap/README.md index 83a15b134..49bc06b3f 100644 --- a/fast/stages/0-bootstrap/README.md +++ b/fast/stages/0-bootstrap/README.md @@ -43,6 +43,7 @@ Use the following diagram as a simple high level reference for the following sec - [Workload Identity Federation](#workload-identity-federation) - [Project folders](#project-folders) - [CI/CD repositories](#cicd-repositories) + - [Add-ons](#add-ons) - [Files](#files) - [Variables](#variables) - [Outputs](#outputs) @@ -636,6 +637,10 @@ The remaining configuration is manual, as it regards the repositories themselves - for Gitlab, rename it to `.gitlab-ci.yml` and place it in the repository root - for Source Repositories, place it in `.cloudbuild/workflow.yaml` +### Add-ons + +FAST defines a simple mechanism to extend stage functionality via the use of [add-ons](../../addons/). Configuration for stage 1 add-ons happens here via the `fast_addon` variable. Refer to the add-ons documentation for more details on their use. + ## Files @@ -644,7 +649,7 @@ The remaining configuration is manual, as it regards the repositories themselves |---|---|---|---| | [automation.tf](./automation.tf) | Automation project and resources. | gcs · iam-service-account · project | | | [billing.tf](./billing.tf) | Billing export project and dataset. | bigquery-dataset · project | google_billing_account_iam_member | -| [cicd.tf](./cicd.tf) | Workload Identity Federation configurations for CI/CD. | iam-service-account | | +| [cicd.tf](./cicd.tf) | CI/CD locals and resources. | iam-service-account | | | [identity-providers-defs.tf](./identity-providers-defs.tf) | Identity provider definitions. | | | | [identity-providers.tf](./identity-providers.tf) | Workload Identity Federation provider definitions. | | google_iam_workforce_pool · google_iam_workforce_pool_provider · google_iam_workload_identity_pool · google_iam_workload_identity_pool_provider | | [log-export.tf](./log-export.tf) | Audit log project and sink. | bigquery-dataset · gcs · logging-bucket · project · pubsub | | @@ -653,7 +658,9 @@ The remaining configuration is manual, as it regards the repositories themselves | [organization.tf](./organization.tf) | Organization-level IAM. | organization | | | [outputs-files.tf](./outputs-files.tf) | Output files persistence to local filesystem. | | local_file | | [outputs-gcs.tf](./outputs-gcs.tf) | Output files persistence to automation GCS bucket. | | google_storage_bucket_object | +| [outputs-providers.tf](./outputs-providers.tf) | Locals for provider output files. | | | | [outputs.tf](./outputs.tf) | Module outputs. | | | +| [variables-addons.tf](./variables-addons.tf) | None | | | | [variables.tf](./variables.tf) | Module variables. | | | ## Variables @@ -661,41 +668,42 @@ The remaining configuration is manual, as it regards the repositories themselves | 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`. | object({…}) | ✓ | | | -| [organization](variables.tf#L290) | Organization details. | object({…}) | ✓ | | | -| [prefix](variables.tf#L305) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | | +| [organization](variables.tf#L277) | Organization details. | object({…}) | ✓ | | | +| [prefix](variables.tf#L292) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | | | [bootstrap_user](variables.tf#L38) | Email of the nominal user running this stage for the first time. | string | | null | | -| [cicd_repositories](variables.tf#L44) | 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. | object({…}) | | null | | -| [custom_roles](variables.tf#L98) | Map of role names => list of permissions to additionally create at the organization level. | map(list(string)) | | {} | | -| [environments](variables.tf#L105) | Environment names. When not defined, short name is set to the key and tag name to lower(name). | map(object({…})) | | {…} | | -| [essential_contacts](variables.tf#L139) | Email used for essential contacts, unset if null. | string | | null | | -| [factories_config](variables.tf#L145) | Configuration for the resource factories or external data. | object({…}) | | {} | | -| [groups](variables.tf#L156) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | | -| [iam](variables.tf#L172) | Organization-level custom IAM settings in role => [principal] format. | map(list(string)) | | {} | | -| [iam_bindings_additive](variables.tf#L179) | Organization-level custom additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | | -| [iam_by_principals](variables.tf#L194) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | map(list(string)) | | {} | | -| [locations](variables.tf#L201) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | | -| [log_sinks](variables.tf#L215) | Org-level log sinks, in name => {type, filter} format. | map(object({…})) | | {…} | | -| [org_policies_config](variables.tf#L271) | Organization policies customization. | object({…}) | | {} | | -| [outputs_location](variables.tf#L299) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | string | | null | | -| [project_parent_ids](variables.tf#L314) | Optional parents for projects created here in folders/nnnnnnn format. Null values will use the organization as parent. | object({…}) | | {} | | -| [resource_names](variables.tf#L325) | Resource names overrides for specific resources. Prefix is always set via code, except where noted in the variable type. | object({…}) | | {} | | -| [workforce_identity_providers](variables.tf#L357) | Workforce Identity Federation pools. | map(object({…})) | | {} | | -| [workload_identity_providers](variables.tf#L373) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | map(object({…})) | | {} | | +| [cicd_config](variables.tf#L44) | 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. | object({…}) | | {} | | +| [custom_roles](variables.tf#L85) | Map of role names => list of permissions to additionally create at the organization level. | map(list(string)) | | {} | | +| [environments](variables.tf#L92) | Environment names. When not defined, short name is set to the key and tag name to lower(name). | map(object({…})) | | {…} | | +| [essential_contacts](variables.tf#L126) | Email used for essential contacts, unset if null. | string | | null | | +| [factories_config](variables.tf#L132) | Configuration for the resource factories or external data. | object({…}) | | {} | | +| [fast_addon](variables-addons.tf#L17) | FAST addons configurations for stages 1. Keys are used as short names for the add-on resources. | map(object({…})) | | {} | | +| [groups](variables.tf#L143) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | | +| [iam](variables.tf#L159) | Organization-level custom IAM settings in role => [principal] format. | map(list(string)) | | {} | | +| [iam_bindings_additive](variables.tf#L166) | Organization-level custom additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | | +| [iam_by_principals](variables.tf#L181) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | map(list(string)) | | {} | | +| [locations](variables.tf#L188) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | | +| [log_sinks](variables.tf#L202) | Org-level log sinks, in name => {type, filter} format. | map(object({…})) | | {…} | | +| [org_policies_config](variables.tf#L258) | Organization policies customization. | object({…}) | | {} | | +| [outputs_location](variables.tf#L286) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | string | | null | | +| [project_parent_ids](variables.tf#L301) | Optional parents for projects created here in folders/nnnnnnn format. Null values will use the organization as parent. | object({…}) | | {} | | +| [resource_names](variables.tf#L312) | Resource names overrides for specific resources. Prefix is always set via code, except where noted in the variable type. | object({…}) | | {} | | +| [workforce_identity_providers](variables.tf#L344) | Workforce Identity Federation pools. | map(object({…})) | | {} | | +| [workload_identity_providers](variables.tf#L360) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | map(object({…})) | | {} | | ## Outputs | name | description | sensitive | consumers | |---|---|:---:|---| -| [automation](outputs.tf#L159) | Automation resources. | | | -| [billing_dataset](outputs.tf#L164) | BigQuery dataset prepared for billing export. | | | -| [cicd_repositories](outputs.tf#L169) | CI/CD repository configurations. | | | -| [custom_roles](outputs.tf#L181) | Organization-level custom roles. | | | -| [outputs_bucket](outputs.tf#L186) | GCS bucket where generated output files are stored. | | | -| [project_ids](outputs.tf#L191) | Projects created by this stage. | | | -| [providers](outputs.tf#L201) | Terraform provider files for this stage and dependent stages. | ✓ | stage-01 | -| [service_accounts](outputs.tf#L208) | Automation service accounts created by this stage. | | | -| [tfvars](outputs.tf#L217) | Terraform variable files for the following stages. | ✓ | | -| [tfvars_globals](outputs.tf#L223) | Terraform Globals variable files for the following stages. | ✓ | | -| [workforce_identity_pool](outputs.tf#L229) | Workforce Identity Federation pool. | | | -| [workload_identity_pool](outputs.tf#L238) | Workload Identity Federation pool and providers. | | | +| [automation](outputs.tf#L112) | Automation resources. | | | +| [billing_dataset](outputs.tf#L117) | BigQuery dataset prepared for billing export. | | | +| [cicd_repositories](outputs.tf#L122) | CI/CD repository configurations. | | | +| [custom_roles](outputs.tf#L134) | Organization-level custom roles. | | | +| [outputs_bucket](outputs.tf#L139) | GCS bucket where generated output files are stored. | | | +| [project_ids](outputs.tf#L144) | Projects created by this stage. | | | +| [providers](outputs.tf#L154) | Terraform provider files for this stage and dependent stages. | ✓ | stage-01 | +| [service_accounts](outputs.tf#L161) | Automation service accounts created by this stage. | | | +| [tfvars](outputs.tf#L170) | Terraform variable files for the following stages. | ✓ | | +| [tfvars_globals](outputs.tf#L176) | Terraform Globals variable files for the following stages. | ✓ | | +| [workforce_identity_pool](outputs.tf#L182) | Workforce Identity Federation pool. | | | +| [workload_identity_pool](outputs.tf#L191) | Workload Identity Federation pool and providers. | | | diff --git a/fast/stages/0-bootstrap/automation.tf b/fast/stages/0-bootstrap/automation.tf index 4dc44093d..82568d684 100644 --- a/fast/stages/0-bootstrap/automation.tf +++ b/fast/stages/0-bootstrap/automation.tf @@ -16,15 +16,6 @@ # tfdoc:file:description Automation project and resources. -locals { - cicd_resman_sa = try(module.automation-tf-cicd-sa["resman"].iam_email, "") - cicd_resman_r_sa = try(module.automation-tf-cicd-r-sa["resman"].iam_email, "") - cicd_tenants_sa = try(module.automation-tf-cicd-sa["tenants"].iam_email, "") - cicd_tenants_r_sa = try(module.automation-tf-cicd-r-sa["tenants"].iam_email, "") - cicd_vpcsc_sa = try(module.automation-tf-cicd-sa["vpcsc"].iam_email, "") - cicd_vpcsc_r_sa = try(module.automation-tf-cicd-r-sa["vpcsc"].iam_email, "") -} - module "automation-project" { source = "../../../modules/project" billing_account = var.billing_account.id @@ -219,9 +210,10 @@ module "automation-tf-bootstrap-sa" { prefix = var.prefix # allow SA used by CI/CD workflow to impersonate this SA iam = { - "roles/iam.serviceAccountTokenCreator" = compact([ - try(module.automation-tf-cicd-sa["bootstrap"].iam_email, null) - ]) + "roles/iam.serviceAccountTokenCreator" = [ + for k, v in local.cicd_repositories : + module.automation-tf-cicd-sa[k].iam_email if v.stage == "bootstrap" + ] } iam_storage_roles = { (module.automation-tf-output-gcs.name) = ["roles/storage.admin"] @@ -236,9 +228,10 @@ module "automation-tf-bootstrap-r-sa" { prefix = var.prefix # allow SA used by CI/CD workflow to impersonate this SA iam = { - "roles/iam.serviceAccountTokenCreator" = compact([ - try(module.automation-tf-cicd-r-sa["bootstrap"].iam_email, null) - ]) + "roles/iam.serviceAccountTokenCreator" = [ + for k, v in local.cicd_repositories : + module.automation-tf-cicd-r-sa[k].iam_email if v.stage == "bootstrap" + ] } # we grant organization roles here as IAM bindings have precedence over # custom roles in the organization module, so these need to depend on it @@ -276,21 +269,12 @@ module "automation-tf-resman-sa" { display_name = "Terraform stage 1 resman service account." prefix = var.prefix # allow SA used by CI/CD workflow to impersonate this SA - # we use additive IAM to allow tenant CI/CD SAs to impersonate it - iam_bindings_additive = merge( - local.cicd_resman_sa == "" ? {} : { - cicd_token_creator_resman = { - member = local.cicd_resman_sa - role = "roles/iam.serviceAccountTokenCreator" - } - }, - local.cicd_tenants_sa == "" ? {} : { - cicd_token_creator_tenants = { - member = local.cicd_tenants_sa - role = "roles/iam.serviceAccountTokenCreator" - } - } - ) + iam = { + "roles/iam.serviceAccountTokenCreator" = [ + for k, v in local.cicd_repositories : + module.automation-tf-cicd-sa[k].iam_email if v.stage == "resman" + ] + } iam_storage_roles = { (module.automation-tf-output-gcs.name) = ["roles/storage.admin"] } @@ -303,21 +287,12 @@ module "automation-tf-resman-r-sa" { display_name = "Terraform stage 1 resman service account (read-only)." prefix = var.prefix # allow SA used by CI/CD workflow to impersonate this SA - # we use additive IAM to allow tenant CI/CD SAs to impersonate it - iam_bindings_additive = merge( - local.cicd_resman_r_sa == "" ? {} : { - cicd_token_creator_resman = { - member = local.cicd_resman_r_sa - role = "roles/iam.serviceAccountTokenCreator" - } - }, - local.cicd_tenants_r_sa == "" ? {} : { - cicd_token_creator_tenants = { - member = local.cicd_tenants_r_sa - role = "roles/iam.serviceAccountTokenCreator" - } - } - ) + iam = { + "roles/iam.serviceAccountTokenCreator" = [ + for k, v in local.cicd_repositories : + module.automation-tf-cicd-r-sa[k].iam_email if v.stage == "resman" + ] + } # we grant organization roles here as IAM bindings have precedence over # custom roles in the organization module, so these need to depend on it iam_organization_roles = { @@ -354,21 +329,18 @@ module "automation-tf-vpcsc-sa" { display_name = "Terraform stage 1 vpcsc service account." prefix = var.prefix # allow SA used by CI/CD workflow to impersonate this SA - # we use additive IAM to allow tenant CI/CD SAs to impersonate it - iam_bindings_additive = merge( - { - security_admins = { - member = local.principals["gcp-security-admins"] - role = "roles/iam.serviceAccountTokenCreator" - } - }, - local.cicd_vpcsc_sa == "" ? {} : { - cicd_token_creator_vpcsc = { - member = local.cicd_vpcsc_sa - role = "roles/iam.serviceAccountTokenCreator" - } + iam = { + "roles/iam.serviceAccountTokenCreator" = [ + for k, v in local.cicd_repositories : + module.automation-tf-cicd-sa[k].iam_email if v.stage == "vpcsc" + ] + } + iam_bindings_additive = { + security_admins = { + member = local.principals["gcp-security-admins"] + role = "roles/iam.serviceAccountTokenCreator" } - ) + } iam_storage_roles = { (module.automation-tf-output-gcs.name) = ["roles/storage.admin"] } @@ -381,12 +353,11 @@ module "automation-tf-vpcsc-r-sa" { display_name = "Terraform stage 1 vpcsc service account (read-only)." prefix = var.prefix # allow SA used by CI/CD workflow to impersonate this SA - # we use additive IAM to allow tenant CI/CD SAs to impersonate it - iam_bindings_additive = local.cicd_vpcsc_r_sa == "" ? {} : { - cicd_token_creator_vpcsc = { - member = local.cicd_vpcsc_r_sa - role = "roles/iam.serviceAccountTokenCreator" - } + iam = { + "roles/iam.serviceAccountTokenCreator" = [ + for k, v in local.cicd_repositories : + module.automation-tf-cicd-r-sa[k].iam_email if v.stage == "vpcsc" + ] } iam_storage_roles = { (module.automation-tf-output-gcs.name) = [module.organization.custom_role_id["storage_viewer"]] diff --git a/fast/stages/0-bootstrap/cicd.tf b/fast/stages/0-bootstrap/cicd.tf index f6f766079..1c2771c45 100644 --- a/fast/stages/0-bootstrap/cicd.tf +++ b/fast/stages/0-bootstrap/cicd.tf @@ -14,9 +14,25 @@ * limitations under the License. */ -# tfdoc:file:description Workload Identity Federation configurations for CI/CD. +# tfdoc:file:description CI/CD locals and resources. locals { + _cicd_configs = merge( + # stages + { + for k, v in var.cicd_config : k => merge(v, { + level = k == "bootstrap" ? 0 : 1 + stage = k + }) if v != null + }, + # addons + { + for k, v in var.fast_addon : k => merge(v.cicd_config, { + level = 1 + stage = substr(v.parent_stage, 2, -1) + }) if v.cicd_config != null + } + ) cicd_providers = { for k, v in google_iam_workload_identity_pool_provider.default : k => { @@ -32,45 +48,21 @@ locals { } } cicd_repositories = { - for k, v in coalesce(var.cicd_repositories, {}) : k => v - if( - v != null - && - contains( - keys(local.workload_identity_providers), - coalesce(try(v.identity_provider, null), ":") - ) - && - fileexists( - format("${path.module}/templates/workflow-%s.yaml", try(v.type, "")) - ) + for k, v in local._cicd_configs : k => v if( + contains(keys(local.workload_identity_providers), v.identity_provider) && + fileexists("${path.module}/templates/workflow-${v.repository.type}.yaml") ) } - cicd_workflow_providers = { - bootstrap = "0-bootstrap-providers.tf" - bootstrap_r = "0-bootstrap-r-providers.tf" - resman = "1-resman-providers.tf" - resman_r = "1-resman-r-providers.tf" - tenants = "1-tenant-factory-providers.tf" - tenants_r = "1-tenant-factory-r-providers.tf" - vpcsc = "1-vpcsc-providers.tf" - vpcsc_r = "1-vpcsc-r-providers.tf" - } - cicd_workflow_var_files = { - bootstrap = [] - resman = [ - "0-bootstrap.auto.tfvars.json", - "0-globals.auto.tfvars.json" - ] - tenants = [ - "0-bootstrap.auto.tfvars.json", - "0-globals.auto.tfvars.json" - ] - vpcsc = [ - "0-bootstrap.auto.tfvars.json", - "0-globals.auto.tfvars.json" - ] - } + cicd_workflow_providers = merge( + { + for k, v in local.cicd_repositories : + k => "${v.level}-${k}-providers.tf" + }, + { + for k, v in local.cicd_repositories : + "${k}-r" => "${v.level}-${k}-r-providers.tf" + } + ) } # SAs used by CI/CD workflows to impersonate automation SAs @@ -86,17 +78,17 @@ module "automation-tf-cicd-sa" { prefix = var.prefix iam = { "roles/iam.workloadIdentityUser" = [ - each.value.branch == null + each.value.repository.branch == null ? format( - local.workload_identity_providers_defs[each.value.type].principal_repo, + local.workload_identity_providers_defs[each.value.repository.type].principal_repo, google_iam_workload_identity_pool.default[0].name, - each.value.name + each.value.repository.name ) : format( - local.workload_identity_providers_defs[each.value.type].principal_branch, + local.workload_identity_providers_defs[each.value.repository.type].principal_branch, google_iam_workload_identity_pool.default[0].name, - each.value.name, - each.value.branch + each.value.repository.name, + each.value.repository.branch ) ] } @@ -120,9 +112,9 @@ module "automation-tf-cicd-r-sa" { iam = { "roles/iam.workloadIdentityUser" = [ format( - local.workload_identity_providers_defs[each.value.type].principal_repo, + local.workload_identity_providers_defs[each.value.repository.type].principal_repo, google_iam_workload_identity_pool.default[0].name, - each.value.name + each.value.repository.name ) ] } diff --git a/fast/stages/0-bootstrap/outputs-providers.tf b/fast/stages/0-bootstrap/outputs-providers.tf new file mode 100644 index 000000000..e32fad605 --- /dev/null +++ b/fast/stages/0-bootstrap/outputs-providers.tf @@ -0,0 +1,99 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Locals for provider output files. + +locals { + providers = merge( + # this stage's providers + { + "0-bootstrap" = templatefile(local._tpl_providers, { + backend_extra = null + bucket = module.automation-tf-bootstrap-gcs.name + name = "bootstrap" + sa = module.automation-tf-bootstrap-sa.email + }) + "0-bootstrap-r" = templatefile(local._tpl_providers, { + backend_extra = null + bucket = module.automation-tf-bootstrap-gcs.name + name = "bootstrap" + sa = module.automation-tf-bootstrap-r-sa.email + }) + }, + # stage 1 providers + { + "1-resman" = templatefile(local._tpl_providers, { + backend_extra = null + bucket = module.automation-tf-resman-gcs.name + name = "resman" + sa = module.automation-tf-resman-sa.email + }) + "1-resman-r" = templatefile(local._tpl_providers, { + backend_extra = null + bucket = module.automation-tf-resman-gcs.name + name = "resman" + sa = module.automation-tf-resman-r-sa.email + }) + "1-vpcsc" = templatefile(local._tpl_providers, { + backend_extra = "prefix = \"vpcsc\"" + bucket = module.automation-tf-vpcsc-gcs.name + name = "vpcsc" + sa = module.automation-tf-vpcsc-sa.email + }) + "1-vpcsc-r" = templatefile(local._tpl_providers, { + backend_extra = "prefix = \"vpcsc\"" + bucket = module.automation-tf-vpcsc-gcs.name + name = "vpcsc" + sa = module.automation-tf-vpcsc-r-sa.email + }) + }, + # stage 1 addons + { + for k, v in var.fast_addon : + "${v.parent_stage}-${k}" => templatefile(local._tpl_providers, { + backend_extra = "prefix = \"addons/${k}\"" + name = "${v.parent_stage}-${k}" + bucket = ( + v.parent_stage == "resman" + ? module.automation-tf-resman-gcs.name + : module.automation-tf-vpcsc-gcs.name + ) + sa = ( + v.parent_stage == "resman" + ? module.automation-tf-resman-sa.name + : module.automation-tf-vpcsc-sa.name + ) + }) + }, + { + for k, v in var.fast_addon : + "${v.parent_stage}-${k}-r" => templatefile(local._tpl_providers, { + backend_extra = "prefix = \"addons/${k}\"" + name = "${v.parent_stage}-${k}" + bucket = ( + v.parent_stage == "resman" + ? module.automation-tf-resman-gcs.name + : module.automation-tf-vpcsc-gcs.name + ) + sa = ( + v.parent_stage == "resman" + ? module.automation-tf-resman-r-sa.name + : module.automation-tf-vpcsc-r-sa.name + ) + }) + } + ) +} diff --git a/fast/stages/0-bootstrap/outputs.tf b/fast/stages/0-bootstrap/outputs.tf index 876b269f0..c7beb1606 100644 --- a/fast/stages/0-bootstrap/outputs.tf +++ b/fast/stages/0-bootstrap/outputs.tf @@ -18,15 +18,15 @@ locals { _tpl_providers = "${path.module}/templates/providers.tf.tpl" # render CI/CD workflow templates cicd_workflows = { - for k, v in local.cicd_repositories : k => templatefile( - "${path.module}/templates/workflow-${v.type}.yaml", { + for k, v in local.cicd_repositories : "${v.level}-${k}" => templatefile( + "${path.module}/templates/workflow-${v.repository.type}.yaml", { # If users give a list of custom audiences we set by default the first element. # If no audiences are given, we set https://iam.googleapis.com/{PROVIDER_NAME} audiences = try( - local.cicd_providers[v["identity_provider"]].audiences, "" + local.cicd_providers[v.identity_provider].audiences, [] ) identity_provider = try( - local.cicd_providers[v["identity_provider"]].name, "" + local.cicd_providers[v.identity_provider].name, "" ) outputs_bucket = module.automation-tf-output-gcs.name service_accounts = { @@ -36,62 +36,15 @@ locals { stage_name = k tf_providers_files = { apply = local.cicd_workflow_providers[k] - plan = local.cicd_workflow_providers["${k}_r"] + plan = local.cicd_workflow_providers["${k}-r"] } - tf_var_files = local.cicd_workflow_var_files[k] + tf_var_files = k == "bootstrap" ? [] : [ + "0-bootstrap.auto.tfvars.json", + "0-globals.auto.tfvars.json" + ] } ) } - providers = { - "0-bootstrap" = templatefile(local._tpl_providers, { - backend_extra = null - bucket = module.automation-tf-bootstrap-gcs.name - name = "bootstrap" - sa = module.automation-tf-bootstrap-sa.email - }) - "0-bootstrap-r" = templatefile(local._tpl_providers, { - backend_extra = null - bucket = module.automation-tf-bootstrap-gcs.name - name = "bootstrap" - sa = module.automation-tf-bootstrap-r-sa.email - }) - "1-resman" = templatefile(local._tpl_providers, { - backend_extra = null - bucket = module.automation-tf-resman-gcs.name - name = "resman" - sa = module.automation-tf-resman-sa.email - }) - "1-resman-r" = templatefile(local._tpl_providers, { - backend_extra = null - bucket = module.automation-tf-resman-gcs.name - name = "resman" - sa = module.automation-tf-resman-r-sa.email - }) - "1-tenant-factory" = templatefile(local._tpl_providers, { - backend_extra = "prefix = \"tenant-factory\"" - bucket = module.automation-tf-resman-gcs.name - name = "tenant-factory" - sa = module.automation-tf-resman-sa.email - }) - "1-tenant-factory-r" = templatefile(local._tpl_providers, { - backend_extra = "prefix = \"tenant-factory\"" - bucket = module.automation-tf-resman-gcs.name - name = "tenant-factory" - sa = module.automation-tf-resman-r-sa.email - }) - "1-vpcsc" = templatefile(local._tpl_providers, { - backend_extra = "prefix = \"vpcsc\"" - bucket = module.automation-tf-vpcsc-gcs.name - name = "vpcsc" - sa = module.automation-tf-vpcsc-sa.email - }) - "1-vpcsc-r" = templatefile(local._tpl_providers, { - backend_extra = "prefix = \"vpcsc\"" - bucket = module.automation-tf-vpcsc-gcs.name - name = "vpcsc" - sa = module.automation-tf-vpcsc-r-sa.email - }) - } tfvars = { automation = { federated_identity_pool = try( @@ -170,8 +123,8 @@ output "cicd_repositories" { description = "CI/CD repository configurations." value = { for k, v in local.cicd_repositories : k => { - branch = v.branch - name = v.name + branch = v.repository.branch + name = v.repository.name provider = try(local.cicd_providers[v.identity_provider].name, null) service_account = try(module.automation-tf-cicd-sa[k].email, null) } diff --git a/fast/stages/0-bootstrap/variables-addons.tf b/fast/stages/0-bootstrap/variables-addons.tf new file mode 100644 index 000000000..cc505db46 --- /dev/null +++ b/fast/stages/0-bootstrap/variables-addons.tf @@ -0,0 +1,48 @@ +/** + * 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. + */ + +variable "fast_addon" { + description = "FAST addons configurations for stages 1. Keys are used as short names for the add-on resources." + type = map(object({ + parent_stage = string + cicd_config = optional(object({ + identity_provider = string + repository = object({ + name = string + branch = optional(string) + type = optional(string, "github") + }) + })) + })) + nullable = false + default = {} + validation { + condition = alltrue([ + for k, v in var.fast_addon : contains(["1-resman", "1-vpcsc"], v.parent_stage) + ]) + error_message = "Bootstrap-defined addons only support '1-resman' and '1-vpcsc' stages." + } + validation { + condition = alltrue([ + for k, v in var.fast_addon : + v.cicd_config == null || contains( + ["github", "gitlab"], + coalesce(try(v.cicd_config.repository.type, null), "-") + ) + ]) + error_message = "Invalid CI/CD repository type." + } +} diff --git a/fast/stages/0-bootstrap/variables.tf b/fast/stages/0-bootstrap/variables.tf index e1fd0bd9f..042207e04 100644 --- a/fast/stages/0-bootstrap/variables.tf +++ b/fast/stages/0-bootstrap/variables.tf @@ -41,54 +41,41 @@ variable "bootstrap_user" { default = null } -variable "cicd_repositories" { +variable "cicd_config" { description = "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." type = object({ bootstrap = optional(object({ - name = string - type = string - branch = optional(string) - identity_provider = optional(string) + identity_provider = string + repository = object({ + name = string + branch = optional(string) + type = optional(string, "github") + }) })) resman = optional(object({ - name = string - type = string - branch = optional(string) - identity_provider = optional(string) - })) - tenants = optional(object({ - name = string - type = string - branch = optional(string) - identity_provider = optional(string) + identity_provider = string + repository = object({ + name = string + branch = optional(string) + type = optional(string, "github") + }) })) vpcsc = optional(object({ - name = string - type = string - branch = optional(string) - identity_provider = optional(string) + identity_provider = string + repository = object({ + name = string + branch = optional(string) + type = optional(string, "github") + }) })) }) - default = null + nullable = false + default = {} validation { condition = alltrue([ - for k, v in coalesce(var.cicd_repositories, {}) : - v == null || try(v.name, null) != null - ]) - error_message = "Non-null repositories need a non-null name." - } - validation { - condition = alltrue([ - for k, v in coalesce(var.cicd_repositories, {}) : - v == null || try(v.identity_provider, null) != null - ]) - error_message = "Non-null repositories need a non-null provider." - } - validation { - condition = alltrue([ - for k, v in coalesce(var.cicd_repositories, {}) : + for k, v in coalesce(var.cicd_config, {}) : v == null || ( - contains(["github", "gitlab", "terraform"], coalesce(try(v.type, null), "null")) + contains(["github", "gitlab", "terraform"], coalesce(try(v.repository.type, null), "null")) ) ]) error_message = "Invalid repository type, supported types: 'github', 'gitlab', or 'terraform'." diff --git a/fast/stages/1-resman/README.md b/fast/stages/1-resman/README.md index 91a120761..25dbc05c4 100644 --- a/fast/stages/1-resman/README.md +++ b/fast/stages/1-resman/README.md @@ -150,7 +150,9 @@ tags = { ### Multitenancy -Multitenancy is supported via a [separate stage](../1-tenant-factory/), which is entirely optional and can be applied after resource management has been deployed. For simpler use cases that do not require complex organization-level multitenancy, [top-level folders](#top-level-folders) can be used in combination with the [project factory stage](../2-project-factory/) support for folder and project management. +Multitenancy is supported via an [add-on](../../addons/1-resman-tenants/) which is entirely optional, and is be applied after resource management has been deployed. The add-on needs to be enabled before use via the `fast_addon` variable in the bootstrap stage. + +For simpler use cases that do not require complex organization-level multitenancy, [top-level folders](#top-level-folders) can be used in combination with the [project factory stage](../2-project-factory/) support for folder and project management. ### Workload Identity Federation and CI/CD @@ -245,17 +247,19 @@ terraform apply | [iam.tf](./iam.tf) | Organization or root node-level IAM bindings. | | | | [main.tf](./main.tf) | Module-level locals and resources. | | | | [organization.tf](./organization.tf) | Organization policies. | organization | | +| [outputs-cicd.tf](./outputs-cicd.tf) | Locals for CI/CD workflow files. | | | | [outputs-files.tf](./outputs-files.tf) | Output files persistence to local filesystem. | | google_storage_bucket_object · local_file | +| [outputs-providers.tf](./outputs-providers.tf) | Locals for provider output files. | | | | [outputs.tf](./outputs.tf) | Module outputs. | | | -| [stage-2-network-security.tf](./stage-2-network-security.tf) | None | gcs · iam-service-account | | | [stage-2-networking.tf](./stage-2-networking.tf) | None | folder · gcs · iam-service-account | | | [stage-2-project-factory.tf](./stage-2-project-factory.tf) | None | gcs · iam-service-account | | | [stage-2-security.tf](./stage-2-security.tf) | None | folder · gcs · iam-service-account | | | [stage-3.tf](./stage-3.tf) | None | folder · gcs · iam-service-account | | -| [stage-cicd.tf](./stage-cicd.tf) | None | iam-service-account | | +| [stage-cicd.tf](./stage-cicd.tf) | CI/CD locals and resources. | iam-service-account | | | [tenant-logging.tf](./tenant-logging.tf) | Audit log project and sink for tenant root folder. | bigquery-dataset · gcs · logging-bucket · pubsub | | | [tenant-root.tf](./tenant-root.tf) | None | folder · project | | | [top-level-folders.tf](./top-level-folders.tf) | None | folder · gcs · iam-service-account | | +| [variables-addons.tf](./variables-addons.tf) | None | | | | [variables-fast.tf](./variables-fast.tf) | FAST stage interface. | | | | [variables-stages.tf](./variables-stages.tf) | None | | | | [variables-toplevel-folders.tf](./variables-toplevel-folders.tf) | None | | | @@ -265,21 +269,22 @@ terraform apply | name | description | type | required | default | producer | |---|---|:---:|:---:|:---:|:---:| -| [automation](variables-fast.tf#L19) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | -| [billing_account](variables-fast.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`. | object({…}) | ✓ | | 0-bootstrap | -| [environments](variables-fast.tf#L71) | Environment names. | map(object({…})) | ✓ | | 0-globals | -| [logging](variables-fast.tf#L116) | Logging configuration for tenants. | object({…}) | ✓ | | 1-tenant-factory | -| [organization](variables-fast.tf#L129) | Organization details. | object({…}) | ✓ | | 0-bootstrap | -| [prefix](variables-fast.tf#L147) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | -| [custom_roles](variables-fast.tf#L53) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | +| [automation](variables-fast.tf#L19) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | +| [billing_account](variables-fast.tf#L43) | 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`. | object({…}) | ✓ | | 0-bootstrap | +| [environments](variables-fast.tf#L72) | Environment names. | map(object({…})) | ✓ | | 0-globals | +| [logging](variables-fast.tf#L118) | Logging configuration for tenants. | object({…}) | ✓ | | 1-tenant-factory | +| [organization](variables-fast.tf#L131) | Organization details. | object({…}) | ✓ | | 0-bootstrap | +| [prefix](variables-fast.tf#L149) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | +| [custom_roles](variables-fast.tf#L54) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | | [factories_config](variables.tf#L20) | Configuration for the resource factories or external data. | object({…}) | | {} | | -| [fast_stage_2](variables-stages.tf#L17) | FAST stages 2 configurations. | object({…}) | | {} | | -| [fast_stage_3](variables-stages.tf#L95) | FAST stages 3 configurations. | map(object({…})) | | {} | | -| [groups](variables-fast.tf#L88) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | 0-bootstrap | -| [locations](variables-fast.tf#L103) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | 0-bootstrap | +| [fast_addon](variables-addons.tf#L17) | FAST addons configurations for stages 2. Keys are used as short names for the add-on resources. | map(object({…})) | | {} | | +| [fast_stage_2](variables-stages.tf#L17) | FAST stages 2 configurations. | object({…}) | | {} | | +| [fast_stage_3](variables-stages.tf#L83) | FAST stages 3 configurations. | map(object({…})) | | {} | | +| [groups](variables-fast.tf#L90) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | 0-bootstrap | +| [locations](variables-fast.tf#L105) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | 0-bootstrap | | [outputs_location](variables.tf#L31) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | string | | null | | | [resource_names](variables.tf#L37) | Resource names overrides for specific resources. Stage names are interpolated via `$${name}`. Prefix is always set via code, except where noted in the variable type. | object({…}) | | {} | | -| [root_node](variables-fast.tf#L153) | Root node for the hierarchy, if running in tenant mode. | string | | null | 0-bootstrap | +| [root_node](variables-fast.tf#L155) | Root node for the hierarchy, if running in tenant mode. | string | | null | 0-bootstrap | | [tag_names](variables.tf#L62) | Customized names for resource management tags. | object({…}) | | {} | | | [tags](variables.tf#L76) | Custom secure tags by key name. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | | | [top_level_folders](variables-toplevel-folders.tf#L17) | Additional top-level folders. Keys are used for service account and bucket names, values implement the folders module interface with the addition of the 'automation' attribute. | map(object({…})) | | {} | | diff --git a/fast/stages/1-resman/iam.tf b/fast/stages/1-resman/iam.tf index 68a9dcb28..3116fd19f 100644 --- a/fast/stages/1-resman/iam.tf +++ b/fast/stages/1-resman/iam.tf @@ -21,31 +21,24 @@ locals { iam_bindings_additive = merge( # stage 2 networking !var.fast_stage_2.networking.enabled ? {} : { - sa_net_fw_policy_admin = { + sa_net_rw_fw_policy_admin = { member = module.net-sa-rw[0].iam_email role = "roles/compute.orgFirewallPolicyAdmin" } - sa_net_xpn_admin = { + sa_net_rw_ngfw_enterprise_admin = { + member = module.net-sa-rw[0].iam_email + role = var.custom_roles["ngfw_enterprise_admin"], + } + sa_net_rw_xpn_admin = { member = module.net-sa-rw[0].iam_email role = "roles/compute.xpnAdmin" } - }, - # stage 2 network security - !var.fast_stage_2.network_security.enabled ? {} : { - sa_nsec_fw_policy_admin = { - member = module.nsec-sa-rw[0].iam_email - role = "roles/compute.orgFirewallPolicyAdmin" - } - sa_net_nsec_ngfw_enterprise_admin = { - member = module.nsec-sa-rw[0].iam_email - role = var.custom_roles["ngfw_enterprise_admin"], - } - sa_net_nsec_fw_policy_user = { - member = module.nsec-sa-rw[0].iam_email + sa_net_ro_fw_policy_user = { + member = module.net-sa-ro[0].iam_email role = "roles/compute.orgFirewallPolicyUser" } - sa_net_nsec_ro_ngfw_enterprise_viewer = { - member = module.nsec-sa-ro[0].iam_email + sa_net_net_ro_ngfw_enterprise_viewer = { + member = module.net-sa-ro[0].iam_email role = var.custom_roles["ngfw_enterprise_viewer"], } }, diff --git a/fast/stages/1-resman/main.tf b/fast/stages/1-resman/main.tf index fbff1284e..fee31c65a 100644 --- a/fast/stages/1-resman/main.tf +++ b/fast/stages/1-resman/main.tf @@ -15,11 +15,6 @@ */ locals { - # leaving this here to document how to get self identity in a stage - # automation_resman_sa = try( - # data.google_client_openid_userinfo.provider_identity[0].email, null - # ) - # tag values use descriptive names identity_providers = coalesce( try(var.automation.federated_identity_providers, null), {} ) @@ -35,6 +30,14 @@ locals { ? "organizations/${var.organization.id}" : var.root_node ) + # normalize parent stages + stage_addons = { + for k, v in var.fast_addon : k => merge(v, { + short_name = k + stage = regex("^(?P[0-9])-(?P.*?)$", v.parent_stage) + }) + } + # combined list of stage service accounts stage_service_accounts = merge( !var.fast_stage_2.networking.enabled ? {} : { networking = module.net-sa-rw[0].email @@ -72,6 +75,10 @@ locals { top_level_service_accounts = { for k, v in module.top-level-sa : k => try(v.email) } + # leaving this here to document how to get self identity in a stage + # automation_resman_sa = try( + # data.google_client_openid_userinfo.provider_identity[0].email, null + # ) } # data "google_client_openid_userinfo" "provider_identity" { diff --git a/fast/stages/1-resman/outputs-cicd.tf b/fast/stages/1-resman/outputs-cicd.tf new file mode 100644 index 000000000..4ee38e6ad --- /dev/null +++ b/fast/stages/1-resman/outputs-cicd.tf @@ -0,0 +1,60 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Locals for CI/CD workflow files. + +locals { + # render CI/CD workflow templates + cicd_workflows = { + for k, v in local.cicd_repositories : "${v.level}-${replace(k, "_", "-")}" => templatefile( + "${path.module}/templates/workflow-${v.repository.type}.yaml", { + # If users give a list of custom audiences we set by default the first element. + # If no audiences are given, we set https://iam.googleapis.com/{PROVIDER_NAME} + audiences = try( + local.cicd_workflow_providers[v.identity_provider].audiences, [] + ) + identity_provider = try( + local.cicd_workflow_providers[v.identity_provider].name, "" + ) + outputs_bucket = var.automation.outputs_bucket + service_accounts = { + apply = try(module.cicd-sa-rw[k].email, "") + plan = try(module.cicd-sa-ro[k].email, "") + } + stage_name = k + tf_providers_files = { + apply = local.cicd_workflow_providers[k] + plan = local.cicd_workflow_providers["${k}-r"] + } + tf_var_files = ( + v.level == 2 + ? [ + "0-bootstrap.auto.tfvars.json", + "0-globals.auto.tfvars.json", + "1-resman.auto.tfvars.json" + ] + : [ + "0-bootstrap.auto.tfvars.json", + "0-globals.auto.tfvars.json", + "1-resman.auto.tfvars.json", + "2-networking.auto.tfvars.json", + "2-security.auto.tfvars.json" + ] + ) + } + ) + } +} diff --git a/fast/stages/1-resman/outputs-files.tf b/fast/stages/1-resman/outputs-files.tf index 166b8558a..b268263de 100644 --- a/fast/stages/1-resman/outputs-files.tf +++ b/fast/stages/1-resman/outputs-files.tf @@ -17,152 +17,8 @@ # tfdoc:file:description Output files persistence to local filesystem. locals { - # output file definitions for enabled stage 2s - _stage2_outputs_attrs = merge( - var.fast_stage_2["networking"].enabled != true ? {} : { - networking = { - bucket = module.net-bucket[0].name - sa = { - apply = module.net-sa-rw[0].email - plan = module.net-sa-ro[0].email - } - } - }, - var.fast_stage_2["network_security"].enabled != true ? {} : { - network_security = { - bucket = module.nsec-bucket[0].name - sa = { - apply = module.nsec-sa-rw[0].email - plan = module.nsec-sa-ro[0].email - } - } - }, - var.fast_stage_2["project_factory"].enabled != true ? {} : { - project_factory = { - bucket = module.pf-bucket[0].name - sa = { - apply = module.pf-sa-rw[0].email - plan = module.pf-sa-ro[0].email - } - } - }, - var.fast_stage_2["security"].enabled != true ? {} : { - security = { - bucket = module.sec-bucket[0].name - sa = { - apply = module.sec-sa-rw[0].email - plan = module.sec-sa-ro[0].email - } - } - } - ) - # CI/CD workflow definitions for enabled stages - _cicd_workflow_attrs = merge( - # stage 2s - { - for k, v in local._stage2_outputs_attrs : k => { - audiences = try( - local.identity_providers[local.cicd_repositories[k].identity_provider].audiences, null - ) - identity_provider = try( - local.identity_providers[local.cicd_repositories[k].identity_provider].name, null - ) - outputs_bucket = var.automation.outputs_bucket - service_accounts = { - apply = try(module.cicd-sa-rw[k].email, "") - plan = try(module.cicd-sa-ro[k].email, "") - } - repository = local.cicd_repositories[k].repository - stage_name = k - tf_providers_files = { - apply = "2-${replace(k, "_", "-")}-providers.tf" - plan = "2-${replace(k, "_", "-")}-r-providers.tf" - } - tf_var_files = local.cicd_workflow_files.stage_2 - } if lookup(local.cicd_repositories, k, null) != null - }, - # stage 3 - { - for k, v in local.cicd_repositories : "${v.lvl}-${k}" => { - audiences = try( - local.identity_providers[v.identity_provider].audiences, null - ) - identity_provider = try( - local.identity_providers[v.identity_provider].name, null - ) - outputs_bucket = var.automation.outputs_bucket - repository = v.repository - service_accounts = { - apply = module.cicd-sa-rw[0].email - plan = module.cicd-sa-ro[0].email - } - stage_name = v.short_name - tf_providers_files = { - apply = "${v.lvl}-${k}-providers.tf" - plan = "${v.lvl}-${k}-r-providers.tf" - } - tf_var_files = local.cicd_workflow_files.stage_3 - } if v.lvl == 3 - } - ) - _tpl_providers = "${path.module}/templates/providers.tf.tpl" - cicd_workflows = { - for k, v in local._cicd_workflow_attrs : k => templatefile( - "${path.module}/templates/workflow-${v.repository.type}.yaml", v - ) - } + _tpl_providers = "${path.module}/templates/providers.tf.tpl" outputs_location = try(pathexpand(var.outputs_location), "") - # render provider files from template - providers = merge( - # stage 2 - { - for k, v in local._stage2_outputs_attrs : - "2-${replace(k, "_", "-")}" => templatefile(local._tpl_providers, { - backend_extra = null - bucket = v.bucket - name = "networking" - sa = v.sa.apply - }) - }, - { - for k, v in local._stage2_outputs_attrs : - "2-${replace(k, "_", "-")}-r" => templatefile(local._tpl_providers, { - backend_extra = null - bucket = v.bucket - name = "networking" - sa = v.sa.plan - }) - }, - # stage 3 - { - for k, v in local.stage3 : - "3-${k}" => templatefile(local._tpl_providers, { - backend_extra = null - bucket = module.stage3-bucket[k].name - name = k - sa = module.stage3-sa-rw[k].email - }) - }, - { - for k, v in local.stage3 : - "3-${k}-r" => templatefile(local._tpl_providers, { - backend_extra = null - bucket = module.stage3-bucket[k].name - name = k - sa = module.stage3-sa-ro[k].email - }) - }, - # top-level folders - { - for k, v in module.top-level-sa : - "1-resman-folder-${k}" => templatefile(local._tpl_providers, { - backend_extra = null - bucket = module.top-level-bucket[k].name - name = k - sa = v.email - }) - }, - ) } resource "local_file" "providers" { diff --git a/fast/stages/1-resman/outputs-providers.tf b/fast/stages/1-resman/outputs-providers.tf new file mode 100644 index 000000000..8ed7b6c58 --- /dev/null +++ b/fast/stages/1-resman/outputs-providers.tf @@ -0,0 +1,133 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Locals for provider output files. + +locals { + # output file definitions for enabled stage 2s + _stage2_outputs_attrs = merge( + var.fast_stage_2["networking"].enabled != true ? {} : { + networking = { + bucket = module.net-bucket[0].name + sa = { + apply = module.net-sa-rw[0].email + plan = module.net-sa-ro[0].email + } + } + }, + var.fast_stage_2["project_factory"].enabled != true ? {} : { + project_factory = { + bucket = module.pf-bucket[0].name + sa = { + apply = module.pf-sa-rw[0].email + plan = module.pf-sa-ro[0].email + } + } + }, + var.fast_stage_2["security"].enabled != true ? {} : { + security = { + bucket = module.sec-bucket[0].name + sa = { + apply = module.sec-sa-rw[0].email + plan = module.sec-sa-ro[0].email + } + } + }, + # TODO: use ${parent_stage}-${key} for the addon file output names + # addons, conditions are repeated to prevent inconsistent result type error + var.fast_stage_2["networking"].enabled != true ? {} : { + for k, v in local.stage_addons : "networking-${k}" => { + bucket = module.net-bucket[0].name + backend_extra = "prefix = \"addons/${k}\"" + sa = { + apply = module.net-sa-rw[0].email + plan = module.net-sa-ro[0].email + } + } if v.parent_stage == "2-networking" + }, + var.fast_stage_2["project_factory"].enabled != true ? {} : { + for k, v in local.stage_addons : "pf-${k}" => { + bucket = module.pf-bucket[0].name + backend_extra = "prefix = \"addons/${k}\"" + sa = { + apply = module.pf-sa-rw[0].email + plan = module.pf-sa-ro[0].email + } + } if v.parent_stage == "2-project-factory" + }, + var.fast_stage_2["security"].enabled != true ? {} : { + for k, v in local.stage_addons : "security-${k}" => { + bucket = module.sec-bucket[0].name + backend_extra = "prefix = \"addons/${k}\"" + sa = { + apply = module.sec-sa-rw[0].email + plan = module.sec-sa-ro[0].email + } + } if v.parent_stage == "2-security" + } + ) + # render provider files from template + providers = merge( + # stage 2 + { + for k, v in local._stage2_outputs_attrs : + "2-${replace(k, "_", "-")}" => templatefile(local._tpl_providers, { + backend_extra = lookup(v, "backend_extra", null) + bucket = v.bucket + name = k + sa = v.sa.apply + }) + }, + { + for k, v in local._stage2_outputs_attrs : + "2-${replace(k, "_", "-")}-r" => templatefile(local._tpl_providers, { + backend_extra = lookup(v, "backend_extra", null) + bucket = v.bucket + name = k + sa = v.sa.plan + }) + }, + # stage 3 + { + for k, v in local.stage3 : + "3-${k}" => templatefile(local._tpl_providers, { + backend_extra = null + bucket = module.stage3-bucket[k].name + name = k + sa = module.stage3-sa-rw[k].email + }) + }, + { + for k, v in local.stage3 : + "3-${k}-r" => templatefile(local._tpl_providers, { + backend_extra = null + bucket = module.stage3-bucket[k].name + name = k + sa = module.stage3-sa-ro[k].email + }) + }, + # top-level folders + { + for k, v in module.top-level-sa : + "1-resman-folder-${k}" => templatefile(local._tpl_providers, { + backend_extra = null + bucket = module.top-level-bucket[k].name + name = k + sa = v.email + }) + }, + ) +} diff --git a/fast/stages/1-resman/stage-2-network-security.tf b/fast/stages/1-resman/stage-2-network-security.tf deleted file mode 100644 index 627fe5008..000000000 --- a/fast/stages/1-resman/stage-2-network-security.tf +++ /dev/null @@ -1,79 +0,0 @@ -/** - * 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. - */ - -# automation service accounts - -module "nsec-sa-rw" { - source = "../../../modules/iam-service-account" - count = var.fast_stage_2.network_security.enabled ? 1 : 0 - project_id = var.automation.project_id - name = templatestring(var.resource_names["sa-nsec_rw"], { - name = var.fast_stage_2.network_security.short_name - }) - display_name = "Terraform resman network security main service account." - prefix = var.prefix - iam = { - "roles/iam.serviceAccountTokenCreator" = compact([ - try(module.cicd-sa-rw["network_security"].iam_email, null) - ]) - } - iam_project_roles = { - (var.automation.project_id) = ["roles/serviceusage.serviceUsageConsumer"] - } - iam_storage_roles = { - (var.automation.outputs_bucket) = ["roles/storage.objectAdmin"] - } -} - -module "nsec-sa-ro" { - source = "../../../modules/iam-service-account" - count = var.fast_stage_2.network_security.enabled ? 1 : 0 - project_id = var.automation.project_id - name = templatestring(var.resource_names["sa-nsec_ro"], { - name = var.fast_stage_2.network_security.short_name - }) - display_name = "Terraform resman network security main service account (read-only)." - prefix = var.prefix - iam = { - "roles/iam.serviceAccountTokenCreator" = compact([ - try(module.cicd-sa-ro["network_security"].iam_email, null) - ]) - } - iam_project_roles = { - (var.automation.project_id) = ["roles/serviceusage.serviceUsageConsumer"] - } - iam_storage_roles = { - (var.automation.outputs_bucket) = [var.custom_roles["storage_viewer"]] - } -} - -# automation bucket - -module "nsec-bucket" { - source = "../../../modules/gcs" - count = var.fast_stage_2.network_security.enabled ? 1 : 0 - project_id = var.automation.project_id - name = templatestring(var.resource_names["gcs-nsec"], { - name = var.fast_stage_2.network_security.short_name - }) - prefix = var.prefix - location = var.locations.gcs - versioning = true - iam = { - "roles/storage.objectAdmin" = [module.nsec-sa-rw[0].iam_email] - "roles/storage.objectViewer" = [module.nsec-sa-ro[0].iam_email] - } -} diff --git a/fast/stages/1-resman/stage-2-networking.tf b/fast/stages/1-resman/stage-2-networking.tf index 8ca86e8a2..a61dae41a 100644 --- a/fast/stages/1-resman/stage-2-networking.tf +++ b/fast/stages/1-resman/stage-2-networking.tf @@ -57,21 +57,6 @@ module "net-folder" { "roles/resourcemanager.folderViewer" = [module.net-sa-ro[0].iam_email] "roles/resourcemanager.tagViewer" = [module.net-sa-ro[0].iam_email] }, - # network security stage 2 service accounts - var.fast_stage_2.network_security.enabled != true ? {} : { - "roles/serviceusage.serviceUsageAdmin" = [ - module.nsec-sa-rw[0].iam_email - ] - (var.custom_roles["network_firewall_policies_admin"]) = [ - module.nsec-sa-rw[0].iam_email - ] - "roles/compute.orgFirewallPolicyUser" = [ - module.nsec-sa-ro[0].iam_email - ] - "roles/serviceusage.serviceUsageConsumer" = [ - module.nsec-sa-ro[0].iam_email - ] - }, # security stage 2 service accounts var.fast_stage_2.security.enabled != true ? {} : { "roles/serviceusage.serviceUsageAdmin" = [ @@ -169,9 +154,10 @@ module "net-sa-rw" { prefix = var.prefix service_account_create = var.root_node == null iam = { - "roles/iam.serviceAccountTokenCreator" = compact([ - try(module.cicd-sa-rw["networking"].iam_email, null) - ]) + "roles/iam.serviceAccountTokenCreator" = [ + for k, v in local.cicd_repositories : + module.cicd-sa-rw[k].iam_email if v.stage == "networking" + ] } iam_project_roles = { (var.automation.project_id) = ["roles/serviceusage.serviceUsageConsumer"] @@ -191,9 +177,10 @@ module "net-sa-ro" { display_name = "Terraform resman networking service account (read-only)." prefix = var.prefix iam = { - "roles/iam.serviceAccountTokenCreator" = compact([ - try(module.cicd-sa-ro["networking"].iam_email, null) - ]) + "roles/iam.serviceAccountTokenCreator" = [ + for k, v in local.cicd_repositories : + module.cicd-sa-ro[k].iam_email if v.stage == "networking" + ] } iam_project_roles = { (var.automation.project_id) = ["roles/serviceusage.serviceUsageConsumer"] diff --git a/fast/stages/1-resman/stage-2-project-factory.tf b/fast/stages/1-resman/stage-2-project-factory.tf index 8b40daf74..91500bdca 100644 --- a/fast/stages/1-resman/stage-2-project-factory.tf +++ b/fast/stages/1-resman/stage-2-project-factory.tf @@ -26,9 +26,10 @@ module "pf-sa-rw" { display_name = "Terraform resman project factory main service account." prefix = var.prefix iam = { - "roles/iam.serviceAccountTokenCreator" = compact([ - try(module.cicd-sa-rw["project_factory"].iam_email, null) - ]) + "roles/iam.serviceAccountTokenCreator" = [ + for k, v in local.cicd_repositories : + module.cicd-sa-rw[k].iam_email if v.stage == "project-factory" + ] } iam_project_roles = { (var.automation.project_id) = ["roles/serviceusage.serviceUsageConsumer"] @@ -48,9 +49,10 @@ module "pf-sa-ro" { display_name = "Terraform resman project factory main service account (read-only)." prefix = var.prefix iam = { - "roles/iam.serviceAccountTokenCreator" = compact([ - try(module.cicd-sa-ro["project_factory"].iam_email, null) - ]) + "roles/iam.serviceAccountTokenCreator" = [ + for k, v in local.cicd_repositories : + module.cicd-sa-ro[k].iam_email if v.stage == "project-factory" + ] } iam_project_roles = { (var.automation.project_id) = ["roles/serviceusage.serviceUsageConsumer"] diff --git a/fast/stages/1-resman/stage-2-security.tf b/fast/stages/1-resman/stage-2-security.tf index 88e1fa713..0c335ab2d 100644 --- a/fast/stages/1-resman/stage-2-security.tf +++ b/fast/stages/1-resman/stage-2-security.tf @@ -143,9 +143,10 @@ module "sec-sa-rw" { prefix = var.prefix service_account_create = var.root_node == null iam = { - "roles/iam.serviceAccountTokenCreator" = compact([ - try(module.cicd-sa-rw["security"].iam_email, null) - ]) + "roles/iam.serviceAccountTokenCreator" = [ + for k, v in local.cicd_repositories : + module.cicd-sa-rw[k].iam_email if v.stage == "security" + ] } iam_project_roles = { (var.automation.project_id) = ["roles/serviceusage.serviceUsageConsumer"] @@ -165,9 +166,10 @@ module "sec-sa-ro" { display_name = "Terraform resman security service account (read-only)." prefix = var.prefix iam = { - "roles/iam.serviceAccountTokenCreator" = compact([ - try(module.cicd-sa-ro["security"].iam_email, null) - ]) + "roles/iam.serviceAccountTokenCreator" = [ + for k, v in local.cicd_repositories : + module.cicd-sa-ro[k].iam_email if v.stage == "security" + ] } iam_project_roles = { (var.automation.project_id) = ["roles/serviceusage.serviceUsageConsumer"] diff --git a/fast/stages/1-resman/stage-cicd.tf b/fast/stages/1-resman/stage-cicd.tf index 1ed22407a..58de5f8d9 100644 --- a/fast/stages/1-resman/stage-cicd.tf +++ b/fast/stages/1-resman/stage-cicd.tf @@ -14,24 +14,36 @@ * limitations under the License. */ +# tfdoc:file:description CI/CD locals and resources. + locals { - # intermediate normalization of repository configurations _cicd_configs = merge( - # stage 2s + # stage 2 { - for k, v in var.fast_stage_2 : - k => merge(v.cicd_config, { - env = "prod", short_name = v.short_name, lvl = 2 - }) - if v.cicd_config != null + for k, v in var.fast_stage_2 : k => merge(v.cicd_config, { + env = "prod" + level = 2 + stage = replace(k, "_", "-") + short_name = v.short_name + }) if v.cicd_config != null }, - # stage 3s + # stage 3 { - for k, v in local.stage3 : - k => merge(v.cicd_config, { - env = v.environment, short_name = coalesce(v.short_name, k), lvl = 3 - }) - if v.cicd_config != null + for k, v in local.stage3 : k => merge(v.cicd_config, { + env = v.environment + level = 3 + short_name = coalesce(v.short_name, k) + stage = replace(k, "_", "-") + }) if v.cicd_config != null + }, + # addons + { + for k, v in var.fast_addon : k => merge(v.cicd_config, { + env = "prod" + level = 2 + short_name = k + stage = substr(v.parent_stage, 2, -1) + }) if v.cicd_config != null } ) # finalize configurations and filter by valid identity provider and type @@ -41,18 +53,16 @@ locals { fileexists("${path.module}/templates/workflow-${v.repository.type}.yaml") ) } - # lists of input files for each stage - cicd_workflow_files = { - stage_2 = [ - "0-bootstrap.auto.tfvars.json", - "1-resman.auto.tfvars.json", - "0-globals.auto.tfvars.json" - ] - stage_3 = [ - for k, v in local._cicd_configs : - "2-${k}.auto.tfvars" if v.lvl == 2 - ] - } + cicd_workflow_providers = merge( + { + for k, v in local.cicd_repositories : + k => "${v.level}-${k}-providers.tf" + }, + { + for k, v in local.cicd_repositories : + "${k}-r" => "${v.level}-${k}-r-providers.tf" + } + ) } module "cicd-sa-rw" { @@ -63,9 +73,9 @@ module "cicd-sa-rw" { name = each.value.short_name }) display_name = ( - "CI/CD ${each.value.lvl}-${each.value.short_name} ${each.value.env} service account." + "CI/CD ${each.value.level}-${each.value.short_name} ${each.value.env} service account." ) - prefix = "${var.prefix}-${each.value.env}" + prefix = "${var.prefix}-${var.environments[each.value.env].short_name}" iam = { "roles/iam.workloadIdentityUser" = [ each.value.repository.branch == null @@ -98,9 +108,9 @@ module "cicd-sa-ro" { name = each.value.short_name }) display_name = ( - "CI/CD ${each.value.lvl}-${each.value.short_name} ${each.value.env} service account (read-only)." + "CI/CD ${each.value.level}-${each.value.short_name} ${each.value.env} service account (read-only)." ) - prefix = "${var.prefix}-${each.value.env}" + prefix = "${var.prefix}-${var.environments[each.value.env].short_name}" iam = { "roles/iam.workloadIdentityUser" = [ format( diff --git a/fast/stages/1-resman/variables-addons.tf b/fast/stages/1-resman/variables-addons.tf new file mode 100644 index 000000000..1c521a386 --- /dev/null +++ b/fast/stages/1-resman/variables-addons.tf @@ -0,0 +1,51 @@ +/** + * Copyright 2025 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. + */ + +variable "fast_addon" { + description = "FAST addons configurations for stages 2. Keys are used as short names for the add-on resources." + type = map(object({ + parent_stage = string + cicd_config = optional(object({ + identity_provider = string + repository = object({ + name = string + branch = optional(string) + type = optional(string, "github") + }) + })) + })) + nullable = false + default = {} + validation { + condition = alltrue([ + for k, v in var.fast_addon : contains( + ["2-networking", "2-project-factory", "2-security"], + v.parent_stage + ) + ]) + error_message = "Resman-defined addons only support '2-networking', '2-project-factory' and '2-security' stages." + } + validation { + condition = alltrue([ + for k, v in var.fast_addon : + v.cicd_config == null || contains( + ["github", "gitlab"], + coalesce(try(v.cicd_config.repository.type, null), "-") + ) + ]) + error_message = "Invalid CI/CD repository type." + } +} diff --git a/fast/stages/1-resman/variables-fast.tf b/fast/stages/1-resman/variables-fast.tf index a35f5eca4..439139b84 100644 --- a/fast/stages/1-resman/variables-fast.tf +++ b/fast/stages/1-resman/variables-fast.tf @@ -33,6 +33,7 @@ variable "automation" { principal_repo = string })) service_accounts = object({ + resman = string resman-r = string }) }) @@ -73,6 +74,7 @@ variable "environments" { description = "Environment names." type = map(object({ name = string + short_name = string tag_name = string is_default = optional(bool, false) })) diff --git a/fast/stages/1-resman/variables-stages.tf b/fast/stages/1-resman/variables-stages.tf index c46bdb977..cecb50551 100644 --- a/fast/stages/1-resman/variables-stages.tf +++ b/fast/stages/1-resman/variables-stages.tf @@ -35,18 +35,6 @@ variable "fast_stage_2" { parent_id = optional(string) }), {}) }), {}) - network_security = optional(object({ - enabled = optional(bool, false) - short_name = optional(string, "nsec") - cicd_config = optional(object({ - identity_provider = string - repository = object({ - name = string - branch = optional(string) - type = optional(string, "github") - }) - })) - }), {}) project_factory = optional(object({ enabled = optional(bool, true) short_name = optional(string, "pf") diff --git a/fast/stages/1-tenant-factory/templates/workflow-github.yaml b/fast/stages/1-tenant-factory/templates/workflow-github.yaml deleted file mode 100644 index d8185e2c7..000000000 --- a/fast/stages/1-tenant-factory/templates/workflow-github.yaml +++ /dev/null @@ -1,229 +0,0 @@ -# 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. - -name: "FAST ${stage_name} stage" - -on: - pull_request: - branches: - - main - types: - - closed - - opened - - synchronize - -env: - FAST_SERVICE_ACCOUNT: ${service_accounts.apply} - FAST_SERVICE_ACCOUNT_PLAN: ${service_accounts.plan} - FAST_WIF_PROVIDER: ${identity_provider} - SSH_AUTH_SOCK: /tmp/ssh_agent.sock - TF_PROVIDERS_FILE: ${tf_providers_files.apply} - TF_PROVIDERS_FILE_PLAN: ${tf_providers_files.plan} - TF_VERSION: 1.7.4 - -jobs: - fast-pr: - # Skip PRs which are closed without being merged. - if: >- - github.event.action == 'closed' && - github.event.pull_request.merged == true || - github.event.action == 'opened' || - github.event.action == 'synchronize' - permissions: - contents: read - id-token: write - issues: write - pull-requests: write - runs-on: ubuntu-latest - steps: - - id: checkout - name: Checkout repository - uses: actions/checkout@v4 - - # set up SSH key authentication to the modules repository - - - id: ssh-config - name: Configure SSH authentication - run: | - ssh-agent -a "$SSH_AUTH_SOCK" > /dev/null - ssh-add - <<< "$${{ secrets.CICD_MODULES_KEY }}" - - # set up step variables for plan / apply - - - id: vars-plan - if: github.event.pull_request.merged != true && success() - name: Set up plan variables - run: | - echo "plan_opts=-lock=false" >> "$GITHUB_ENV" - echo "provider_file=$${{env.TF_PROVIDERS_FILE_PLAN}}" >> "$GITHUB_ENV" - echo "service_account=$${{env.FAST_SERVICE_ACCOUNT_PLAN}}" >> "$GITHUB_ENV" - - - id: vars-apply - if: github.event.pull_request.merged == true && success() - name: Set up apply variables - run: | - echo "provider_file=$${{env.TF_PROVIDERS_FILE}}" >> "$GITHUB_ENV" - echo "service_account=$${{env.FAST_SERVICE_ACCOUNT}}" >> "$GITHUB_ENV" - - # set up authentication via Workload identity Federation and gcloud - - - id: gcp-auth - name: Authenticate to Google Cloud - uses: google-github-actions/auth@v2 - with: - workload_identity_provider: $${{env.FAST_WIF_PROVIDER}} - service_account: $${{env.service_account}} - access_token_lifetime: 900s - - - id: gcp-sdk - name: Set up Cloud SDK - uses: google-github-actions/setup-gcloud@v2 - with: - install_components: alpha - - # copy provider file - - - id: tf-config-provider - name: Copy Terraform provider file - run: | - gcloud storage cp -r \ - "gs://${outputs_bucket}/providers/$${{env.provider_file}}" ./ - %{~ for f in tf_var_files ~} - gcloud storage cp -r \ - "gs://${outputs_bucket}/tfvars/${f}" ./ - %{~ endfor ~} - - - id: tf-setup - name: Set up Terraform - uses: hashicorp/setup-terraform@v3 - with: - terraform_version: $${{env.TF_VERSION}} - - # run Terraform init/validate/plan - - - id: tf-init - name: Terraform init - continue-on-error: true - run: | - terraform init -no-color - - - id: tf-validate - continue-on-error: true - name: Terraform validate - run: terraform validate -no-color - - - id: tf-plan - name: Terraform plan - continue-on-error: true - run: | - terraform plan -input=false -out ../plan.out -no-color $${{env.plan_opts}} - - - id: tf-apply - if: github.event.pull_request.merged == true && success() - name: Terraform apply - continue-on-error: true - run: | - terraform apply -input=false -auto-approve -no-color ../plan.out - - # PR comment with Terraform result from previous steps - # length is checked and trimmed for length so as to stay within the limit - - - id: pr-comment - name: Post comment to Pull Request - continue-on-error: true - uses: actions/github-script@v7 - if: github.event_name == 'pull_request' - env: - PLAN: $${{steps.tf-plan.outputs.stdout}}\n$${{steps.tf-plan.outputs.stderr}} - with: - script: | - const output = `### Terraform Initialization \`$${{steps.tf-init.outcome}}\` - - ### Terraform Validation \`$${{steps.tf-validate.outcome}}\` - -
Validation Output - - \`\`\`\n - $${{steps.tf-validate.outputs.stdout}} - \`\`\` - -
- - ### Terraform Plan \`$${{steps.tf-plan.outcome}}\` - -
Show Plan - - \`\`\`\n - $${process.env.PLAN.split('\n').filter(l => l.match(/^([A-Z\s].*|)$$/)).join('\n')} - \`\`\` - -
- - ### Terraform Apply \`$${{steps.tf-apply.outcome}}\` - - *Pusher: @$${{github.actor}}, Action: \`$${{github.event_name}}\`, Working Directory: \`$${{env.tf_actions_working_dir}}\`, Workflow: \`$${{github.workflow}}\`*`; - - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: output - }) - - - id: pr-short-comment - name: Post comment to Pull Request (abbreviated) - uses: actions/github-script@v7 - if: github.event_name == 'pull_request' && steps.pr-comment.outcome != 'success' - with: - script: | - const output = `### Terraform Initialization \`$${{steps.tf-init.outcome}}\` - - ### Terraform Validation \`$${{steps.tf-validate.outcome}}\` - - ### Terraform Plan \`$${{steps.tf-plan.outcome}}\` - - Plan output is in the action log. - - ### Terraform Apply \`$${{steps.tf-apply.outcome}}\` - - *Pusher: @$${{github.actor}}, Action: \`$${{github.event_name}}\`, Working Directory: \`$${{env.tf_actions_working_dir}}\`, Workflow: \`$${{github.workflow}}\`*`; - - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: output - }) - - # exit on error from previous steps - - - id: check-init - name: Check init failure - if: steps.tf-init.outcome != 'success' - run: exit 1 - - - id: check-validate - name: Check validate failure - if: steps.tf-validate.outcome != 'success' - run: exit 1 - - - id: check-plan - name: Check plan failure - if: steps.tf-plan.outcome != 'success' - run: exit 1 - - - id: check-apply - name: Check apply failure - if: github.event.pull_request.merged == true && steps.tf-apply.outcome != 'success' - run: exit 1 diff --git a/fast/stages/1-tenant-factory/templates/workflow-gitlab.yaml b/fast/stages/1-tenant-factory/templates/workflow-gitlab.yaml deleted file mode 100644 index 150340835..000000000 --- a/fast/stages/1-tenant-factory/templates/workflow-gitlab.yaml +++ /dev/null @@ -1,106 +0,0 @@ -# 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. - -variables: - GOOGLE_CREDENTIALS: cicd-sa-credentials.json - FAST_OUTPUTS_BUCKET: ${outputs_bucket} - FAST_WIF_PROVIDER: ${identity_provider} - SSH_AUTH_SOCK: /tmp/ssh_agent.sock - %{~ if tf_var_files != [] ~} - TF_VAR_FILES: ${join("\n ", tf_var_files)} - %{~ endif ~} - -workflow: - rules: - # merge / apply - - if: $CI_PIPELINE_SOURCE == 'push' && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH - variables: - COMMAND: apply - FAST_SERVICE_ACCOUNT: ${service_accounts.apply} - TF_PROVIDERS_FILE: ${tf_providers_files.apply} - # pr / plan - - if: $CI_PIPELINE_SOURCE == 'merge_request_event' - variables: - COMMAND: plan - FAST_SERVICE_ACCOUNT: ${service_accounts.plan} - TF_PROVIDERS_FILE: ${tf_providers_files.plan} - -stages: - - gcp-setup - - tf-plan-apply - -# TODO: document project-level deploy key used to fetch modules - -gcp-setup: - stage: gcp-setup - image: - name: google/cloud-sdk:slim - artifacts: - paths: - - cicd-sa-credentials.json - - providers.tf - %{~ for f in tf_var_files ~} - - ${f} - %{~ endfor ~} - id_tokens: - GITLAB_TOKEN: - aud: - %{~ for aud in audiences ~} - - ${aud} - %{~ endfor ~} - before_script: - - echo "$GITLAB_TOKEN" > token.txt - script: - - | - gcloud iam workload-identity-pools create-cred-config \ - $FAST_WIF_PROVIDER \ - --service-account=$FAST_SERVICE_ACCOUNT \ - --service-account-token-lifetime-seconds=900 \ - --output-file=$GOOGLE_CREDENTIALS \ - --credential-source-file=token.txt - - gcloud config set auth/credential_file_override $GOOGLE_CREDENTIALS - - gcloud storage cp -r "gs://$FAST_OUTPUTS_BUCKET/providers/$TF_PROVIDERS_FILE" ./providers.tf - %{~ for f in tf_var_files ~} - - gcloud storage cp gs://$FAST_OUTPUTS_BUCKET/tfvars/${f} ./ - %{~ endfor ~} - - -tf-plan-apply: - stage: tf-plan-apply - dependencies: - - gcp-setup - id_tokens: - GITLAB_TOKEN: - aud: - %{~ for aud in audiences ~} - - ${aud} - %{~ endfor ~} - image: - name: hashicorp/terraform - entrypoint: - - "/usr/bin/env" - variables: - SSH_AUTH_SOCK: /tmp/ssh-agent.sock - script: - - | - ssh-agent -a $SSH_AUTH_SOCK - echo "$CICD_MODULES_KEY" | ssh-add - - mkdir -p ~/.ssh - ssh-keyscan -H 'gitlab.com' >> ~/.ssh/known_hosts - ssh-keyscan gitlab.com | sort -u - ~/.ssh/known_hosts -o ~/.ssh/known_hosts - - echo "$GITLAB_TOKEN" > token.txt - - terraform init - - terraform validate - - "if [ $COMMAND == 'plan' ]; then terraform plan -input=false -no-color -lock=false; fi" - - "if [ $COMMAND == 'apply' ]; then terraform apply -input=false -no-color -auto-approve; fi" diff --git a/fast/stages/2-network-security/.fast-stage.env b/fast/stages/2-network-security/.fast-stage.env deleted file mode 100644 index 6c51deaae..000000000 --- a/fast/stages/2-network-security/.fast-stage.env +++ /dev/null @@ -1,5 +0,0 @@ -FAST_STAGE_DESCRIPTION="network securoty (optional)" -FAST_STAGE_LEVEL=2 -FAST_STAGE_NAME=network-security -FAST_STAGE_DEPS="0-globals 0-bootstrap 1-resman" -FAST_STAGE_OPTIONAL="2-networking 2-security" \ No newline at end of file diff --git a/fast/stages/2-network-security/README.md b/fast/stages/2-network-security/README.md deleted file mode 100644 index ebad67615..000000000 --- a/fast/stages/2-network-security/README.md +++ /dev/null @@ -1,199 +0,0 @@ -# Network Security - -This stage enables NGFW Enterprise in the dev `dev` and `prod` VPCs. This includes: - -- security profiles -- security profile groups -- NGFW endpoints -- NGFW endpoint associations -- global network firewall policies and some recommended firewall policy rules - -The following diagram is a high level reference of the resources created and managed here (excludes projects and VPCs): - -

- Network security NGFW diagram -

- - -- [Design overview and choices](#design-overview-and-choices) -- [How to run this stage](#how-to-run-this-stage) - - [Provider and Terraform variables](#provider-and-terraform-variables) - - [Impersonating the automation service account](#impersonating-the-automation-service-account) - - [Variable configuration](#variable-configuration) - - [Running the stage](#running-the-stage) -- [Customizations](#customizations) - - [Firewall policy rules factories](#firewall-policy-rules-factories) - - [NGFW Enterprise configuration](#ngfw-enterprise-configuration) -- [Files](#files) -- [Variables](#variables) -- [Outputs](#outputs) - - -## Design overview and choices - -- We create one security profile (and security profile group) per environment in the spoke VPCs only. That's usually where inspection is needed, as it's where workloads run. -- By default, we create NGFW Enterprise endpoints in three zones in the default, primary region (europe-west1). You can adapt this, depending on where your workloads run, using the dedicated variable. -- We install default firewall policy rules in each spoke, so that we allow and inspect all traffic going to the Internet and we allow egress towards RFC-1918 addresses. In ingress, you'll need to add your own rules. We provided some examples that need to be adapted to your topology (number of regions, subnets). -- We use global network firewall policies, as legacy VPC firewall rules are not compatible with NGFW Enterprise. These policies coexist with the legacy VPC firewall rules that we create in the netwroking stage. -- For your convenience, firewall policy rules leverage factories, so that you can define firewall policy rules using yaml files. The path of these files is configurable. Look in the [Customization](#customizations) section for more details. -- NGFW Enterprise endpoints are org-level resources that need to reference a quota project for billing purposes. By default, we create a dedicated `xxx-net-ngfw-0` quota project. Anyway, you can choose to leverage an existing project. Look in the [Customization](#customizations) section for more details. -- Firewall endpoint associations in this stage can reference TLS inspection policies created in the [2-security stage](../2-security/README.md). More info in the customization section of this document. -- While TLS inspection policies are created in the [2-security stage](../2-security/README.md), FAST still allows the service accounts of this stage and the `gcp-network-admins` group to create and manage them anywhere in the organization. - -## How to run this stage - -This stage is meant to be executed after any [networking](../2-networking-a-simple) stage has run and it leverages dedicated automation service accounts and a bucket created in the [resman](../1-resman) stage. - -It's to run this stage in isolation, but that's outside the scope of this document, and you would need to refer to the code for the bootstrap and resman stages for the roles needed. - -Before running this stage, you need to make sure you have the correct credentials and permissions, and localize variables by assigning values that match your configuration. - -### Provider and Terraform variables - -As all other FAST stages, the [mechanism used to pass variable values and pre-built provider files from one stage to the next](../0-bootstrap/README.md#output-files-and-cross-stage-variables) is also leveraged here. - -The commands to link or copy the provider and terraform variable files can be easily derived from the `fast-links.sh` script in the FAST stages folder, passing it a single argument with the local output files folder (if configured) or the GCS output bucket in the automation project (derived from stage 0 outputs). The following examples demonstrate both cases, and the resulting commands that then need to be copy/pasted and run. - -```bash -../fast-links.sh ~/fast-config - -# File linking commands for network securoty (optional) stage - -# provider file -ln -s ~/fast-config/fast-test-00/providers/2-network-security-providers.tf ./ - -# input files from other stages -ln -s ~/fast-config/fast-test-00/tfvars/0-globals.auto.tfvars.json ./ -ln -s ~/fast-config/fast-test-00/tfvars/0-bootstrap.auto.tfvars.json ./ -ln -s ~/fast-config/fast-test-00/tfvars/1-resman.auto.tfvars.json ./ - -# conventional place for stage tfvars (manually created) -ln -s ~/fast-config/fast-test-00/2-network-security.auto.tfvars ./ - -# optional files -ln -s ~/fast-config/fast-test-00/2-networking.auto.tfvars.json ./ -ln -s ~/fast-config/fast-test-00/2-security.auto.tfvars.json ./ -``` - -```bash -../fast-links.sh gs://xxx-prod-iac-core-outputs-0 - -# File linking commands for network securoty (optional) stage - -# provider file -gcloud storage cp gs://xxx-prod-iac-core-outputs-0/providers/2-network-security-providers.tf ./ - -# input files from other stages -gcloud storage cp gs://xxx-prod-iac-core-outputs-0/tfvars/0-globals.auto.tfvars.json ./ -gcloud storage cp gs://xxx-prod-iac-core-outputs-0/tfvars/0-bootstrap.auto.tfvars.json ./ -gcloud storage cp gs://xxx-prod-iac-core-outputs-0/tfvars/1-resman.auto.tfvars.json ./ - -# conventional place for stage tfvars (manually created) -gcloud storage cp gs://xxx-prod-iac-core-outputs-0/2-network-security.auto.tfvars ./ - -# optional files -gcloud storage cp gs://xxx-prod-iac-core-outputs-0/2-networking.auto.tfvars.json ./ -gcloud storage cp gs://xxx-prod-iac-core-outputs-0/2-security.auto.tfvars.json ./ -``` - -### Impersonating the automation service account - -The preconfigured provider file uses impersonation to run with this stage's automation service account's credentials. The `gcp-devops` and `organization-admins` groups have the necessary IAM bindings in place to do that, so make sure the current user is a member of one of those groups. - -### Variable configuration - -Variables in this stage -- like most other FAST stages -- are broadly divided into three separate sets: - -- variables which refer to global values for the whole organization (org id, billing account id, prefix, etc.), which are pre-populated via the `0-globals.auto.tfvars.json` file linked or copied above -- variables which refer to resources managed by previous stages, which are prepopulated here via the `0-bootstrap.auto.tfvars.json`, `1-resman.auto.tfvars.json` and `2-networking.auto.tfvars.json` files linked or copied above -- and finally variables that optionally control this stage's behaviour and customizations, and can to be set in a custom `terraform.tfvars` file - -The latter set is explained in the [Customization](#customizations) sections below, and the full list can be found in the [Variables](#variables) table at the bottom of this document. - -Note that the `outputs_location` variable is disabled by default, you need to explicitly set it in your `terraform.tfvars` file if you want output files to be generated by this stage. This is a sample `terraform.tfvars` that configures it, refer to the [bootstrap stage documentation](../0-bootstrap/README.md#output-files-and-cross-stage-variables) for more details: - -```tfvars -outputs_location = "~/fast-config" -``` - -### Running the stage - -Once provider and variable values are in place and the correct user is configured, the stage can be run: - -```bash -terraform init -terraform apply -``` - -## Customizations - -You can optionally customize a few options adding a `terraform.tfvars` file to this stage. - -### Firewall policy rules factories - -By default, firewall policy rules yaml files are contained in the `data` folder within this module. Anyway, you can customize this location. - -### NGFW Enterprise configuration - -You can decide the zones where to deploy the NGFW Enterprise endpoints. These are set by default to `europe-west1-b`, `europe-west1-c` and `europe-west1-d`. - -```tfvars -ngfw_enterprise_config = { - endpoint_zones = [ - "us-east4-a", - "us-east4-b", - "australia-southeast1-b", - "australia-southeast1-c" - ] -} -``` - -Instead of creating a dedicated NGFW Enterprise billing/quota project, you can choose to leverage an existing project. These can even be one of your existing networking projects. -You'll need to make sure your network security service account can activate the `networksecurity.googleapis.com` on that project (for example, assigning the `roles/serviceusage.serviceUsageAdmin` role). - -```tfvars -ngfw_enterprise_config = { - quota_project_id = "your-quota-project-id" -} -``` - -You can optionally enable TLS inspection in stage [2-security](../2-security/README.md). -Ingesting outputs from [stage 2-security](../2-security/README.md), this stage will configure TLS inspection in NGFW Enterprise and will reference the CAs and the trust-configs you created in [stage 2-security](../2-security/README.md). -Make sure the CAs and the trusted configs created for NGFW Enterprise in the [2-security stage](../2-security/README.md) match the region where you defined your zonal firewall endpoints. - - - -## Files - -| name | description | modules | resources | -|---|---|---|---| -| [main.tf](./main.tf) | Next-Generation Firewall Enterprise configuration. | project | google_network_security_firewall_endpoint | -| [net-dev.tf](./net-dev.tf) | Security components for dev spoke VPC. | net-firewall-policy | google_network_security_firewall_endpoint_association · google_network_security_security_profile · google_network_security_security_profile_group | -| [net-prod.tf](./net-prod.tf) | Security components for prod spoke VPC. | net-firewall-policy | google_network_security_firewall_endpoint_association · google_network_security_security_profile · google_network_security_security_profile_group | -| [outputs.tf](./outputs.tf) | Module outputs. | | google_storage_bucket_object · local_file | -| [variables-fast.tf](./variables-fast.tf) | FAST stage interface. | | | -| [variables.tf](./variables.tf) | Module variables. | | | - -## Variables - -| name | description | type | required | default | producer | -|---|---|:---:|:---:|:---:|:---:| -| [automation](variables-fast.tf#L19) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | -| [billing_account](variables-fast.tf#L27) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | -| [folder_ids](variables-fast.tf#L40) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | -| [organization](variables-fast.tf#L82) | Organization details. | object({…}) | ✓ | | 00-globals | -| [prefix](variables-fast.tf#L92) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | -| [vpc_self_links](variables-fast.tf#L102) | Self link for the shared VPC. | object({…}) | ✓ | | 2-networking | -| [factories_config](variables.tf#L17) | Configuration for network resource factories. | object({…}) | | {…} | | -| [host_project_ids](variables-fast.tf#L51) | Host project for the shared VPC. | object({…}) | | {} | 2-networking | -| [ngfw_enterprise_config](variables.tf#L35) | NGFW Enterprise configuration. | object({…}) | | {…} | | -| [ngfw_tls_configs](variables-fast.tf#L62) | The NGFW Enterprise TLS configurations. | object({…}) | | {…} | 2-security | -| [outputs_location](variables.tf#L51) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | - -## Outputs - -| name | description | sensitive | consumers | -|---|---|:---:|---| -| [ngfw_enterprise_endpoint_ids](outputs.tf#L69) | The NGFW Enterprise endpoint ids. | | | -| [ngfw_enterprise_endpoints_quota_project](outputs.tf#L74) | The NGFW Enterprise endpoints quota project. | | | - diff --git a/fast/stages/2-network-security/data/cidrs.yaml b/fast/stages/2-network-security/data/cidrs.yaml deleted file mode 100644 index 3591e95a0..000000000 --- a/fast/stages/2-network-security/data/cidrs.yaml +++ /dev/null @@ -1,18 +0,0 @@ -# skip boilerplate check ---- -# Terraform will be unable to decode this file if it does not contain valid YAML -# You can retain `---` (start of the document) to indicate an empty document. - -healthchecks: - - 35.191.0.0/16 - - 130.211.0.0/22 - - 209.85.152.0/22 - - 209.85.204.0/22 - -rfc1918: - - 10.0.0.0/8 - - 172.16.0.0/12 - - 192.168.0.0/16 - -onprem_probes: - - 10.255.255.254/32 diff --git a/fast/stages/2-network-security/data/firewall-policy-rules/dev/egress.yaml b/fast/stages/2-network-security/data/firewall-policy-rules/dev/egress.yaml deleted file mode 100644 index b193b872c..000000000 --- a/fast/stages/2-network-security/data/firewall-policy-rules/dev/egress.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# skip boilerplate check ---- -# start of document (---) avoids errors if the file only contains comments - -# yaml-language-server: $schema=../../../schemas/firewall-policy-rules.schema.json - -egress-allow-rfc1918: - description: "Allow all hosts to RFC-1918" - priority: 2147483546 - match: - destination_ranges: - - rfc1918 - action: allow - -egress-inspect-internet: - description: "Inspect egress traffic from all dev hosts to Internet" - priority: 2147483547 - match: - destination_ranges: - - "0.0.0.0/0" - action: "apply_security_profile_group" - security_profile_group: "dev" - # Uncomment the line below to enable TLS inspection for this egress rule - # tls_inspect: true diff --git a/fast/stages/2-network-security/data/firewall-policy-rules/dev/ingress.yaml b/fast/stages/2-network-security/data/firewall-policy-rules/dev/ingress.yaml deleted file mode 100644 index 7feca48e6..000000000 --- a/fast/stages/2-network-security/data/firewall-policy-rules/dev/ingress.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# skip boilerplate check ---- - -# yaml-language-server: $schema=../../../schemas/firewall-policy-rules.schema.json - -# Sample NGFW Enterprise ingress rules to uncomment and customize as needed - -# ingress-allow-inspect-cross: -# description: "Allow and inspect cross-env traffic from prod." -# priority: 1 -# match: -# source_ranges: -# - prod (to be defined) -# action: "apply_security_profile_group" -# security_profile_group: "dev" -# tls_inspect: true - -# ingress-allow-inspect-intra: -# description: "Allow and inspect same-env (intra-vpc) traffic." -# priority: 2 -# match: -# source_ranges: -# - dev (to be defined) -# action: "apply_security_profile_group" -# security_profile_group: "dev" -# tls_inspect: true diff --git a/fast/stages/2-network-security/data/firewall-policy-rules/prod/egress.yaml b/fast/stages/2-network-security/data/firewall-policy-rules/prod/egress.yaml deleted file mode 100644 index 527428a19..000000000 --- a/fast/stages/2-network-security/data/firewall-policy-rules/prod/egress.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# skip boilerplate check ---- -egress-allow-rfc1918: - description: "Allow all hosts to RFC-1918" - priority: 2147483546 - match: - destination_ranges: - - rfc1918 - action: "allow" - -egress-inspect-internet: - description: "Inspect egress traffic from all prod hosts to Internet" - priority: 2147483547 - match: - destination_ranges: - - "0.0.0.0/0" - action: "apply_security_profile_group" - security_profile_group: "prod" - # Uncomment the line below to enable TLS inspection for this egress rule - # tls_inspect: true diff --git a/fast/stages/2-network-security/data/firewall-policy-rules/prod/ingress.yaml b/fast/stages/2-network-security/data/firewall-policy-rules/prod/ingress.yaml deleted file mode 100644 index 96f8d3ee3..000000000 --- a/fast/stages/2-network-security/data/firewall-policy-rules/prod/ingress.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# skip boilerplate check ---- -# yaml-language-server: $schema=../../../schemas/firewall-policy-rules.schema.json - -# Sample NGFW Enterprise ingress rules to uncomment and customize as needed - -# ingress-allow-inspect-cross: -# description: "Allow and inspect cross-env traffic." -# priority: 1 -# match: -# source_ranges: -# - dev (to be defined) -# action: "apply_security_profile_group" -# security_profile_group: "prod" -# tls_inspect: true - -# ingress-allow-inspect-intra: -# description: "Allow and inspect intra-VPC traffic." -# priority: 2 -# match: -# source_ranges: -# - prod (to be defined) -# action: "apply_security_profile_group" -# security_profile_group: "prod" -# tls_inspect: true diff --git a/fast/stages/2-network-security/diagram.png b/fast/stages/2-network-security/diagram.png deleted file mode 100644 index 9fb05873a..000000000 Binary files a/fast/stages/2-network-security/diagram.png and /dev/null differ diff --git a/fast/stages/2-network-security/diagram.svg b/fast/stages/2-network-security/diagram.svg deleted file mode 100644 index 83547ed34..000000000 --- a/fast/stages/2-network-security/diagram.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/fast/stages/2-network-security/main.tf b/fast/stages/2-network-security/main.tf deleted file mode 100644 index 946dba43f..000000000 --- a/fast/stages/2-network-security/main.tf +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -# tfdoc:file:description Next-Generation Firewall Enterprise configuration. - -locals { - create_quota_project = ( - var.ngfw_enterprise_config.quota_project_id == null - ? true - : false - ) - vpc_ids = { - for k, v in var.vpc_self_links - : k => replace(v, "https://www.googleapis.com/compute/v1/", "") - } -} - -# Dedicated quota project for ngfw enterprise endpoints -module "ngfw-quota-project" { - source = "../../../modules/project" - name = ( - local.create_quota_project - ? "net-ngfw-0" - : var.ngfw_enterprise_config.quota_project_id - ) - billing_account = ( - local.create_quota_project - ? var.billing_account.id - : null - ) - parent = ( - local.create_quota_project - ? var.folder_ids.networking - : null - ) - prefix = ( - local.create_quota_project - ? var.prefix - : null - ) - project_create = ( - local.create_quota_project - ? true - : false - ) - services = ["networksecurity.googleapis.com"] -} - -resource "google_network_security_firewall_endpoint" "firewall_endpoint" { - for_each = toset(var.ngfw_enterprise_config.endpoint_zones) - name = "${var.prefix}-ngfw-endpoint-${each.key}" - parent = "organizations/${var.organization.id}" - location = each.value - billing_project_id = module.ngfw-quota-project.id -} diff --git a/fast/stages/2-network-security/net-dev.tf b/fast/stages/2-network-security/net-dev.tf deleted file mode 100644 index b068a8f6c..000000000 --- a/fast/stages/2-network-security/net-dev.tf +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -# tfdoc:file:description Security components for dev spoke VPC. - -moved { - from = google_network_security_security_profile.dev_sec_profile - to = google_network_security_security_profile.dev -} - -resource "google_network_security_security_profile" "dev" { - name = "${var.prefix}-dev-sp-0" - type = "THREAT_PREVENTION" - parent = "organizations/${var.organization.id}" - location = "global" -} - -moved { - from = google_network_security_security_profile_group.dev_sec_profile_group - to = google_network_security_security_profile_group.dev -} - -resource "google_network_security_security_profile_group" "dev" { - name = "${var.prefix}-dev-spg-0" - parent = "organizations/${var.organization.id}" - location = "global" - description = "Dev security profile group." - threat_prevention_profile = try( - google_network_security_security_profile.dev.id, null - ) -} - -moved { - from = google_network_security_firewall_endpoint_association.dev_fw_ep_association - to = google_network_security_firewall_endpoint_association.dev -} - -resource "google_network_security_firewall_endpoint_association" "dev" { - for_each = toset(var.ngfw_enterprise_config.endpoint_zones) - name = "${var.prefix}-dev-epa-${each.key}" - parent = "projects/${try(var.host_project_ids.dev-spoke-0, null)}" - location = each.value - firewall_endpoint = ( - google_network_security_firewall_endpoint.firewall_endpoint[each.key].id - ) - network = try(local.vpc_ids.dev-spoke-0, null) - # If TLS inspection is enabled, link the regional TLS inspection policy - tls_inspection_policy = ( - var.ngfw_tls_configs.tls_enabled - # TODO: make this try less verbose and more readable - ? try( - var.ngfw_tls_configs.tls_ip_ids_by_region.dev[substr(each.value, 0, length(each.value) - 2)], - null - ) - : null - ) -} - -module "dev-spoke-firewall-policy" { - source = "../../../modules/net-firewall-policy" - name = "${var.prefix}-dev-fw-policy" - parent_id = try(var.host_project_ids.dev-spoke-0, null) - region = "global" - security_profile_group_ids = { - dev = local.security_profile_group_ids.dev - } - attachments = { - dev-spoke = try(var.vpc_self_links.dev-spoke-0, null) - } - factories_config = { - cidr_file_path = var.factories_config.cidrs - egress_rules_file_path = ( - "${var.factories_config.firewall_policy_rules.dev}/egress.yaml" - ) - ingress_rules_file_path = ( - "${var.factories_config.firewall_policy_rules.dev}/ingress.yaml" - ) - } -} diff --git a/fast/stages/2-network-security/net-prod.tf b/fast/stages/2-network-security/net-prod.tf deleted file mode 100644 index 74d18ce1b..000000000 --- a/fast/stages/2-network-security/net-prod.tf +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -# tfdoc:file:description Security components for prod spoke VPC. - -moved { - from = google_network_security_security_profile.prod_sec_profile - to = google_network_security_security_profile.prod -} - -resource "google_network_security_security_profile" "prod" { - name = "${var.prefix}-prod-sp-0" - type = "THREAT_PREVENTION" - parent = "organizations/${var.organization.id}" - location = "global" -} - -moved { - from = google_network_security_security_profile_group.prod_sec_profile_group - to = google_network_security_security_profile_group.prod -} - -resource "google_network_security_security_profile_group" "prod" { - name = "${var.prefix}-prod-spg-0" - parent = "organizations/${var.organization.id}" - location = "global" - description = "prod security profile group." - threat_prevention_profile = try( - google_network_security_security_profile.prod.id, null - ) -} - -moved { - from = google_network_security_firewall_endpoint_association.prod_fw_ep_association - to = google_network_security_firewall_endpoint_association.prod -} - -resource "google_network_security_firewall_endpoint_association" "prod" { - for_each = toset(var.ngfw_enterprise_config.endpoint_zones) - name = "${var.prefix}-prod-epa-${each.key}" - parent = "projects/${try(var.host_project_ids.prod-spoke-0, null)}" - location = each.value - firewall_endpoint = ( - google_network_security_firewall_endpoint.firewall_endpoint[each.key].id - ) - network = try(local.vpc_ids.prod-spoke-0, null) - # If TLS inspection is enabled, link the regional TLS inspection policy - tls_inspection_policy = ( - var.ngfw_tls_configs.tls_enabled - # TODO: make this try less verbose and more readable - ? try( - var.ngfw_tls_configs.tls_ip_ids_by_region.prod[substr(each.value, 0, length(each.value) - 2)], - null - ) - : null - ) -} - -module "prod-spoke-firewall-policy" { - source = "../../../modules/net-firewall-policy" - name = "${var.prefix}-prod-fw-policy" - parent_id = try(var.host_project_ids.prod-spoke-0, null) - region = "global" - security_profile_group_ids = { - prod = local.security_profile_group_ids.prod - } - attachments = { - prod-spoke = try(var.vpc_self_links.prod-spoke-0, null) - } - factories_config = { - cidr_file_path = var.factories_config.cidrs - egress_rules_file_path = ( - "${var.factories_config.firewall_policy_rules.prod}/egress.yaml" - ) - ingress_rules_file_path = ( - "${var.factories_config.firewall_policy_rules.prod}/ingress.yaml" - ) - } -} diff --git a/fast/stages/2-network-security/outputs.tf b/fast/stages/2-network-security/outputs.tf deleted file mode 100644 index acd5944e2..000000000 --- a/fast/stages/2-network-security/outputs.tf +++ /dev/null @@ -1,77 +0,0 @@ -/** - * 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 { - security_profile_group_ids = { - dev = format( - "//networksecurity.googleapis.com/%s", - try(google_network_security_security_profile_group.dev.id, "") - ) - prod = format( - "//networksecurity.googleapis.com/%s", - try(google_network_security_security_profile_group.prod.id, "") - ) - } - tfvars = { - association_ids = { - dev = { - for k, v in google_network_security_firewall_endpoint_association.dev : - k => v.id - } - prod = { - for k, v in google_network_security_firewall_endpoint_association.prod : - k => v.id - } - } - endpoint_ids = { - for _, v in google_network_security_firewall_endpoint.firewall_endpoint - : v.location => v.id - } - firewall_policy_ids = { - dev = module.dev-spoke-firewall-policy.id - prod = module.prod-spoke-firewall-policy.id - } - security_profile_group_ids = local.security_profile_group_ids - quota_project_id = module.ngfw-quota-project.id - } -} - -# generate tfvars file for subsequent stages - -resource "local_file" "tfvars" { - for_each = var.outputs_location == null ? {} : { 1 = 1 } - file_permission = "0644" - filename = "${try(pathexpand(var.outputs_location), "")}/tfvars/2-nsec.auto.tfvars.json" - content = jsonencode(local.tfvars) -} - -resource "google_storage_bucket_object" "tfvars" { - bucket = var.automation.outputs_bucket - name = "tfvars/2-nsec.auto.tfvars.json" - content = jsonencode(local.tfvars) -} - -# outputs - -output "ngfw_enterprise_endpoint_ids" { - description = "The NGFW Enterprise endpoint ids." - value = local.tfvars.endpoint_ids -} - -output "ngfw_enterprise_endpoints_quota_project" { - description = "The NGFW Enterprise endpoints quota project." - value = module.ngfw-quota-project.id -} diff --git a/fast/stages/2-network-security/schemas/firewall-policy-rules.schema.json b/fast/stages/2-network-security/schemas/firewall-policy-rules.schema.json deleted file mode 120000 index e37a764d2..000000000 --- a/fast/stages/2-network-security/schemas/firewall-policy-rules.schema.json +++ /dev/null @@ -1 +0,0 @@ -../../../../modules/net-firewall-policy/schemas/firewall-policy-rules.schema.json \ No newline at end of file diff --git a/fast/stages/2-network-security/variables-fast.tf b/fast/stages/2-network-security/variables-fast.tf deleted file mode 100644 index 45fb1c0fb..000000000 --- a/fast/stages/2-network-security/variables-fast.tf +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -# tfdoc:file:description FAST stage interface. - -variable "automation" { - # tfdoc:variable:source 0-bootstrap - description = "Automation resources created by the bootstrap stage." - type = object({ - outputs_bucket = string - }) -} - -variable "billing_account" { - # tfdoc:variable:source 0-bootstrap - description = "Billing account id. If billing account is not part of the same org set `is_org_level` to false." - type = object({ - id = string - is_org_level = optional(bool, true) - }) - validation { - condition = var.billing_account.is_org_level != null - error_message = "Invalid `null` value for `billing_account.is_org_level`." - } -} - -variable "folder_ids" { - # tfdoc:variable:source 1-resman - description = "Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created." - type = object({ - networking = string - networking-dev = string - networking-prod = string - }) - nullable = false -} - -variable "host_project_ids" { - # tfdoc:variable:source 2-networking - description = "Host project for the shared VPC." - type = object({ - dev-spoke-0 = optional(string) - prod-spoke-0 = optional(string) - }) - nullable = false - default = {} -} - -variable "ngfw_tls_configs" { - # tfdoc:variable:source 2-security - description = "The NGFW Enterprise TLS configurations." - type = object({ - tls_enabled = optional(bool, false) - tls_ip_ids_by_region = optional(object({ - dev = optional(map(string), {}) - prod = optional(map(string), {}) - })) - }) - nullable = false - default = { - tls_enabled = false - tls_ip_ids_by_region = { - dev = {} - prod = {} - } - } -} - -variable "organization" { - # tfdoc:variable:source 00-globals - description = "Organization details." - type = object({ - domain = string - id = number - customer_id = string - }) -} - -variable "prefix" { - # tfdoc:variable:source 0-bootstrap - description = "Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants." - type = string - validation { - condition = try(length(var.prefix), 0) < 12 - error_message = "Use a maximum of 9 chars for organizations, and 11 chars for tenants." - } -} - -variable "vpc_self_links" { - # tfdoc:variable:source 2-networking - description = "Self link for the shared VPC." - type = object({ - dev-spoke-0 = string - prod-spoke-0 = string - }) - nullable = false -} diff --git a/fast/stages/2-network-security/variables.tf b/fast/stages/2-network-security/variables.tf deleted file mode 100644 index 94f00b6fe..000000000 --- a/fast/stages/2-network-security/variables.tf +++ /dev/null @@ -1,55 +0,0 @@ -/** - * 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. - */ - -variable "factories_config" { - description = "Configuration for network resource factories." - type = object({ - cidrs = optional(string, "data/cidrs.yaml") - firewall_policy_rules = optional(object({ - dev = string - prod = string - })) - }) - nullable = false - default = { - firewall_policy_rules = { - dev = "data/firewall-policy-rules/dev" - prod = "data/firewall-policy-rules/prod" - } - } -} - -variable "ngfw_enterprise_config" { - description = "NGFW Enterprise configuration." - type = object({ - endpoint_zones = list(string) - quota_project_id = optional(string, null) - }) - nullable = false - default = { - endpoint_zones = [ - "europe-west1-b", - "europe-west1-c", - "europe-west1-d" - ] - } -} - -variable "outputs_location" { - description = "Path where providers and tfvars files for the following stages are written. Leave empty to disable." - type = string - default = null -} diff --git a/fast/stages/2-networking-a-simple/README.md b/fast/stages/2-networking-a-simple/README.md index 760272d98..415390d5f 100644 --- a/fast/stages/2-networking-a-simple/README.md +++ b/fast/stages/2-networking-a-simple/README.md @@ -523,11 +523,11 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS | name | description | sensitive | consumers | |---|---|:---:|---| -| [cloud_dns_inbound_policy](outputs.tf#L87) | IP Addresses for Cloud DNS inbound policy. | | | -| [host_project_ids](outputs.tf#L92) | Network project ids. | | | -| [host_project_numbers](outputs.tf#L97) | Network project numbers. | | | -| [ping_commands](outputs.tf#L102) | Ping commands for test instances to be run to check VPC reachability. | | | -| [shared_vpc_self_links](outputs.tf#L107) | Shared VPC host projects. | | | -| [tfvars](outputs.tf#L112) | Terraform variables file for the following stages. | ✓ | | -| [vpn_gateway_endpoints](outputs.tf#L118) | External IP Addresses for the GCP VPN gateways. | | | +| [cloud_dns_inbound_policy](outputs.tf#L88) | IP Addresses for Cloud DNS inbound policy. | | | +| [host_project_ids](outputs.tf#L93) | Network project ids. | | | +| [host_project_numbers](outputs.tf#L98) | Network project numbers. | | | +| [ping_commands](outputs.tf#L103) | Ping commands for test instances to be run to check VPC reachability. | | | +| [shared_vpc_self_links](outputs.tf#L108) | Shared VPC host projects. | | | +| [tfvars](outputs.tf#L113) | Terraform variables file for the following stages. | ✓ | | +| [vpn_gateway_endpoints](outputs.tf#L119) | External IP Addresses for the GCP VPN gateways. | | | diff --git a/fast/stages/2-networking-a-simple/outputs.tf b/fast/stages/2-networking-a-simple/outputs.tf index e38837c7f..092028f1d 100644 --- a/fast/stages/2-networking-a-simple/outputs.tf +++ b/fast/stages/2-networking-a-simple/outputs.tf @@ -55,6 +55,7 @@ locals { tfvars = { host_project_ids = local.host_project_ids host_project_numbers = local.host_project_numbers + regions = var.regions subnet_self_links = local.subnet_self_links subnet_proxy_only_self_links = local.subnet_proxy_only_self_links subnet_psc_self_links = local.subnet_psc_self_links diff --git a/fast/stages/2-networking-b-nva/README.md b/fast/stages/2-networking-b-nva/README.md index 36bddf8c0..db6db9716 100644 --- a/fast/stages/2-networking-b-nva/README.md +++ b/fast/stages/2-networking-b-nva/README.md @@ -586,10 +586,10 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS | name | description | sensitive | consumers | |---|---|:---:|---| -| [host_project_ids](outputs.tf#L106) | Network project ids. | | | -| [host_project_numbers](outputs.tf#L111) | Network project numbers. | | | -| [ping_commands](outputs.tf#L116) | Ping commands for test instances to be run to check VPC reachability. | | | -| [shared_vpc_self_links](outputs.tf#L121) | Shared VPC host projects. | | | -| [tfvars](outputs.tf#L126) | Terraform variables file for the following stages. | ✓ | | -| [vpn_gateway_endpoints](outputs.tf#L132) | External IP Addresses for the GCP VPN gateways. | | | +| [host_project_ids](outputs.tf#L107) | Network project ids. | | | +| [host_project_numbers](outputs.tf#L112) | Network project numbers. | | | +| [ping_commands](outputs.tf#L117) | Ping commands for test instances to be run to check VPC reachability. | | | +| [shared_vpc_self_links](outputs.tf#L122) | Shared VPC host projects. | | | +| [tfvars](outputs.tf#L127) | Terraform variables file for the following stages. | ✓ | | +| [vpn_gateway_endpoints](outputs.tf#L133) | External IP Addresses for the GCP VPN gateways. | | | diff --git a/fast/stages/2-networking-b-nva/outputs.tf b/fast/stages/2-networking-b-nva/outputs.tf index a143a2415..6969be18a 100644 --- a/fast/stages/2-networking-b-nva/outputs.tf +++ b/fast/stages/2-networking-b-nva/outputs.tf @@ -67,6 +67,7 @@ locals { tfvars = { host_project_ids = local.host_project_ids host_project_numbers = local.host_project_numbers + regions = var.regions subnet_self_links = local.subnet_self_links subnet_proxy_only_self_links = local.subnet_proxy_only_self_links subnet_psc_self_links = local.subnet_psc_self_links diff --git a/fast/stages/2-networking-c-separate-envs/README.md b/fast/stages/2-networking-c-separate-envs/README.md index af512fe80..853a0a846 100644 --- a/fast/stages/2-networking-c-separate-envs/README.md +++ b/fast/stages/2-networking-c-separate-envs/README.md @@ -381,11 +381,11 @@ Regions are defined via the `regions` variable which sets up a mapping between t | name | description | sensitive | consumers | |---|---|:---:|---| -| [dev_cloud_dns_inbound_policy](outputs.tf#L79) | IP Addresses for Cloud DNS inbound policy for the dev environment. | | | -| [host_project_ids](outputs.tf#L84) | Network project ids. | | | -| [host_project_numbers](outputs.tf#L89) | Network project numbers. | | | -| [prod_cloud_dns_inbound_policy](outputs.tf#L94) | IP Addresses for Cloud DNS inbound policy for the prod environment. | | | -| [shared_vpc_self_links](outputs.tf#L99) | Shared VPC host projects. | | | -| [tfvars](outputs.tf#L104) | Terraform variables file for the following stages. | ✓ | | -| [vpn_gateway_endpoints](outputs.tf#L110) | External IP Addresses for the GCP VPN gateways. | | | +| [dev_cloud_dns_inbound_policy](outputs.tf#L80) | IP Addresses for Cloud DNS inbound policy for the dev environment. | | | +| [host_project_ids](outputs.tf#L85) | Network project ids. | | | +| [host_project_numbers](outputs.tf#L90) | Network project numbers. | | | +| [prod_cloud_dns_inbound_policy](outputs.tf#L95) | IP Addresses for Cloud DNS inbound policy for the prod environment. | | | +| [shared_vpc_self_links](outputs.tf#L100) | Shared VPC host projects. | | | +| [tfvars](outputs.tf#L105) | Terraform variables file for the following stages. | ✓ | | +| [vpn_gateway_endpoints](outputs.tf#L111) | External IP Addresses for the GCP VPN gateways. | | | diff --git a/fast/stages/2-networking-c-separate-envs/outputs.tf b/fast/stages/2-networking-c-separate-envs/outputs.tf index 22a667895..d6331ba50 100644 --- a/fast/stages/2-networking-c-separate-envs/outputs.tf +++ b/fast/stages/2-networking-c-separate-envs/outputs.tf @@ -48,6 +48,7 @@ locals { tfvars = { host_project_ids = local.host_project_ids host_project_numbers = local.host_project_numbers + regions = var.regions subnet_self_links = local.subnet_self_links subnet_proxy_only_self_links = local.subnet_proxy_only_self_links subnet_psc_self_links = local.subnet_psc_self_links diff --git a/fast/stages/2-security/.fast-stage.env b/fast/stages/2-security/.fast-stage.env index d174c2162..9e157e378 100644 --- a/fast/stages/2-security/.fast-stage.env +++ b/fast/stages/2-security/.fast-stage.env @@ -2,4 +2,3 @@ FAST_STAGE_DESCRIPTION="security" FAST_STAGE_LEVEL=2 FAST_STAGE_NAME=security FAST_STAGE_DEPS="0-globals 0-bootstrap 1-resman" -FAST_STAGE_OPTIONAL="2-nsec" \ No newline at end of file diff --git a/fast/stages/2-security/README.md b/fast/stages/2-security/README.md index 318dfd68e..981f1082e 100644 --- a/fast/stages/2-security/README.md +++ b/fast/stages/2-security/README.md @@ -16,8 +16,6 @@ The following diagram illustrates the high-level design of resources managed her - [Design overview and choices](#design-overview-and-choices) - [Cloud KMS](#cloud-kms) - [Certificate Authority Service (CAS)](#certificate-authority-service-cas) - - [Trust Configs](#trust-configs) - - [NGFW Enterprise and TLS inspection support](#ngfw-enterprise-and-tls-inspection-support) - [How to run this stage](#how-to-run-this-stage) - [Provider and Terraform variables](#provider-and-terraform-variables) - [Impersonating the automation service account](#impersonating-the-automation-service-account) @@ -50,19 +48,7 @@ IAM roles on keys can be configured at the logical level for all locations where ### Certificate Authority Service (CAS) -With this stage you can leverage Certificate Authority Services (CAS) and create as many CAs you need for each environments. To create custom CAS, you can use the `cas_configs` variable. The variable comes with some defaults, useful for demos: in each environment, specifying the CA `location` should be enough for most of your test scenarios. - -### Trust Configs - -The stage lets you also create Certificate Manager trust configs. With trust configs you can trust whole CAs or specific server certificates, when you use them with other services, such as NGFW Enterprise. You can create additional trust configs for each environment with the `trust_configs` variable. At a very minimum, each trust config needs a `location` (the region) and either a `trust_stores` block or an `allowed_certificates` block. - -### NGFW Enterprise and TLS inspection support - -We deploy NGFW Enterprise in the [network security stage](../2-network-security/README.md). If you require TLS inspection, NGFW needs to interact with CAS and -optionally- Certificate Manager trust-configs. These components bind to firewall endpoint associations (created in the network security stage) with zonal TLS inspection policies. -Using this module, you can define CAS configurations and trust-configs for NGFW Enterprise. You can create them using the `cas_configs` and `trust_configs` variables. Anyway, these will need to use specific keys (defined in `ngfw_tls_configs.keys`), so that FAST knows which configurations to use for NGFW Enterprise. -You can then enable TLS inspection and customize its behavior for NGFW Enterprise, using the `ngfw_tls_configs.tls_inspection` variable. FAST will create the TLS inspection policies for you in the regions where you defined your CAs for NGFW Enterprise. -When you create your CAs and trust-configs for NGFW Enterprise, make sure their region matches the zones where you will define your firewall endpoints. -You can read more about NGFW configurations in the [Customizations section](#customizations) of this document. +With this stage you can leverage Certificate Authority Services (CAS) and create as many CAs you need for each environments. To create custom CAS, you can use the `certificate_authorities` variable. ## How to run this stage @@ -286,7 +272,7 @@ tls_inspection = { | name | description | modules | resources | |---|---|---|---| -| [certs.tf](./certs.tf) | Per-environment certificate resources. | certificate-authority-service | google_certificate_manager_trust_config | +| [cas.tf](./cas.tf) | Per-environment certificate resources. | certificate-authority-service | | | [kms.tf](./kms.tf) | Per-environment KMS. | | | | [main.tf](./main.tf) | Module-level locals and resources. | folder · project | | | [outputs.tf](./outputs.tf) | Module outputs. | | google_storage_bucket_object · local_file | @@ -302,21 +288,19 @@ tls_inspection = { | [environments](variables-fast.tf#L47) | Environment names. | map(object({…})) | ✓ | | 0-globals | | [folder_ids](variables-fast.tf#L65) | Folder name => id mappings, the 'security' folder name must exist. | object({…}) | ✓ | | 1-resman | | [prefix](variables-fast.tf#L75) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | -| [certificate_authorities](variables.tf#L17) | Certificate Authority Service CAs. If environments is null the CA is created in all environments. | map(object({…})) | | {} | | +| [certificate_authorities](variables.tf#L17) | Certificate Authority Service pool and CAs. If environments is null identical pools and CAs are created in all environments. | map(object({…})) | | {} | | | [custom_roles](variables-fast.tf#L38) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | -| [essential_contacts](variables.tf#L91) | Email used for essential contacts, unset if null. | string | | null | | -| [kms_keys](variables.tf#L97) | KMS keys to create, keyed by name. | map(object({…})) | | {} | | -| [outputs_location](variables.tf#L162) | Path where providers, tfvars files, and lists for the following stages are written. Leave empty to disable. | string | | null | | +| [essential_contacts](variables.tf#L94) | Email used for essential contacts, unset if null. | string | | null | | +| [kms_keys](variables.tf#L100) | KMS keys to create, keyed by name. | map(object({…})) | | {} | | +| [outputs_location](variables.tf#L138) | Path where providers, tfvars files, and lists for the following stages are written. Leave empty to disable. | string | | null | | | [stage_config](variables-fast.tf#L85) | FAST stage configuration. | object({…}) | | {} | 1-resman | | [tag_values](variables-fast.tf#L99) | Root-level tag values. | map(string) | | {} | 1-resman | -| [trust_configs](variables.tf#L168) | The Certificate Manager trust configs. If environments is null the trust config is created in all environments. | map(object({…})) | | {} | | ## Outputs | name | description | sensitive | consumers | |---|---|:---:|---| -| [cas_configs](outputs.tf#L57) | Certificate Authority Service configurations. | | | -| [kms_keys](outputs.tf#L62) | KMS key ids. | | | -| [tfvars](outputs.tf#L67) | Terraform variable files for the following stages. | ✓ | | -| [trust_config_ids](outputs.tf#L73) | Certificate Manager trust-config ids. | | | +| [certificate_authority_pools](outputs.tf#L53) | Certificate Authority Service pools and CAs. | | | +| [kms_keys](outputs.tf#L58) | KMS key ids. | | | +| [tfvars](outputs.tf#L63) | Terraform variable files for the following stages. | ✓ | | diff --git a/fast/stages/2-security/cas.tf b/fast/stages/2-security/cas.tf new file mode 100644 index 000000000..10116e884 --- /dev/null +++ b/fast/stages/2-security/cas.tf @@ -0,0 +1,42 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Per-environment certificate resources. + +locals { + cas = flatten([ + for k, v in var.certificate_authorities : [ + for e in coalesce(v.environments, keys(var.environments)) : merge(v, { + environment = e + key = "${e}-${k}" + name = k + }) + ] + ]) +} + +module "cas" { + source = "../../../modules/certificate-authority-service" + for_each = { for k in local.cas : k.key => k } + project_id = module.project[each.value.environment].project_id + ca_configs = each.value.ca_configs + ca_pool_config = each.value.ca_pool_config + iam = each.value.iam + iam_bindings = each.value.iam_bindings + iam_bindings_additive = each.value.iam_bindings_additive + iam_by_principals = each.value.iam_by_principals + location = each.value.location +} diff --git a/fast/stages/2-security/main.tf b/fast/stages/2-security/main.tf index 50ff5625d..644f5b4ab 100644 --- a/fast/stages/2-security/main.tf +++ b/fast/stages/2-security/main.tf @@ -28,8 +28,8 @@ locals { project_services = [ "certificatemanager.googleapis.com", "cloudkms.googleapis.com", - "networkmanagement.googleapis.com", - "networksecurity.googleapis.com", + # "networkmanagement.googleapis.com", + # "networksecurity.googleapis.com", "privateca.googleapis.com", "secretmanager.googleapis.com", "stackdriver.googleapis.com" diff --git a/fast/stages/2-security/outputs.tf b/fast/stages/2-security/outputs.tf index efb6ebee6..75014ae8e 100644 --- a/fast/stages/2-security/outputs.tf +++ b/fast/stages/2-security/outputs.tf @@ -24,20 +24,16 @@ locals { ] ]) tfvars = { - cas_configs = { + certificate_authority_pools = { for k, v in module.cas : k => { - ca_pool_id = v.ca_pool_id - ca_ids = v.ca_ids - location = v.ca_pool.location + ca_ids = v.ca_ids + id = v.ca_pool_id + location = v.ca_pool.location } } kms_keys = { for k in local._output_kms_keys : k.key => k.id } - trust_configs = { - for k, v in google_certificate_manager_trust_config.default : - k => v.id - } } } @@ -54,9 +50,9 @@ resource "google_storage_bucket_object" "tfvars" { content = jsonencode(local.tfvars) } -output "cas_configs" { - description = "Certificate Authority Service configurations." - value = local.tfvars.cas_configs +output "certificate_authority_pools" { + description = "Certificate Authority Service pools and CAs." + value = local.tfvars.certificate_authority_pools } output "kms_keys" { @@ -69,8 +65,3 @@ output "tfvars" { sensitive = true value = local.tfvars } - -output "trust_config_ids" { - description = "Certificate Manager trust-config ids." - value = local.tfvars.trust_configs -} diff --git a/fast/stages/2-security/variables.tf b/fast/stages/2-security/variables.tf index c6a78f99b..2d25672c6 100644 --- a/fast/stages/2-security/variables.tf +++ b/fast/stages/2-security/variables.tf @@ -15,9 +15,14 @@ */ variable "certificate_authorities" { - description = "Certificate Authority Service CAs. If environments is null the CA is created in all environments." + description = "Certificate Authority Service pool and CAs. If environments is null identical pools and CAs are created in all environments." type = map(object({ - environments = optional(list(string)) + location = string + environments = optional(list(string)) + iam = optional(map(list(string)), {}) + iam_bindings = optional(map(any), {}) + iam_bindings_additive = optional(map(any), {}) + iam_by_principals = optional(map(list(string)), {}) ca_configs = map(object({ deletion_protection = optional(string, true) type = optional(string, "SELF_SIGNED") @@ -49,19 +54,22 @@ variable "certificate_authorities" { server_auth = optional(bool, true) time_stamping = optional(bool, false) }), {}) - subject = optional(object({ - common_name = string - organization = string - country_code = optional(string) - locality = optional(string) - organizational_unit = optional(string) - postal_code = optional(string) - province = optional(string) - street_address = optional(string) - }), { - common_name = "test.example.com" - organization = "Test Example" - }) + subject = optional( + object({ + common_name = string + organization = string + country_code = optional(string) + locality = optional(string) + organizational_unit = optional(string) + postal_code = optional(string) + province = optional(string) + street_address = optional(string) + }), + { + common_name = "test.example.com" + organization = "Test Example" + } + ) subject_alt_name = optional(object({ dns_names = optional(list(string), null) email_addresses = optional(list(string), null) @@ -78,11 +86,6 @@ variable "certificate_authorities" { name = optional(string, null) tier = optional(string, "DEVOPS") }) - location = string - iam = optional(map(list(string)), {}) - iam_bindings = optional(map(any), {}) - iam_bindings_additive = optional(map(any), {}) - iam_by_principals = optional(map(list(string)), {}) })) nullable = false default = {} @@ -132,51 +135,8 @@ variable "kms_keys" { nullable = false } -# move to netsec -# variable "tls_inspection_policies" { -# description = "The CAS and trust configurations key names to be used for NGFW Enterprise." -# type = object({ -# keys = optional(object({ -# dev = optional(object({ -# cas = optional(list(string), ["ngfw-dev-cas-0"]) -# trust_configs = optional(list(string), ["ngfw-dev-tc-0"]) -# }), {}) -# prod = optional(object({ -# cas = optional(list(string), ["ngfw-prod-cas-0"]) -# trust_configs = optional(list(string), ["ngfw-prod-tc-0"]) -# }), {}) -# }), {}) -# tls_inspection = optional(object({ -# enabled = optional(bool, false) -# exclude_public_ca_set = optional(bool, false) -# min_tls_version = optional(string, "TLS_1_0") -# }), {}) -# }) -# nullable = false -# default = { -# dev = {} -# prod = {} -# } -# } - variable "outputs_location" { description = "Path where providers, tfvars files, and lists for the following stages are written. Leave empty to disable." type = string default = null } - -variable "trust_configs" { - description = "The Certificate Manager trust configs. If environments is null the trust config is created in all environments." - type = map(object({ - location = string - description = optional(string) - environments = optional(list(string)) - allowlisted_certificates = optional(map(string), {}) - trust_stores = optional(map(object({ - intermediate_cas = optional(map(string), {}) - trust_anchors = optional(map(string), {}) - })), {}) - })) - nullable = false - default = {} -} diff --git a/fast/stages/README.md b/fast/stages/README.md index 6a30dcb01..c0ed1d6fb 100644 --- a/fast/stages/README.md +++ b/fast/stages/README.md @@ -17,6 +17,8 @@ To achieve this, we rely on specific GCP functionality like [delegated role gran Refer to each stage's documentation for a detailed description of its purpose, the architectural choices made in its design, and how it can be configured and wired together to terraform a whole GCP organization. The following is a brief overview of each stage. +Stages encapsulate core designs and functionality that is common in most type of GCP organization set-ups. Specialized designs or additional configurations that add specific functionality on top of stages to meet very specific use cases are defined via [add-ons](../addons/). + To destroy a previous FAST deployment follow the instructions detailed in [cleanup](CLEANUP.md). ## Organization (0 and 1) @@ -32,7 +34,7 @@ To destroy a previous FAST deployment follow the instructions detailed in [clean ## Multitenancy -Implemented as an [add-on stage 1](./1-tenant-factory/), with optional FAST compatibility for tenants. +Implemented as an [add-on stage 1](../addons/1-resman-tenants/), with optional FAST compatibility for tenants. ## Shared resources (2) @@ -44,7 +46,6 @@ Implemented as an [add-on stage 1](./1-tenant-factory/), with optional FAST comp Exports: host project ids and numbers, vpc self links - [Project Factory](./2-project-factory/) YAML-based factory to create and configure application or team-level projects. Configuration includes VPC-level settings for Shared VPC, service-level configuration for CMEK encryption via centralized keys, and service account creation for workloads and applications. This stage can be cloned if an org-wide or dedicated per-environment factories are needed. -- [Network Security](./2-network-security/) Optional stage that integrates with security and networking stages to manage a centralized [NGFW Enterprise](https://cloud.google.com/firewall/docs/about-firewalls) deployment. ## Environment-level resources (3) diff --git a/tests/fast/__init__.py b/tests/fast/__init__.py index 7ba50f933..c37e93b74 100644 --- a/tests/fast/__init__.py +++ b/tests/fast/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/fast/stages/s1_tenant_factory/__init__.py b/tests/fast/addons/__init__.py similarity index 95% rename from tests/fast/stages/s1_tenant_factory/__init__.py rename to tests/fast/addons/__init__.py index 7ba50f933..c37e93b74 100644 --- a/tests/fast/stages/s1_tenant_factory/__init__.py +++ b/tests/fast/addons/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/fast/stages/s2_network_security/tftest.yaml b/tests/fast/addons/a1_resman_tenants/__init__.py similarity index 85% rename from tests/fast/stages/s2_network_security/tftest.yaml rename to tests/fast/addons/a1_resman_tenants/__init__.py index 05246b4eb..c37e93b74 100644 --- a/tests/fast/stages/s2_network_security/tftest.yaml +++ b/tests/fast/addons/a1_resman_tenants/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -11,9 +11,3 @@ # 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/2-network-security/ - -tests: - simple: - tls: diff --git a/tests/fast/stages/s1_tenant_factory/simple.tfvars b/tests/fast/addons/a1_resman_tenants/simple.tfvars similarity index 93% rename from tests/fast/stages/s1_tenant_factory/simple.tfvars rename to tests/fast/addons/a1_resman_tenants/simple.tfvars index afac7a3e8..a1a588c70 100644 --- a/tests/fast/stages/s1_tenant_factory/simple.tfvars +++ b/tests/fast/addons/a1_resman_tenants/simple.tfvars @@ -23,6 +23,20 @@ custom_roles = { storage_viewer = "organizations/123456789012/roles/storageViewer" tenant_network_admin = "organizations/123456789012/roles/tenantNetworkAdmin" } +environments = { + dev = { + is_default = false + name = "Development" + short_name = "dev" + tag_name = "development" + } + prod = { + is_default = true + name = "Production" + short_name = "prod" + tag_name = "production" + } +} groups = { gcp-billing-admins = "gcp-billing-admins", gcp-devops = "gcp-devops", diff --git a/tests/fast/stages/s1_tenant_factory/simple.yaml b/tests/fast/addons/a1_resman_tenants/simple.yaml similarity index 100% rename from tests/fast/stages/s1_tenant_factory/simple.yaml rename to tests/fast/addons/a1_resman_tenants/simple.yaml diff --git a/tests/fast/stages/s1_tenant_factory/tftest.yaml b/tests/fast/addons/a1_resman_tenants/tftest.yaml similarity index 94% rename from tests/fast/stages/s1_tenant_factory/tftest.yaml rename to tests/fast/addons/a1_resman_tenants/tftest.yaml index cfac5807d..e59f77706 100644 --- a/tests/fast/stages/s1_tenant_factory/tftest.yaml +++ b/tests/fast/addons/a1_resman_tenants/tftest.yaml @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -module: fast/stages/1-tenant-factory +module: fast/addons/1-resman-tenants tests: simple: diff --git a/tests/fast/stages/s2_network_security/__init__.py b/tests/fast/addons/a2_networking_ngfw/__init__.py similarity index 100% rename from tests/fast/stages/s2_network_security/__init__.py rename to tests/fast/addons/a2_networking_ngfw/__init__.py diff --git a/tests/fast/addons/a2_networking_ngfw/data/ca.cert.pem b/tests/fast/addons/a2_networking_ngfw/data/ca.cert.pem new file mode 100644 index 000000000..be23688f3 --- /dev/null +++ b/tests/fast/addons/a2_networking_ngfw/data/ca.cert.pem @@ -0,0 +1,36 @@ +-----BEGIN CERTIFICATE----- +MIIGQTCCBCmgAwIBAgIUVxAf6dcD0KxZqP5FKnxbqotFZtswDQYJKoZIhvcNAQEL +BQAwgacxHTAbBgNVBAMMFEZhc3QgRXhhbXBsZSBSb290IENBMQswCQYDVQQGEwJJ +VDEPMA0GA1UECAwGTWlsYW5vMQ8wDQYDVQQHDAZNaWxhbm8xEjAQBgNVBAoMCUZh +c3QgVGVzdDEVMBMGA1UECwwMRmFzdCBUZXN0IENBMSwwKgYJKoZIhvcNAQkBFh1m +YWJyaWMtZmFzdC1vd25lcnNAZ29vZ2xlLmNvbTAeFw0yNTAxMDUxNzQ4NDNaFw00 +NDEyMzExNzQ4NDNaMIGnMR0wGwYDVQQDDBRGYXN0IEV4YW1wbGUgUm9vdCBDQTEL +MAkGA1UEBhMCSVQxDzANBgNVBAgMBk1pbGFubzEPMA0GA1UEBwwGTWlsYW5vMRIw +EAYDVQQKDAlGYXN0IFRlc3QxFTATBgNVBAsMDEZhc3QgVGVzdCBDQTEsMCoGCSqG +SIb3DQEJARYdZmFicmljLWZhc3Qtb3duZXJzQGdvb2dsZS5jb20wggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQDOXAfSIIXDz3Fr1OOJDXhRMYvPydZCEbTT +DijXEFBo9igJkYyDA9IxAJvpsDOyQNpDYcxQtojQN3pTSHhmqfN3/EQVjAhEvb4g +kAONBsz4wQQCmfRujDty8vilu6BNuxqvFGuoVrfe1l9UIn4gh3ICbKSoK1SflzYJ +MDs4GWs4wTQMPo2PFjEnC8bejraVQUTKW1iIQRrFKtzsKzRznx0TPaM6vrqwvcVK +polYohkxT7YK1NXQ5zm27Hkoz2Id5Exht927mbrjGOg8/SGkY8AgUe4ObnQP4fTl +/b3fewMCgnvhNbXtvriPyAgKO9pgQAI7OvyPMcrJAUji9wy5U+ETGtw7rz+Wj0Tx +JDpmGJgUUxHMU/FGa56B2f9MDEaUk8JrQVpv+p4qj6JmsyrTU+xz5B0QGBmkVWn1 +TWxnRtnRG9G3ERnkv8hdLKS+Np/e5W/J16VTZIuc6m5N2Ak6mzdV3+MAwywRVvUJ +SfCmJC7rxExRL1HFiJUzTCZEB5ddDQcJeqs+ZsCM2bwPAK/XCIM7kBEwpxkasGNd +vVYUA/ipuAiRjGFGEyjfwJj4yy2rh4UIxVzx8Pw8JfQwgVuACTNcsnbJFlU3BgCE +5YclYFV/tof1jyVG5aFf4isAC2qw2e0OZcDj7b4lHNGsU5TXhipvQWqgaQbM5VZw +TaxVT5I9nwIDAQABo2MwYTAdBgNVHQ4EFgQUaO7/OgPCi7VqAr8oPjxBtY5X+sgw +HwYDVR0jBBgwFoAUaO7/OgPCi7VqAr8oPjxBtY5X+sgwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBAClteb0b3EzVj8VH +fESFEwrJ+vbhxO/xQfjrhyPcu9Z4pVbKy9YUTz2R2GilLLX/RGBJgJBqp23NozDb +OV9f+oxTKJmqXxMqjyAnccA8J+CgemW7zrkBFax2veFZ9Mnvu5Se1kAOgbUXfaiT +cLzrAX17Xw5VVAGTjYhPXJNXfPLSyWJTWNVJYdVZ/9MdU64Rulh3PhRZwLxqdvho +IzXR3mnK1Q97zLp7mKvm+K/QVUJDcZ8OguAG0qVlr0rSk3vFP+ti7ts50/IWK1ad +wDTRT7W42oM4yrdUy/SAQV6k66LluVleHtPQS6FzPRffTwtv4i7L+RIZMpoKIYxt +uCWZ0v7Lln3nCyW5DcwpbjF/k5kiIQvJOHMx1VuvSEs3ku9s2NnIsHbgz5hFReZw +MOIJXzOJOMasay2IfvmA+Ue+HcQ7eMcdQ3KCxHLDnxu/Bv6Mmvp8BlswPHZkc8KA +Nbzgu9JknJeOaH+IhO8gDShiOYulb8SxJS7IZn6MR9ijcWzvbaAxzZMCtAmoJRoV +2R54jhZdw5JWlqrSH9ADBOvv9BpiHKl4/Opwumm13KkMDp9i5fPoYKSt5Xb/vwKS +T5grhIKgzdeKU6dyR1ZBf9jvlzcXsk0+HFeKbtCTMxtYW2PG3tXXx/86LHvANyhe +6tHmve7JLOKynHcIhypua18aX64w +-----END CERTIFICATE----- diff --git a/tests/fast/addons/a2_networking_ngfw/data/example.com.cert.pem b/tests/fast/addons/a2_networking_ngfw/data/example.com.cert.pem new file mode 100644 index 000000000..a846366b9 --- /dev/null +++ b/tests/fast/addons/a2_networking_ngfw/data/example.com.cert.pem @@ -0,0 +1,38 @@ +-----BEGIN CERTIFICATE----- +MIIGmjCCBIKgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZsxCzAJBgNVBAYTAklU +MQ8wDQYDVQQIDAZNaWxhbm8xEjAQBgNVBAoMCUZhc3QgVGVzdDEVMBMGA1UECwwM +RmFzdCBUZXN0IENBMSIwIAYDVQQDDBlGYXN0IFRlc3QgSW50ZXJtZWRpYXRlIENB +MSwwKgYJKoZIhvcNAQkBFh1mYWJyaWMtZmFzdC1vd25lcnNAZ29vZ2xlLmNvbTAe +Fw0yNTAxMDUxNzU0NTNaFw0yNjAxMTUxNzU0NTNaMIGeMQswCQYDVQQGEwJJVDEP +MA0GA1UECAwGTWlsYW5vMQ8wDQYDVQQHDAZNaWxhbm8xEjAQBgNVBAoMCUZhc3Qg +VGVzdDEVMBMGA1UECwwMRmFzdCBUZXN0IENBMRQwEgYDVQQDDAtleGFtcGxlLmNv +bTEsMCoGCSqGSIb3DQEJARYdZmFicmljLWZhc3Qtb3duZXJzQGdvb2dsZS5jb20w +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBsjW38EG4Id6MqO/bQXXV +DaA4LbP+Eu9ekSQcIkddFGvC9+Jv0O8YC9t+FTL0cMPZDwne2fqPsk5skR2xTYFA +JJKsz6NfuSSpZYRiWmfZXKF/c2u2BBmYkBgv1MiJrM/Oum1t98QbIn5y2apS0wDw +aGmVJ/aT0SeR46zyPbXi9Dx5NqbZQmfdftmNNpDKLg68hZ42HsGcqBjn9dIiKdJL +98LQnBUzSxENAfrQbzii3bm274n7M1Nodnf00tX341IdFWgbuK+zIM4FinrSigv4 +LTzebF2ow7EhrA386zjEeHsTn8gVFVak/RhJgYbZI4MMoxgotH5/1maJVPVwCSSh +AgMBAAGjggHhMIIB3TAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIGQDAzBglg +hkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgU2VydmVyIENlcnRpZmljYXRl +MB0GA1UdDgQWBBR+TcFut49DkNpPPmsa6JOLpNdsKjCB1QYDVR0jBIHNMIHKgBQ8 +GKPN8aSuhtdfzTMBY06se4nyN6GBraSBqjCBpzEdMBsGA1UEAwwURmFzdCBFeGFt +cGxlIFJvb3QgQ0ExCzAJBgNVBAYTAklUMQ8wDQYDVQQIDAZNaWxhbm8xDzANBgNV +BAcMBk1pbGFubzESMBAGA1UECgwJRmFzdCBUZXN0MRUwEwYDVQQLDAxGYXN0IFRl +c3QgQ0ExLDAqBgkqhkiG9w0BCQEWHWZhYnJpYy1mYXN0LW93bmVyc0Bnb29nbGUu +Y29tggIQADAOBgNVHQ8BAf8EBAMCBeAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwNAYI +KwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcDIuZXhhbXBsZS5j +b20wNgYDVR0RBC8wLYILZXhhbXBsZS5jb22CD3d3dy5leGFtcGxlLmNvbYINbS5l +eGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOCAgEAk5aNZU32TarWOJRWaFQXCHSC +o9m5LCK+nyE1a2r4XeuacYc1wXEFJubBSRc6Ms5pSuEZw0zil4FEJXvaJNURqh+K +40CaiLPOkB6N/YiF4xuQHJ4lXHrGP/d2QkOp+jX0Ic/jwpQ27y0dcFKLjIgnEZub +LEhwIffwI+dWE6LnczcAj37c3+bmRE5euf71vaZy749dv1aDeR5CO7wOK7vsZZXV +/h6npnZrWQ6CsZ8xW69L29sKsZlx33VD+Lu6dNS+Nc2EXYC3WtGNeHrlJk1wy5AV +uebVKbsw94wb53h0NP8TNQrClWmz5mImyNzGPmNJ74l6Q508gppxWdjFqPWIfAnR +FK6dW7zkLAJcXcr3qHcG+KuwoxZFKI2NBTdJFPSNqil3RCSOY5ZaWN6ZroXGn3e3 +suGXBh3s53uLSMS5WsY+LoAZ8DPcbCB9LIvxHj2JnFaBl8xpy+Nys8+5CI3RxgMq +jq3gz3pSz8/JuXKpb4lCofSgVsWN5iycm8Hz23b2H2mlgoT4WRPGl+aLMeDwBFkW +1kPS02POvTf0n2MEUAwmbHEfH6b9cY62A9g8OrtKj72ItNyJPELRZKZZVOsyRutn +CMi5xDJ8NdOl3XN7TOwSazzhPbr7usZp4DPrJIaG5POaPHUmThSPsD1VoctaL23W +OkP+gNET6MuCRvTk0EA= +-----END CERTIFICATE----- diff --git a/tests/fast/addons/a2_networking_ngfw/data/intermediate.cert.pem b/tests/fast/addons/a2_networking_ngfw/data/intermediate.cert.pem new file mode 100644 index 000000000..cfb44fe38 --- /dev/null +++ b/tests/fast/addons/a2_networking_ngfw/data/intermediate.cert.pem @@ -0,0 +1,35 @@ +-----BEGIN CERTIFICATE----- +MIIGJjCCBA6gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgacxHTAbBgNVBAMMFEZh +c3QgRXhhbXBsZSBSb290IENBMQswCQYDVQQGEwJJVDEPMA0GA1UECAwGTWlsYW5v +MQ8wDQYDVQQHDAZNaWxhbm8xEjAQBgNVBAoMCUZhc3QgVGVzdDEVMBMGA1UECwwM +RmFzdCBUZXN0IENBMSwwKgYJKoZIhvcNAQkBFh1mYWJyaWMtZmFzdC1vd25lcnNA +Z29vZ2xlLmNvbTAeFw0yNTAxMDUxNzUxMTdaFw0zNTAxMDMxNzUxMTdaMIGbMQsw +CQYDVQQGEwJJVDEPMA0GA1UECAwGTWlsYW5vMRIwEAYDVQQKDAlGYXN0IFRlc3Qx +FTATBgNVBAsMDEZhc3QgVGVzdCBDQTEiMCAGA1UEAwwZRmFzdCBUZXN0IEludGVy +bWVkaWF0ZSBDQTEsMCoGCSqGSIb3DQEJARYdZmFicmljLWZhc3Qtb3duZXJzQGdv +b2dsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCXqzX+NtTr +V0YkIeaAQo7aHPRnmuG4OIp2nLl2EFU9/FxQCRznt5kp5xctrKDamf8QeFI9cd4k +gkf55BWcCDeTDN1Eeg+3IEocYFgQx0jMfAmK9+mTLIQyFJ0h5P/JN4UrwUrPG+Z/ +i7C1pwDcyWDXtw6CySMuP05LwWmEojaGC+XihGDPftK29GTekz+dcpex+3XyyWWS +vcxOxdYNmnEDO1MJN4ExJfBhUsT7qWVYAKYiWBftAcij8xnVRU/RX/Ch2aJIxqL6 +rzoi/g9vEUIgQrAWUiNob+yFV/1aM5VQRUzYuDYNGNWfLxwvpD/TZ/n/5ofDEzlQ +op0X6Zo0OTdGfhTLd6JreAZvad4QsLex7cH/kiDTsTk4vlxQMbfn9zQFlS3tR+xY +gaqupD5EhQULi3U6yJEUF2vvxQCMyl5RZLDsZgxfuJsVbYMC/eYcf5L7HAi9bzCS +bHErk/ifuqjRht/rAFCwGvoq27r0HhY/W7zmz/ZVKlvWOXy0rMiAeE1S01Te7woM +TrWLRXwkzKDeqw/SDVK5zt7uB6a4FO01kzgq7YagX34Y064CU5cOvQ0aHcBBgezd +B9bUZKSukrr+8Cg8viNc2VRYLO9h3yQmja5DZlUqWV+medFzxoWd7nUF1bk1KknM +ZNK+oB3Ri/MYDjAUXhAQyqP2/Okt9dWDvQIDAQABo2YwZDAdBgNVHQ4EFgQUPBij +zfGkrobXX80zAWNOrHuJ8jcwHwYDVR0jBBgwFoAUaO7/OgPCi7VqAr8oPjxBtY5X ++sgwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcN +AQELBQADggIBAHSZ/1HBBntat0w8yP+0VO2Y7tCbJm9bUSC+dxMHp7VZgpmaCrGg +6YHMeth3iVIzsLroboppdyrb1O3sIe7xJQjqiQQSEZd9iJUXMe/kMNxC4tOPbEQC +cZzjHbmyNUOQegQXQU6+HpqZr2eXevMI6mUbfJ18WoKOdsyBrGEVOv7WXHR4zNpO +AC7lh1hbxHmRStcHSM5HkeoAwoIIYoSp6mMoiQgvFtDhUCwR3xAaRGsdR6QM4wSP +5oimHiL/dN/ndSwQjFLKmYw5QB0b1LGyYr7T53X0M0Kk56rSdhQlWWT+BpPBA4Tv +Dnl14PPtH5ENZbu8MbMe/68B5lhrvhwDR/yDWubjE7VefYrCnyb3SaBPWBYwk2bI +t2eUVNkEPSuLUXgTwH/F8mqFt++RW8JF9dARhCONQs04I7peGtxGc1F4ggkXkIWh +fzR48m6hMngpQvAs2XuWyaoWoaOfmwqwWdiHDj27Kus6lePkw3KyT2h6uxmrw8l9 +YoLgnbsp74kvTq4ID/QW4uMfPD5vA8xs17U2MpgEdJYs5mzp1gjkYhhST0g5d5gT +KSiY4NCSLaDP+3+a8lZZ64tLZvSOoCbmZlNcndTv2DOxsLGV6Yn/XOEKTCZWHRGp +aCTHloS7taETFUj98Bwsct7dIbHX5I3PD5okxiMKvpmVf0NeN9/vT/M7 +-----END CERTIFICATE----- diff --git a/tests/fast/addons/a2_networking_ngfw/simple.tfvars b/tests/fast/addons/a2_networking_ngfw/simple.tfvars new file mode 100644 index 000000000..ad6a203d6 --- /dev/null +++ b/tests/fast/addons/a2_networking_ngfw/simple.tfvars @@ -0,0 +1,87 @@ +_fast_debug = { + skip_datasources = true +} +automation = { + outputs_bucket = "test" +} +certificate_authorities = { + ngfw-0 = { + location = "europe-west8" + ca_configs = { + ca-0 = { + deletion_protection = false + subject = { + common_name = "fast.example.com" + organization = "FAST Test" + } + } + } + ca_pool_config = { + authz_nsec_sa = true + name = "ca-pool-0" + } + } +} +ngfw_config = { + name = "ngfw-0" + endpoint_zones = ["europe-west8-b"] + network_associations = { + prod = { + vpc_id = "projects/xxx-prod-net-spoke-0/global/networks/prod-spoke-0" + tls_inspection_policy = "ngfw-0" + } + } +} +organization = { + domain = "fast.example.com" + id = 123456789012 + customer_id = "C00000000" +} +project_id = "xxx-prod-net-landing-0" +security_profiles = { + ngfw-0 = { + threat_prevention_profile = { + severity_overrides = { + informational-allow = { + action = "ALLOW" + severity = "INFORMATIONAL" + } + } + threat_overrides = { + allow-280647 = { + action = "ALLOW" + threat_id = "280647" + } + } + } + } +} +tls_inspection_policies = { + ngfw-0 = { + ca_pool_id = "ngfw-0" + location = "europe-west8" + trust_config = "ngfw-0" + } +} +trust_configs = { + ngfw-0 = { + location = "europe-west8" + allowlisted_certificates = { + server-0 = "../../../tests/fast/addons/a2_networking_ngfw/data/example.com.cert.pem" + } + trust_stores = { + ludo-joonix = { + intermediate_cas = { + issuing-ca-1 = "../../../tests/fast/addons/a2_networking_ngfw/data/intermediate.cert.pem" + } + trust_anchors = { + root-ca-1 = "../../../tests/fast/addons/a2_networking_ngfw/data/ca.cert.pem" + } + } + } + } +} +vpc_self_links = { + dev-spoke-0 = "https://www.googleapis.com/compute/v1/projects/123456789/networks/vpc-1" + prod-spoke-0 = "https://www.googleapis.com/compute/v1/projects/123456789/networks/vpc-2" +} diff --git a/tests/fast/stages/s2_network_security/simple.yaml b/tests/fast/addons/a2_networking_ngfw/simple.yaml similarity index 56% rename from tests/fast/stages/s2_network_security/simple.yaml rename to tests/fast/addons/a2_networking_ngfw/simple.yaml index c0ff76a2d..8fa8ebc28 100644 --- a/tests/fast/stages/s2_network_security/simple.yaml +++ b/tests/fast/addons/a2_networking_ngfw/simple.yaml @@ -13,16 +13,14 @@ # limitations under the License. counts: - google_compute_network_firewall_policy: 2 - google_compute_network_firewall_policy_association: 2 - google_compute_network_firewall_policy_rule: 4 - google_network_security_firewall_endpoint: 3 - google_network_security_firewall_endpoint_association: 6 - google_network_security_security_profile: 2 - google_network_security_security_profile_group: 2 - google_project: 1 - google_project_service: 1 - google_project_service_identity: 1 + google_certificate_manager_trust_config: 1 + google_network_security_firewall_endpoint: 1 + google_network_security_firewall_endpoint_association: 1 + google_network_security_security_profile: 1 + google_network_security_security_profile_group: 1 + google_network_security_tls_inspection_policy: 1 + google_privateca_ca_pool: 1 + google_privateca_certificate_authority: 1 google_storage_bucket_object: 1 - modules: 3 - resources: 25 + modules: 1 + resources: 9 diff --git a/tests/fast/addons/a2_networking_ngfw/tftest.yaml b/tests/fast/addons/a2_networking_ngfw/tftest.yaml new file mode 100644 index 000000000..bc7491a5a --- /dev/null +++ b/tests/fast/addons/a2_networking_ngfw/tftest.yaml @@ -0,0 +1,22 @@ +# 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/addons/2-networking-ngfw/ + +tests: + simple: + extra_files: + - ../../../tests/fast/addons/a2_networking_ngfw/data/ca.cert.pem + - ../../../tests/fast/addons/a2_networking_ngfw/data/example.com.cert.pem + - ../../../tests/fast/addons/a2_networking_ngfw/data/intermediate.cert.pem diff --git a/tests/fast/stages/__init__.py b/tests/fast/stages/__init__.py index 7ba50f933..c37e93b74 100644 --- a/tests/fast/stages/__init__.py +++ b/tests/fast/stages/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/fast/stages/s0_bootstrap/__init__.py b/tests/fast/stages/s0_bootstrap/__init__.py index 7ba50f933..c37e93b74 100644 --- a/tests/fast/stages/s0_bootstrap/__init__.py +++ b/tests/fast/stages/s0_bootstrap/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/fast/stages/s0_bootstrap/cicd.tfvars b/tests/fast/stages/s0_bootstrap/cicd.tfvars new file mode 100644 index 000000000..4e6490266 --- /dev/null +++ b/tests/fast/stages/s0_bootstrap/cicd.tfvars @@ -0,0 +1,58 @@ +billing_account = { + id = "000000-111111-222222" +} +essential_contacts = "gcp-organization-admins@fast.example.com" +groups = { + gcp-support = "group:gcp-support@example.com" +} +org_policies_config = { + import_defaults = false +} +organization = { + domain = "fast.example.com" + id = 123456789012 + customer_id = "C00000000" +} +outputs_location = "/fast-config" +prefix = "fast" +cicd_config = { + bootstrap = { + identity_provider = "gh-test" + repository = { + name = "fast/bootstrap" + type = "github" + branch = "main" + } + } + resman = { + identity_provider = "gl-test" + repository = { + name = "fast/resource_management" + type = "gitlab" + branch = "main" + } + } +} +fast_addon = { + resman-tenants = { + parent_stage = "1-resman" + cicd_config = { + identity_provider = "gh-test" + repository = { + name = "fast/tenants" + type = "github" + branch = "main" + } + } + } +} +workload_identity_providers = { + gh-test = { + attribute_condition = "attribute.repository_owner==\"fast\"" + issuer = "github" + } + gl-test = { + attribute_condition = "attribute.namespace_path==\"fast\"" + issuer = "gitlab" + } +} diff --git a/tests/fast/stages/s0_bootstrap/cicd.yaml b/tests/fast/stages/s0_bootstrap/cicd.yaml new file mode 100644 index 000000000..30259d6b7 --- /dev/null +++ b/tests/fast/stages/s0_bootstrap/cicd.yaml @@ -0,0 +1,359 @@ +# Copyright 2025 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: + google_iam_workload_identity_pool.default[0]: + description: null + disabled: null + display_name: null + project: fast-prod-iac-core-0 + timeouts: null + workload_identity_pool_id: fast-bootstrap + google_iam_workload_identity_pool_provider.default["gh-test"]: + attribute_condition: attribute.repository_owner=="fast" + attribute_mapping: + attribute.actor: assertion.actor + attribute.fast_sub: '"repo:" + assertion.repository + ":ref:" + assertion.ref' + attribute.ref: assertion.ref + attribute.repository: assertion.repository + attribute.repository_owner: assertion.repository_owner + attribute.sub: assertion.sub + google.subject: assertion.sub + aws: [] + description: null + disabled: null + display_name: null + oidc: + - allowed_audiences: [] + issuer_uri: https://token.actions.githubusercontent.com + jwks_json: null + project: fast-prod-iac-core-0 + saml: [] + timeouts: null + workload_identity_pool_id: fast-bootstrap + workload_identity_pool_provider_id: fast-bootstrap-gh-test + x509: [] + google_iam_workload_identity_pool_provider.default["gl-test"]: + attribute_condition: attribute.namespace_path=="fast" + attribute_mapping: + attribute.environment: assertion.environment + attribute.environment_protected: assertion.environment_protected + attribute.namespace_id: assertion.namespace_id + attribute.namespace_path: assertion.namespace_path + attribute.pipeline_id: assertion.pipeline_id + attribute.pipeline_source: assertion.pipeline_source + attribute.project_id: assertion.project_id + attribute.project_path: assertion.project_path + attribute.ref: assertion.ref + attribute.ref_protected: assertion.ref_protected + attribute.ref_type: assertion.ref_type + attribute.repository: assertion.project_path + attribute.sub: assertion.sub + google.subject: assertion.sub + aws: [] + description: null + disabled: null + display_name: null + oidc: + - allowed_audiences: [] + issuer_uri: https://gitlab.com + jwks_json: null + project: fast-prod-iac-core-0 + saml: [] + timeouts: null + workload_identity_pool_id: fast-bootstrap + workload_identity_pool_provider_id: fast-bootstrap-gl-test + x509: [] + google_storage_bucket_object.workflows["0-bootstrap"]: + bucket: fast-prod-iac-core-outputs-0 + cache_control: null + content_disposition: null + content_encoding: null + content_language: null + customer_encryption: [] + detect_md5hash: different hash + event_based_hold: null + metadata: null + name: workflows/0-bootstrap-workflow.yaml + retention: [] + source: null + temporary_hold: null + timeouts: null + google_storage_bucket_object.workflows["1-resman"]: + bucket: fast-prod-iac-core-outputs-0 + cache_control: null + content_disposition: null + content_encoding: null + content_language: null + customer_encryption: [] + detect_md5hash: different hash + event_based_hold: null + metadata: null + name: workflows/1-resman-workflow.yaml + retention: [] + source: null + temporary_hold: null + timeouts: null + google_storage_bucket_object.workflows["1-resman-tenants"]: + bucket: fast-prod-iac-core-outputs-0 + cache_control: null + content_disposition: null + content_encoding: null + content_language: null + customer_encryption: [] + detect_md5hash: different hash + event_based_hold: null + metadata: null + name: workflows/1-resman-tenants-workflow.yaml + retention: [] + source: null + temporary_hold: null + timeouts: null + module.automation-tf-bootstrap-r-sa.google_service_account.service_account[0]: + account_id: fast-prod-bootstrap-0r + create_ignore_already_exists: null + description: null + disabled: false + display_name: Terraform organization bootstrap service account (read-only). + email: fast-prod-bootstrap-0r@fast-prod-iac-core-0.iam.gserviceaccount.com + member: serviceAccount:fast-prod-bootstrap-0r@fast-prod-iac-core-0.iam.gserviceaccount.com + project: fast-prod-iac-core-0 + timeouts: null + ? module.automation-tf-bootstrap-r-sa.google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"] + : condition: [] + members: + - serviceAccount:fast-prod-bootstrap-1r@fast-prod-iac-core-0.iam.gserviceaccount.com + role: roles/iam.serviceAccountTokenCreator + ? module.automation-tf-bootstrap-r-sa.google_storage_bucket_iam_member.bucket-roles["fast-prod-iac-core-outputs-0-organizations/123456789012/roles/storageViewer"] + : bucket: fast-prod-iac-core-outputs-0 + condition: [] + role: organizations/123456789012/roles/storageViewer + module.automation-tf-bootstrap-sa.google_service_account.service_account[0]: + account_id: fast-prod-bootstrap-0 + create_ignore_already_exists: null + description: null + disabled: false + display_name: Terraform organization bootstrap service account. + email: fast-prod-bootstrap-0@fast-prod-iac-core-0.iam.gserviceaccount.com + member: serviceAccount:fast-prod-bootstrap-0@fast-prod-iac-core-0.iam.gserviceaccount.com + project: fast-prod-iac-core-0 + timeouts: null + module.automation-tf-bootstrap-sa.google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: + condition: [] + members: + - serviceAccount:fast-prod-bootstrap-1@fast-prod-iac-core-0.iam.gserviceaccount.com + role: roles/iam.serviceAccountTokenCreator + ? module.automation-tf-bootstrap-sa.google_storage_bucket_iam_member.bucket-roles["fast-prod-iac-core-outputs-0-roles/storage.admin"] + : bucket: fast-prod-iac-core-outputs-0 + condition: [] + role: roles/storage.admin + ? module.automation-tf-cicd-r-sa["bootstrap"].google_project_iam_member.project-roles["fast-prod-iac-core-0-roles/logging.logWriter"] + : condition: [] + project: fast-prod-iac-core-0 + role: roles/logging.logWriter + module.automation-tf-cicd-r-sa["bootstrap"].google_service_account.service_account[0]: + account_id: fast-prod-bootstrap-1r + create_ignore_already_exists: null + description: null + disabled: false + display_name: Terraform CI/CD bootstrap service account (read-only). + email: fast-prod-bootstrap-1r@fast-prod-iac-core-0.iam.gserviceaccount.com + member: serviceAccount:fast-prod-bootstrap-1r@fast-prod-iac-core-0.iam.gserviceaccount.com + project: fast-prod-iac-core-0 + timeouts: null + ? module.automation-tf-cicd-r-sa["bootstrap"].google_service_account_iam_binding.authoritative["roles/iam.workloadIdentityUser"] + : condition: [] + role: roles/iam.workloadIdentityUser + ? module.automation-tf-cicd-r-sa["bootstrap"].google_storage_bucket_iam_member.bucket-roles["fast-prod-iac-core-outputs-0-roles/storage.objectViewer"] + : bucket: fast-prod-iac-core-outputs-0 + condition: [] + role: roles/storage.objectViewer + ? module.automation-tf-cicd-r-sa["resman"].google_project_iam_member.project-roles["fast-prod-iac-core-0-roles/logging.logWriter"] + : condition: [] + project: fast-prod-iac-core-0 + role: roles/logging.logWriter + module.automation-tf-cicd-r-sa["resman"].google_service_account.service_account[0]: + account_id: fast-prod-resman-1r + create_ignore_already_exists: null + description: null + disabled: false + display_name: Terraform CI/CD resman service account (read-only). + email: fast-prod-resman-1r@fast-prod-iac-core-0.iam.gserviceaccount.com + member: serviceAccount:fast-prod-resman-1r@fast-prod-iac-core-0.iam.gserviceaccount.com + project: fast-prod-iac-core-0 + timeouts: null + ? module.automation-tf-cicd-r-sa["resman"].google_service_account_iam_binding.authoritative["roles/iam.workloadIdentityUser"] + : condition: [] + role: roles/iam.workloadIdentityUser + ? module.automation-tf-cicd-r-sa["resman"].google_storage_bucket_iam_member.bucket-roles["fast-prod-iac-core-outputs-0-roles/storage.objectViewer"] + : bucket: fast-prod-iac-core-outputs-0 + condition: [] + role: roles/storage.objectViewer + ? module.automation-tf-cicd-r-sa["resman-tenants"].google_project_iam_member.project-roles["fast-prod-iac-core-0-roles/logging.logWriter"] + : condition: [] + project: fast-prod-iac-core-0 + role: roles/logging.logWriter + module.automation-tf-cicd-r-sa["resman-tenants"].google_service_account.service_account[0]: + account_id: fast-prod-resman-tenants-1r + create_ignore_already_exists: null + description: null + disabled: false + display_name: Terraform CI/CD resman-tenants service account (read-only). + email: fast-prod-resman-tenants-1r@fast-prod-iac-core-0.iam.gserviceaccount.com + member: serviceAccount:fast-prod-resman-tenants-1r@fast-prod-iac-core-0.iam.gserviceaccount.com + project: fast-prod-iac-core-0 + timeouts: null + ? module.automation-tf-cicd-r-sa["resman-tenants"].google_service_account_iam_binding.authoritative["roles/iam.workloadIdentityUser"] + : condition: [] + role: roles/iam.workloadIdentityUser + ? module.automation-tf-cicd-r-sa["resman-tenants"].google_storage_bucket_iam_member.bucket-roles["fast-prod-iac-core-outputs-0-roles/storage.objectViewer"] + : bucket: fast-prod-iac-core-outputs-0 + condition: [] + role: roles/storage.objectViewer + ? module.automation-tf-cicd-sa["bootstrap"].google_project_iam_member.project-roles["fast-prod-iac-core-0-roles/logging.logWriter"] + : condition: [] + project: fast-prod-iac-core-0 + role: roles/logging.logWriter + module.automation-tf-cicd-sa["bootstrap"].google_service_account.service_account[0]: + account_id: fast-prod-bootstrap-1 + create_ignore_already_exists: null + description: null + disabled: false + display_name: Terraform CI/CD bootstrap service account. + email: fast-prod-bootstrap-1@fast-prod-iac-core-0.iam.gserviceaccount.com + member: serviceAccount:fast-prod-bootstrap-1@fast-prod-iac-core-0.iam.gserviceaccount.com + project: fast-prod-iac-core-0 + timeouts: null + ? module.automation-tf-cicd-sa["bootstrap"].google_service_account_iam_binding.authoritative["roles/iam.workloadIdentityUser"] + : condition: [] + role: roles/iam.workloadIdentityUser + ? module.automation-tf-cicd-sa["bootstrap"].google_storage_bucket_iam_member.bucket-roles["fast-prod-iac-core-outputs-0-roles/storage.objectViewer"] + : bucket: fast-prod-iac-core-outputs-0 + condition: [] + role: roles/storage.objectViewer + ? module.automation-tf-cicd-sa["resman"].google_project_iam_member.project-roles["fast-prod-iac-core-0-roles/logging.logWriter"] + : condition: [] + project: fast-prod-iac-core-0 + role: roles/logging.logWriter + module.automation-tf-cicd-sa["resman"].google_service_account.service_account[0]: + account_id: fast-prod-resman-1 + create_ignore_already_exists: null + description: null + disabled: false + display_name: Terraform CI/CD resman service account. + email: fast-prod-resman-1@fast-prod-iac-core-0.iam.gserviceaccount.com + member: serviceAccount:fast-prod-resman-1@fast-prod-iac-core-0.iam.gserviceaccount.com + project: fast-prod-iac-core-0 + timeouts: null + module.automation-tf-cicd-sa["resman"].google_service_account_iam_binding.authoritative["roles/iam.workloadIdentityUser"]: + condition: [] + role: roles/iam.workloadIdentityUser + ? module.automation-tf-cicd-sa["resman"].google_storage_bucket_iam_member.bucket-roles["fast-prod-iac-core-outputs-0-roles/storage.objectViewer"] + : bucket: fast-prod-iac-core-outputs-0 + condition: [] + role: roles/storage.objectViewer + ? module.automation-tf-cicd-sa["resman-tenants"].google_project_iam_member.project-roles["fast-prod-iac-core-0-roles/logging.logWriter"] + : condition: [] + project: fast-prod-iac-core-0 + role: roles/logging.logWriter + module.automation-tf-cicd-sa["resman-tenants"].google_service_account.service_account[0]: + account_id: fast-prod-resman-tenants-1 + create_ignore_already_exists: null + description: null + disabled: false + display_name: Terraform CI/CD resman-tenants service account. + email: fast-prod-resman-tenants-1@fast-prod-iac-core-0.iam.gserviceaccount.com + member: serviceAccount:fast-prod-resman-tenants-1@fast-prod-iac-core-0.iam.gserviceaccount.com + project: fast-prod-iac-core-0 + timeouts: null + module.automation-tf-cicd-sa["resman-tenants"].google_service_account_iam_binding.authoritative["roles/iam.workloadIdentityUser"]: + condition: [] + role: roles/iam.workloadIdentityUser + ? module.automation-tf-cicd-sa["resman-tenants"].google_storage_bucket_iam_member.bucket-roles["fast-prod-iac-core-outputs-0-roles/storage.objectViewer"] + : bucket: fast-prod-iac-core-outputs-0 + condition: [] + role: roles/storage.objectViewer + module.automation-tf-resman-r-sa.google_service_account.service_account[0]: + account_id: fast-prod-resman-0r + create_ignore_already_exists: null + description: null + disabled: false + display_name: Terraform stage 1 resman service account (read-only). + email: fast-prod-resman-0r@fast-prod-iac-core-0.iam.gserviceaccount.com + member: serviceAccount:fast-prod-resman-0r@fast-prod-iac-core-0.iam.gserviceaccount.com + project: fast-prod-iac-core-0 + timeouts: null + module.automation-tf-resman-r-sa.google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: + condition: [] + members: + - serviceAccount:fast-prod-resman-1r@fast-prod-iac-core-0.iam.gserviceaccount.com + - serviceAccount:fast-prod-resman-tenants-1r@fast-prod-iac-core-0.iam.gserviceaccount.com + role: roles/iam.serviceAccountTokenCreator + ? module.automation-tf-resman-r-sa.google_storage_bucket_iam_member.bucket-roles["fast-prod-iac-core-outputs-0-organizations/123456789012/roles/storageViewer"] + : bucket: fast-prod-iac-core-outputs-0 + condition: [] + role: organizations/123456789012/roles/storageViewer + module.automation-tf-resman-sa.google_service_account.service_account[0]: + account_id: fast-prod-resman-0 + create_ignore_already_exists: null + description: null + disabled: false + display_name: Terraform stage 1 resman service account. + email: fast-prod-resman-0@fast-prod-iac-core-0.iam.gserviceaccount.com + member: serviceAccount:fast-prod-resman-0@fast-prod-iac-core-0.iam.gserviceaccount.com + project: fast-prod-iac-core-0 + timeouts: null + module.automation-tf-resman-sa.google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: + condition: [] + members: + - serviceAccount:fast-prod-resman-1@fast-prod-iac-core-0.iam.gserviceaccount.com + - serviceAccount:fast-prod-resman-tenants-1@fast-prod-iac-core-0.iam.gserviceaccount.com + role: roles/iam.serviceAccountTokenCreator + ? module.automation-tf-resman-sa.google_storage_bucket_iam_member.bucket-roles["fast-prod-iac-core-outputs-0-roles/storage.admin"] + : bucket: fast-prod-iac-core-outputs-0 + condition: [] + role: roles/storage.admin + +counts: + google_bigquery_dataset: 1 + google_bigquery_default_service_account: 3 + google_essential_contacts_contact: 3 + google_iam_workload_identity_pool: 1 + google_iam_workload_identity_pool_provider: 2 + google_logging_organization_settings: 1 + google_logging_organization_sink: 4 + google_logging_project_bucket_config: 4 + google_org_policy_policy: 24 + google_organization_iam_binding: 27 + google_organization_iam_custom_role: 13 + google_organization_iam_member: 29 + google_project: 3 + google_project_iam_audit_config: 1 + google_project_iam_binding: 19 + google_project_iam_member: 22 + google_project_service: 31 + google_project_service_identity: 7 + google_service_account: 12 + google_service_account_iam_binding: 12 + google_service_account_iam_member: 1 + google_storage_bucket: 4 + google_storage_bucket_iam_binding: 4 + google_storage_bucket_iam_member: 12 + google_storage_bucket_object: 13 + google_storage_project_service_account: 3 + google_tags_tag_key: 1 + google_tags_tag_value: 2 + local_file: 13 + modules: 26 + resources: 272 diff --git a/tests/fast/stages/s0_bootstrap/iam_by_principals.tfvars b/tests/fast/stages/s0_bootstrap/iam_by_principals.tfvars index 4ceaffb6a..d9ed7eb75 100644 --- a/tests/fast/stages/s0_bootstrap/iam_by_principals.tfvars +++ b/tests/fast/stages/s0_bootstrap/iam_by_principals.tfvars @@ -1,20 +1,20 @@ +billing_account = { + id = "000000-111111-222222" +} +essential_contacts = "gcp-organization-admins@fast.example.com" +groups = { + gcp-support = "group:gcp-support@example.com" +} +org_policies_config = { + import_defaults = false +} organization = { domain = "fast.example.com" id = 123456789012 customer_id = "C00000000" } -billing_account = { - id = "000000-111111-222222" -} -essential_contacts = "gcp-organization-admins@fast.example.com" +outputs_location = "/fast-config" +prefix = "fast" iam_by_principals = { "user:other@fast.example.com" = ["roles/browser"] } -prefix = "fast" -org_policies_config = { - import_defaults = false -} -outputs_location = "/fast-config" -groups = { - gcp-support = "group:gcp-support@example.com" -} diff --git a/tests/fast/stages/s0_bootstrap/simple.tfvars b/tests/fast/stages/s0_bootstrap/simple.tfvars index 335283c3c..879a044e2 100644 --- a/tests/fast/stages/s0_bootstrap/simple.tfvars +++ b/tests/fast/stages/s0_bootstrap/simple.tfvars @@ -1,17 +1,17 @@ +billing_account = { + id = "000000-111111-222222" +} +essential_contacts = "gcp-organization-admins@fast.example.com" +groups = { + gcp-support = "group:gcp-support@example.com" +} +org_policies_config = { + import_defaults = false +} organization = { domain = "fast.example.com" id = 123456789012 customer_id = "C00000000" } -billing_account = { - id = "000000-111111-222222" -} -essential_contacts = "gcp-organization-admins@fast.example.com" -prefix = "fast" -org_policies_config = { - import_defaults = false -} outputs_location = "/fast-config" -groups = { - gcp-support = "group:gcp-support@example.com" -} +prefix = "fast" diff --git a/tests/fast/stages/s0_bootstrap/simple.yaml b/tests/fast/stages/s0_bootstrap/simple.yaml index 259d2e38b..17dc99a88 100644 --- a/tests/fast/stages/s0_bootstrap/simple.yaml +++ b/tests/fast/stages/s0_bootstrap/simple.yaml @@ -1,4 +1,4 @@ -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -30,16 +30,16 @@ counts: google_project_service: 31 google_project_service_identity: 7 google_service_account: 6 - google_service_account_iam_binding: 2 + google_service_account_iam_binding: 6 google_service_account_iam_member: 1 google_storage_bucket: 4 google_storage_bucket_iam_binding: 4 google_storage_bucket_iam_member: 6 - google_storage_bucket_object: 10 + google_storage_bucket_object: 8 google_storage_project_service_account: 3 google_tags_tag_key: 1 google_tags_tag_value: 2 - local_file: 10 + local_file: 8 modules: 20 resources: 235 @@ -70,8 +70,47 @@ outputs: bootstrap: fast-prod-bootstrap-0@fast-prod-iac-core-0.iam.gserviceaccount.com resman: fast-prod-resman-0@fast-prod-iac-core-0.iam.gserviceaccount.com tfvars: __missing__ + tfvars_globals: + billing_account: + force_create: + dataset: false + project: false + id: 000000-111111-222222 + is_org_level: true + no_iam: false + environments: + dev: + is_default: false + key: dev + name: Development + short_name: dev + tag_name: development + prod: + is_default: true + key: prod + name: Production + short_name: prod + tag_name: production + groups: + gcp-billing-admins: group:gcp-billing-admins@fast.example.com + gcp-devops: group:gcp-devops@fast.example.com + gcp-network-admins: group:gcp-vpc-network-admins@fast.example.com + gcp-organization-admins: group:gcp-organization-admins@fast.example.com + gcp-security-admins: group:gcp-security-admins@fast.example.com + gcp-support: group:gcp-support@example.com + locations: + bq: EU + gcs: EU + logging: global + pubsub: [] + organization: + customer_id: C00000000 + domain: fast.example.com + id: 123456789012 + prefix: fast workforce_identity_pool: pool: null workload_identity_pool: pool: null providers: {} + diff --git a/tests/fast/stages/s0_bootstrap/simple_sas.yaml b/tests/fast/stages/s0_bootstrap/simple_sas.yaml index 741885f2a..02c089ebd 100644 --- a/tests/fast/stages/s0_bootstrap/simple_sas.yaml +++ b/tests/fast/stages/s0_bootstrap/simple_sas.yaml @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/fast/stages/s0_bootstrap/tftest.yaml b/tests/fast/stages/s0_bootstrap/tftest.yaml index dd8319456..b51af9e3f 100644 --- a/tests/fast/stages/s0_bootstrap/tftest.yaml +++ b/tests/fast/stages/s0_bootstrap/tftest.yaml @@ -14,12 +14,11 @@ # limitations under the License. module: fast/stages/0-bootstrap - tests: simple: inventory: - simple.yaml - simple_projects.yaml - simple_sas.yaml - iam_by_principals: + cicd: \ No newline at end of file diff --git a/tests/fast/stages/s1_resman/__init__.py b/tests/fast/stages/s1_resman/__init__.py index 7ba50f933..c37e93b74 100644 --- a/tests/fast/stages/s1_resman/__init__.py +++ b/tests/fast/stages/s1_resman/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/fast/stages/s1_resman/simple.tfvars b/tests/fast/stages/s1_resman/simple.tfvars index b5297bc25..97adcfbdf 100644 --- a/tests/fast/stages/s1_resman/simple.tfvars +++ b/tests/fast/stages/s1_resman/simple.tfvars @@ -3,6 +3,20 @@ billing_account = { id = "000000-111111-222222" } +environments = { + dev = { + is_default = false + name = "Development" + short_name = "dev" + tag_name = "development" + } + prod = { + is_default = true + name = "Production" + short_name = "prod" + tag_name = "production" + } +} groups = { gcp-billing-admins = "gcp-billing-admins", gcp-devops = "gcp-devops", @@ -48,6 +62,7 @@ automation = { project_id = "fast2-prod-automation" project_number = 123456 service_accounts = { + resman = "fast2-prod-resman-0@fast2-prod-iac-core-0.iam.gserviceaccount.com" resman-r = "fast2-prod-resman-0r@fast2-prod-iac-core-0.iam.gserviceaccount.com" } } @@ -64,24 +79,17 @@ custom_roles = { service_project_network_admin = "organizations/123456789012/roles/xpnServiceAdmin" storage_viewer = "organizations/123456789012/roles/storageViewer" } -environments = { - dev = { - is_default = false - name = "Development" - tag_name = "development" - } - prod = { - is_default = true - name = "Production" - tag_name = "production" - } -} logging = { project_id = "fast-prod-log-audit-0" } # stage variables +fast_addon = { + ngfw = { + parent_stage = "2-networking" + } +} fast_stage_2 = { networking = { cicd_config = { @@ -105,9 +113,6 @@ fast_stage_2 = { } } } - network_security = { - enabled = true - } } tags = { context = { @@ -138,16 +143,10 @@ tags = { } top_level_folders = { tenants = { - name = "Tenants" - automation = { - enable = false - } + name = "Tenants" iam_by_principals = {} } shared = { name = "Shared Infrastructure" - automation = { - enable = false - } } } diff --git a/tests/fast/stages/s1_resman/simple.yaml b/tests/fast/stages/s1_resman/simple.yaml index 73290e911..6be8a34e0 100644 --- a/tests/fast/stages/s1_resman/simple.yaml +++ b/tests/fast/stages/s1_resman/simple.yaml @@ -1,4 +1,4 @@ -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -105,1672 +105,25 @@ values: : bucket: fast2-prod-iac-core-outputs condition: [] role: roles/storage.objectViewer - module.net-bucket[0].google_storage_bucket.bucket: - autoclass: [] - cors: [] - custom_placement_config: [] - default_event_based_hold: null - effective_labels: - goog-terraform-provisioned: 'true' - enable_object_retention: null - encryption: [] - force_destroy: false - hierarchical_namespace: [] - labels: null - lifecycle_rule: [] - location: EU - logging: [] - name: fast2-prod-resman-net-0 - project: fast2-prod-automation - requester_pays: null - retention_policy: [] - storage_class: STANDARD - terraform_labels: - goog-terraform-provisioned: 'true' - timeouts: null - uniform_bucket_level_access: true - versioning: - - enabled: true - module.net-bucket[0].google_storage_bucket_iam_binding.authoritative["roles/storage.objectAdmin"]: - bucket: fast2-prod-resman-net-0 - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectAdmin - module.net-bucket[0].google_storage_bucket_iam_binding.authoritative["roles/storage.objectViewer"]: - bucket: fast2-prod-resman-net-0 - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectViewer - module.net-folder-envs["dev"].google_folder.folder[0]: - deletion_protection: false - display_name: Development - tags: null - timeouts: null - module.net-folder-envs["dev"].google_tags_tag_binding.binding["environment"]: - timeouts: null - module.net-folder-envs["prod"].google_folder.folder[0]: - deletion_protection: false - display_name: Production - tags: null - timeouts: null - module.net-folder-envs["prod"].google_tags_tag_binding.binding["environment"]: - timeouts: null - module.net-folder[0].google_folder.folder[0]: - deletion_protection: false - display_name: Networking - tags: null - timeouts: null - ? module.net-folder[0].google_folder_iam_binding.authoritative["organizations/123456789012/roles/networkFirewallPoliciesAdmin"] - : condition: [] - members: - - serviceAccount:fast2-resman-nsec-0@fast2-prod-automation.iam.gserviceaccount.com - role: organizations/123456789012/roles/networkFirewallPoliciesAdmin - module.net-folder[0].google_folder_iam_binding.authoritative["organizations/123456789012/roles/projectIamViewer"]: - condition: [] - members: - - serviceAccount:fast2-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - role: organizations/123456789012/roles/projectIamViewer - module.net-folder[0].google_folder_iam_binding.authoritative["organizations/123456789012/roles/xpnServiceAdmin"]: - condition: [] - members: - - serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: organizations/123456789012/roles/xpnServiceAdmin - module.net-folder[0].google_folder_iam_binding.authoritative["roles/compute.networkViewer"]: - condition: [] - members: - - serviceAccount:fast2-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/compute.networkViewer - module.net-folder[0].google_folder_iam_binding.authoritative["roles/compute.orgFirewallPolicyUser"]: - condition: [] - members: - - serviceAccount:fast2-resman-nsec-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/compute.orgFirewallPolicyUser - module.net-folder[0].google_folder_iam_binding.authoritative["roles/compute.xpnAdmin"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/compute.xpnAdmin - module.net-folder[0].google_folder_iam_binding.authoritative["roles/editor"]: - condition: [] - members: - - group:gcp-vpc-network-admins@fast.example.com - role: roles/editor - module.net-folder[0].google_folder_iam_binding.authoritative["roles/logging.admin"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/logging.admin - module.net-folder[0].google_folder_iam_binding.authoritative["roles/owner"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/owner - module.net-folder[0].google_folder_iam_binding.authoritative["roles/resourcemanager.folderAdmin"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderAdmin - module.net-folder[0].google_folder_iam_binding.authoritative["roles/resourcemanager.folderViewer"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderViewer - module.net-folder[0].google_folder_iam_binding.authoritative["roles/resourcemanager.projectCreator"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.projectCreator - module.net-folder[0].google_folder_iam_binding.authoritative["roles/resourcemanager.tagUser"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.tagUser - module.net-folder[0].google_folder_iam_binding.authoritative["roles/resourcemanager.tagViewer"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.tagViewer - module.net-folder[0].google_folder_iam_binding.authoritative["roles/serviceusage.serviceUsageAdmin"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-sec-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/serviceusage.serviceUsageAdmin - module.net-folder[0].google_folder_iam_binding.authoritative["roles/serviceusage.serviceUsageConsumer"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-sec-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/serviceusage.serviceUsageConsumer - module.net-folder[0].google_folder_iam_binding.authoritative["roles/viewer"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/viewer - module.net-folder[0].google_folder_iam_binding.bindings["organizations/123456789012/roles/gcveNetworkAdmin:development"]: - condition: - - description: null - expression: "resource.matchTag(\n '123456789012/environment',\n 'development'\n\ - )\n" - title: stage 3 development - members: - - serviceAccount:fast2-dev-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: organizations/123456789012/roles/gcveNetworkAdmin - module.net-folder[0].google_folder_iam_binding.bindings["organizations/123456789012/roles/gcveNetworkAdmin:production"]: - condition: - - description: null - expression: "resource.matchTag(\n '123456789012/environment',\n 'production'\n\ - )\n" - title: stage 3 production - members: - - serviceAccount:fast2-prod-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: organizations/123456789012/roles/gcveNetworkAdmin - module.net-folder[0].google_folder_iam_binding.bindings["organizations/123456789012/roles/gcveNetworkViewer:development"]: - condition: - - description: null - expression: "resource.matchTag(\n '123456789012/environment',\n 'development'\n\ - )\n" - title: stage 3 development - members: - - serviceAccount:fast2-dev-resman-gcve-0r@fast2-prod-automation.iam.gserviceaccount.com - role: organizations/123456789012/roles/gcveNetworkViewer - module.net-folder[0].google_folder_iam_binding.bindings["organizations/123456789012/roles/gcveNetworkViewer:production"]: - condition: - - description: null - expression: "resource.matchTag(\n '123456789012/environment',\n 'production'\n\ - )\n" - title: stage 3 production - members: - - serviceAccount:fast2-prod-resman-gcve-0r@fast2-prod-automation.iam.gserviceaccount.com - role: organizations/123456789012/roles/gcveNetworkViewer - module.net-folder[0].google_folder_iam_binding.bindings["pf_delegated_grant"]: - condition: - - description: Project factory delegated grant. - expression: api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly(['roles/compute.networkUser', - 'roles/composer.sharedVpcAgent', 'roles/container.hostServiceAgentUser', 'roles/vpcaccess.user']) - title: project factory project delegated admin - members: - - serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.projectIamAdmin - module.net-folder[0].google_folder_iam_binding.bindings["roles/dns.admin:development"]: - condition: - - description: null - expression: "resource.matchTag(\n '123456789012/environment',\n 'development'\n\ - )\n" - title: stage 3 development - members: - - serviceAccount:fast2-dev-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/dns.admin - module.net-folder[0].google_folder_iam_binding.bindings["roles/dns.admin:production"]: - condition: - - description: null - expression: "resource.matchTag(\n '123456789012/environment',\n 'production'\n\ - )\n" - title: stage 3 production - members: - - serviceAccount:fast2-prod-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/dns.admin - module.net-folder[0].google_folder_iam_binding.bindings["roles/dns.reader:development"]: - condition: - - description: null - expression: "resource.matchTag(\n '123456789012/environment',\n 'development'\n\ - )\n" - title: stage 3 development - members: - - serviceAccount:fast2-dev-resman-gke-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/dns.reader - module.net-folder[0].google_folder_iam_binding.bindings["roles/dns.reader:production"]: - condition: - - description: null - expression: "resource.matchTag(\n '123456789012/environment',\n 'production'\n\ - )\n" - title: stage 3 production - members: - - serviceAccount:fast2-prod-resman-gke-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/dns.reader - module.net-folder[0].google_tags_tag_binding.binding["context"]: - timeouts: null - ? module.net-sa-ro[0].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.net-sa-ro[0].google_service_account.service_account[0]: - account_id: fast2-prod-resman-net-0r - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman networking service account (read-only). - email: fast2-prod-resman-net-0r@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-prod-resman-net-0r@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.net-sa-ro[0].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-1r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/iam.serviceAccountTokenCreator - ? module.net-sa-ro[0].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-organizations/123456789012/roles/storageViewer"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: organizations/123456789012/roles/storageViewer - ? module.net-sa-rw[0].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.net-sa-rw[0].google_service_account.service_account[0]: - account_id: fast2-prod-resman-net-0 - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman networking service account. - email: fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.net-sa-rw[0].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-1@fast2-prod-automation.iam.gserviceaccount.com - role: roles/iam.serviceAccountTokenCreator - module.net-sa-rw[0].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-roles/storage.objectAdmin"]: - bucket: fast2-prod-iac-core-outputs - condition: [] - role: roles/storage.objectAdmin - module.nsec-bucket[0].google_storage_bucket.bucket: - autoclass: [] - cors: [] - custom_placement_config: [] - default_event_based_hold: null - effective_labels: - goog-terraform-provisioned: 'true' - enable_object_retention: null - encryption: [] - force_destroy: false - hierarchical_namespace: [] - labels: null - lifecycle_rule: [] - location: EU - logging: [] - name: fast2-resman-nsec-0 - project: fast2-prod-automation - requester_pays: null - retention_policy: [] - storage_class: STANDARD - terraform_labels: - goog-terraform-provisioned: 'true' - timeouts: null - uniform_bucket_level_access: true - versioning: - - enabled: true - module.nsec-bucket[0].google_storage_bucket_iam_binding.authoritative["roles/storage.objectAdmin"]: - bucket: fast2-resman-nsec-0 - condition: [] - members: - - serviceAccount:fast2-resman-nsec-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectAdmin - module.nsec-bucket[0].google_storage_bucket_iam_binding.authoritative["roles/storage.objectViewer"]: - bucket: fast2-resman-nsec-0 - condition: [] - members: - - serviceAccount:fast2-resman-nsec-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectViewer - ? module.nsec-sa-ro[0].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.nsec-sa-ro[0].google_service_account.service_account[0]: - account_id: fast2-resman-nsec-0r - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman network security main service account (read-only). - email: fast2-resman-nsec-0r@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-resman-nsec-0r@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.nsec-sa-ro[0].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.nsec-sa-ro[0].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-organizations/123456789012/roles/storageViewer"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: organizations/123456789012/roles/storageViewer - ? module.nsec-sa-rw[0].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.nsec-sa-rw[0].google_service_account.service_account[0]: - account_id: fast2-resman-nsec-0 - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman network security main service account. - email: fast2-resman-nsec-0@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-resman-nsec-0@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.nsec-sa-rw[0].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.nsec-sa-rw[0].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-roles/storage.objectAdmin"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: roles/storage.objectAdmin - module.organization[0].google_organization_iam_member.bindings["gcve-dev"]: - condition: [] - member: serviceAccount:fast2-dev-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/billing.user - module.organization[0].google_organization_iam_member.bindings["gcve-prod"]: - condition: [] - member: serviceAccount:fast2-prod-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/billing.user - module.organization[0].google_organization_iam_member.bindings["gke-dev"]: - condition: [] - member: serviceAccount:fast2-dev-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/billing.user - module.organization[0].google_organization_iam_member.bindings["gke-prod"]: - condition: [] - member: serviceAccount:fast2-prod-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/billing.user - module.organization[0].google_organization_iam_member.bindings["project-factory-dev"]: - condition: [] - member: serviceAccount:fast2-dev-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/billing.user - module.organization[0].google_organization_iam_member.bindings["project-factory-prod"]: - condition: [] - member: serviceAccount:fast2-prod-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/billing.user - module.organization[0].google_organization_iam_member.bindings["sa_net_billing"]: - condition: [] - member: serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/billing.user - module.organization[0].google_organization_iam_member.bindings["sa_net_fw_policy_admin"]: - condition: [] - member: serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/compute.orgFirewallPolicyAdmin - module.organization[0].google_organization_iam_member.bindings["sa_net_nsec_fw_policy_user"]: - condition: [] - member: serviceAccount:fast2-resman-nsec-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/compute.orgFirewallPolicyUser - module.organization[0].google_organization_iam_member.bindings["sa_net_nsec_ngfw_enterprise_admin"]: - condition: [] - member: serviceAccount:fast2-resman-nsec-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: organizations/123456789012/roles/ngfwEnterpriseAdmin - module.organization[0].google_organization_iam_member.bindings["sa_net_nsec_ro_ngfw_enterprise_viewer"]: - condition: [] - member: serviceAccount:fast2-resman-nsec-0r@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: organizations/123456789012/roles/ngfwEnterpriseViewer - module.organization[0].google_organization_iam_member.bindings["sa_net_xpn_admin"]: - condition: [] - member: serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/compute.xpnAdmin - module.organization[0].google_organization_iam_member.bindings["sa_nsec_fw_policy_admin"]: - condition: [] - member: serviceAccount:fast2-resman-nsec-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/compute.orgFirewallPolicyAdmin - module.organization[0].google_organization_iam_member.bindings["sa_pf_billing"]: - condition: [] - member: serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/billing.user - module.organization[0].google_organization_iam_member.bindings["sa_pf_conditional_org_policy"]: - condition: - - description: Org policy tag scoped grant for project factory. - expression: 'resource.matchTag(''123456789012/context'', ''project-factory'') - - ' - title: org_policy_tag_pf_scoped - member: serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/orgpolicy.policyAdmin - module.organization[0].google_organization_iam_member.bindings["sa_pf_costs_manager"]: - condition: [] - member: serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/billing.costsManager - module.organization[0].google_organization_iam_member.bindings["sa_pf_ro_viewer"]: - condition: [] - member: serviceAccount:fast2-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: organizations/123456789012/roles/billingViewer - module.organization[0].google_organization_iam_member.bindings["sa_sec_asset_viewer"]: - condition: [] - member: serviceAccount:fast2-prod-resman-sec-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/cloudasset.viewer - module.organization[0].google_organization_iam_member.bindings["sa_sec_billing"]: - condition: [] - member: serviceAccount:fast2-prod-resman-sec-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/billing.user - module.organization[0].google_tags_tag_key.default["context"]: - description: Resource management context. - parent: organizations/123456789012 - purpose: null - purpose_data: null - short_name: context - timeouts: null - module.organization[0].google_tags_tag_key.default["environment"]: - description: Environment definition. - parent: organizations/123456789012 - purpose: null - purpose_data: null - short_name: environment - timeouts: null - module.organization[0].google_tags_tag_value.default["context/data-platform"]: - description: Managed by the Terraform organization module. - short_name: data-platform - timeouts: null - module.organization[0].google_tags_tag_value.default["context/gcve"]: - description: Managed by the Terraform organization module. - short_name: gcve - timeouts: null - module.organization[0].google_tags_tag_value.default["context/gke"]: - description: Managed by the Terraform organization module. - short_name: gke - timeouts: null - module.organization[0].google_tags_tag_value.default["context/network-security"]: - description: Managed by the Terraform organization module. - short_name: network-security - timeouts: null - module.organization[0].google_tags_tag_value.default["context/networking"]: - description: Managed by the Terraform organization module. - short_name: networking - timeouts: null - module.organization[0].google_tags_tag_value.default["context/nsec"]: - description: Managed by the Terraform organization module. - short_name: nsec - timeouts: null - module.organization[0].google_tags_tag_value.default["context/project-factory"]: - description: Managed by the Terraform organization module. - short_name: project-factory - timeouts: null - module.organization[0].google_tags_tag_value.default["context/sandbox"]: - description: Managed by the Terraform organization module. - short_name: sandbox - timeouts: null - module.organization[0].google_tags_tag_value.default["context/security"]: - description: Managed by the Terraform organization module. - short_name: security - timeouts: null - module.organization[0].google_tags_tag_value.default["context/tenants"]: - description: Managed by the Terraform organization module. - short_name: tenants - timeouts: null - module.organization[0].google_tags_tag_value.default["environment/development"]: - description: Managed by the Terraform organization module. - short_name: development - timeouts: null - module.organization[0].google_tags_tag_value.default["environment/production"]: - description: Managed by the Terraform organization module. - short_name: production - timeouts: null - module.organization[0].google_tags_tag_value_iam_binding.default["environment/development:roles/resourcemanager.tagUser"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - - serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - - serviceAccount:fast2-prod-resman-sec-0@fast2-prod-automation.iam.gserviceaccount.com - - serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.tagUser - ? module.organization[0].google_tags_tag_value_iam_binding.default["environment/development:roles/resourcemanager.tagViewer"] - : condition: [] - members: - - serviceAccount:fast2-dev-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - - serviceAccount:fast2-prod-resman-net-0r@fast2-prod-automation.iam.gserviceaccount.com - - serviceAccount:fast2-prod-resman-sec-0r@fast2-prod-automation.iam.gserviceaccount.com - - serviceAccount:fast2-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.tagViewer - module.organization[0].google_tags_tag_value_iam_binding.default["environment/production:roles/resourcemanager.tagUser"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - - serviceAccount:fast2-prod-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - - serviceAccount:fast2-prod-resman-sec-0@fast2-prod-automation.iam.gserviceaccount.com - - serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.tagUser - module.organization[0].google_tags_tag_value_iam_binding.default["environment/production:roles/resourcemanager.tagViewer"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0r@fast2-prod-automation.iam.gserviceaccount.com - - serviceAccount:fast2-prod-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - - serviceAccount:fast2-prod-resman-sec-0r@fast2-prod-automation.iam.gserviceaccount.com - - serviceAccount:fast2-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.tagViewer - module.pf-bucket[0].google_storage_bucket.bucket: - autoclass: [] - cors: [] - custom_placement_config: [] - default_event_based_hold: null - effective_labels: - goog-terraform-provisioned: 'true' - enable_object_retention: null - encryption: [] - force_destroy: false - hierarchical_namespace: [] - labels: null - lifecycle_rule: [] - location: EU - logging: [] - name: fast2-resman-pf-0 - project: fast2-prod-automation - requester_pays: null - retention_policy: [] - storage_class: STANDARD - terraform_labels: - goog-terraform-provisioned: 'true' - timeouts: null - uniform_bucket_level_access: true - versioning: - - enabled: true - module.pf-bucket[0].google_storage_bucket_iam_binding.authoritative["roles/storage.objectAdmin"]: - bucket: fast2-resman-pf-0 - condition: [] - members: - - serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectAdmin - module.pf-bucket[0].google_storage_bucket_iam_binding.authoritative["roles/storage.objectViewer"]: - bucket: fast2-resman-pf-0 - condition: [] - members: - - serviceAccount:fast2-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectViewer - ? module.pf-sa-ro[0].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.pf-sa-ro[0].google_service_account.service_account[0]: - account_id: fast2-resman-pf-0r - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman project factory main service account (read-only). - email: fast2-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.pf-sa-ro[0].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.pf-sa-ro[0].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-organizations/123456789012/roles/storageViewer"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: organizations/123456789012/roles/storageViewer - ? module.pf-sa-rw[0].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.pf-sa-rw[0].google_service_account.service_account[0]: - account_id: fast2-resman-pf-0 - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman project factory main service account. - email: fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.pf-sa-rw[0].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - module.pf-sa-rw[0].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-roles/storage.objectAdmin"]: - bucket: fast2-prod-iac-core-outputs - condition: [] - role: roles/storage.objectAdmin - module.sec-bucket[0].google_storage_bucket.bucket: - autoclass: [] - cors: [] - custom_placement_config: [] - default_event_based_hold: null - effective_labels: - goog-terraform-provisioned: 'true' - enable_object_retention: null - encryption: [] - force_destroy: false - hierarchical_namespace: [] - labels: null - lifecycle_rule: [] - location: EU - logging: [] - name: fast2-prod-resman-sec-0 - project: fast2-prod-automation - requester_pays: null - retention_policy: [] - storage_class: STANDARD - terraform_labels: - goog-terraform-provisioned: 'true' - timeouts: null - uniform_bucket_level_access: true - versioning: - - enabled: true - module.sec-bucket[0].google_storage_bucket_iam_binding.authoritative["roles/storage.objectAdmin"]: - bucket: fast2-prod-resman-sec-0 - condition: [] - members: - - serviceAccount:fast2-prod-resman-sec-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectAdmin - module.sec-bucket[0].google_storage_bucket_iam_binding.authoritative["roles/storage.objectViewer"]: - bucket: fast2-prod-resman-sec-0 - condition: [] - members: - - serviceAccount:fast2-prod-resman-sec-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectViewer - module.sec-folder[0].google_folder.folder[0]: - deletion_protection: false - display_name: Security - parent: organizations/123456789012 - tags: null - timeouts: null - module.sec-folder[0].google_folder_iam_binding.authoritative["organizations/123456789012/roles/projectIamViewer"]: - condition: [] - members: - - serviceAccount:fast2-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - role: organizations/123456789012/roles/projectIamViewer - module.sec-folder[0].google_folder_iam_binding.authoritative["roles/cloudkms.cryptoKeyEncrypterDecrypter"]: - condition: [] - members: - - serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/cloudkms.cryptoKeyEncrypterDecrypter - module.sec-folder[0].google_folder_iam_binding.authoritative["roles/cloudkms.viewer"]: - condition: [] - members: - - serviceAccount:fast2-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/cloudkms.viewer - module.sec-folder[0].google_folder_iam_binding.authoritative["roles/editor"]: - condition: [] - members: - - group:gcp-security-admins@fast.example.com - role: roles/editor - module.sec-folder[0].google_folder_iam_binding.authoritative["roles/logging.admin"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-sec-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/logging.admin - module.sec-folder[0].google_folder_iam_binding.authoritative["roles/owner"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-sec-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/owner - module.sec-folder[0].google_folder_iam_binding.authoritative["roles/resourcemanager.folderAdmin"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-sec-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderAdmin - module.sec-folder[0].google_folder_iam_binding.authoritative["roles/resourcemanager.folderViewer"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-sec-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderViewer - module.sec-folder[0].google_folder_iam_binding.authoritative["roles/resourcemanager.projectCreator"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-sec-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.projectCreator - module.sec-folder[0].google_folder_iam_binding.authoritative["roles/resourcemanager.tagUser"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.tagUser - module.sec-folder[0].google_folder_iam_binding.authoritative["roles/resourcemanager.tagViewer"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.tagViewer - module.sec-folder[0].google_folder_iam_binding.authoritative["roles/viewer"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-sec-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/viewer - module.sec-folder[0].google_folder_iam_binding.bindings["pf_delegated_grant"]: - condition: - - description: Project factory delegated grant. - expression: api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly(['roles/cloudkms.cryptoKeyEncrypterDecrypter']) - title: pf_delegated_grant - members: - - serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.projectIamAdmin - module.sec-folder[0].google_tags_tag_binding.binding["context"]: - timeouts: null - ? module.sec-sa-ro[0].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.sec-sa-ro[0].google_service_account.service_account[0]: - account_id: fast2-prod-resman-sec-0r - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman security service account (read-only). - email: fast2-prod-resman-sec-0r@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-prod-resman-sec-0r@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.sec-sa-ro[0].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-sec-1r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/iam.serviceAccountTokenCreator - ? module.sec-sa-ro[0].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-organizations/123456789012/roles/storageViewer"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: organizations/123456789012/roles/storageViewer - ? module.sec-sa-rw[0].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.sec-sa-rw[0].google_service_account.service_account[0]: - account_id: fast2-prod-resman-sec-0 - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman security service account. - email: fast2-prod-resman-sec-0@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-prod-resman-sec-0@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.sec-sa-rw[0].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-sec-1@fast2-prod-automation.iam.gserviceaccount.com - role: roles/iam.serviceAccountTokenCreator - module.sec-sa-rw[0].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-roles/storage.objectAdmin"]: - bucket: fast2-prod-iac-core-outputs - condition: [] - role: roles/storage.objectAdmin - module.stage3-bucket["gcve-dev"].google_storage_bucket.bucket: - autoclass: [] - cors: [] - custom_placement_config: [] - default_event_based_hold: null - effective_labels: - goog-terraform-provisioned: 'true' - enable_object_retention: null - encryption: [] - force_destroy: false - hierarchical_namespace: [] - labels: null - lifecycle_rule: [] - location: EU - logging: [] - name: fast2-dev-resman-gcve-0 - project: fast2-prod-automation - requester_pays: null - retention_policy: [] - storage_class: STANDARD - terraform_labels: - goog-terraform-provisioned: 'true' - timeouts: null - uniform_bucket_level_access: true - versioning: - - enabled: true - module.stage3-bucket["gcve-dev"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectAdmin"]: - bucket: fast2-dev-resman-gcve-0 - condition: [] - members: - - serviceAccount:fast2-dev-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectAdmin - module.stage3-bucket["gcve-dev"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectViewer"]: - bucket: fast2-dev-resman-gcve-0 - condition: [] - members: - - serviceAccount:fast2-dev-resman-gcve-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectViewer - module.stage3-bucket["gcve-prod"].google_storage_bucket.bucket: - autoclass: [] - cors: [] - custom_placement_config: [] - default_event_based_hold: null - effective_labels: - goog-terraform-provisioned: 'true' - enable_object_retention: null - encryption: [] - force_destroy: false - hierarchical_namespace: [] - labels: null - lifecycle_rule: [] - location: EU - logging: [] - name: fast2-prod-resman-gcve-0 - project: fast2-prod-automation - requester_pays: null - retention_policy: [] - storage_class: STANDARD - terraform_labels: - goog-terraform-provisioned: 'true' - timeouts: null - uniform_bucket_level_access: true - versioning: - - enabled: true - module.stage3-bucket["gcve-prod"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectAdmin"]: - bucket: fast2-prod-resman-gcve-0 - condition: [] - members: - - serviceAccount:fast2-prod-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectAdmin - module.stage3-bucket["gcve-prod"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectViewer"]: - bucket: fast2-prod-resman-gcve-0 - condition: [] - members: - - serviceAccount:fast2-prod-resman-gcve-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectViewer - module.stage3-bucket["gke-dev"].google_storage_bucket.bucket: - autoclass: [] - cors: [] - custom_placement_config: [] - default_event_based_hold: null - effective_labels: - goog-terraform-provisioned: 'true' - enable_object_retention: null - encryption: [] - force_destroy: false - hierarchical_namespace: [] - labels: null - lifecycle_rule: [] - location: EU - logging: [] - name: fast2-dev-resman-gke-0 - project: fast2-prod-automation - requester_pays: null - retention_policy: [] - storage_class: STANDARD - terraform_labels: - goog-terraform-provisioned: 'true' - timeouts: null - uniform_bucket_level_access: true - versioning: - - enabled: true - module.stage3-bucket["gke-dev"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectAdmin"]: - bucket: fast2-dev-resman-gke-0 - condition: [] - members: - - serviceAccount:fast2-dev-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectAdmin - module.stage3-bucket["gke-dev"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectViewer"]: - bucket: fast2-dev-resman-gke-0 - condition: [] - members: - - serviceAccount:fast2-dev-resman-gke-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectViewer - module.stage3-bucket["gke-prod"].google_storage_bucket.bucket: - autoclass: [] - cors: [] - custom_placement_config: [] - default_event_based_hold: null - effective_labels: - goog-terraform-provisioned: 'true' - enable_object_retention: null - encryption: [] - force_destroy: false - hierarchical_namespace: [] - labels: null - lifecycle_rule: [] - location: EU - logging: [] - name: fast2-prod-resman-gke-0 - project: fast2-prod-automation - requester_pays: null - retention_policy: [] - storage_class: STANDARD - terraform_labels: - goog-terraform-provisioned: 'true' - timeouts: null - uniform_bucket_level_access: true - versioning: - - enabled: true - module.stage3-bucket["gke-prod"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectAdmin"]: - bucket: fast2-prod-resman-gke-0 - condition: [] - members: - - serviceAccount:fast2-prod-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectAdmin - module.stage3-bucket["gke-prod"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectViewer"]: - bucket: fast2-prod-resman-gke-0 - condition: [] - members: - - serviceAccount:fast2-prod-resman-gke-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectViewer - module.stage3-bucket["project-factory-dev"].google_storage_bucket.bucket: - autoclass: [] - cors: [] - custom_placement_config: [] - default_event_based_hold: null - effective_labels: - goog-terraform-provisioned: 'true' - enable_object_retention: null - encryption: [] - force_destroy: false - hierarchical_namespace: [] - labels: null - lifecycle_rule: [] - location: EU - logging: [] - name: fast2-dev-resman-pf-0 - project: fast2-prod-automation - requester_pays: null - retention_policy: [] - storage_class: STANDARD - terraform_labels: - goog-terraform-provisioned: 'true' - timeouts: null - uniform_bucket_level_access: true - versioning: - - enabled: true - module.stage3-bucket["project-factory-dev"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectAdmin"]: - bucket: fast2-dev-resman-pf-0 - condition: [] - members: - - serviceAccount:fast2-dev-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectAdmin - module.stage3-bucket["project-factory-dev"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectViewer"]: - bucket: fast2-dev-resman-pf-0 - condition: [] - members: - - serviceAccount:fast2-dev-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectViewer - module.stage3-bucket["project-factory-prod"].google_storage_bucket.bucket: - autoclass: [] - cors: [] - custom_placement_config: [] - default_event_based_hold: null - effective_labels: - goog-terraform-provisioned: 'true' - enable_object_retention: null - encryption: [] - force_destroy: false - hierarchical_namespace: [] - labels: null - lifecycle_rule: [] - location: EU - logging: [] - name: fast2-prod-resman-pf-0 - project: fast2-prod-automation - requester_pays: null - retention_policy: [] - storage_class: STANDARD - terraform_labels: - goog-terraform-provisioned: 'true' - timeouts: null - uniform_bucket_level_access: true - versioning: - - enabled: true - module.stage3-bucket["project-factory-prod"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectAdmin"]: - bucket: fast2-prod-resman-pf-0 - condition: [] - members: - - serviceAccount:fast2-prod-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectAdmin - module.stage3-bucket["project-factory-prod"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectViewer"]: - bucket: fast2-prod-resman-pf-0 - condition: [] - members: - - serviceAccount:fast2-prod-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectViewer - module.stage3-folder["gcve-dev"].google_folder.folder[0]: - deletion_protection: false - display_name: Development - tags: null - timeouts: null - module.stage3-folder["gcve-dev"].google_folder_iam_binding.authoritative["roles/compute.xpnAdmin"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/compute.xpnAdmin - module.stage3-folder["gcve-dev"].google_folder_iam_binding.authoritative["roles/logging.admin"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/logging.admin - module.stage3-folder["gcve-dev"].google_folder_iam_binding.authoritative["roles/owner"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/owner - module.stage3-folder["gcve-dev"].google_folder_iam_binding.authoritative["roles/resourcemanager.folderAdmin"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderAdmin - module.stage3-folder["gcve-dev"].google_folder_iam_binding.authoritative["roles/resourcemanager.folderViewer"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gcve-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderViewer - module.stage3-folder["gcve-dev"].google_folder_iam_binding.authoritative["roles/resourcemanager.projectCreator"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.projectCreator - module.stage3-folder["gcve-dev"].google_folder_iam_binding.authoritative["roles/viewer"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gcve-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/viewer - module.stage3-folder["gcve-dev"].google_tags_tag_binding.binding["environment"]: - timeouts: null - module.stage3-folder["gcve-prod"].google_folder.folder[0]: - deletion_protection: false - display_name: Production - tags: null - timeouts: null - module.stage3-folder["gcve-prod"].google_folder_iam_binding.authoritative["roles/compute.xpnAdmin"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/compute.xpnAdmin - module.stage3-folder["gcve-prod"].google_folder_iam_binding.authoritative["roles/logging.admin"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/logging.admin - module.stage3-folder["gcve-prod"].google_folder_iam_binding.authoritative["roles/owner"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/owner - module.stage3-folder["gcve-prod"].google_folder_iam_binding.authoritative["roles/resourcemanager.folderAdmin"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderAdmin - module.stage3-folder["gcve-prod"].google_folder_iam_binding.authoritative["roles/resourcemanager.folderViewer"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gcve-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderViewer - module.stage3-folder["gcve-prod"].google_folder_iam_binding.authoritative["roles/resourcemanager.projectCreator"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.projectCreator - module.stage3-folder["gcve-prod"].google_folder_iam_binding.authoritative["roles/viewer"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gcve-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/viewer - module.stage3-folder["gcve-prod"].google_tags_tag_binding.binding["environment"]: - timeouts: null - module.stage3-folder["gke-dev"].google_folder.folder[0]: - deletion_protection: false - display_name: Development - tags: null - timeouts: null - module.stage3-folder["gke-dev"].google_folder_iam_binding.authoritative["roles/compute.xpnAdmin"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/compute.xpnAdmin - module.stage3-folder["gke-dev"].google_folder_iam_binding.authoritative["roles/logging.admin"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/logging.admin - module.stage3-folder["gke-dev"].google_folder_iam_binding.authoritative["roles/owner"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/owner - module.stage3-folder["gke-dev"].google_folder_iam_binding.authoritative["roles/resourcemanager.folderAdmin"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderAdmin - module.stage3-folder["gke-dev"].google_folder_iam_binding.authoritative["roles/resourcemanager.folderViewer"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gke-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderViewer - module.stage3-folder["gke-dev"].google_folder_iam_binding.authoritative["roles/resourcemanager.projectCreator"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.projectCreator - module.stage3-folder["gke-dev"].google_folder_iam_binding.authoritative["roles/viewer"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gke-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/viewer - module.stage3-folder["gke-dev"].google_tags_tag_binding.binding["environment"]: - timeouts: null - module.stage3-folder["gke-prod"].google_folder.folder[0]: - deletion_protection: false - display_name: Production - tags: null - timeouts: null - module.stage3-folder["gke-prod"].google_folder_iam_binding.authoritative["roles/compute.xpnAdmin"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/compute.xpnAdmin - module.stage3-folder["gke-prod"].google_folder_iam_binding.authoritative["roles/logging.admin"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/logging.admin - module.stage3-folder["gke-prod"].google_folder_iam_binding.authoritative["roles/owner"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/owner - module.stage3-folder["gke-prod"].google_folder_iam_binding.authoritative["roles/resourcemanager.folderAdmin"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderAdmin - module.stage3-folder["gke-prod"].google_folder_iam_binding.authoritative["roles/resourcemanager.folderViewer"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gke-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderViewer - module.stage3-folder["gke-prod"].google_folder_iam_binding.authoritative["roles/resourcemanager.projectCreator"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.projectCreator - module.stage3-folder["gke-prod"].google_folder_iam_binding.authoritative["roles/viewer"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gke-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/viewer - module.stage3-folder["gke-prod"].google_tags_tag_binding.binding["environment"]: - timeouts: null - ? module.stage3-sa-ro["gcve-dev"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.stage3-sa-ro["gcve-dev"].google_service_account.service_account[0]: - account_id: fast2-dev-resman-gcve-0r - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman gcve-dev service account (read-only). - email: fast2-dev-resman-gcve-0r@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-dev-resman-gcve-0r@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.stage3-sa-ro["gcve-dev"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.stage3-sa-ro["gcve-dev"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-organizations/123456789012/roles/storageViewer"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: organizations/123456789012/roles/storageViewer - ? module.stage3-sa-ro["gcve-prod"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.stage3-sa-ro["gcve-prod"].google_service_account.service_account[0]: - account_id: fast2-prod-resman-gcve-0r - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman gcve-prod service account (read-only). - email: fast2-prod-resman-gcve-0r@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-prod-resman-gcve-0r@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.stage3-sa-ro["gcve-prod"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.stage3-sa-ro["gcve-prod"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-organizations/123456789012/roles/storageViewer"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: organizations/123456789012/roles/storageViewer - ? module.stage3-sa-ro["gke-dev"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.stage3-sa-ro["gke-dev"].google_service_account.service_account[0]: - account_id: fast2-dev-resman-gke-0r - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman gke-dev service account (read-only). - email: fast2-dev-resman-gke-0r@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-dev-resman-gke-0r@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.stage3-sa-ro["gke-dev"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.stage3-sa-ro["gke-dev"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-organizations/123456789012/roles/storageViewer"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: organizations/123456789012/roles/storageViewer - ? module.stage3-sa-ro["gke-prod"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.stage3-sa-ro["gke-prod"].google_service_account.service_account[0]: - account_id: fast2-prod-resman-gke-0r - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman gke-prod service account (read-only). - email: fast2-prod-resman-gke-0r@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-prod-resman-gke-0r@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.stage3-sa-ro["gke-prod"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.stage3-sa-ro["gke-prod"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-organizations/123456789012/roles/storageViewer"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: organizations/123456789012/roles/storageViewer - ? module.stage3-sa-ro["project-factory-dev"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.stage3-sa-ro["project-factory-dev"].google_service_account.service_account[0]: - account_id: fast2-dev-resman-pf-0r - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman project-factory-dev service account (read-only). - email: fast2-dev-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-dev-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - ? module.stage3-sa-ro["project-factory-dev"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"] - : condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.stage3-sa-ro["project-factory-dev"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-organizations/123456789012/roles/storageViewer"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: organizations/123456789012/roles/storageViewer - ? module.stage3-sa-ro["project-factory-prod"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.stage3-sa-ro["project-factory-prod"].google_service_account.service_account[0]: - account_id: fast2-prod-resman-pf-0r - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman project-factory-prod service account (read-only). - email: fast2-prod-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-prod-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - ? module.stage3-sa-ro["project-factory-prod"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"] - : condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.stage3-sa-ro["project-factory-prod"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-organizations/123456789012/roles/storageViewer"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: organizations/123456789012/roles/storageViewer - ? module.stage3-sa-rw["gcve-dev"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.stage3-sa-rw["gcve-dev"].google_service_account.service_account[0]: - account_id: fast2-dev-resman-gcve-0 - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman gcve-dev service account. - email: fast2-dev-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-dev-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.stage3-sa-rw["gcve-dev"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.stage3-sa-rw["gcve-dev"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-roles/storage.objectAdmin"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: roles/storage.objectAdmin - ? module.stage3-sa-rw["gcve-prod"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.stage3-sa-rw["gcve-prod"].google_service_account.service_account[0]: - account_id: fast2-prod-resman-gcve-0 - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman gcve-prod service account. - email: fast2-prod-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-prod-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.stage3-sa-rw["gcve-prod"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.stage3-sa-rw["gcve-prod"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-roles/storage.objectAdmin"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: roles/storage.objectAdmin - ? module.stage3-sa-rw["gke-dev"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.stage3-sa-rw["gke-dev"].google_service_account.service_account[0]: - account_id: fast2-dev-resman-gke-0 - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman gke-dev service account. - email: fast2-dev-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-dev-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.stage3-sa-rw["gke-dev"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.stage3-sa-rw["gke-dev"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-roles/storage.objectAdmin"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: roles/storage.objectAdmin - ? module.stage3-sa-rw["gke-prod"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.stage3-sa-rw["gke-prod"].google_service_account.service_account[0]: - account_id: fast2-prod-resman-gke-0 - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman gke-prod service account. - email: fast2-prod-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-prod-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.stage3-sa-rw["gke-prod"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.stage3-sa-rw["gke-prod"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-roles/storage.objectAdmin"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: roles/storage.objectAdmin - ? module.stage3-sa-rw["project-factory-dev"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.stage3-sa-rw["project-factory-dev"].google_service_account.service_account[0]: - account_id: fast2-dev-resman-pf-0 - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman project-factory-dev service account. - email: fast2-dev-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-dev-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - ? module.stage3-sa-rw["project-factory-dev"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"] - : condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.stage3-sa-rw["project-factory-dev"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-roles/storage.objectAdmin"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: roles/storage.objectAdmin - ? module.stage3-sa-rw["project-factory-prod"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.stage3-sa-rw["project-factory-prod"].google_service_account.service_account[0]: - account_id: fast2-prod-resman-pf-0 - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman project-factory-prod service account. - email: fast2-prod-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-prod-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - ? module.stage3-sa-rw["project-factory-prod"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"] - : condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.stage3-sa-rw["project-factory-prod"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-roles/storage.objectAdmin"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: roles/storage.objectAdmin - module.top-level-bucket["sandbox"].google_storage_bucket.bucket: - autoclass: [] - cors: [] - custom_placement_config: [] - default_event_based_hold: null - effective_labels: - goog-terraform-provisioned: 'true' - enable_object_retention: null - encryption: [] - force_destroy: false - hierarchical_namespace: [] - labels: null - lifecycle_rule: [] - location: EU - logging: [] - name: fast2-dev-resman-sbox-0 - project: fast2-prod-automation - requester_pays: null - retention_policy: [] - storage_class: STANDARD - terraform_labels: - goog-terraform-provisioned: 'true' - timeouts: null - uniform_bucket_level_access: true - versioning: - - enabled: true - module.top-level-bucket["sandbox"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectAdmin"]: - bucket: fast2-dev-resman-sbox-0 - condition: [] - members: - - serviceAccount:fast2-dev-resman-sbox-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectAdmin - module.top-level-bucket["sandbox"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectViewer"]: - bucket: fast2-dev-resman-sbox-0 - condition: [] - members: - - serviceAccount:fast2-dev-resman-sbox-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectViewer - module.top-level-bucket["tenants"].google_storage_bucket.bucket: - autoclass: [] - cors: [] - custom_placement_config: [] - default_event_based_hold: null - effective_labels: - goog-terraform-provisioned: 'true' - enable_object_retention: null - encryption: [] - force_destroy: false - hierarchical_namespace: [] - labels: null - lifecycle_rule: [] - location: EU - logging: [] - name: fast2-prod-resman-tenants-0 - project: fast2-prod-automation - requester_pays: null - retention_policy: [] - storage_class: STANDARD - terraform_labels: - goog-terraform-provisioned: 'true' - timeouts: null - uniform_bucket_level_access: true - versioning: - - enabled: true - module.top-level-bucket["tenants"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectAdmin"]: - bucket: fast2-prod-resman-tenants-0 - condition: [] - members: - - serviceAccount:fast2-prod-resman-tenants-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectAdmin - module.top-level-bucket["tenants"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectViewer"]: - bucket: fast2-prod-resman-tenants-0 - condition: [] - members: - - serviceAccount:fast2-prod-resman-tenants-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectViewer - module.top-level-folder["gcve"].google_folder.folder[0]: - deletion_protection: false - display_name: GCVE - parent: organizations/123456789012 - tags: null - timeouts: null - module.top-level-folder["gcve"].google_tags_tag_binding.binding["context"]: - timeouts: null - module.top-level-folder["gke"].google_folder.folder[0]: - deletion_protection: false - display_name: GKE - parent: organizations/123456789012 - tags: null - timeouts: null - module.top-level-folder["gke"].google_tags_tag_binding.binding["context"]: - timeouts: null - module.top-level-folder["sandbox"].google_folder.folder[0]: - deletion_protection: false - display_name: Sandbox - parent: organizations/123456789012 - tags: null - timeouts: null - module.top-level-folder["teams"].google_folder.folder[0]: - deletion_protection: false - display_name: Teams - parent: organizations/123456789012 - tags: null - timeouts: null - ? module.top-level-folder["teams"].google_folder_iam_binding.authoritative["organizations/123456789012/roles/xpnServiceAdmin"] - : condition: [] - members: - - serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: organizations/123456789012/roles/xpnServiceAdmin - module.top-level-folder["teams"].google_folder_iam_binding.authoritative["roles/owner"]: - condition: [] - members: - - serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/owner - module.top-level-folder["teams"].google_folder_iam_binding.authoritative["roles/resourcemanager.folderAdmin"]: - condition: [] - members: - - serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderAdmin - module.top-level-folder["teams"].google_folder_iam_binding.authoritative["roles/resourcemanager.folderViewer"]: - condition: [] - members: - - serviceAccount:fast2-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderViewer - module.top-level-folder["teams"].google_folder_iam_binding.authoritative["roles/resourcemanager.projectCreator"]: - condition: [] - members: - - serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.projectCreator - module.top-level-folder["teams"].google_folder_iam_binding.authoritative["roles/resourcemanager.tagUser"]: - condition: [] - members: - - serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.tagUser - module.top-level-folder["teams"].google_folder_iam_binding.authoritative["roles/resourcemanager.tagViewer"]: - condition: [] - members: - - serviceAccount:fast2-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.tagViewer - module.top-level-folder["teams"].google_folder_iam_binding.authoritative["roles/viewer"]: - condition: [] - members: - - serviceAccount:fast2-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/viewer - module.top-level-folder["teams"].google_tags_tag_binding.binding["context"]: - timeouts: null - module.top-level-folder["tenants"].google_folder.folder[0]: - deletion_protection: false - display_name: Tenants - parent: organizations/123456789012 - tags: null - timeouts: null - module.top-level-folder["tenants"].google_tags_tag_binding.binding["context"]: - timeouts: null - ? module.top-level-sa["sandbox"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.top-level-sa["sandbox"].google_service_account.service_account[0]: - account_id: fast2-dev-resman-sbox-0 - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman sandbox folder service account. - email: fast2-dev-resman-sbox-0@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-dev-resman-sbox-0@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.top-level-sa["sandbox"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.top-level-sa["sandbox"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-roles/storage.objectAdmin"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: roles/storage.objectAdmin - ? module.top-level-sa["tenants"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.top-level-sa["tenants"].google_service_account.service_account[0]: - account_id: fast2-prod-resman-tenants-0 - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman tenants folder service account. - email: fast2-prod-resman-tenants-0@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-prod-resman-tenants-0@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.top-level-sa["tenants"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.top-level-sa["tenants"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-roles/storage.objectAdmin"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: roles/storage.objectAdmin counts: google_folder: 14 - google_folder_iam_binding: 75 + google_folder_iam_binding: 73 google_org_policy_policy: 2 - google_organization_iam_member: 19 - google_project_iam_member: 27 - google_service_account: 27 - google_service_account_iam_binding: 27 - google_storage_bucket: 13 - google_storage_bucket_iam_binding: 26 - google_storage_bucket_iam_member: 27 - google_storage_bucket_object: 26 + google_organization_iam_member: 18 + google_project_iam_member: 23 + google_service_account: 23 + google_service_account_iam_binding: 23 + google_storage_bucket: 10 + google_storage_bucket_iam_binding: 20 + google_storage_bucket_iam_member: 23 + google_storage_bucket_object: 24 google_tags_tag_binding: 14 google_tags_tag_key: 2 - google_tags_tag_value: 13 + google_tags_tag_value: 12 google_tags_tag_value_iam_binding: 4 - modules: 55 - resources: 316 + modules: 48 + resources: 285 outputs: cicd_repositories: diff --git a/tests/fast/stages/s2_network_security/simple.tfvars b/tests/fast/stages/s2_network_security/simple.tfvars deleted file mode 100644 index 1ab02d8d2..000000000 --- a/tests/fast/stages/s2_network_security/simple.tfvars +++ /dev/null @@ -1,25 +0,0 @@ -automation = { - outputs_bucket = "test" -} -billing_account = { - id = "000000-111111-222222" -} -folder_ids = { - networking = "folders/12345678900" - networking-dev = "folders/12345678901" - networking-prod = "folders/12345678902" -} -host_project_ids = { - dev-spoke-0 = "dev-project" - prod-spoke-0 = "prod-project" -} -organization = { - domain = "fast.example.com" - id = 123456789012 - customer_id = "C00000000" -} -prefix = "fast2" -vpc_self_links = { - dev-spoke-0 = "https://www.googleapis.com/compute/v1/projects/123456789/networks/vpc-1" - prod-spoke-0 = "https://www.googleapis.com/compute/v1/projects/123456789/networks/vpc-2" -} diff --git a/tests/fast/stages/s2_network_security/tls.tfvars b/tests/fast/stages/s2_network_security/tls.tfvars deleted file mode 100644 index ecd91f565..000000000 --- a/tests/fast/stages/s2_network_security/tls.tfvars +++ /dev/null @@ -1,43 +0,0 @@ -automation = { - outputs_bucket = "test" -} -billing_account = { - id = "000000-111111-222222" -} -folder_ids = { - networking = "folders/12345678900" - networking-dev = "folders/12345678901" - networking-prod = "folders/12345678902" -} -host_project_ids = { - dev-spoke-0 = "dev-project" - prod-spoke-0 = "prod-project" -} -ngfw_enterprise_config = { - endpoint_zones = [ - "europe-west1-b", - "europe-west1-c", - "europe-west1-d" - ] -} -ngfw_tls_configs = { - tls_enabled = true - tls_ip_ids_by_region = { - dev = { - europe-west1 = "projects/project1/locations/europe-west1/tlsInspectionPolicies/dev-tls-ip-0" - } - prod = { - europe-west1 = "projects/project1/locations/europe-west1/tlsInspectionPolicies/prod-tls-ip-0" - } - } -} -organization = { - domain = "fast.example.com" - id = 123456789012 - customer_id = "C00000000" -} -prefix = "fast2" -vpc_self_links = { - dev-spoke-0 = "https://www.googleapis.com/compute/v1/projects/123456789/networks/vpc-1" - prod-spoke-0 = "https://www.googleapis.com/compute/v1/projects/123456789/networks/vpc-2" -} diff --git a/tests/fast/stages/s2_network_security/tls.yaml b/tests/fast/stages/s2_network_security/tls.yaml deleted file mode 100644 index 8c6e0a93d..000000000 --- a/tests/fast/stages/s2_network_security/tls.yaml +++ /dev/null @@ -1,325 +0,0 @@ -# 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: - google_network_security_firewall_endpoint.firewall_endpoint["europe-west1-b"]: - billing_project_id: fast2-net-ngfw-0 - labels: null - location: europe-west1-b - name: fast2-ngfw-endpoint-europe-west1-b - parent: organizations/123456789012 - timeouts: null - google_network_security_firewall_endpoint.firewall_endpoint["europe-west1-c"]: - billing_project_id: fast2-net-ngfw-0 - labels: null - location: europe-west1-c - name: fast2-ngfw-endpoint-europe-west1-c - parent: organizations/123456789012 - timeouts: null - google_network_security_firewall_endpoint.firewall_endpoint["europe-west1-d"]: - billing_project_id: fast2-net-ngfw-0 - labels: null - location: europe-west1-d - name: fast2-ngfw-endpoint-europe-west1-d - parent: organizations/123456789012 - timeouts: null - google_network_security_firewall_endpoint_association.dev["europe-west1-b"]: - disabled: false - labels: null - location: europe-west1-b - name: fast2-dev-epa-europe-west1-b - network: projects/123456789/networks/vpc-1 - parent: projects/dev-project - timeouts: null - tls_inspection_policy: projects/project1/locations/europe-west1/tlsInspectionPolicies/dev-tls-ip-0 - google_network_security_firewall_endpoint_association.dev["europe-west1-c"]: - disabled: false - labels: null - location: europe-west1-c - name: fast2-dev-epa-europe-west1-c - network: projects/123456789/networks/vpc-1 - parent: projects/dev-project - timeouts: null - tls_inspection_policy: projects/project1/locations/europe-west1/tlsInspectionPolicies/dev-tls-ip-0 - google_network_security_firewall_endpoint_association.dev["europe-west1-d"]: - disabled: false - labels: null - location: europe-west1-d - name: fast2-dev-epa-europe-west1-d - network: projects/123456789/networks/vpc-1 - parent: projects/dev-project - timeouts: null - tls_inspection_policy: projects/project1/locations/europe-west1/tlsInspectionPolicies/dev-tls-ip-0 - google_network_security_firewall_endpoint_association.prod["europe-west1-b"]: - disabled: false - labels: null - location: europe-west1-b - name: fast2-prod-epa-europe-west1-b - network: projects/123456789/networks/vpc-2 - parent: projects/prod-project - timeouts: null - tls_inspection_policy: projects/project1/locations/europe-west1/tlsInspectionPolicies/prod-tls-ip-0 - google_network_security_firewall_endpoint_association.prod["europe-west1-c"]: - disabled: false - labels: null - location: europe-west1-c - name: fast2-prod-epa-europe-west1-c - network: projects/123456789/networks/vpc-2 - parent: projects/prod-project - timeouts: null - tls_inspection_policy: projects/project1/locations/europe-west1/tlsInspectionPolicies/prod-tls-ip-0 - google_network_security_firewall_endpoint_association.prod["europe-west1-d"]: - disabled: false - labels: null - location: europe-west1-d - name: fast2-prod-epa-europe-west1-d - network: projects/123456789/networks/vpc-2 - parent: projects/prod-project - timeouts: null - tls_inspection_policy: projects/project1/locations/europe-west1/tlsInspectionPolicies/prod-tls-ip-0 - google_network_security_security_profile.dev: - description: null - labels: null - location: global - name: fast2-dev-sp-0 - parent: organizations/123456789012 - threat_prevention_profile: [] - timeouts: null - type: THREAT_PREVENTION - google_network_security_security_profile.prod: - description: null - labels: null - location: global - name: fast2-prod-sp-0 - parent: organizations/123456789012 - threat_prevention_profile: [] - timeouts: null - type: THREAT_PREVENTION - google_network_security_security_profile_group.dev: - description: Dev security profile group. - labels: null - location: global - name: fast2-dev-spg-0 - parent: organizations/123456789012 - timeouts: null - google_network_security_security_profile_group.prod: - description: prod security profile group. - labels: null - location: global - name: fast2-prod-spg-0 - parent: organizations/123456789012 - timeouts: null - google_storage_bucket_object.tfvars: - bucket: test - cache_control: null - content_disposition: null - content_encoding: null - content_language: null - customer_encryption: [] - detect_md5hash: different hash - event_based_hold: null - metadata: null - name: tfvars/2-nsec.auto.tfvars.json - retention: [] - source: null - temporary_hold: null - timeouts: null - module.dev-spoke-firewall-policy.google_compute_network_firewall_policy.net-global[0]: - description: null - name: fast2-dev-fw-policy - project: dev-project - timeouts: null - module.dev-spoke-firewall-policy.google_compute_network_firewall_policy_association.net-global["dev-spoke"]: - attachment_target: https://www.googleapis.com/compute/v1/projects/123456789/networks/vpc-1 - firewall_policy: fast2-dev-fw-policy - name: fast2-dev-fw-policy-dev-spoke - project: dev-project - timeouts: null - module.dev-spoke-firewall-policy.google_compute_network_firewall_policy_rule.net-global["egress/egress-allow-rfc1918"]: - action: allow - description: Allow all hosts to RFC-1918 - direction: EGRESS - disabled: false - enable_logging: null - firewall_policy: fast2-dev-fw-policy - match: - - dest_address_groups: null - dest_fqdns: null - dest_ip_ranges: - - 10.0.0.0/8 - - 172.16.0.0/12 - - 192.168.0.0/16 - dest_region_codes: null - dest_threat_intelligences: null - layer4_configs: - - ip_protocol: all - ports: null - src_address_groups: null - src_fqdns: null - src_ip_ranges: null - src_region_codes: null - src_secure_tags: [] - src_threat_intelligences: null - priority: 2147483546 - project: dev-project - rule_name: egress-allow-rfc1918 - security_profile_group: null - target_secure_tags: [] - target_service_accounts: null - timeouts: null - tls_inspect: null - module.dev-spoke-firewall-policy.google_compute_network_firewall_policy_rule.net-global["egress/egress-inspect-internet"]: - action: apply_security_profile_group - description: Inspect egress traffic from all dev hosts to Internet - direction: EGRESS - disabled: false - enable_logging: null - firewall_policy: fast2-dev-fw-policy - match: - - dest_address_groups: null - dest_fqdns: null - dest_ip_ranges: - - 0.0.0.0/0 - dest_region_codes: null - dest_threat_intelligences: null - layer4_configs: - - ip_protocol: all - ports: null - src_address_groups: null - src_fqdns: null - src_ip_ranges: null - src_region_codes: null - src_secure_tags: [] - src_threat_intelligences: null - priority: 2147483547 - project: dev-project - rule_name: egress-inspect-internet - target_secure_tags: [] - target_service_accounts: null - timeouts: null - tls_inspect: null - module.ngfw-quota-project.google_project.project[0]: - auto_create_network: false - billing_account: 000000-111111-222222 - deletion_policy: DELETE - folder_id: '12345678900' - labels: null - name: fast2-net-ngfw-0 - org_id: null - project_id: fast2-net-ngfw-0 - timeouts: null - module.ngfw-quota-project.google_project_service.project_services["networksecurity.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast2-net-ngfw-0 - service: networksecurity.googleapis.com - timeouts: null - module.ngfw-quota-project.google_project_service_identity.default["networksecurity.googleapis.com"]: - project: fast2-net-ngfw-0 - service: networksecurity.googleapis.com - timeouts: null - module.prod-spoke-firewall-policy.google_compute_network_firewall_policy.net-global[0]: - description: null - name: fast2-prod-fw-policy - project: prod-project - timeouts: null - module.prod-spoke-firewall-policy.google_compute_network_firewall_policy_association.net-global["prod-spoke"]: - attachment_target: https://www.googleapis.com/compute/v1/projects/123456789/networks/vpc-2 - firewall_policy: fast2-prod-fw-policy - name: fast2-prod-fw-policy-prod-spoke - project: prod-project - timeouts: null - module.prod-spoke-firewall-policy.google_compute_network_firewall_policy_rule.net-global["egress/egress-allow-rfc1918"]: - action: allow - description: Allow all hosts to RFC-1918 - direction: EGRESS - disabled: false - enable_logging: null - firewall_policy: fast2-prod-fw-policy - match: - - dest_address_groups: null - dest_fqdns: null - dest_ip_ranges: - - 10.0.0.0/8 - - 172.16.0.0/12 - - 192.168.0.0/16 - dest_region_codes: null - dest_threat_intelligences: null - layer4_configs: - - ip_protocol: all - ports: null - src_address_groups: null - src_fqdns: null - src_ip_ranges: null - src_region_codes: null - src_secure_tags: [] - src_threat_intelligences: null - priority: 2147483546 - project: prod-project - rule_name: egress-allow-rfc1918 - security_profile_group: null - target_secure_tags: [] - target_service_accounts: null - timeouts: null - tls_inspect: null - module.prod-spoke-firewall-policy.google_compute_network_firewall_policy_rule.net-global["egress/egress-inspect-internet"]: - action: apply_security_profile_group - description: Inspect egress traffic from all prod hosts to Internet - direction: EGRESS - disabled: false - enable_logging: null - firewall_policy: fast2-prod-fw-policy - match: - - dest_address_groups: null - dest_fqdns: null - dest_ip_ranges: - - 0.0.0.0/0 - dest_region_codes: null - dest_threat_intelligences: null - layer4_configs: - - ip_protocol: all - ports: null - src_address_groups: null - src_fqdns: null - src_ip_ranges: null - src_region_codes: null - src_secure_tags: [] - src_threat_intelligences: null - priority: 2147483547 - project: prod-project - rule_name: egress-inspect-internet - target_secure_tags: [] - target_service_accounts: null - timeouts: null - tls_inspect: null - -counts: - google_compute_network_firewall_policy: 2 - google_compute_network_firewall_policy_association: 2 - google_compute_network_firewall_policy_rule: 4 - google_network_security_firewall_endpoint: 3 - google_network_security_firewall_endpoint_association: 6 - google_network_security_security_profile: 2 - google_network_security_security_profile_group: 2 - google_project: 1 - google_project_service: 1 - google_project_service_identity: 1 - google_storage_bucket_object: 1 - modules: 3 - resources: 25 - -outputs: - ngfw_enterprise_endpoint_ids: __missing__ - ngfw_enterprise_endpoints_quota_project: fast2-net-ngfw-0 - diff --git a/tests/fast/stages/s2_networking_a_simple/__init__.py b/tests/fast/stages/s2_networking_a_simple/__init__.py index 7ba50f933..c37e93b74 100644 --- a/tests/fast/stages/s2_networking_a_simple/__init__.py +++ b/tests/fast/stages/s2_networking_a_simple/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/fast/stages/s2_networking_a_simple/ncc.yaml b/tests/fast/stages/s2_networking_a_simple/ncc.yaml index 65630a089..36d2c9c8b 100644 --- a/tests/fast/stages/s2_networking_a_simple/ncc.yaml +++ b/tests/fast/stages/s2_networking_a_simple/ncc.yaml @@ -22,7 +22,7 @@ counts: google_compute_router: 2 google_compute_router_nat: 2 google_compute_shared_vpc_host_project: 3 - google_compute_subnetwork: 7 + google_compute_subnetwork: 5 google_dns_managed_zone: 9 google_dns_policy: 3 google_dns_record_set: 3 @@ -39,8 +39,7 @@ counts: google_project_iam_member: 20 google_project_service: 26 google_project_service_identity: 20 - google_storage_bucket_object: 2 + google_storage_bucket_object: 1 google_tags_tag_binding: 3 - google_vpc_access_connector: 2 - modules: 24 - resources: 180 + modules: 22 + resources: 175 diff --git a/tests/fast/stages/s2_networking_a_simple/simple.yaml b/tests/fast/stages/s2_networking_a_simple/simple.yaml index eaf9c6690..ed6159b6a 100644 --- a/tests/fast/stages/s2_networking_a_simple/simple.yaml +++ b/tests/fast/stages/s2_networking_a_simple/simple.yaml @@ -27,7 +27,7 @@ counts: google_compute_router_nat: 3 google_compute_router_peer: 2 google_compute_shared_vpc_host_project: 3 - google_compute_subnetwork: 7 + google_compute_subnetwork: 5 google_compute_vpn_tunnel: 2 google_dns_managed_zone: 9 google_dns_policy: 3 @@ -43,9 +43,8 @@ counts: google_project_iam_member: 20 google_project_service: 26 google_project_service_identity: 20 - google_storage_bucket_object: 2 + google_storage_bucket_object: 1 google_tags_tag_binding: 3 - google_vpc_access_connector: 2 - modules: 29 + modules: 27 random_id: 3 - resources: 199 + resources: 194 diff --git a/tests/fast/stages/s2_networking_a_simple/tftest.yaml b/tests/fast/stages/s2_networking_a_simple/tftest.yaml index 9b14f136b..67b70d4a5 100644 --- a/tests/fast/stages/s2_networking_a_simple/tftest.yaml +++ b/tests/fast/stages/s2_networking_a_simple/tftest.yaml @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,11 +17,5 @@ module: fast/stages/2-networking-a-simple tests: # peering (default) is implemented in simple.tfvars ncc: - extra_files: - - ../../plugins/2-networking-serverless-connector/*.tf simple: - extra_files: - - ../../plugins/2-networking-serverless-connector/*.tf vpn: - extra_files: - - ../../plugins/2-networking-serverless-connector/*.tf diff --git a/tests/fast/stages/s2_networking_a_simple/vpn.yaml b/tests/fast/stages/s2_networking_a_simple/vpn.yaml index d948bcb93..f6609d003 100644 --- a/tests/fast/stages/s2_networking_a_simple/vpn.yaml +++ b/tests/fast/stages/s2_networking_a_simple/vpn.yaml @@ -25,7 +25,7 @@ counts: google_compute_router_nat: 3 google_compute_router_peer: 12 google_compute_shared_vpc_host_project: 3 - google_compute_subnetwork: 7 + google_compute_subnetwork: 5 google_compute_vpn_tunnel: 12 google_dns_managed_zone: 9 google_dns_policy: 3 @@ -41,9 +41,8 @@ counts: google_project_iam_member: 20 google_project_service: 26 google_project_service_identity: 20 - google_storage_bucket_object: 2 + google_storage_bucket_object: 1 google_tags_tag_binding: 3 - google_vpc_access_connector: 2 - modules: 31 + modules: 29 random_id: 17 - resources: 244 + resources: 239 diff --git a/tests/fast/stages/s2_networking_b_nva/ncc-ra.yaml b/tests/fast/stages/s2_networking_b_nva/ncc-ra.yaml index bb1a44770..054ff476c 100644 --- a/tests/fast/stages/s2_networking_b_nva/ncc-ra.yaml +++ b/tests/fast/stages/s2_networking_b_nva/ncc-ra.yaml @@ -28,7 +28,7 @@ counts: google_compute_router_nat: 2 google_compute_router_peer: 20 google_compute_shared_vpc_host_project: 3 - google_compute_subnetwork: 12 + google_compute_subnetwork: 10 google_compute_vpn_tunnel: 4 google_dns_managed_zone: 9 google_dns_policy: 4 @@ -46,9 +46,8 @@ counts: google_project_iam_member: 19 google_project_service: 25 google_project_service_identity: 19 - google_storage_bucket_object: 2 + google_storage_bucket_object: 1 google_tags_tag_binding: 3 - google_vpc_access_connector: 2 - modules: 39 + modules: 37 random_id: 6 - resources: 261 + resources: 256 diff --git a/tests/fast/stages/s2_networking_b_nva/regional.yaml b/tests/fast/stages/s2_networking_b_nva/regional.yaml index bcd5a8555..d3c76f0fd 100644 --- a/tests/fast/stages/s2_networking_b_nva/regional.yaml +++ b/tests/fast/stages/s2_networking_b_nva/regional.yaml @@ -32,7 +32,7 @@ counts: google_compute_router_nat: 2 google_compute_router_peer: 4 google_compute_shared_vpc_host_project: 3 - google_compute_subnetwork: 14 + google_compute_subnetwork: 12 google_compute_vpn_tunnel: 4 google_dns_managed_zone: 9 google_dns_policy: 6 @@ -48,9 +48,8 @@ counts: google_project_iam_member: 19 google_project_service: 25 google_project_service_identity: 19 - google_storage_bucket_object: 2 + google_storage_bucket_object: 1 google_tags_tag_binding: 3 - google_vpc_access_connector: 2 - modules: 47 + modules: 45 random_id: 6 - resources: 269 + resources: 264 diff --git a/tests/fast/stages/s2_networking_b_nva/simple.yaml b/tests/fast/stages/s2_networking_b_nva/simple.yaml index 51c09deb6..bb1699354 100644 --- a/tests/fast/stages/s2_networking_b_nva/simple.yaml +++ b/tests/fast/stages/s2_networking_b_nva/simple.yaml @@ -32,7 +32,7 @@ counts: google_compute_router_nat: 2 google_compute_router_peer: 4 google_compute_shared_vpc_host_project: 3 - google_compute_subnetwork: 12 + google_compute_subnetwork: 10 google_compute_vpn_tunnel: 4 google_dns_managed_zone: 9 google_dns_policy: 4 @@ -48,9 +48,8 @@ counts: google_project_iam_member: 19 google_project_service: 25 google_project_service_identity: 19 - google_storage_bucket_object: 2 + google_storage_bucket_object: 1 google_tags_tag_binding: 3 - google_vpc_access_connector: 2 - modules: 43 + modules: 41 random_id: 6 - resources: 247 + resources: 242 diff --git a/tests/fast/stages/s2_networking_b_nva/tftest.yaml b/tests/fast/stages/s2_networking_b_nva/tftest.yaml index a60ec3adb..50d6b13ed 100644 --- a/tests/fast/stages/s2_networking_b_nva/tftest.yaml +++ b/tests/fast/stages/s2_networking_b_nva/tftest.yaml @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,11 +16,5 @@ module: fast/stages/2-networking-b-nva tests: simple: - extra_files: - - ../../plugins/2-networking-serverless-connector/*.tf ncc-ra: - extra_files: - - ../../plugins/2-networking-serverless-connector/*.tf regional: - extra_files: - - ../../plugins/2-networking-serverless-connector/*.tf \ No newline at end of file diff --git a/tests/fast/stages/s2_project_factory/tftest.yaml b/tests/fast/stages/s2_project_factory/tftest.yaml index 470b47fbc..2e7b27a19 100644 --- a/tests/fast/stages/s2_project_factory/tftest.yaml +++ b/tests/fast/stages/s2_project_factory/tftest.yaml @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/fast/stages/s2_security/simple.tfvars b/tests/fast/stages/s2_security/simple.tfvars index dcea9e4b1..742f6c85e 100644 --- a/tests/fast/stages/s2_security/simple.tfvars +++ b/tests/fast/stages/s2_security/simple.tfvars @@ -4,6 +4,25 @@ automation = { billing_account = { id = "000000-111111-222222" } +certificate_authorities = { + ngfw-0 = { + environments = ["prod"] + ca_configs = { + ca-0 = { + deletion_protection = false + subject = { + common_name = "fast.example.com" + organization = "FAST Test" + } + } + } + ca_pool_config = { + authz_nsec_sa = true + name = "ca-pool-0" + } + location = "europe-west8" + } +} custom_roles = { project_iam_viewer = "organizations/123456789012/roles/bar" service_project_network_admin = "organizations/123456789012/roles/foo" diff --git a/tests/fast/stages/s2_security/simple.yaml b/tests/fast/stages/s2_security/simple.yaml index 23a3d6a2a..e2191cfec 100644 --- a/tests/fast/stages/s2_security/simple.yaml +++ b/tests/fast/stages/s2_security/simple.yaml @@ -1,4 +1,4 @@ -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,21 +13,84 @@ # limitations under the License. values: - google_storage_bucket_object.tfvars: - bucket: test - cache_control: null - content_disposition: null - content_encoding: null - content_language: null - customer_encryption: [] - detect_md5hash: different hash - event_based_hold: null - metadata: null - name: tfvars/2-security.auto.tfvars.json - retention: [] - source: null - temporary_hold: null + module.cas["prod-ngfw-0"].google_privateca_ca_pool.ca_pool[0]: + effective_labels: + goog-terraform-provisioned: 'true' + issuance_policy: [] + labels: null + location: europe-west8 + name: ca-pool-0 + project: fast-prod-sec-core-0 + publishing_options: [] + terraform_labels: + goog-terraform-provisioned: 'true' + tier: DEVOPS timeouts: null + module.cas["prod-ngfw-0"].google_privateca_certificate_authority.cas["ca-0"]: + certificate_authority_id: ca-0 + config: + - subject_config: + - subject: + - common_name: fast.example.com + country_code: null + locality: null + organization: FAST Test + organizational_unit: null + postal_code: null + province: null + street_address: null + subject_alt_name: [] + subject_key_id: [] + x509_config: + - additional_extensions: [] + aia_ocsp_servers: null + ca_options: + - is_ca: true + max_issuer_path_length: null + non_ca: null + zero_max_issuer_path_length: null + key_usage: + - base_key_usage: + - cert_sign: true + content_commitment: false + crl_sign: true + data_encipherment: false + decipher_only: false + digital_signature: false + encipher_only: false + key_agreement: false + key_encipherment: true + extended_key_usage: + - client_auth: false + code_signing: false + email_protection: false + ocsp_signing: false + server_auth: true + time_stamping: false + unknown_extended_key_usages: [] + name_constraints: [] + policy_ids: [] + deletion_protection: false + desired_state: null + effective_labels: + goog-terraform-provisioned: 'true' + gcs_bucket: null + ignore_active_certificates_on_deletion: false + key_spec: + - algorithm: RSA_PKCS1_2048_SHA256 + cloud_kms_key_version: null + labels: null + lifetime: 315360000s + location: europe-west8 + pem_ca_certificate: null + pool: ca-pool-0 + project: fast-prod-sec-core-0 + skip_grace_period: true + subordinate_config: [] + terraform_labels: + goog-terraform-provisioned: 'true' + timeouts: null + type: SELF_SIGNED module.folder.google_essential_contacts_contact.contact["gcp-security-admins@fast.example.com"]: email: gcp-security-admins@fast.example.com language_tag: en @@ -227,257 +290,26 @@ values: name: prod-global project: fast-prod-sec-core-0 timeouts: null - module.project["dev"].google_project.project[0]: - auto_create_network: false - billing_account: 000000-111111-222222 - deletion_policy: DELETE - effective_labels: - environment: dev - goog-terraform-provisioned: 'true' - folder_id: '12345678' - labels: - environment: dev - name: fast-dev-sec-core-0 - org_id: null - project_id: fast-dev-sec-core-0 - tags: null - terraform_labels: - environment: dev - goog-terraform-provisioned: 'true' - timeouts: null - module.project["dev"].google_project_iam_binding.authoritative["organizations/123456789012/roles/bar"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gcve-0r@fast2-prod-iac-core-0.iam.gserviceaccount.com - - serviceAccount:fast2-dev-resman-pf-0r@fast2-prod-iac-core-0.iam.gserviceaccount.com - project: fast-dev-sec-core-0 - role: organizations/123456789012/roles/bar - module.project["dev"].google_project_iam_binding.bindings["sa_delegated_grants"]: - condition: - - description: Development project delegated grants. - expression: api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly(['roles/cloudkms.cryptoKeyEncrypterDecrypter']) - title: dev_stage3_sa_delegated_grants - members: - - serviceAccount:fast2-dev-resman-gcve-0@fast2-prod-iac-core-0.iam.gserviceaccount.com - - serviceAccount:fast2-dev-resman-pf-0@fast2-prod-iac-core-0.iam.gserviceaccount.com - project: fast-dev-sec-core-0 - role: roles/resourcemanager.projectIamAdmin - module.project["dev"].google_project_iam_member.service_agents["certificatemanager"]: - condition: [] - project: fast-dev-sec-core-0 - role: roles/certificatemanager.serviceAgent - module.project["dev"].google_project_iam_member.service_agents["cloudkms"]: - condition: [] - project: fast-dev-sec-core-0 - role: roles/cloudkms.serviceAgent - module.project["dev"].google_project_iam_member.service_agents["networkmanagement"]: - condition: [] - project: fast-dev-sec-core-0 - role: roles/networkmanagement.serviceAgent - module.project["dev"].google_project_service.project_services["certificatemanager.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-dev-sec-core-0 - service: certificatemanager.googleapis.com - timeouts: null - module.project["dev"].google_project_service.project_services["cloudkms.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-dev-sec-core-0 - service: cloudkms.googleapis.com - timeouts: null - module.project["dev"].google_project_service.project_services["networkmanagement.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-dev-sec-core-0 - service: networkmanagement.googleapis.com - timeouts: null - module.project["dev"].google_project_service.project_services["networksecurity.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-dev-sec-core-0 - service: networksecurity.googleapis.com - timeouts: null - module.project["dev"].google_project_service.project_services["privateca.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-dev-sec-core-0 - service: privateca.googleapis.com - timeouts: null - module.project["dev"].google_project_service.project_services["secretmanager.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-dev-sec-core-0 - service: secretmanager.googleapis.com - timeouts: null - module.project["dev"].google_project_service.project_services["stackdriver.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-dev-sec-core-0 - service: stackdriver.googleapis.com - timeouts: null - module.project["dev"].google_project_service_identity.default["certificatemanager.googleapis.com"]: - project: fast-dev-sec-core-0 - service: certificatemanager.googleapis.com - timeouts: null - module.project["dev"].google_project_service_identity.default["cloudkms.googleapis.com"]: - project: fast-dev-sec-core-0 - service: cloudkms.googleapis.com - timeouts: null - module.project["dev"].google_project_service_identity.default["networkmanagement.googleapis.com"]: - project: fast-dev-sec-core-0 - service: networkmanagement.googleapis.com - timeouts: null - module.project["dev"].google_project_service_identity.default["networksecurity.googleapis.com"]: - project: fast-dev-sec-core-0 - service: networksecurity.googleapis.com - timeouts: null - module.project["dev"].google_project_service_identity.default["privateca.googleapis.com"]: - project: fast-dev-sec-core-0 - service: privateca.googleapis.com - timeouts: null - module.project["dev"].google_project_service_identity.default["secretmanager.googleapis.com"]: - project: fast-dev-sec-core-0 - service: secretmanager.googleapis.com - timeouts: null - module.project["dev"].google_tags_tag_binding.binding["environment"]: - tag_value: tagValues/12345 - timeouts: null - module.project["prod"].google_project.project[0]: - auto_create_network: false - billing_account: 000000-111111-222222 - deletion_policy: DELETE - effective_labels: - environment: prod - goog-terraform-provisioned: 'true' - folder_id: '12345678' - labels: - environment: prod - name: fast-prod-sec-core-0 - org_id: null - project_id: fast-prod-sec-core-0 - tags: null - terraform_labels: - environment: prod - goog-terraform-provisioned: 'true' - timeouts: null - module.project["prod"].google_project_iam_binding.authoritative["organizations/123456789012/roles/bar"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gcve-0r@fast2-prod-iac-core-0.iam.gserviceaccount.com - - serviceAccount:fast2-prod-resman-pf-0r@fast2-prod-iac-core-0.iam.gserviceaccount.com - project: fast-prod-sec-core-0 - role: organizations/123456789012/roles/bar - module.project["prod"].google_project_iam_binding.bindings["sa_delegated_grants"]: - condition: - - description: Production project delegated grants. - expression: api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly(['roles/cloudkms.cryptoKeyEncrypterDecrypter']) - title: prod_stage3_sa_delegated_grants - members: - - serviceAccount:fast2-prod-resman-gcve-0@fast2-prod-iac-core-0.iam.gserviceaccount.com - - serviceAccount:fast2-prod-resman-pf-0@fast2-prod-iac-core-0.iam.gserviceaccount.com - project: fast-prod-sec-core-0 - role: roles/resourcemanager.projectIamAdmin - module.project["prod"].google_project_iam_member.service_agents["certificatemanager"]: - condition: [] - project: fast-prod-sec-core-0 - role: roles/certificatemanager.serviceAgent - module.project["prod"].google_project_iam_member.service_agents["cloudkms"]: - condition: [] - project: fast-prod-sec-core-0 - role: roles/cloudkms.serviceAgent - module.project["prod"].google_project_iam_member.service_agents["networkmanagement"]: - condition: [] - project: fast-prod-sec-core-0 - role: roles/networkmanagement.serviceAgent - module.project["prod"].google_project_service.project_services["certificatemanager.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-prod-sec-core-0 - service: certificatemanager.googleapis.com - timeouts: null - module.project["prod"].google_project_service.project_services["cloudkms.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-prod-sec-core-0 - service: cloudkms.googleapis.com - timeouts: null - module.project["prod"].google_project_service.project_services["networkmanagement.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-prod-sec-core-0 - service: networkmanagement.googleapis.com - timeouts: null - module.project["prod"].google_project_service.project_services["networksecurity.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-prod-sec-core-0 - service: networksecurity.googleapis.com - timeouts: null - module.project["prod"].google_project_service.project_services["privateca.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-prod-sec-core-0 - service: privateca.googleapis.com - timeouts: null - module.project["prod"].google_project_service.project_services["secretmanager.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-prod-sec-core-0 - service: secretmanager.googleapis.com - timeouts: null - module.project["prod"].google_project_service.project_services["stackdriver.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-prod-sec-core-0 - service: stackdriver.googleapis.com - timeouts: null - module.project["prod"].google_project_service_identity.default["certificatemanager.googleapis.com"]: - project: fast-prod-sec-core-0 - service: certificatemanager.googleapis.com - timeouts: null - module.project["prod"].google_project_service_identity.default["cloudkms.googleapis.com"]: - project: fast-prod-sec-core-0 - service: cloudkms.googleapis.com - timeouts: null - module.project["prod"].google_project_service_identity.default["networkmanagement.googleapis.com"]: - project: fast-prod-sec-core-0 - service: networkmanagement.googleapis.com - timeouts: null - module.project["prod"].google_project_service_identity.default["networksecurity.googleapis.com"]: - project: fast-prod-sec-core-0 - service: networksecurity.googleapis.com - timeouts: null - module.project["prod"].google_project_service_identity.default["privateca.googleapis.com"]: - project: fast-prod-sec-core-0 - service: privateca.googleapis.com - timeouts: null - module.project["prod"].google_project_service_identity.default["secretmanager.googleapis.com"]: - project: fast-prod-sec-core-0 - service: secretmanager.googleapis.com - timeouts: null - module.project["prod"].google_tags_tag_binding.binding["environment"]: - tag_value: tagValues/12346 - timeouts: null counts: google_essential_contacts_contact: 1 google_kms_crypto_key: 8 google_kms_crypto_key_iam_binding: 8 google_kms_key_ring: 8 + google_privateca_ca_pool: 1 + google_privateca_certificate_authority: 1 google_project: 2 google_project_iam_binding: 4 - google_project_iam_member: 6 - google_project_service: 14 - google_project_service_identity: 12 + google_project_iam_member: 4 + google_project_service: 10 + google_project_service_identity: 8 google_storage_bucket_object: 1 google_tags_tag_binding: 2 - modules: 11 - resources: 66 + modules: 12 + resources: 58 outputs: - cas_configs: {} + certificate_authority_pools: __missing__ kms_keys: __missing__ tfvars: __missing__ - trust_config_ids: {} diff --git a/tests/fast/stages/s2_security/tftest.yaml b/tests/fast/stages/s2_security/tftest.yaml index 5555caac3..ba6eba583 100644 --- a/tests/fast/stages/s2_security/tftest.yaml +++ b/tests/fast/stages/s2_security/tftest.yaml @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/fast/stages/s3_gcve_dev/__init__.py b/tests/fast/stages/s3_gcve_dev/__init__.py index 7ba50f933..c37e93b74 100644 --- a/tests/fast/stages/s3_gcve_dev/__init__.py +++ b/tests/fast/stages/s3_gcve_dev/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/fast/stages/s3_gke_dev/tftest.yaml b/tests/fast/stages/s3_gke_dev/tftest.yaml index 14b8860e0..925dc10d4 100644 --- a/tests/fast/stages/s3_gke_dev/tftest.yaml +++ b/tests/fast/stages/s3_gke_dev/tftest.yaml @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/fixtures.py b/tests/fixtures.py index 701e0a5c3..c1ab300e7 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -186,7 +186,7 @@ def plan_validator(module_path, inventory_paths, basedir, tf_var_files=None, # 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})) + # print("", yaml.dump({'counts': summary.counts})) if 'values' in inventory: try: