Merge pull request #16 from juliodiez/master

Sync branch
This commit is contained in:
Julio Diez 2023-03-07 13:13:35 +01:00 committed by GitHub
commit 6eb82a2214
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
91 changed files with 3207 additions and 547 deletions

55
.github/actions/fabric-tests/action.yml vendored Normal file
View File

@ -0,0 +1,55 @@
# 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.
name: fabric-tests
description: Set up Fabric testing environment
inputs:
PYTHON_VERSION:
required: true
TERRAFORM_VERSION:
required: true
runs:
using: composite
steps:
- name: Config auth
shell: bash
run: |
echo '{"type": "service_account", "project_id": "test-only"}' \
| tee -a $GOOGLE_APPLICATION_CREDENTIALS
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ inputs.PYTHON_VERSION }}
cache: 'pip'
cache-dependency-path: 'tests/requirements.txt'
- name: Set up Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_version: ${{ inputs.TERRAFORM_VERSION }}
terraform_wrapper: false
- name: Configure provider cache
shell: bash
run: |
echo 'plugin_cache_dir = "/home/runner/.terraform.d/plugin-cache"' \
| tee -a /home/runner/.terraformrc
echo 'disable_checkpoint = true' \
| tee -a /home/runner/.terraformrc
mkdir -p /home/runner/.terraform.d/plugin-cache
# avoid conflicts with user-installed providers on local machines
- name: Pin provider versions
shell: bash
run: |
for f in $(find . -name versions.tf); do
sed -i 's/>=\(.*# tftest\)/=\1/g' $f;
done

View File

@ -31,72 +31,51 @@ env:
TF_VERSION: 1.3.9
jobs:
examples:
examples-blueprints:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Config auth
run: |
echo '{"type": "service_account", "project_id": "test-only"}' \
| tee -a $GOOGLE_APPLICATION_CREDENTIALS
- name: Set up Python
uses: actions/setup-python@v4
- name: Call composite action fabric-tests
uses: ./.github/actions/fabric-tests
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
cache-dependency-path: 'tests/requirements.txt'
PYTHON_VERSION: ${{ env.PYTHON_VERSION }}
TERRAFORM_VERSION: ${{ env.TERRAFORM_VERSION }}
- name: Set up Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_version: ${{ env.TF_VERSION }}
terraform_wrapper: false
# avoid conflicts with user-installed providers on local machines
- name: Pin provider versions
- name: Run tests on documentation examples
id: pytest
run: |
for f in $(find . -name versions.tf); do
sed -i 's/>=\(.*# tftest\)/=\1/g' $f;
done
pip install -r tests/requirements.txt
pytest -vv -k blueprints/ tests/examples
examples-modules:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Call composite action fabric-tests
uses: ./.github/actions/fabric-tests
with:
PYTHON_VERSION: ${{ env.PYTHON_VERSION }}
TERRAFORM_VERSION: ${{ env.TERRAFORM_VERSION }}
- name: Run tests on documentation examples
id: pytest
run: |
mkdir -p ${{ env.TF_PLUGIN_CACHE_DIR }}
pip install -r tests/requirements.txt
pytest -vv tests/examples
pytest -vv -k modules/ tests/examples
blueprints:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Config auth
run: |
echo '{"type": "service_account", "project_id": "test-only"}' \
| tee -a $GOOGLE_APPLICATION_CREDENTIALS
- name: Set up Python
uses: actions/setup-python@v4
- name: Call composite action fabric-tests
uses: ./.github/actions/fabric-tests
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
cache-dependency-path: 'tests/requirements.txt'
- name: Set up Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_version: ${{ env.TF_VERSION }}
terraform_wrapper: false
# avoid conflicts with user-installed providers on local machines
- name: Pin provider versions
run: |
for f in $(find . -name versions.tf); do
sed -i 's/>=\(.*# tftest\)/=\1/g' $f;
done
PYTHON_VERSION: ${{ env.PYTHON_VERSION }}
TERRAFORM_VERSION: ${{ env.TERRAFORM_VERSION }}
- name: Run tests environments
id: pytest
@ -110,30 +89,11 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Config auth
run: |
echo '{"type": "service_account", "project_id": "test-only"}' \
| tee -a $GOOGLE_APPLICATION_CREDENTIALS
- name: Set up Python
uses: actions/setup-python@v4
- name: Call composite action fabric-tests
uses: ./.github/actions/fabric-tests
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
cache-dependency-path: 'tests/requirements.txt'
- name: Set up Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_version: ${{ env.TF_VERSION }}
terraform_wrapper: false
# avoid conflicts with user-installed providers on local machines
- name: Pin provider versions
run: |
for f in $(find . -name versions.tf); do
sed -i 's/>=\(.*# tftest\)/=\1/g' $f;
done
PYTHON_VERSION: ${{ env.PYTHON_VERSION }}
TERRAFORM_VERSION: ${{ env.TERRAFORM_VERSION }}
- name: Run tests modules
id: pytest
@ -147,30 +107,11 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Config auth
run: |
echo '{"type": "service_account", "project_id": "test-only"}' \
| tee -a $GOOGLE_APPLICATION_CREDENTIALS
- name: Set up Python
uses: actions/setup-python@v4
- name: Call composite action fabric-tests
uses: ./.github/actions/fabric-tests
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
cache-dependency-path: 'tests/requirements.txt'
- name: Set up Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_version: ${{ env.TF_VERSION }}
terraform_wrapper: false
# avoid conflicts with user-installed providers on local machines
- name: Pin provider versions
run: |
for f in $(find . -name versions.tf); do
sed -i 's/>=\(.*# tftest\)/=\1/g' $f;
done
PYTHON_VERSION: ${{ env.PYTHON_VERSION }}
TERRAFORM_VERSION: ${{ env.TERRAFORM_VERSION }}
- name: Run tests on FAST stages
id: pytest

View File

@ -8,6 +8,11 @@ All notable changes to this project will be documented in this file.
### BLUEPRINTS
- [[#1208](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1208)] Fix outdated go deps, dependabot alerts ([averbuks](https://github.com/averbuks)) <!-- 2023-03-03 06:15:09+00:00 -->
- [[#1150](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1150)] Blueprint: GLB hybrid NEG internal ([LucaPrete](https://github.com/LucaPrete)) <!-- 2023-03-02 08:53:07+00:00 -->
- [[#1201](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1201)] Add missing tfvars template to the tfc blueprint ([averbuks](https://github.com/averbuks)) <!-- 2023-03-01 20:10:46+00:00 -->
- [[#1196](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1196)] Fix compute-vm:CloudKMS test for provider>=4.54.0 ([dan-farmer](https://github.com/dan-farmer)) <!-- 2023-02-28 15:53:41+00:00 -->
- [[#1189](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1189)] Update healthchecker deps (dependabot alerts) ([averbuks](https://github.com/averbuks)) <!-- 2023-02-27 21:48:49+00:00 -->
- [[#1184](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1184)] **incompatible change:** Allow multiple peer gateways in VPN HA module ([ludoo](https://github.com/ludoo)) <!-- 2023-02-27 10:19:00+00:00 -->
- [[#1143](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1143)] Test blueprints from README files ([juliocc](https://github.com/juliocc)) <!-- 2023-02-27 08:57:41+00:00 -->
- [[#1181](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1181)] Bump golang.org/x/sys from 0.0.0-20220310020820-b874c991c1a5 to 0.1.0 in /blueprints/cloud-operations/unmanaged-instances-healthcheck/function/healthchecker ([dependabot[bot]](https://github.com/dependabot[bot])) <!-- 2023-02-25 17:02:08+00:00 -->
@ -27,6 +32,10 @@ All notable changes to this project will be documented in this file.
### DOCUMENTATION
- [[#1150](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1150)] Blueprint: GLB hybrid NEG internal ([LucaPrete](https://github.com/LucaPrete)) <!-- 2023-03-02 08:53:07+00:00 -->
- [[#1193](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1193)] Add reference to Cloud Run blueprints ([juliodiez](https://github.com/juliodiez)) <!-- 2023-02-28 10:16:45+00:00 -->
- [[#1188](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1188)] Add reference to Cloud Run blueprints ([juliodiez](https://github.com/juliodiez)) <!-- 2023-02-27 21:22:31+00:00 -->
- [[#1187](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1187)] Add references to the serverless chapters ([juliodiez](https://github.com/juliodiez)) <!-- 2023-02-27 17:16:20+00:00 -->
- [[#1179](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1179)] Added a PSC GCLB example ([cgrotz](https://github.com/cgrotz)) <!-- 2023-02-24 20:09:31+00:00 -->
- [[#1165](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1165)] DataPlatform: Support project creation ([lcaggio](https://github.com/lcaggio)) <!-- 2023-02-23 11:10:44+00:00 -->
- [[#1145](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1145)] FAST stage docs cleanup ([ludoo](https://github.com/ludoo)) <!-- 2023-02-15 05:42:14+00:00 -->
@ -36,6 +45,9 @@ All notable changes to this project will be documented in this file.
### FAST
- [[#1211](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1211)] **incompatible change:** Add support for proxy and psc subnets to net-vpc module factory ([ludoo](https://github.com/ludoo)) <!-- 2023-03-05 16:08:43+00:00 -->
- [[#1209](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1209)] Billing exclusion support for FAST mt resman ([ludoo](https://github.com/ludoo)) <!-- 2023-03-03 16:23:37+00:00 -->
- [[#1207](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1207)] Allow preventing creation of billing IAM roles in FAST, add instructions on delayed billing association ([ludoo](https://github.com/ludoo)) <!-- 2023-03-03 08:24:42+00:00 -->
- [[#1184](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1184)] **incompatible change:** Allow multiple peer gateways in VPN HA module ([ludoo](https://github.com/ludoo)) <!-- 2023-02-27 10:19:00+00:00 -->
- [[#1165](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1165)] DataPlatform: Support project creation ([lcaggio](https://github.com/lcaggio)) <!-- 2023-02-23 11:10:44+00:00 -->
- [[#1170](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1170)] Add documentation about referring modules stored on CSR ([wiktorn](https://github.com/wiktorn)) <!-- 2023-02-22 09:02:54+00:00 -->
@ -52,6 +64,19 @@ All notable changes to this project will be documented in this file.
### MODULES
- [[#1211](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1211)] **incompatible change:** Add support for proxy and psc subnets to net-vpc module factory ([ludoo](https://github.com/ludoo)) <!-- 2023-03-05 16:08:43+00:00 -->
- [[#1206](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1206)] Dataproc module. Fix output. ([lcaggio](https://github.com/lcaggio)) <!-- 2023-03-02 12:59:19+00:00 -->
- [[#1205](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1205)] Fix issue with GKE cluster notifications topic & static output for pubsub module ([rosmo](https://github.com/rosmo)) <!-- 2023-03-02 10:43:40+00:00 -->
- [[#1204](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1204)] Fix url_redirect issue on net-glb module ([erabusi](https://github.com/erabusi)) <!-- 2023-03-02 06:51:40+00:00 -->
- [[#1199](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1199)] [Dataproc module] Fix Variables ([lcaggio](https://github.com/lcaggio)) <!-- 2023-03-01 11:16:11+00:00 -->
- [[#1200](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1200)] Add test for #1197 ([juliocc](https://github.com/juliocc)) <!-- 2023-03-01 09:15:13+00:00 -->
- [[#1198](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1198)] Fix secondary ranges in net-vpc readme ([ludoo](https://github.com/ludoo)) <!-- 2023-03-01 07:08:08+00:00 -->
- [[#1196](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1196)] Fix compute-vm:CloudKMS test for provider>=4.54.0 ([dan-farmer](https://github.com/dan-farmer)) <!-- 2023-02-28 15:53:41+00:00 -->
- [[#1194](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1194)] Fix HTTPS health check mismapped to HTTP in compute-mig and net-ilb modules ([jogoldberg](https://github.com/jogoldberg)) <!-- 2023-02-28 14:48:13+00:00 -->
- [[#1192](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1192)] Dataproc module: Fix outputs ([lcaggio](https://github.com/lcaggio)) <!-- 2023-02-28 10:47:23+00:00 -->
- [[#1190](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1190)] Dataproc Module ([lcaggio](https://github.com/lcaggio)) <!-- 2023-02-28 06:45:41+00:00 -->
- [[#1191](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1191)] Fix external gateway in VPN HA module ([ludoo](https://github.com/ludoo)) <!-- 2023-02-27 23:46:51+00:00 -->
- [[#1186](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1186)] Fix Workload Identity for ASM in GKE hub module ([valeriobponza](https://github.com/valeriobponza)) <!-- 2023-02-27 19:17:45+00:00 -->
- [[#1184](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1184)] **incompatible change:** Allow multiple peer gateways in VPN HA module ([ludoo](https://github.com/ludoo)) <!-- 2023-02-27 10:19:00+00:00 -->
- [[#1177](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1177)] Implemented conditional dynamic blocks for `google_access_context_manager_service_perimeter` `spec` and `status` ([calexandre](https://github.com/calexandre)) <!-- 2023-02-25 16:04:19+00:00 -->
- [[#1178](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1178)] adding meshconfig.googleapis.com to JIT list. ([valeriobponza](https://github.com/valeriobponza)) <!-- 2023-02-24 18:28:05+00:00 -->
@ -77,6 +102,9 @@ All notable changes to this project will be documented in this file.
### TOOLS
- [[#1211](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1211)] **incompatible change:** Add support for proxy and psc subnets to net-vpc module factory ([ludoo](https://github.com/ludoo)) <!-- 2023-03-05 16:08:43+00:00 -->
- [[#1209](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1209)] Billing exclusion support for FAST mt resman ([ludoo](https://github.com/ludoo)) <!-- 2023-03-03 16:23:37+00:00 -->
- [[#1208](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1208)] Fix outdated go deps, dependabot alerts ([averbuks](https://github.com/averbuks)) <!-- 2023-03-03 06:15:09+00:00 -->
- [[#1182](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1182)] Bump actions versions ([juliocc](https://github.com/juliocc)) <!-- 2023-02-25 16:27:20+00:00 -->
- [[#1052](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1052)] **incompatible change:** FAST multitenant bootstrap and resource management, rename org-level FAST stages ([ludoo](https://github.com/ludoo)) <!-- 2023-02-04 14:00:46+00:00 -->

View File

@ -6,10 +6,10 @@ Currently available blueprints:
- **apigee** - [Apigee Hybrid on GKE](./apigee/hybrid-gke/), [Apigee X analytics in BigQuery](./apigee/bigquery-analytics), [Apigee network patterns](./apigee/network-patterns/)
- **cloud operations** - [Active Directory Federation Services](./cloud-operations/adfs), [Cloud Asset Inventory feeds for resource change tracking and remediation](./cloud-operations/asset-inventory-feed-remediation), [Fine-grained Cloud DNS IAM via Service Directory](./cloud-operations/dns-fine-grained-iam), [Cloud DNS & Shared VPC design](./cloud-operations/dns-shared-vpc), [Delegated Role Grants](./cloud-operations/iam-delegated-role-grants), [Networking Dashboard](./cloud-operations/network-dashboard), [Managing on-prem service account keys by uploading public keys](./cloud-operations/onprem-sa-key-management), [Compute Image builder with Hashicorp Packer](./cloud-operations/packer-image-builder), [Packer example](./cloud-operations/packer-image-builder/packer), [Compute Engine quota monitoring](./cloud-operations/quota-monitoring), [Scheduled Cloud Asset Inventory Export to Bigquery](./cloud-operations/scheduled-asset-inventory-export-bq), [Configuring workload identity federation with Terraform Cloud/Enterprise workflows](./cloud-operations/terraform-cloud-dynamic-credentials), [TCP healthcheck and restart for unmanaged GCE instances](./cloud-operations/unmanaged-instances-healthcheck), [Migrate for Compute Engine (v5) blueprints](./cloud-operations/vm-migration), [Configuring workload identity federation to access Google Cloud resources from apps running on Azure](./cloud-operations/workload-identity-federation)
- **data solutions** - [GCE and GCS CMEK via centralized Cloud KMS](./data-solutions/cmek-via-centralized-kms), [Cloud Composer version 2 private instance, supporting Shared VPC and external CMEK key](./data-solutions/composer-2), [Cloud SQL instance with multi-region read replicas](./data-solutions/cloudsql-multiregion), [Data Platform](./data-solutions/data-platform-foundations), [Spinning up a foundation data pipeline on Google Cloud using Cloud Storage, Dataflow and BigQuery](./data-solutions/gcs-to-bq-with-least-privileges), [#SQL Server Always On Groups blueprint](./data-solutions/sqlserver-alwayson), [Data Playground](./data-solutions/data-playground), [MLOps with Vertex AI](./data-solutions/vertex-mlops), [Shielded Folder](./data-solutions/shielded-folder)
- **data solutions** - [GCE and GCS CMEK via centralized Cloud KMS](./data-solutions/cmek-via-centralized-kms), [Cloud Composer version 2 private instance, supporting Shared VPC and external CMEK key](./data-solutions/composer-2), [Cloud SQL instance with multi-region read replicas](./data-solutions/cloudsql-multiregion), [Data Platform](./data-solutions/data-platform-foundations), [Spinning up a foundation data pipeline on Google Cloud using Cloud Storage, Dataflow and BigQuery](./data-solutions/gcs-to-bq-with-least-privileges), [#SQL Server Always On Groups blueprint](./data-solutions/sqlserver-alwayson), [Data Playground](./data-solutions/data-playground), [MLOps with Vertex AI](./data-solutions/vertex-mlops), [Shielded Folder](./data-solutions/shielded-folder), [BigQuery ML and Vertex AI Pipeline](./data-solutions/bq-ml)
- **factories** - [The why and the how of Resource Factories](./factories), [Google Cloud Identity Group Factory](./factories/cloud-identity-group-factory), [Google Cloud BQ Factory](./factories/bigquery-factory), [Google Cloud VPC Firewall Factory](./factories/net-vpc-firewall-yaml), [Minimal Project Factory](./factories/project-factory)
- **GKE** - [Binary Authorization Pipeline Blueprint](./gke/binauthz), [Storage API](./gke/binauthz/image), [Multi-cluster mesh on GKE (fleet API)](./gke/multi-cluster-mesh-gke-fleet-api), [GKE Multitenant Blueprint](./gke/multitenant-fleet), [Shared VPC with GKE support](./networking/shared-vpc-gke/)
- **networking** - [Decentralized firewall management](./networking/decentralized-firewall), [Decentralized firewall validator](./networking/decentralized-firewall/validator), [Network filtering with Squid](./networking/filtering-proxy), [Network filtering with Squid with isolated VPCs using Private Service Connect](./networking/filtering-proxy-psc), [HTTP Load Balancer with Cloud Armor](./networking/glb-and-armor), [Hub and Spoke via VPN](./networking/hub-and-spoke-vpn), [Hub and Spoke via VPC Peering](./networking/hub-and-spoke-peering), [Internal Load Balancer as Next Hop](./networking/ilb-next-hop), On-prem DNS and Google Private Access, [Calling a private Cloud Function from On-premises](./networking/private-cloud-function-from-onprem), [Hybrid connectivity to on-premise services through PSC](./networking/psc-hybrid), [PSC Producer](./networking/psc-hybrid/psc-producer), [PSC Consumer](./networking/psc-hybrid/psc-consumer), [Shared VPC with optional GKE cluster](./networking/shared-vpc-gke)
- **networking** - [Calling a private Cloud Function from On-premises](./networking/private-cloud-function-from-onprem), [Decentralized firewall management](./networking/decentralized-firewall), [Decentralized firewall validator](./networking/decentralized-firewall/validator), [Network filtering with Squid](./networking/filtering-proxy), [GLB and multi-regional daisy-chaining through hybrid NEGs](./networking/glb-hybrid-neg-internal), [Hybrid connectivity to on-premise services through PSC](./networking/psc-hybrid), [HTTP Load Balancer with Cloud Armor](./networking/glb-and-armor), [Hub and Spoke via VPN](./networking/hub-and-spoke-vpn), [Hub and Spoke via VPC Peering](./networking/hub-and-spoke-peering), [Internal Load Balancer as Next Hop](./networking/ilb-next-hop), [Network filtering with Squid with isolated VPCs using Private Service Connect](./networking/filtering-proxy-psc), On-prem DNS and Google Private Access, [PSC Producer](./networking/psc-hybrid/psc-producer), [PSC Consumer](./networking/psc-hybrid/psc-consumer), [Shared VPC with optional GKE cluster](./networking/shared-vpc-gke)
- **serverless** - [Creating multi-region deployments for API Gateway](./serverless/api-gateway), [Cloud Run series](./serverless/cloud-run-explore)
- **third party solutions** - [OpenShift on GCP user-provisioned infrastructure](./third-party-solutions/openshift), [Wordpress deployment on Cloud Run](./third-party-solutions/wordpress/cloudrun)

View File

@ -0,0 +1,23 @@
# 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
#
# https://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.
billing_account = "xxx"
project_create = false
project_id = "xxx"
parent = "organizations/xxx"
tfc_organization_id = "org-xxxxxxxxxxxxx"
tfc_workspace_id = "ws-xxxxxxxxxxxxx"
workload_identity_pool_id = "tfc-pool"
workload_identity_pool_provider_id = "tfc-provider"
issuer_uri = "https://app.terraform.io/"

View File

@ -3,12 +3,10 @@ module example.com/restarter
go 1.16
require (
cloud.google.com/go/iam v0.3.0 // indirect
cloud.google.com/go/pubsub v1.19.0
cloud.google.com/go/pubsub v1.28.0
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a // indirect
golang.org/x/sys v0.1.0 // indirect
google.golang.org/api v0.71.0
google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6 // indirect
google.golang.org/grpc v1.45.0 // indirect
golang.org/x/net v0.7.0
golang.org/x/text v0.7.0
google.golang.org/api v0.111.0
google.golang.org/genproto v0.0.0-20230301171018-9ab4bdc49ad5 // indirect
)

View File

@ -69,3 +69,9 @@ This [blueprint](./vertex-mlops/) implements the infrastructure required to have
This [blueprint](./shielded-folder/) implements an opinionated folder configuration according to GCP best practices. Configurations implemented on the folder would be beneficial to host workloads inheriting constraints from the folder they belong to.
<br clear="left">
### BigQuery ML and Vertex AI Pipeline
<a href="./bq-ml/" title="BigQuery ML and Vertex AI Pipeline"><img src="./bq-ml/images/diagram.png" align="left" width="280px"></a>
This [blueprint](./bq-ml/) provides the necessary infrastructure to create a complete development environment for building and deploying machine learning models using BigQuery ML and Vertex AI. With this blueprint, you can deploy your models to a Vertex AI endpoint or use them within BigQuery ML.
<br clear="left">

View File

@ -0,0 +1,102 @@
# BigQuery ML and Vertex AI Pipeline
This blueprint provides the necessary infrastructure to create a complete development environment for building and deploying machine learning models using BigQuery ML and Vertex AI. With this blueprint, you can deploy your models to a Vertex AI endpoint or use them within BigQuery ML.
This is the high-level diagram:
![High-level diagram](diagram.png "High-level diagram")
It also includes the IAM wiring needed to make such scenarios work. Regional resources are used in this example, but the same logic applies to 'dual regional', 'multi regional', or 'global' resources.
The example is designed to match real-world use cases with a minimum amount of resources and be used as a starting point for your scenario.
## Managed resources and services
This sample creates several distinct groups of resources:
- Networking
- VPC network
- Subnet
- Firewall rules for SSH access via IAP and open communication within the VPC
- Cloud Nat
- IAM
- Vertex AI workbench service account
- Vertex AI pipeline service account
- Storage
- GCS bucket
- Bigquery dataset
## Customization
### Virtual Private Cloud (VPC) design
As is often the case in real-world configurations, this blueprint accepts an existing Shared-VPC via the `vpc_config` variable as input.
### Customer Managed Encryption Keys
As is often the case in real-world configurations, this blueprint accepts as input existing Cloud KMS keys to encrypt resources via the `service_encryption_keys` variable.
## Demo
In the [`demo`](./demo/) folder, you can find an example of creating a Vertex AI pipeline from a publicly available dataset and deploying the model to be used from a Vertex AI managed endpoint or from within Bigquery.
To run the demo:
- Connect to the Vertex AI workbench instance
- Clone this repository
- Run the and run [`demo/bmql_pipeline.ipynb`](demo/bmql_pipeline.ipynb) Jupyter Notebook.
## Files
| name | description | modules | resources |
|---|---|---|---|
| [datastorage.tf](./datastorage.tf) | Datastorage resources. | <code>bigquery-dataset</code> · <code>gcs</code> | |
| [main.tf](./main.tf) | Core resources. | <code>project</code> | |
| [outputs.tf](./outputs.tf) | Output variables. | | |
| [variables.tf](./variables.tf) | Terraform variables. | | |
| [versions.tf](./versions.tf) | Version pins. | | |
| [vertex.tf](./vertex.tf) | Vertex resources. | <code>iam-service-account</code> | <code>google_notebooks_instance</code> · <code>google_vertex_ai_metadata_store</code> |
| [vpc.tf](./vpc.tf) | VPC resources. | <code>net-cloudnat</code> · <code>net-vpc</code> · <code>net-vpc-firewall</code> | <code>google_project_iam_member</code> |
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [prefix](variables.tf#L23) | Prefix used for resource names. | <code>string</code> | ✓ | |
| [project_id](variables.tf#L41) | Project id references existing project if `project_create` is null. | <code>string</code> | ✓ | |
| [location](variables.tf#L17) | The location where resources will be deployed. | <code>string</code> | | <code>&#34;US&#34;</code> |
| [project_create](variables.tf#L32) | Provide values if project creation is needed, use existing project if null. Parent format: folders/folder_id or organizations/org_id. | <code title="object&#40;&#123;&#10; billing_account_id &#61; string&#10; parent &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [region](variables.tf#L46) | The region where resources will be deployed. | <code>string</code> | | <code>&#34;us-central1&#34;</code> |
| [service_encryption_keys](variables.tf#L52) | Cloud KMS to use to encrypt different services. The key location should match the service region. | <code title="object&#40;&#123;&#10; bq &#61; string&#10; compute &#61; string&#10; storage &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [vpc_config](variables.tf#L62) | Shared VPC network configurations to use. If null networks will be created in projects with pre-configured values. | <code title="object&#40;&#123;&#10; host_project &#61; string&#10; network_self_link &#61; string&#10; subnet_self_link &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| [bucket](outputs.tf#L17) | GCS Bucket URL. | |
| [dataset](outputs.tf#L22) | GCS Bucket URL. | |
| [notebook](outputs.tf#L27) | Vertex AI notebook details. | |
| [project](outputs.tf#L35) | Project id. | |
| [service-account-vertex](outputs.tf#L40) | Service account to be used for Vertex AI pipelines. | |
| [vertex-ai-metadata-store](outputs.tf#L45) | Vertex AI Metadata Store ID. | |
| [vpc](outputs.tf#L50) | VPC Network. | |
<!-- END TFDOC -->
## Test
```hcl
module "test" {
source = "./fabric/blueprints/data-solutions/bq-ml/"
project_create = {
billing_account_id = "123456-123456-123456"
parent = "folders/12345678"
}
project_id = "project-1"
prefix = "prefix"
}
# tftest modules=9 resources=46
```

View File

@ -0,0 +1,32 @@
# 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
#
# https://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 Datastorage resources.
module "bucket" {
source = "../../../modules/gcs"
project_id = module.project.project_id
prefix = var.prefix
location = var.location
name = "data"
encryption_key = try(local.service_encryption_keys.storage, null) # Example assignment of an encryption key
}
module "dataset" {
source = "../../../modules/bigquery-dataset"
project_id = module.project.project_id
id = "${replace(var.prefix, "-", "_")}_data"
encryption_key = try(local.service_encryption_keys.bq, null) # Example assignment of an encryption key
location = "US"
}

View File

@ -0,0 +1,40 @@
# BigQuery ML and Vertex AI Pipeline Demo
This demo shows how to combine BigQuery ML (BQML) and Vertex AI to create a ML pipeline leveraging the infrastructure created in the blueprint.
More in details, this tutorial will focus on the following three steps:
- define a Vertex AI pipeline to create features, train and evaluate BQML models
- serve a BQ model through an API powered by Vertex AI Endpoint
- create batch prediction via BigQuery
In this tutorial we will also see how to make explainable predictions, in order to understand what are the most important features that most influence the algorithm outputs.
# Dataset
This tutorial uses a fictitious e-commerce dataset collecting programmatically generated data from the fictitious e-commerce store called The Look. The dataset is publicy available on BigQuery at this location `bigquery-public-data.thelook_ecommerce`.
# Goal
The goal of this tutorial is to train a classification ML model using BigQuery ML and predict if a new web session is going to convert.
The tutorial focuses more on how to combine Vertex AI and BigQuery ML to create a model that can be used both for near-real time and batch predictions rather than the design of the model itself.
# Main components
In this tutorial we will make use of the following main components:
- Big Query:
- standard: to create a view which contains the model features and the target variable
- ML: to train, evaluate and make batch predictions
- Vertex AI:
- Pipeline: to define a configurable and re-usable set of steps to train and evaluate a BQML model
- Experiment: to keep track of all the trainings done via the Pipeline
- Model Registry: to keep track of the trained versions of a specific model
- Endpoint: to serve the model via API
- Workbench: to run this demo
# How to get started
1. Access the Vertex AI Workbench
2. clone this repository
2. run the [`bmql_pipeline.ipynb`](bmql_pipeline.ipynb) Jupyter Notebook

View File

@ -0,0 +1,459 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"**Copyright 2023 Google LLC**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Copyright 2023 Google LLC\n",
"#\n",
"# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
"# you may not use this file except in compliance with the License.\n",
"# You may obtain a copy of the License at\n",
"#\n",
"# https://www.apache.org/licenses/LICENSE-2.0\n",
"#\n",
"# Unless required by applicable law or agreed to in writing, software\n",
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
"# See the License for the specific language governing permissions and\n",
"# limitations under the License."
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Install python requirements and import packages"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%pip install -r requirements.txt"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import kfp\n",
"import google_cloud_pipeline_components.v1.bigquery as bqop\n",
"\n",
"from google.cloud import aiplatform as aip\n",
"from google.cloud import bigquery"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Set your env variables"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"EXPERIMENT_NAME = 'bqml-experiment'\n",
"ENDPOINT_DISPLAY_NAME = 'bqml-endpoint'\n",
"DATASET = \"{}_data\".format(PREFIX.replace(\"-\",\"_\")) \n",
"LOCATION = 'US'\n",
"MODEL_NAME = 'bqml-model'\n",
"PIPELINE_NAME = 'bqml-vertex-pipeline'\n",
"PIPELINE_ROOT = f\"gs://{PREFIX}-data\"\n",
"PREFIX = 'your-prefix'\n",
"PROJECT_ID = 'your-project-id'\n",
"REGION = 'us-central1'\n",
"SERVICE_ACCOUNT = f\"vertex-sa@{PROJECT_ID}.iam.gserviceaccount.com\""
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Vertex AI Pipeline Definition\n",
"\n",
"Let's first define the queries for the features and target creation and the query to train the model\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# this query creates the features for our model and the target value we would like to predict\n",
"\n",
"features_query = \"\"\"\n",
"CREATE VIEW if NOT EXISTS `{project_id}.{dataset}.ecommerce_abt` AS\n",
"WITH abt AS (\n",
" SELECT user_id,\n",
" session_id,\n",
" city,\n",
" postal_code,\n",
" browser,\n",
" traffic_source,\n",
" min(created_at) AS session_starting_ts,\n",
" sum(CASE WHEN event_type = 'purchase' THEN 1 ELSE 0 END) has_purchased\n",
" FROM `bigquery-public-data.thelook_ecommerce.events` \n",
" GROUP BY user_id,\n",
" session_id,\n",
" city,\n",
" postal_code,\n",
" browser,\n",
" traffic_source\n",
"), previous_orders AS (\n",
" SELECT user_id,\n",
" array_agg (struct(created_at AS order_creations_ts,\n",
" o.order_id,\n",
" o.status,\n",
" oi.order_cost)) as user_orders\n",
" FROM `bigquery-public-data.thelook_ecommerce.orders` o\n",
" JOIN (SELECT order_id,\n",
" sum(sale_price) order_cost \n",
" FROM `bigquery-public-data.thelook_ecommerce.order_items`\n",
" GROUP BY 1) oi\n",
" ON o.order_id = oi.order_id\n",
" GROUP BY 1\n",
")\n",
"SELECT abt.*,\n",
" CASE WHEN extract(DAYOFWEEK FROM session_starting_ts) IN (1,7)\n",
" THEN 'WEEKEND' \n",
" ELSE 'WEEKDAY'\n",
" END AS day_of_week,\n",
" extract(HOUR FROM session_starting_ts) hour_of_day,\n",
" (SELECT count(DISTINCT uo.order_id) \n",
" FROM unnest(user_orders) uo \n",
" WHERE uo.order_creations_ts < session_starting_ts \n",
" AND status IN ('Shipped', 'Complete', 'Processing')) AS number_of_successful_orders,\n",
" IFNULL((SELECT sum(DISTINCT uo.order_cost) \n",
" FROM unnest(user_orders) uo \n",
" WHERE uo.order_creations_ts < session_starting_ts \n",
" AND status IN ('Shipped', 'Complete', 'Processing')), 0) AS sum_previous_orders,\n",
" (SELECT count(DISTINCT uo.order_id) \n",
" FROM unnest(user_orders) uo \n",
" WHERE uo.order_creations_ts < session_starting_ts \n",
" AND status IN ('Cancelled', 'Returned')) AS number_of_unsuccessful_orders\n",
"FROM abt \n",
"LEFT JOIN previous_orders pso \n",
"ON abt.user_id = pso.user_id\n",
"\"\"\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# this query create the train job on BQ ML\n",
"train_query = \"\"\"\n",
"CREATE OR REPLACE MODEL `{project_id}.{dataset}.{model_name}`\n",
"OPTIONS(MODEL_TYPE='{model_type}',\n",
" INPUT_LABEL_COLS=['has_purchased'],\n",
" ENABLE_GLOBAL_EXPLAIN=TRUE,\n",
" MODEL_REGISTRY='VERTEX_AI',\n",
" DATA_SPLIT_METHOD = 'RANDOM',\n",
" DATA_SPLIT_EVAL_FRACTION = {split_fraction}\n",
" ) AS \n",
"SELECT * EXCEPT (session_id, session_starting_ts, user_id) \n",
"FROM `{project_id}.{dataset}.ecommerce_abt`\n",
"WHERE extract(ISOYEAR FROM session_starting_ts) = 2022\n",
"\"\"\""
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"In the following code block, we are defining our Vertex AI pipeline. It is made up of three main steps:\n",
"1. Create a BigQuery dataset that will contain the BigQuery ML models\n",
"2. Train the BigQuery ML model, in this case, a logistic regression\n",
"3. Evaluate the BigQuery ML model with the standard evaluation metrics\n",
"\n",
"The pipeline takes as input the following variables:\n",
"- ```dataset```: name of the dataset where the artifacts will be stored\n",
"- ```evaluate_job_conf```: bq dict configuration to define where to store evaluation metrics\n",
"- ```location```: BigQuery location\n",
"- ```model_name```: the display name of the BigQuery ML model\n",
"- ```project_id```: the project id where the GCP resources will be created\n",
"- ```split_fraction```: the percentage of data that will be used as an evaluation dataset"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"@kfp.dsl.pipeline(name='bqml-pipeline', pipeline_root=PIPELINE_ROOT)\n",
"def pipeline(\n",
" model_name: str,\n",
" split_fraction: float,\n",
" evaluate_job_conf: dict, \n",
" dataset: str = DATASET,\n",
" project_id: str = PROJECT_ID,\n",
" location: str = LOCATION,\n",
" ):\n",
"\n",
" create_dataset = bqop.BigqueryQueryJobOp(\n",
" project=project_id,\n",
" location=location,\n",
" query=f'CREATE SCHEMA IF NOT EXISTS {dataset}'\n",
" )\n",
"\n",
" create_features_view = bqop.BigqueryQueryJobOp(\n",
" project=project_id,\n",
" location=location,\n",
" query=features_query.format(dataset=dataset, project_id=project_id),\n",
" #job_configuration_query = {\"writeDisposition\": \"WRITE_TRUNCATE\"} #, \"destinationTable\":{\"projectId\":project_id,\"datasetId\":dataset,\"tableId\":\"ecommerce_abt_table\"}} #{\"destinationTable\":{\"projectId\":\"project_id\",\"datasetId\":dataset,\"tableId\":\"ecommerce_abt_table\"}}, #\"writeDisposition\": \"WRITE_TRUNCATE\", \n",
"\n",
" ).after(create_dataset)\n",
"\n",
" create_bqml_model = bqop.BigqueryCreateModelJobOp(\n",
" project=project_id,\n",
" location=location,\n",
" query=train_query.format(model_type = 'LOGISTIC_REG'\n",
" , project_id = project_id\n",
" , dataset = dataset\n",
" , model_name = model_name\n",
" , split_fraction=split_fraction)\n",
" ).after(create_features_view)\n",
"\n",
" evaluate_bqml_model = bqop.BigqueryEvaluateModelJobOp(\n",
" project=project_id,\n",
" location=location,\n",
" model=create_bqml_model.outputs[\"model\"],\n",
" job_configuration_query=evaluate_job_conf\n",
" ).after(create_bqml_model)\n",
"\n",
"\n",
"# this is to compile our pipeline and generate the json description file\n",
"kfp.v2.compiler.Compiler().compile(pipeline_func=pipeline,\n",
" package_path=f'{PIPELINE_NAME}.json') "
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Create Experiment\n",
"\n",
"We will create an experiment to keep track of our training and tasks on a specific issue or problem."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"my_experiment = aip.Experiment.get_or_create(\n",
" experiment_name=EXPERIMENT_NAME,\n",
" description='This is a new experiment to keep track of bqml trainings',\n",
" project=PROJECT_ID,\n",
" location=REGION\n",
")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Running the same training Vertex AI pipeline with different parameters\n",
"\n",
"One of the main tasks during the training phase is to compare different models or to try the same model with different inputs. We can leverage the power of Vertex AI Pipelines to submit the same steps with different training parameters. Thanks to the experiments artifact, it is possible to easily keep track of all the tests that have been done. This simplifies the process of selecting the best model to deploy.\n",
"\n",
"In this demo case, we will run the same training pipeline while changing the split data percentage between training and test data."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# this configuration is needed in order to persist the evaluation metrics on big query\n",
"job_configuration_query = {\"destinationTable\": {\"projectId\": PROJECT_ID, \"datasetId\": DATASET}, \"writeDisposition\": \"WRITE_TRUNCATE\"}\n",
"\n",
"for split_fraction in [0.1, 0.2]:\n",
" job_configuration_query['destinationTable']['tableId'] = MODEL_NAME+'-fraction-{}-eval_table'.format(int(split_fraction*100))\n",
" pipeline = aip.PipelineJob(\n",
" parameter_values = {'split_fraction':split_fraction, 'model_name': MODEL_NAME+'-fraction-{}'.format(int(split_fraction*100)), 'evaluate_job_conf': job_configuration_query },\n",
" display_name=PIPELINE_NAME,\n",
" template_path=f'{PIPELINE_NAME}.json',\n",
" pipeline_root=PIPELINE_ROOT,\n",
" enable_caching=True\n",
" )\n",
"\n",
" pipeline.submit(service_account=SERVICE_ACCOUNT, experiment=my_experiment)"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Deploy the model on a Vertex AI endpoint\n",
"\n",
"Thanks to the integration of Vertex AI Endpoint, creating a live endpoint to serve the model we prefer is very straightforward."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# get the model from the Model Registry \n",
"model = aip.Model(model_name=f'{MODEL_NAME}-fraction-10')\n",
"\n",
"# let's create a Vertex Endpoint where we will deploy the ML model\n",
"endpoint = aip.Endpoint.create(\n",
" display_name=ENDPOINT_DISPLAY_NAME,\n",
" project=PROJECT_ID,\n",
" location=REGION,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# deploy the BigQuery ML model on Vertex Endpoint\n",
"# have a coffe - this step can take up 10/15 minutes to finish\n",
"model.deploy(endpoint=endpoint, deployed_model_display_name='bqml-deployed-model')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Let's get a prediction from new data\n",
"inference_test = {\n",
" 'postal_code': '97700-000',\n",
" 'number_of_successful_orders': 0,\n",
" 'city': 'Santiago',\n",
" 'sum_previous_orders': 1,\n",
" 'number_of_unsuccessful_orders': 0,\n",
" 'day_of_week': 'WEEKDAY',\n",
" 'traffic_source': 'Facebook',\n",
" 'browser': 'Firefox',\n",
" 'hour_of_day': 20\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"my_prediction = endpoint.predict([inference_test])\n",
"\n",
"my_prediction"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# batch prediction on BigQuery\n",
"\n",
"explain_predict_query = \"\"\"\n",
"SELECT *\n",
"FROM ML.EXPLAIN_PREDICT(MODEL `{project_id}.{dataset}.{model_name}`,\n",
" (SELECT * EXCEPT (session_id, session_starting_ts, user_id, has_purchased) \n",
" FROM `{project_id}.{dataset}.ecommerce_abt`\n",
" WHERE extract(ISOYEAR FROM session_starting_ts) = 2023),\n",
" STRUCT(5 AS top_k_features, 0.5 AS threshold))\n",
"LIMIT 100\n",
"\"\"\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# batch prediction on BigQuery\n",
"\n",
"with open(\"sql/explain_predict.sql\") as file:\n",
" explain_predict_query = file.read()\n",
"\n",
"client = bigquery_client = bigquery.Client(location=LOCATION, project=PROJECT_ID)\n",
"batch_predictions = bigquery_client.query(\n",
" explain_predict_query.format(\n",
" project_id=PROJECT_ID,\n",
" dataset=DATASET,\n",
" model_name=f'{MODEL_NAME}-fraction-10')\n",
" ).to_dataframe()\n",
"\n",
"batch_predictions"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Conclusions\n",
"\n",
"Thanks to this tutorial we were able to:\n",
"- Define a re-usable Vertex AI pipeline to train and evaluate BQ ML models\n",
"- Use a Vertex AI Experiment to keep track of multiple trainings for the same model with different paramenters (in this case a different split for train/test data)\n",
"- Deploy the preferred model on a Vertex AI managed Endpoint in order to serve the model for real-time use cases via API\n",
"- Make batch prediction via Big Query and see what are the top 5 features which influenced the algorithm output"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.8.9"
},
"orig_nbformat": 4,
"vscode": {
"interpreter": {
"hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6"
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -0,0 +1,2 @@
kfp==1.8.19
google-cloud-pipeline-components==1.0.39

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View File

@ -0,0 +1,65 @@
# 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
#
# https://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 Core resources.
locals {
service_encryption_keys = var.service_encryption_keys
shared_vpc_project = try(var.vpc_config.host_project, null)
subnet = (
local.use_shared_vpc
? var.vpc_config.subnet_self_link
: values(module.vpc.0.subnet_self_links)[0]
)
use_shared_vpc = var.vpc_config != null
vpc = (
local.use_shared_vpc
? var.vpc_config.network_self_link
: module.vpc.0.self_link
)
}
module "project" {
source = "../../../modules/project"
name = var.project_id
parent = try(var.project_create.parent, null)
billing_account = try(var.project_create.billing_account_id, null)
project_create = var.project_create != null
prefix = var.project_create == null ? null : var.prefix
services = [
"aiplatform.googleapis.com",
"bigquery.googleapis.com",
"bigquerystorage.googleapis.com",
"bigqueryreservation.googleapis.com",
"compute.googleapis.com",
"ml.googleapis.com",
"notebooks.googleapis.com",
"servicenetworking.googleapis.com",
"stackdriver.googleapis.com",
"storage.googleapis.com",
"storage-component.googleapis.com"
]
shared_vpc_service_config = local.shared_vpc_project == null ? null : {
attach = true
host_project = local.shared_vpc_project
}
service_encryption_key_ids = {
compute = [try(local.service_encryption_keys.compute, null)]
bq = [try(local.service_encryption_keys.bq, null)]
storage = [try(local.service_encryption_keys.storage, null)]
}
service_config = {
disable_on_destroy = false, disable_dependent_services = false
}
}

View File

@ -0,0 +1,53 @@
# Copyright 2022 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
#
# https://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 Output variables.
output "bucket" {
description = "GCS Bucket URL."
value = module.bucket.url
}
output "dataset" {
description = "GCS Bucket URL."
value = module.dataset.id
}
output "notebook" {
description = "Vertex AI notebook details."
value = {
name = resource.google_notebooks_instance.playground.name
id = resource.google_notebooks_instance.playground.id
}
}
output "project" {
description = "Project id."
value = module.project.project_id
}
output "service-account-vertex" {
description = "Service account to be used for Vertex AI pipelines."
value = module.service-account-vertex.email
}
output "vertex-ai-metadata-store" {
description = "Vertex AI Metadata Store ID."
value = google_vertex_ai_metadata_store.store.id
}
output "vpc" {
description = "VPC Network."
value = local.vpc
}

View File

@ -0,0 +1,70 @@
# Copyright 2022 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
#
# https://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 Terraform variables.
variable "location" {
description = "The location where resources will be deployed."
type = string
default = "US"
}
variable "prefix" {
description = "Prefix used for resource names."
type = string
validation {
condition = var.prefix != ""
error_message = "Prefix cannot be empty."
}
}
variable "project_create" {
description = "Provide values if project creation is needed, use existing project if null. Parent format: folders/folder_id or organizations/org_id."
type = object({
billing_account_id = string
parent = string
})
default = null
}
variable "project_id" {
description = "Project id references existing project if `project_create` is null."
type = string
}
variable "region" {
description = "The region where resources will be deployed."
type = string
default = "us-central1"
}
variable "service_encryption_keys" {
description = "Cloud KMS to use to encrypt different services. The key location should match the service region."
type = object({
bq = string
compute = string
storage = string
})
default = null
}
variable "vpc_config" {
description = "Shared VPC network configurations to use. If null networks will be created in projects with pre-configured values."
type = object({
host_project = string
network_self_link = string
subnet_self_link = string
})
default = null
}

View File

@ -0,0 +1,27 @@
# Copyright 2022 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
#
# https://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.
terraform {
required_version = ">= 1.3.1"
required_providers {
google = {
source = "hashicorp/google"
version = ">= 4.55.0" # tftest
}
google-beta = {
source = "hashicorp/google-beta"
version = ">= 4.55.0" # tftest
}
}
}

View File

@ -0,0 +1,104 @@
# 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
#
# https://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 Vertex resources.
resource "google_vertex_ai_metadata_store" "store" {
provider = google-beta
project = module.project.project_id
name = "default" #"${var.prefix}-metadata-store"
description = "Vertex Ai Metadata Store"
region = var.region
#TODO Check/Implement P4SA logic for IAM role
# encryption_spec {
# kms_key_name = var.service_encryption_keys.ai_metadata_store
# }
}
module "service-account-notebook" {
source = "../../../modules/iam-service-account"
project_id = module.project.project_id
name = "notebook-sa"
iam_project_roles = {
(module.project.project_id) = [
"roles/bigquery.admin",
"roles/bigquery.jobUser",
"roles/bigquery.dataEditor",
"roles/bigquery.user",
"roles/dialogflow.client",
"roles/storage.admin",
"roles/aiplatform.user",
"roles/iam.serviceAccountUser"
]
}
}
module "service-account-vertex" {
source = "../../../modules/iam-service-account"
project_id = module.project.project_id
name = "vertex-sa"
iam_project_roles = {
(module.project.project_id) = [
"roles/bigquery.admin",
"roles/bigquery.jobUser",
"roles/bigquery.dataEditor",
"roles/bigquery.user",
"roles/dialogflow.client",
"roles/storage.admin",
"roles/aiplatform.user"
]
}
}
resource "google_notebooks_instance" "playground" {
name = "${var.prefix}-notebook"
location = format("%s-%s", var.region, "b")
machine_type = "e2-medium"
project = module.project.project_id
container_image {
repository = "gcr.io/deeplearning-platform-release/base-cpu"
tag = "latest"
}
install_gpu_driver = true
boot_disk_type = "PD_SSD"
boot_disk_size_gb = 110
disk_encryption = try(local.service_encryption_keys.compute != null, false) ? "CMEK" : null
kms_key = try(local.service_encryption_keys.compute, null)
no_public_ip = true
no_proxy_access = false
network = local.vpc
subnet = local.subnet
service_account = module.service-account-notebook.email
# Enable Secure Boot
shielded_instance_config {
enable_secure_boot = true
}
# Remove once terraform-provider-google/issues/9164 is fixed
lifecycle {
ignore_changes = [disk_encryption, kms_key]
}
#TODO Uncomment once terraform-provider-google/issues/9273 is fixed
# tags = ["ssh"]
depends_on = [
google_project_iam_member.shared_vpc,
]
}

View File

@ -0,0 +1,64 @@
# 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
#
# https://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 VPC resources.
module "vpc" {
source = "../../../modules/net-vpc"
count = local.use_shared_vpc ? 0 : 1
project_id = module.project.project_id
name = "${var.prefix}-vpc"
subnets = [
{
ip_cidr_range = "10.0.0.0/20"
name = "${var.prefix}-subnet"
region = var.region
}
]
}
module "vpc-firewall" {
source = "../../../modules/net-vpc-firewall"
count = local.use_shared_vpc ? 0 : 1
project_id = module.project.project_id
network = module.vpc.0.name
default_rules_config = {
admin_ranges = ["10.0.0.0/20"]
}
ingress_rules = {
#TODO Remove and rely on 'ssh' tag once terraform-provider-google/issues/9273 is fixed
("${var.prefix}-iap") = {
description = "Enable SSH from IAP on Notebooks."
source_ranges = ["35.235.240.0/20"]
targets = ["notebook-instance"]
rules = [{ protocol = "tcp", ports = [22] }]
}
}
}
module "cloudnat" {
source = "../../../modules/net-cloudnat"
count = local.use_shared_vpc ? 0 : 1
project_id = module.project.project_id
name = "${var.prefix}-default"
region = var.region
router_network = module.vpc.0.name
}
resource "google_project_iam_member" "shared_vpc" {
count = local.use_shared_vpc ? 1 : 0
project = var.vpc_config.host_project
role = "roles/compute.networkUser"
member = "serviceAccount:${module.project.service_accounts.robots.notebooks}"
}

View File

@ -6,15 +6,27 @@ They are meant to be used as minimal but complete starting points to create actu
## Blueprints
### Calling a private Cloud Function from on-premises
<a href="./private-cloud-function-from-onprem/" title="Private Cloud Function from On-premises"><img src="./private-cloud-function-from-onprem/diagram.png" align="left" width="280px"></a> This [blueprint](./private-cloud-function-from-onprem/) shows how to invoke a [private Google Cloud Function](https://cloud.google.com/functions/docs/networking/network-settings) from the on-prem environment via a [Private Service Connect endpoint](https://cloud.google.com/vpc/docs/private-service-connect#benefits-apis).
<br clear="left">
### Calling on-premise services through PSC and hybrid NEGs
<a href="./psc-hybrid/" title="Hybrid connectivity to on-premise services thrugh PSC"><img src="./psc-hybrid/diagram.png" align="left" width="280px"></a> This [blueprint](./psc-hybrid/) shows how to privately connect to on-premise services (IP + port) from GCP, leveraging [Private Service Connect (PSC)](https://cloud.google.com/vpc/docs/private-service-connect) and [Hybrid Network Endpoint Groups](https://cloud.google.com/load-balancing/docs/negs/hybrid-neg-concepts).
<br clear="left">
### Decentralized firewall management
<a href="./decentralized-firewall/" title="Decentralized firewall management"><img src="./decentralized-firewall/diagram.png" align="left" width="280px"></a> This [blueprint](./decentralized-firewall/) shows how a decentralized firewall management can be organized using the [firewall factory](../factories/net-vpc-firewall-yaml/).
<br clear="left">
### Network filtering with Squid
### GLB and multi-regional daisy-chaining through hybrid NEGs
<a href="./filtering-proxy/" title="Network filtering with Squid"><img src="./filtering-proxy/squid.png" align="left" width="280px"></a> This [blueprint](./filtering-proxy/) how to deploy a filtering HTTP proxy to restrict Internet access, in a simplified setup using a VPC with two subnets and a Cloud DNS zone, and an optional MIG for scaling.
<a href="./glb-hybrid-neg-internal/" title="XGLB and multi-regional daisy-chaining through hybrid NEGs"><img src="./glb-hybrid-neg-internal/diagram.png" align="left" width="280px"></a> This [blueprint](./glb-hybrid-neg-internal/) shows the experimental use of hybrid NEGs behind external Global Load Balancers (GLBs) to connect to GCP instances living in spoke VPCs and behind Network Virtual Appliances (NVAs).
<br clear="left">
@ -24,14 +36,6 @@ They are meant to be used as minimal but complete starting points to create actu
<br clear="left">
### Hub and Spoke via Peering
<a href="./hub-and-spoke-peering/" title="Hub and spoke via peering blueprint"><img src="./hub-and-spoke-peering/diagram.png" align="left" width="280px"></a> This [blueprint](./hub-and-spoke-peering/) implements a hub and spoke topology via VPC peering, a common design where a landing zone VPC (hub) is connected to on-premises, and then peered with satellite VPCs (spokes) to further partition the infrastructure.
The sample highlights the lack of transitivity in peering: the absence of connectivity between spokes, and the need create workarounds for private service access to managed services. One such workaround is shown for private GKE, allowing access from hub and all spokes to GKE masters via a dedicated VPN.
<br clear="left">
### Hub and Spoke via Dynamic VPN
<a href="./hub-and-spoke-vpn/" title="Hub and spoke via dynamic VPN"><img src="./hub-and-spoke-vpn/diagram.png" align="left" width="280px"></a> This [blueprint](./hub-and-spoke-vpn/) implements a hub and spoke topology via dynamic VPN tunnels, a common design where peering cannot be used due to limitations on the number of spokes or connectivity to managed services.
@ -40,6 +44,14 @@ The blueprint shows how to implement spoke transitivity via BGP advertisements,
<br clear="left">
### Hub and Spoke via Peering
<a href="./hub-and-spoke-peering/" title="Hub and spoke via peering blueprint"><img src="./hub-and-spoke-peering/diagram.png" align="left" width="280px"></a> This [blueprint](./hub-and-spoke-peering/) implements a hub and spoke topology via VPC peering, a common design where a landing zone VPC (hub) is connected to on-premises, and then peered with satellite VPCs (spokes) to further partition the infrastructure.
The sample highlights the lack of transitivity in peering: the absence of connectivity between spokes, and the need create workarounds for private service access to managed services. One such workaround is shown for private GKE, allowing access from hub and all spokes to GKE masters via a dedicated VPN.
<br clear="left">
### ILB as next hop
<a href="./ilb-next-hop/" title="ILB as next hop"><img src="./ilb-next-hop/diagram.png" align="left" width="280px"></a> This [blueprint](./ilb-next-hop/) allows testing [ILB as next hop](https://cloud.google.com/load-balancing/docs/internal/ilb-next-hop-overview) using simple Linux gateway VMS between two VPCs, to emulate virtual appliances. An optional additional ILB can be enabled to test multiple load balancer configurations and hashing.
@ -63,15 +75,9 @@ The emulated on-premises environment can be used to test access to different ser
-->
### Calling a private Cloud Function from on-premises
### Network filtering with Squid
<a href="./private-cloud-function-from-onprem/" title="Private Cloud Function from On-premises"><img src="./private-cloud-function-from-onprem/diagram.png" align="left" width="280px"></a> This [blueprint](./private-cloud-function-from-onprem/) shows how to invoke a [private Google Cloud Function](https://cloud.google.com/functions/docs/networking/network-settings) from the on-prem environment via a [Private Service Connect endpoint](https://cloud.google.com/vpc/docs/private-service-connect#benefits-apis).
<br clear="left">
### Calling on-premise services through PSC and hybrid NEGs
<a href="./psc-hybrid/" title="Hybrid connectivity to on-premise services thrugh PSC"><img src="./psc-hybrid/diagram.png" align="left" width="280px"></a> This [blueprint](./psc-hybrid/) shows how to privately connect to on-premise services (IP + port) from GCP, leveraging [Private Service Connect (PSC)](https://cloud.google.com/vpc/docs/private-service-connect) and [Hybrid Network Endpoint Groups](https://cloud.google.com/load-balancing/docs/negs/hybrid-neg-concepts).
<a href="./filtering-proxy/" title="Network filtering with Squid"><img src="./filtering-proxy/squid.png" align="left" width="280px"></a> This [blueprint](./filtering-proxy/) how to deploy a filtering HTTP proxy to restrict Internet access, in a simplified setup using a VPC with two subnets and a Cloud DNS zone, and an optional MIG for scaling.
<br clear="left">

View File

@ -0,0 +1,100 @@
# GLB and multi-regional daisy-chaining through hybrid NEGs
The blueprint shows the experimental use of hybrid NEGs behind eXternal Global Load Balancers (GLBs) to connect to GCP instances living in spoke VPCs and behind Network Virtual Appliances (NVAs).
<p align="center"> <img src="diagram.png" width="700"> </p>
This allows users to not configure per-destination-VM NAT rules in the NVAs.
The user traffic will enter the GLB, it will go across the NVAs and it will be routed to the destination VMs (or the ILBs behind the VMs) in the spokes.
## What the blueprint creates
This is what the blueprint brings up, using the default module values.
The ids `primary` and `secondary` are used to identify two regions. By default, `europe-west1` and `europe-west4`.
- Projects: landing, spoke-01
- VPCs and subnets
+ landing-untrusted: primary - 192.168.1.0/24 and secondary - 192.168.2.0/24
+ landing-trusted: primary - 192.168.11.0/24 and secondary - 192.168.22.0/24
+ spoke-01: primary - 192.168.101.0/24 and secondary - 192.168.102.0/24
- Cloud NAT
+ landing-untrusted (both for primary and secondary)
+ in spoke-01 (both for primary and secondary) - this is just for test purposes, so you VMs can automatically install nginx, even if NVAs are still not ready
- VMs
+ NVAs in MIGs in the landing project, both in primary and secondary, with NICs in the untrusted and in the trusted VPCs
+ Test VMs, in spoke-01, both in primary and secondary. Optionally, deployed in MIGs
- Hybrid NEGs in the untrusted VPC, both in primary and secondary, either pointing to the test VMs in the spoke or -optionally- to ILBs in the spokes (if test VMs are deployed as MIGs)
- Internal Load balancers (L4 ILBs)
+ in the untrusted VPC, pointing to NVA MIGs, both in primary and secondary. Their VIPs are used by custom routes in the untrusted VPC, so that all traffic that arrives in the untrusted VPC destined for the test VMs in the spoke is sent through the NVAs
+ optionally, in the spokes. They are created if the user decides to deploy the test VMs as MIGs
- External Global Load balancer (GLB) in the untrusted VPC, using the hybrid NEGs as its backends
## Health Checks
Google Cloud sends [health checks](https://cloud.google.com/load-balancing/docs/health-checks) using [specific IP ranges](https://cloud.google.com/load-balancing/docs/health-checks#fw-netlb). Each VPC uses [implicit routes](https://cloud.google.com/vpc/docs/routes#special_return_paths) to send the health check replies back to Google.
At the moment of writing, when Google Cloud sends out [health checks](https://cloud.google.com/load-balancing/docs/health-checks) against backend services, it expects replies to come back from the same VPC where they have been sent out to.
Given the GLB lives in the untrusted VPC, its backend service health checks are sent out to that VPC, and so the replies are expected from it. Anyway, the destinations of the health checks are the test VMs in the spokes.
The blueprint configures some custom routes in the untrusted VPC and routing/NAT rules in the NVAs, so that health checks reach the test VMs through the NVAs, and replies come back through the NVAs in the untrusted VPC. Without these configurations health checks will fail and backend services won't be reachable.
Specifically:
- we create two custom routes in the untrusted VPC (one per region) so that traffic for the spoke subnets is sent to the VIP of the L4 ILBs in front of the NVAs
- we configure the NVAs so they know how to route traffic to the spokes via the trusted VPC gateway
- we configure the NVAs to s-NAT (specifically, masquerade) health checks traffic destined to the test VMs
## Change the ilb_create variable
Through the `ilb_create` variable you can decide whether test VMs in the spoke will be deployed as MIGs with ILBs in front. This will also configure NEGs, so they point to the ILB VIPs, instead of the VM IPs.
At the moment, every time a user changes the configuration of a NEG, the NEG is recreated. When this happens, the provider doesn't check if it is used by other resources, such as GLB backend services. Until this doesn't get fixed, every time you'll need to change the NEG configuration (i.e. when changing the variable `ilb_create`) you'll have to workaround it. Here is how:
- Destroy the existing backend service: `terraform destroy -target 'module.hybrid-glb.google_compute_backend_service.default["default"]'`
- Change the variable `ilb_create`
- run `terraform apply`
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [prefix](variables.tf#L36) | Prefix used for resource names. | <code>string</code> | ✓ | |
| [ilb_create](variables.tf#L17) | Whether we should create an ILB L4 in front of the test VMs in the spoke. | <code>string</code> | | <code>&#34;false&#34;</code> |
| [ip_config](variables.tf#L23) | The subnet IP configurations. | <code title="object&#40;&#123;&#10; spoke_primary &#61; optional&#40;string, &#34;192.168.101.0&#47;24&#34;&#41;&#10; spoke_secondary &#61; optional&#40;string, &#34;192.168.102.0&#47;24&#34;&#41;&#10; trusted_primary &#61; optional&#40;string, &#34;192.168.11.0&#47;24&#34;&#41;&#10; trusted_secondary &#61; optional&#40;string, &#34;192.168.22.0&#47;24&#34;&#41;&#10; untrusted_primary &#61; optional&#40;string, &#34;192.168.1.0&#47;24&#34;&#41;&#10; untrusted_secondary &#61; optional&#40;string, &#34;192.168.2.0&#47;24&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [project_names](variables.tf#L45) | The project names. | <code title="object&#40;&#123;&#10; landing &#61; string&#10; spoke_01 &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; landing &#61; &#34;landing&#34;&#10; spoke_01 &#61; &#34;spoke-01&#34;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [projects_create](variables.tf#L57) | Parameters for the creation of the new project. | <code title="object&#40;&#123;&#10; billing_account_id &#61; string&#10; parent &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [regions](variables.tf#L66) | Region definitions. | <code title="object&#40;&#123;&#10; primary &#61; string&#10; secondary &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; primary &#61; &#34;europe-west1&#34;&#10; secondary &#61; &#34;europe-west4&#34;&#10;&#125;">&#123;&#8230;&#125;</code> |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| [glb_ip_address](outputs.tf#L17) | Load balancer IP address. | |
<!-- END TFDOC -->
## Test
```hcl
module "test" {
source = "./fabric/blueprints/networking/glb-hybrid-neg-internal"
prefix = "prefix"
projects_create = {
billing_account_id = "123456-123456-123456"
parent = "folders/123456789"
}
}
# tftest modules=21 resources=64
```

View File

@ -0,0 +1,42 @@
#!/bin/bash
# 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.
echo 'Enabling IP forwarding'
sed '/net.ipv4.ip_forward=1/s/^#//g' -i /etc/sysctl.conf &&
sysctl -p /etc/sysctl.conf &&
/etc/init.d/procps restart
echo 'Setting routes'
ip route add ${spoke-primary} via ${gateway-trusted} dev ens5
ip route add ${spoke-secondary} via ${gateway-trusted} dev ens5
echo 'Setting NAT masquerade, so that HCs can reach the spoke through the NVA using the trusted intf source IP'
iptables \
-t nat \
-A POSTROUTING \
-s 130.211.0.0/22,35.191.0.0/16 \
-d ${spoke-primary} \
-p tcp \
--dport 80 \
-j MASQUERADE
iptables \
-t nat \
-A POSTROUTING \
-s 130.211.0.0/22,35.191.0.0/16 \
-d ${spoke-secondary} \
-p tcp \
--dport 80 \
-j MASQUERADE

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 328 KiB

View File

@ -0,0 +1,71 @@
/**
* 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 External Global Load Balancer.
module "hybrid-glb" {
source = "../../../modules/net-glb"
project_id = module.project_landing.project_id
name = "hybrid-glb"
backend_service_configs = {
default = {
backends = [
{
backend = "neg-primary"
balancing_mode = "RATE"
max_rate = { per_endpoint = 100 }
},
{
backend = "neg-secondary"
balancing_mode = "RATE"
max_rate = { per_endpoint = 100 }
}
]
}
}
neg_configs = {
neg-primary = {
hybrid = {
network = module.vpc_landing_untrusted.name
zone = local.zones["primary"]
endpoints = {
primary = {
ip_address = (var.ilb_create
? module.test_vm_ilbs["primary"].forwarding_rule_address
: module.test_vms["primary"].internal_ip
)
port = 80
}
}
}
}
neg-secondary = {
hybrid = {
network = module.vpc_landing_untrusted.name
zone = local.zones["secondary"]
endpoints = {
secondary = {
ip_address = (var.ilb_create
? module.test_vm_ilbs["secondary"].forwarding_rule_address
: module.test_vms["secondary"].internal_ip
)
port = 80
}
}
}
}
}
}

View File

@ -0,0 +1,122 @@
/**
* 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.
*/
locals {
zones = {
primary = "${var.regions.primary}-b"
secondary = "${var.regions.secondary}-b"
}
}
module "project_landing" {
source = "../../../modules/project"
billing_account = (var.projects_create != null
? var.projects_create.billing_account_id
: null
)
name = var.project_names.landing
parent = (var.projects_create != null
? var.projects_create.parent
: null
)
prefix = var.prefix
project_create = var.projects_create != null
services = [
"compute.googleapis.com",
"networkmanagement.googleapis.com",
# Logging and Monitoring
"logging.googleapis.com",
"monitoring.googleapis.com"
]
}
module "vpc_landing_untrusted" {
source = "../../../modules/net-vpc"
project_id = module.project_landing.project_id
name = "landing-untrusted"
routes = {
spoke1-primary = {
dest_range = var.ip_config.spoke_primary
next_hop_type = "ilb"
next_hop = module.nva_untrusted_ilbs["primary"].forwarding_rule_self_link
}
spoke1-secondary = {
dest_range = var.ip_config.spoke_secondary
next_hop_type = "ilb"
next_hop = module.nva_untrusted_ilbs["secondary"].forwarding_rule_self_link
}
}
subnets = [
{
ip_cidr_range = var.ip_config.untrusted_primary
name = "untrusted-${var.regions.primary}"
region = var.regions.primary
},
{
ip_cidr_range = var.ip_config.untrusted_secondary
name = "untrusted-${var.regions.secondary}"
region = var.regions.secondary
}
]
}
module "vpc_landing_trusted" {
source = "../../../modules/net-vpc"
project_id = module.project_landing.project_id
name = "landing-trusted"
subnets = [
{
ip_cidr_range = var.ip_config.trusted_primary
name = "trusted-${var.regions.primary}"
region = var.regions.primary
},
{
ip_cidr_range = var.ip_config.trusted_secondary
name = "trusted-${var.regions.secondary}"
region = var.regions.secondary
}
]
}
module "firewall_landing_untrusted" {
source = "../../../modules/net-vpc-firewall"
project_id = module.project_landing.project_id
network = module.vpc_landing_untrusted.name
ingress_rules = {
allow-ssh-from-hcs = {
description = "Allow health checks to NVAs coming on port 22."
targets = ["ssh"]
source_ranges = [
"130.211.0.0/22",
"35.191.0.0/16"
]
rules = [{ protocol = "tcp", ports = [22] }]
}
}
}
module "nats_landing" {
for_each = var.regions
source = "../../../modules/net-cloudnat"
project_id = module.project_landing.project_id
region = each.value
name = "nat-${each.value}"
router_network = module.vpc_landing_untrusted.self_link
}

View File

@ -0,0 +1,87 @@
/**
* 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 Network Virtual Appliances (NVAs).
module "nva_instance_templates" {
for_each = var.regions
source = "../../../modules/compute-vm"
project_id = module.project_landing.project_id
can_ip_forward = true
create_template = true
name = "nva-${each.value}"
service_account_create = true
zone = local.zones[each.key]
metadata = {
startup-script = templatefile(
"${path.module}/data/nva-startup-script.tftpl",
{
gateway-trusted = cidrhost(module.vpc_landing_trusted.subnet_ips["${each.value}/trusted-${each.value}"], 1)
spoke-primary = var.ip_config.spoke_primary
spoke-secondary = var.ip_config.spoke_secondary
}
)
}
network_interfaces = [
{
network = module.vpc_landing_untrusted.self_link
subnetwork = module.vpc_landing_untrusted.subnet_self_links["${each.value}/untrusted-${each.value}"]
},
{
network = module.vpc_landing_trusted.self_link
subnetwork = module.vpc_landing_trusted.subnet_self_links["${each.value}/trusted-${each.value}"]
}
]
tags = [
"http-server",
"https-server",
"ssh"
]
}
module "nva_migs" {
for_each = var.regions
source = "../../../modules/compute-mig"
project_id = module.project_landing.project_id
location = local.zones[each.key]
name = "nva-${each.value}"
target_size = 1
instance_template = module.nva_instance_templates[each.key].template.self_link
}
module "nva_untrusted_ilbs" {
for_each = var.regions
source = "../../../modules/net-ilb"
project_id = module.project_landing.project_id
region = each.value
name = "nva-ilb-${local.zones[each.key]}"
service_label = "nva-ilb-${local.zones[each.key]}"
vpc_config = {
network = module.vpc_landing_untrusted.self_link
subnetwork = module.vpc_landing_untrusted.subnet_self_links["${each.value}/untrusted-${each.value}"]
}
backends = [{
group = module.nva_migs[each.key].group_manager.instance_group
}]
health_check_config = {
tcp = {
port = 22
}
}
}

View File

@ -0,0 +1,20 @@
/**
* 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.
*/
output "glb_ip_address" {
description = "Load balancer IP address."
value = module.hybrid-glb.address
}

View File

@ -0,0 +1,146 @@
/**
* 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 VPC Spoke(s) and test VMs.
module "project_spoke_01" {
source = "../../../modules/project"
billing_account = (var.projects_create != null
? var.projects_create.billing_account_id
: null
)
name = var.project_names.spoke_01
parent = (var.projects_create != null
? var.projects_create.parent
: null
)
prefix = var.prefix
services = [
"compute.googleapis.com",
"networkmanagement.googleapis.com",
# Logging and Monitoring
"logging.googleapis.com",
"monitoring.googleapis.com"
]
}
module "vpc_spoke_01" {
source = "../../../modules/net-vpc"
project_id = module.project_spoke_01.project_id
name = "spoke-01"
subnets = [
{
ip_cidr_range = var.ip_config.spoke_primary
name = "spoke-01-${var.regions.primary}"
region = var.regions.primary
},
{
ip_cidr_range = var.ip_config.spoke_secondary
name = "spoke-01-${var.regions.secondary}"
region = var.regions.secondary
}
]
peering_config = {
peer_vpc_self_link = module.vpc_landing_trusted.self_link
import_routes = true
}
}
module "firewall_spoke_01" {
source = "../../../modules/net-vpc-firewall"
project_id = module.project_spoke_01.project_id
network = module.vpc_spoke_01.name
ingress_rules = {
allow-nva-hcs = {
description = "Allow health checks coming on port 80 and 443 from NVAs."
targets = ["http-server", "https-server"]
source_ranges = [
var.ip_config.trusted_primary,
var.ip_config.trusted_secondary
]
rules = [{ protocol = "tcp", ports = [80, 443] }]
}
}
}
# NAT is used to install nginx for test purposed, even if NVAs are still not ready
module "nats_spoke_01" {
for_each = var.regions
source = "../../../modules/net-cloudnat"
name = "spoke-01-${each.value}"
project_id = module.project_spoke_01.project_id
region = each.value
router_network = module.vpc_spoke_01.name
}
module "test_vms" {
for_each = var.regions
source = "../../../modules/compute-vm"
name = "spoke-01-${each.value}"
project_id = module.project_spoke_01.project_id
create_template = var.ilb_create
service_account_create = true
zone = local.zones[each.key]
metadata = {
startup-script = "apt update && apt install -y nginx"
}
network_interfaces = [{
network = module.vpc_spoke_01.self_link
subnetwork = module.vpc_spoke_01.subnet_self_links["${each.value}/spoke-01-${each.value}"]
}]
tags = [
"http-server",
"https-server",
"ssh"
]
}
module "test_vm_migs" {
for_each = var.ilb_create ? var.regions : {}
source = "../../../modules/compute-mig"
project_id = module.project_spoke_01.project_id
location = local.zones[each.key]
name = "test-vm-${each.value}"
target_size = 1
instance_template = module.test_vms[each.key].template.self_link
}
module "test_vm_ilbs" {
for_each = var.ilb_create ? var.regions : {}
source = "../../../modules/net-ilb"
project_id = module.project_spoke_01.project_id
region = each.value
name = "test-vm-ilb-${each.value}"
service_label = "test-vm-ilb-${each.value}"
vpc_config = {
network = module.vpc_spoke_01.self_link
subnetwork = module.vpc_spoke_01.subnet_self_links["${each.value}/spoke-01-${each.value}"]
}
backends = [{
group = module.test_vm_migs[each.key].group_manager.instance_group
}]
health_check_config = {
tcp = {
port = 80
}
}
}

View File

@ -0,0 +1,76 @@
/**
* 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.
*/
variable "ilb_create" {
description = "Whether we should create an ILB L4 in front of the test VMs in the spoke."
type = string
default = false
}
variable "ip_config" {
description = "The subnet IP configurations."
type = object({
spoke_primary = optional(string, "192.168.101.0/24")
spoke_secondary = optional(string, "192.168.102.0/24")
trusted_primary = optional(string, "192.168.11.0/24")
trusted_secondary = optional(string, "192.168.22.0/24")
untrusted_primary = optional(string, "192.168.1.0/24")
untrusted_secondary = optional(string, "192.168.2.0/24")
})
default = {}
}
variable "prefix" {
description = "Prefix used for resource names."
type = string
validation {
condition = var.prefix != ""
error_message = "Prefix cannot be empty."
}
}
variable "project_names" {
description = "The project names."
type = object({
landing = string
spoke_01 = string
})
default = {
landing = "landing"
spoke_01 = "spoke-01"
}
}
variable "projects_create" {
description = "Parameters for the creation of the new project."
type = object({
billing_account_id = string
parent = string
})
default = null
}
variable "regions" {
description = "Region definitions."
type = object({
primary = string
secondary = string
})
default = {
primary = "europe-west1"
secondary = "europe-west4"
}
}

View File

@ -116,8 +116,27 @@ Initial population depends on a modules repository being configured in the `modu
### Commit configuration
Finally, a `commit_config` variable is optional: it can be used to configure author, email and message used in commits for initial population of files, its defaults are probably fine for most use cases.
An optional variable `commit_config` can be used to configure the author, email, and message used in commits for the initial population of files. Its defaults are probably fine for most use cases.
### Pull Request configuration
An optional variable `pull_request_config` can be used to configure the title, body, head_ref, and base_ref of the pull request created for the initial population or update of files. By default, no pull request is created. To create a pull request, set the `create` attribute to `true`. `base_ref` defaults to `main` and `head_ref` to the head branch name. If the head branch does not exist, it will be created from the base_ref branch.
```hcl
pull_request_config = {
create = true
title = "FAST: initial loading or update"
body = ""
base_ref = "main"
head_ref = "fast-loader"
}
# tftest skip
```
To start using a pull request workflow, if the initial loading was created without a pull request in the past, please use the following command to delete the actual branch files from the Terraform state to keep it in the current state:
```bash
terraform state list | grep github_repository_file | awk '{print "terraform state rm '\''"$1"'\''"}'
```
<!-- TFDOC OPTS files:1 -->
<!-- BEGIN TFDOC -->
@ -126,7 +145,7 @@ Finally, a `commit_config` variable is optional: it can be used to configure aut
| name | description | resources |
|---|---|---|
| [cicd-versions.tf](./cicd-versions.tf) | Provider version. | |
| [main.tf](./main.tf) | Module-level locals and resources. | <code>github_actions_secret</code> · <code>github_repository</code> · <code>github_repository_deploy_key</code> · <code>github_repository_file</code> · <code>tls_private_key</code> |
| [main.tf](./main.tf) | Module-level locals and resources. | <code>github_actions_secret</code> · <code>github_branch</code> · <code>github_repository</code> · <code>github_repository_deploy_key</code> · <code>github_repository_file</code> · <code>github_repository_pull_request</code> · <code>tls_private_key</code> |
| [outputs.tf](./outputs.tf) | Module outputs. | |
| [providers.tf](./providers.tf) | Provider configuration. | |
| [variables.tf](./variables.tf) | Module variables. | |
@ -138,7 +157,8 @@ Finally, a `commit_config` variable is optional: it can be used to configure aut
| [organization](variables.tf#L51) | GitHub organization. | <code>string</code> | ✓ | |
| [commmit_config](variables.tf#L17) | Configure commit metadata. | <code title="object&#40;&#123;&#10; author &#61; optional&#40;string, &#34;FAST loader&#34;&#41;&#10; email &#61; optional&#40;string, &#34;fast-loader&#64;fast.gcp.tf&#34;&#41;&#10; message &#61; optional&#40;string, &#34;FAST initial loading&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [modules_config](variables.tf#L28) | Configure access to repository module via key, and replacement for modules sources in stage repositories. | <code title="object&#40;&#123;&#10; repository_name &#61; string&#10; source_ref &#61; optional&#40;string&#41;&#10; module_prefix &#61; optional&#40;string, &#34;&#34;&#41;&#10; key_config &#61; optional&#40;object&#40;&#123;&#10; create_key &#61; optional&#40;bool, false&#41;&#10; create_secrets &#61; optional&#40;bool, false&#41;&#10; keypair_path &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [repositories](variables.tf#L56) | Repositories to create. | <code title="map&#40;object&#40;&#123;&#10; create_options &#61; optional&#40;object&#40;&#123;&#10; allow &#61; optional&#40;object&#40;&#123;&#10; auto_merge &#61; optional&#40;bool&#41;&#10; merge_commit &#61; optional&#40;bool&#41;&#10; rebase_merge &#61; optional&#40;bool&#41;&#10; squash_merge &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#10; auto_init &#61; optional&#40;bool&#41;&#10; description &#61; optional&#40;string&#41;&#10; features &#61; optional&#40;object&#40;&#123;&#10; issues &#61; optional&#40;bool&#41;&#10; projects &#61; optional&#40;bool&#41;&#10; wiki &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#10; templates &#61; optional&#40;object&#40;&#123;&#10; gitignore &#61; optional&#40;string, &#34;Terraform&#34;&#41;&#10; license &#61; optional&#40;string&#41;&#10; repository &#61; optional&#40;object&#40;&#123;&#10; name &#61; string&#10; owner &#61; string&#10; &#125;&#41;&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; visibility &#61; optional&#40;string, &#34;private&#34;&#41;&#10; &#125;&#41;&#41;&#10; populate_from &#61; optional&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [pull_request_config](variables.tf#L56) | Configure pull request metadata. | <code title="object&#40;&#123;&#10; create &#61; optional&#40;bool, false&#41;&#10; title &#61; optional&#40;string, &#34;FAST: initial loading or update&#34;&#41;&#10; body &#61; optional&#40;string, &#34;&#34;&#41;&#10; base_ref &#61; optional&#40;string, &#34;main&#34;&#41;&#10; head_ref &#61; optional&#40;string, &#34;fast-loader&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [repositories](variables.tf#L69) | Repositories to create. | <code title="map&#40;object&#40;&#123;&#10; create_options &#61; optional&#40;object&#40;&#123;&#10; allow &#61; optional&#40;object&#40;&#123;&#10; auto_merge &#61; optional&#40;bool&#41;&#10; merge_commit &#61; optional&#40;bool&#41;&#10; rebase_merge &#61; optional&#40;bool&#41;&#10; squash_merge &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#10; auto_init &#61; optional&#40;bool&#41;&#10; description &#61; optional&#40;string&#41;&#10; features &#61; optional&#40;object&#40;&#123;&#10; issues &#61; optional&#40;bool&#41;&#10; projects &#61; optional&#40;bool&#41;&#10; wiki &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#10; templates &#61; optional&#40;object&#40;&#123;&#10; gitignore &#61; optional&#40;string, &#34;Terraform&#34;&#41;&#10; license &#61; optional&#40;string&#41;&#10; repository &#61; optional&#40;object&#40;&#123;&#10; name &#61; string&#10; owner &#61; string&#10; &#125;&#41;&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; visibility &#61; optional&#40;string, &#34;private&#34;&#41;&#10; &#125;&#41;&#41;&#10; populate_from &#61; optional&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
## Outputs

View File

@ -136,10 +136,21 @@ resource "github_actions_secret" "default" {
)
}
resource "github_branch" "default" {
for_each = (
try(var.pull_request_config.create, null) == true
? local.repositories
: {}
)
repository = each.key
branch = var.pull_request_config.head_ref
source_branch = var.pull_request_config.base_ref
}
resource "github_repository_file" "default" {
for_each = local.modules_repo == null ? {} : local.repository_files
repository = local.repositories[each.value.repository]
branch = "main"
branch = try(var.pull_request_config.head_ref, "main")
file = each.value.name
content = (
endswith(each.value.name, ".tf") && local.modules_repo != null
@ -154,4 +165,23 @@ resource "github_repository_file" "default" {
commit_author = var.commmit_config.author
commit_email = var.commmit_config.email
overwrite_on_create = true
lifecycle {
ignore_changes = [
content,
]
}
}
resource "github_repository_pull_request" "default" {
for_each = (
try(var.pull_request_config.create, null) == true
? local.repositories
: {}
)
base_repository = each.key
title = var.pull_request_config.title
body = var.pull_request_config.body
base_ref = var.pull_request_config.base_ref
head_ref = var.pull_request_config.head_ref
}

View File

@ -53,6 +53,19 @@ variable "organization" {
type = string
}
variable "pull_request_config" {
description = "Configure pull request metadata."
type = object({
create = optional(bool, false)
title = optional(string, "FAST: initial loading or update")
body = optional(string, "")
base_ref = optional(string, "main")
head_ref = optional(string, "fast-loader")
})
default = {}
nullable = false
}
variable "repositories" {
description = "Repositories to create."
type = map(object({

View File

@ -147,6 +147,20 @@ Configure the tenant variable in a tfvars file for this stage. A few minor point
Once the configuration is done just go through the usual `init/apply` cycle. On successful apply, a tfvars file specific for this tenant and a set of provider files will be created.
#### Using delayed billing association for projects
This configuration is possible but unsupported and only exists for development purposes, use at your own risk:
- temporarily switch `billing_account.id` to `null` in `globals.auto.tfvars.json`
- for each project resources in the project modules used in this stage (`automation-project`, `log-export-project`)
- apply using `-target`, for example
`terraform apply -target 'module.automation-project.google_project.project[0]'`
- untaint the project resource after applying, for example
`terraform untaint 'module.automation-project.google_project.project[0]'`
- go through the process to associate the billing account with the two projects
- switch `billing_account.id` back to the real billing account id
- resume applying normally
### TODO
- [ ] tenant-level Workload Identity Federation pool and providers configuration
@ -177,25 +191,25 @@ Once the configuration is done just go through the usual `init/apply` cycle. On
| name | description | type | required | default | producer |
|---|---|:---:|:---:|:---:|:---:|
| [automation](variables.tf#L20) | Automation resources created by the organization-level bootstrap stage. | <code title="object&#40;&#123;&#10; outputs_bucket &#61; string&#10; project_id &#61; string&#10; project_number &#61; string&#10; federated_identity_pool &#61; string&#10; federated_identity_providers &#61; map&#40;object&#40;&#123;&#10; issuer &#61; string&#10; issuer_uri &#61; string&#10; name &#61; string&#10; principal_tpl &#61; string&#10; principalset_tpl &#61; string&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [billing_account](variables.tf#L38) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | <code title="object&#40;&#123;&#10; id &#61; string&#10; is_org_level &#61; optional&#40;bool, true&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [organization](variables.tf#L194) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [prefix](variables.tf#L210) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>0-bootstrap</code> |
| [tag_keys](variables.tf#L233) | Organization tag keys. | <code title="object&#40;&#123;&#10; context &#61; string&#10; environment &#61; string&#10; tenant &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>1-resman</code> |
| [tag_names](variables.tf#L244) | Customized names for resource management tags. | <code title="object&#40;&#123;&#10; context &#61; string&#10; environment &#61; string&#10; tenant &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>1-resman</code> |
| [tag_values](variables.tf#L255) | Organization resource management tag values. | <code>map&#40;string&#41;</code> | ✓ | | <code>1-resman</code> |
| [tenant_config](variables.tf#L262) | Tenant configuration. Short name must be 4 characters or less. | <code title="object&#40;&#123;&#10; descriptive_name &#61; string&#10; groups &#61; object&#40;&#123;&#10; gcp-admins &#61; string&#10; gcp-devops &#61; optional&#40;string&#41;&#10; gcp-network-admins &#61; optional&#40;string&#41;&#10; gcp-security-admins &#61; optional&#40;string&#41;&#10; &#125;&#41;&#10; short_name &#61; string&#10; fast_features &#61; optional&#40;object&#40;&#123;&#10; data_platform &#61; optional&#40;bool&#41;&#10; gke &#61; optional&#40;bool&#41;&#10; project_factory &#61; optional&#40;bool&#41;&#10; sandbox &#61; optional&#40;bool&#41;&#10; teams &#61; optional&#40;bool&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; locations &#61; optional&#40;object&#40;&#123;&#10; bq &#61; optional&#40;string&#41;&#10; gcs &#61; optional&#40;string&#41;&#10; logging &#61; optional&#40;string&#41;&#10; pubsub &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | |
| [cicd_repositories](variables.tf#L51) | CI/CD repository configuration. Identity providers reference keys in the `federated_identity_providers` variable. Set to null to disable, or set individual repositories to null if not needed. | <code title="object&#40;&#123;&#10; bootstrap &#61; optional&#40;object&#40;&#123;&#10; branch &#61; optional&#40;string&#41;&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10; resman &#61; optional&#40;object&#40;&#123;&#10; branch &#61; optional&#40;string&#41;&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [custom_roles](variables.tf#L97) | Custom roles defined at the organization level, in key => id format. | <code title="object&#40;&#123;&#10; service_project_network_admin &#61; string&#10; tenant_network_admin &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>0-bootstrap</code> |
| [fast_features](variables.tf#L107) | Selective control for top-level FAST features. | <code title="object&#40;&#123;&#10; data_platform &#61; optional&#40;bool, true&#41;&#10; gke &#61; optional&#40;bool, true&#41;&#10; project_factory &#61; optional&#40;bool, true&#41;&#10; sandbox &#61; optional&#40;bool, true&#41;&#10; teams &#61; optional&#40;bool, true&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | <code>0-bootstrap</code> |
| [federated_identity_providers](variables.tf#L121) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | <code title="map&#40;object&#40;&#123;&#10; attribute_condition &#61; string&#10; issuer &#61; string&#10; custom_settings &#61; object&#40;&#123;&#10; issuer_uri &#61; string&#10; allowed_audiences &#61; list&#40;string&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [group_iam](variables.tf#L135) | Tenant-level custom group IAM settings in group => [roles] format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [iam](variables.tf#L141) | Tenant-level custom IAM settings in role => [principal] format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [iam_additive](variables.tf#L147) | Tenant-level custom IAM settings in role => [principal] format for non-authoritative bindings. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [locations](variables.tf#L153) | Optional locations for GCS, BigQuery, and logging buckets created here. These are the defaults set at the organization level, and can be overridden via the tenant config variable. | <code title="object&#40;&#123;&#10; bq &#61; string&#10; gcs &#61; string&#10; logging &#61; string&#10; pubsub &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; bq &#61; &#34;EU&#34;&#10; gcs &#61; &#34;EU&#34;&#10; logging &#61; &#34;global&#34;&#10; pubsub &#61; &#91;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> | <code>0-bootstrap</code> |
| [log_sinks](variables.tf#L173) | Tenant-level log sinks, in name => {type, filter} format. | <code title="map&#40;object&#40;&#123;&#10; filter &#61; string&#10; type &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; audit-logs &#61; &#123;&#10; filter &#61; &#34;logName:&#92;&#34;&#47;logs&#47;cloudaudit.googleapis.com&#37;2Factivity&#92;&#34; OR logName:&#92;&#34;&#47;logs&#47;cloudaudit.googleapis.com&#37;2Fsystem_event&#92;&#34;&#34;&#10; type &#61; &#34;logging&#34;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [outputs_location](variables.tf#L204) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | <code>string</code> | | <code>null</code> | |
| [project_parent_ids](variables.tf#L220) | Optional parents for projects created here in folders/nnnnnnn format. Null values will use the tenant folder as parent. | <code title="object&#40;&#123;&#10; automation &#61; string&#10; logging &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; automation &#61; null&#10; logging &#61; null&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [test_principal](variables.tf#L302) | Used when testing to bypass the data source returning the current identity. | <code>string</code> | | <code>null</code> | |
| [billing_account](variables.tf#L38) | Billing account id. If billing account is not part of the same org set `is_org_level` to `false`. To disable handling of billing IAM roles set `no_iam` to `true`. | <code title="object&#40;&#123;&#10; id &#61; string&#10; is_org_level &#61; optional&#40;bool, true&#41;&#10; no_iam &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | |
| [organization](variables.tf#L191) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [prefix](variables.tf#L207) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>0-bootstrap</code> |
| [tag_keys](variables.tf#L230) | Organization tag keys. | <code title="object&#40;&#123;&#10; context &#61; string&#10; environment &#61; string&#10; tenant &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>1-resman</code> |
| [tag_names](variables.tf#L241) | Customized names for resource management tags. | <code title="object&#40;&#123;&#10; context &#61; string&#10; environment &#61; string&#10; tenant &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>1-resman</code> |
| [tag_values](variables.tf#L252) | Organization resource management tag values. | <code>map&#40;string&#41;</code> | ✓ | | <code>1-resman</code> |
| [tenant_config](variables.tf#L259) | Tenant configuration. Short name must be 4 characters or less. | <code title="object&#40;&#123;&#10; descriptive_name &#61; string&#10; groups &#61; object&#40;&#123;&#10; gcp-admins &#61; string&#10; gcp-devops &#61; optional&#40;string&#41;&#10; gcp-network-admins &#61; optional&#40;string&#41;&#10; gcp-security-admins &#61; optional&#40;string&#41;&#10; &#125;&#41;&#10; short_name &#61; string&#10; fast_features &#61; optional&#40;object&#40;&#123;&#10; data_platform &#61; optional&#40;bool&#41;&#10; gke &#61; optional&#40;bool&#41;&#10; project_factory &#61; optional&#40;bool&#41;&#10; sandbox &#61; optional&#40;bool&#41;&#10; teams &#61; optional&#40;bool&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; locations &#61; optional&#40;object&#40;&#123;&#10; bq &#61; optional&#40;string&#41;&#10; gcs &#61; optional&#40;string&#41;&#10; logging &#61; optional&#40;string&#41;&#10; pubsub &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | |
| [cicd_repositories](variables.tf#L48) | CI/CD repository configuration. Identity providers reference keys in the `federated_identity_providers` variable. Set to null to disable, or set individual repositories to null if not needed. | <code title="object&#40;&#123;&#10; bootstrap &#61; optional&#40;object&#40;&#123;&#10; branch &#61; optional&#40;string&#41;&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10; resman &#61; optional&#40;object&#40;&#123;&#10; branch &#61; optional&#40;string&#41;&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [custom_roles](variables.tf#L94) | Custom roles defined at the organization level, in key => id format. | <code title="object&#40;&#123;&#10; service_project_network_admin &#61; string&#10; tenant_network_admin &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>0-bootstrap</code> |
| [fast_features](variables.tf#L104) | Selective control for top-level FAST features. | <code title="object&#40;&#123;&#10; data_platform &#61; optional&#40;bool, true&#41;&#10; gke &#61; optional&#40;bool, true&#41;&#10; project_factory &#61; optional&#40;bool, true&#41;&#10; sandbox &#61; optional&#40;bool, true&#41;&#10; teams &#61; optional&#40;bool, true&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | <code>0-bootstrap</code> |
| [federated_identity_providers](variables.tf#L118) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | <code title="map&#40;object&#40;&#123;&#10; attribute_condition &#61; string&#10; issuer &#61; string&#10; custom_settings &#61; object&#40;&#123;&#10; issuer_uri &#61; string&#10; allowed_audiences &#61; list&#40;string&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [group_iam](variables.tf#L132) | Tenant-level custom group IAM settings in group => [roles] format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [iam](variables.tf#L138) | Tenant-level custom IAM settings in role => [principal] format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [iam_additive](variables.tf#L144) | Tenant-level custom IAM settings in role => [principal] format for non-authoritative bindings. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [locations](variables.tf#L150) | Optional locations for GCS, BigQuery, and logging buckets created here. These are the defaults set at the organization level, and can be overridden via the tenant config variable. | <code title="object&#40;&#123;&#10; bq &#61; string&#10; gcs &#61; string&#10; logging &#61; string&#10; pubsub &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; bq &#61; &#34;EU&#34;&#10; gcs &#61; &#34;EU&#34;&#10; logging &#61; &#34;global&#34;&#10; pubsub &#61; &#91;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> | <code>0-bootstrap</code> |
| [log_sinks](variables.tf#L170) | Tenant-level log sinks, in name => {type, filter} format. | <code title="map&#40;object&#40;&#123;&#10; filter &#61; string&#10; type &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; audit-logs &#61; &#123;&#10; filter &#61; &#34;logName:&#92;&#34;&#47;logs&#47;cloudaudit.googleapis.com&#37;2Factivity&#92;&#34; OR logName:&#92;&#34;&#47;logs&#47;cloudaudit.googleapis.com&#37;2Fsystem_event&#92;&#34;&#34;&#10; type &#61; &#34;logging&#34;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [outputs_location](variables.tf#L201) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | <code>string</code> | | <code>null</code> | |
| [project_parent_ids](variables.tf#L217) | Optional parents for projects created here in folders/nnnnnnn format. Null values will use the tenant folder as parent. | <code title="object&#40;&#123;&#10; automation &#61; string&#10; logging &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; automation &#61; null&#10; logging &#61; null&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [test_principal](variables.tf#L299) | Used when testing to bypass the data source returning the current identity. | <code>string</code> | | <code>null</code> | |
## Outputs

View File

@ -93,12 +93,12 @@ module "automation-tf-resman-sa-stage2-3" {
name = "${each.key}-0"
display_name = "Terraform ${each.value.description} service account."
prefix = local.prefix
iam_billing_roles = !var.billing_account.is_org_level ? {
iam_billing_roles = local.billing_mode == "resource" ? {
(var.billing_account.id) = [
"roles/billing.user", "roles/billing.costsManager"
]
} : {}
iam_organization_roles = var.billing_account.is_org_level ? {
iam_organization_roles = local.billing_mode == "org" ? {
(var.organization.id) = [
"roles/billing.user", "roles/billing.costsManager"
]

View File

@ -125,12 +125,12 @@ module "automation-tf-resman-sa" {
try(module.automation-tf-cicd-sa-resman["0"].iam_email, null)
])
}
iam_billing_roles = !var.billing_account.is_org_level ? {
iam_billing_roles = local.billing_mode == "resource" ? {
(var.billing_account.id) = [
"roles/billing.admin", "roles/billing.costsManager"
]
} : {}
iam_organization_roles = var.billing_account.is_org_level ? {
iam_organization_roles = local.billing_mode == "org" ? {
(var.organization.id) = [
"roles/billing.admin", "roles/billing.costsManager"
]

View File

@ -16,23 +16,39 @@
# tfdoc:file:description Billing roles for standalone billing accounts.
locals {
billing_mode = (
var.billing_account.no_iam
? null
: var.billing_account.is_org_level ? "org" : "resource"
)
}
# service account billing roles are in the SA module in automation.tf
resource "google_billing_account_iam_member" "billing_ext_admin" {
for_each = toset(var.billing_account.is_org_level ? [] : [
"group:${local.groups.gcp-admins}",
module.automation-tf-resman-sa.iam_email
])
for_each = toset(
local.billing_mode == "resource"
? [
"group:${local.groups.gcp-admins}",
module.automation-tf-resman-sa.iam_email
]
: []
)
billing_account_id = var.billing_account.id
role = "roles/billing.admin"
member = each.key
}
resource "google_billing_account_iam_member" "billing_ext_cost_manager" {
for_each = toset(var.billing_account.is_org_level ? [] : [
"group:${local.groups.gcp-admins}",
module.automation-tf-resman-sa.iam_email
])
for_each = toset(
local.billing_mode == "resource"
? [
"group:${local.groups.gcp-admins}",
module.automation-tf-resman-sa.iam_email
]
: []
)
billing_account_id = var.billing_account.id
role = "roles/billing.costsManager"
member = each.key

View File

@ -32,7 +32,7 @@ module "organization" {
"group:${local.groups.gcp-admins}"
]
},
var.billing_account.is_org_level ? {
local.billing_mode == "org" ? {
"roles/billing.admin" = [
"group:${local.groups.gcp-admins}",
module.automation-tf-resman-sa.iam_email

View File

@ -36,16 +36,13 @@ variable "automation" {
}
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."
description = "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`."
type = object({
id = string
is_org_level = optional(bool, true)
no_iam = optional(bool, false)
})
validation {
condition = var.billing_account.is_org_level != null
error_message = "Invalid `null` value for `billing_account.is_org_level`."
}
nullable = false
}
variable "cicd_repositories" {

View File

@ -149,22 +149,22 @@ Once the configuration is done just go through the usual `init/apply` cycle. On
| name | description | type | required | default | producer |
|---|---|:---:|:---:|:---:|:---:|
| [automation](variables.tf#L20) | Automation resources created by the bootstrap stage. | <code title="object&#40;&#123;&#10; outputs_bucket &#61; string&#10; project_id &#61; string&#10; project_number &#61; string&#10; federated_identity_pools &#61; list&#40;string&#41;&#10; federated_identity_providers &#61; map&#40;object&#40;&#123;&#10; issuer &#61; string&#10; issuer_uri &#61; string&#10; name &#61; string&#10; principal_tpl &#61; string&#10; principalset_tpl &#61; string&#10; &#125;&#41;&#41;&#10; service_accounts &#61; object&#40;&#123;&#10; networking &#61; string&#10; resman &#61; string&#10; security &#61; string&#10; dp-dev &#61; optional&#40;string&#41;&#10; dp-prod &#61; optional&#40;string&#41;&#10; gke-dev &#61; optional&#40;string&#41;&#10; gke-prod &#61; optional&#40;string&#41;&#10; pf-dev &#61; optional&#40;string&#41;&#10; pf-prod &#61; optional&#40;string&#41;&#10; sandbox &#61; optional&#40;string&#41;&#10; teams &#61; optional&#40;string&#41;&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [billing_account](variables.tf#L51) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | <code title="object&#40;&#123;&#10; id &#61; string&#10; is_org_level &#61; optional&#40;bool, true&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [organization](variables.tf#L206) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [prefix](variables.tf#L228) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>0-bootstrap</code> |
| [root_node](variables.tf#L239) | Root folder node for the tenant, in folders/nnnnnn format. | <code>string</code> | ✓ | | |
| [short_name](variables.tf#L244) | Short name used to identify the tenant. | <code>string</code> | ✓ | | |
| [tags](variables.tf#L249) | Resource management tags. | <code title="object&#40;&#123;&#10; keys &#61; object&#40;&#123;&#10; context &#61; string&#10; environment &#61; string&#10; tenant &#61; string&#10; &#125;&#41;&#10; names &#61; object&#40;&#123;&#10; context &#61; string&#10; environment &#61; string&#10; tenant &#61; string&#10; &#125;&#41;&#10; values &#61; map&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | |
| [cicd_repositories](variables.tf#L64) | CI/CD repository configuration. Identity providers reference keys in the `automation.federated_identity_providers` variable. Set to null to disable, or set individual repositories to null if not needed. | <code title="object&#40;&#123;&#10; data_platform_dev &#61; object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#10; data_platform_prod &#61; object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#10; gke_dev &#61; object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#10; gke_prod &#61; object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#10; networking &#61; object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#10; project_factory_dev &#61; object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#10; project_factory_prod &#61; object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#10; security &#61; object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [custom_roles](variables.tf#L146) | Custom roles defined at the org level, in key => id format. | <code title="object&#40;&#123;&#10; service_project_network_admin &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>0-bootstrap</code> |
| [data_dir](variables.tf#L155) | Relative path for the folder storing configuration data. | <code>string</code> | | <code>&#34;data&#34;</code> | |
| [fast_features](variables.tf#L161) | Selective control for top-level FAST features. | <code title="object&#40;&#123;&#10; data_platform &#61; optional&#40;bool, false&#41;&#10; gke &#61; optional&#40;bool, false&#41;&#10; project_factory &#61; optional&#40;bool, false&#41;&#10; sandbox &#61; optional&#40;bool, false&#41;&#10; teams &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | <code>0-0-bootstrap</code> |
| [groups](variables.tf#L175) | Group names to grant organization-level permissions. | <code title="object&#40;&#123;&#10; gcp-devops &#61; optional&#40;string&#41;&#10; gcp-network-admins &#61; optional&#40;string&#41;&#10; gcp-security-admins &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | <code>0-bootstrap</code> |
| [locations](variables.tf#L188) | Optional locations for GCS, BigQuery, and logging buckets created here. | <code title="object&#40;&#123;&#10; bq &#61; string&#10; gcs &#61; string&#10; logging &#61; string&#10; pubsub &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; bq &#61; &#34;EU&#34;&#10; gcs &#61; &#34;EU&#34;&#10; logging &#61; &#34;global&#34;&#10; pubsub &#61; &#91;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> | <code>0-bootstrap</code> |
| [organization_policy_data_path](variables.tf#L216) | Path for the data folder used by the organization policies factory. | <code>string</code> | | <code>null</code> | |
| [outputs_location](variables.tf#L222) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | <code>string</code> | | <code>null</code> | |
| [team_folders](variables.tf#L267) | Team folders to be created. Format is described in a code comment. | <code title="map&#40;object&#40;&#123;&#10; descriptive_name &#61; string&#10; group_iam &#61; map&#40;list&#40;string&#41;&#41;&#10; impersonation_groups &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>null</code> | |
| [test_skip_data_sources](variables.tf#L277) | Used when testing to bypass data sources. | <code>bool</code> | | <code>false</code> | |
| [billing_account](variables.tf#L51) | Billing account id. If billing account is not part of the same org set `is_org_level` to `false`. To disable handling of billing IAM roles set `no_iam` to `true`. | <code title="object&#40;&#123;&#10; id &#61; string&#10; is_org_level &#61; optional&#40;bool, true&#41;&#10; no_iam &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [organization](variables.tf#L204) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [prefix](variables.tf#L226) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>0-bootstrap</code> |
| [root_node](variables.tf#L237) | Root folder node for the tenant, in folders/nnnnnn format. | <code>string</code> | ✓ | | |
| [short_name](variables.tf#L242) | Short name used to identify the tenant. | <code>string</code> | ✓ | | |
| [tags](variables.tf#L247) | Resource management tags. | <code title="object&#40;&#123;&#10; keys &#61; object&#40;&#123;&#10; context &#61; string&#10; environment &#61; string&#10; tenant &#61; string&#10; &#125;&#41;&#10; names &#61; object&#40;&#123;&#10; context &#61; string&#10; environment &#61; string&#10; tenant &#61; string&#10; &#125;&#41;&#10; values &#61; map&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | |
| [cicd_repositories](variables.tf#L62) | CI/CD repository configuration. Identity providers reference keys in the `automation.federated_identity_providers` variable. Set to null to disable, or set individual repositories to null if not needed. | <code title="object&#40;&#123;&#10; data_platform_dev &#61; object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#10; data_platform_prod &#61; object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#10; gke_dev &#61; object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#10; gke_prod &#61; object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#10; networking &#61; object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#10; project_factory_dev &#61; object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#10; project_factory_prod &#61; object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#10; security &#61; object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [custom_roles](variables.tf#L144) | Custom roles defined at the org level, in key => id format. | <code title="object&#40;&#123;&#10; service_project_network_admin &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>0-bootstrap</code> |
| [data_dir](variables.tf#L153) | Relative path for the folder storing configuration data. | <code>string</code> | | <code>&#34;data&#34;</code> | |
| [fast_features](variables.tf#L159) | Selective control for top-level FAST features. | <code title="object&#40;&#123;&#10; data_platform &#61; optional&#40;bool, false&#41;&#10; gke &#61; optional&#40;bool, false&#41;&#10; project_factory &#61; optional&#40;bool, false&#41;&#10; sandbox &#61; optional&#40;bool, false&#41;&#10; teams &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | <code>0-0-bootstrap</code> |
| [groups](variables.tf#L173) | Group names to grant organization-level permissions. | <code title="object&#40;&#123;&#10; gcp-devops &#61; optional&#40;string&#41;&#10; gcp-network-admins &#61; optional&#40;string&#41;&#10; gcp-security-admins &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | <code>0-bootstrap</code> |
| [locations](variables.tf#L186) | Optional locations for GCS, BigQuery, and logging buckets created here. | <code title="object&#40;&#123;&#10; bq &#61; string&#10; gcs &#61; string&#10; logging &#61; string&#10; pubsub &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; bq &#61; &#34;EU&#34;&#10; gcs &#61; &#34;EU&#34;&#10; logging &#61; &#34;global&#34;&#10; pubsub &#61; &#91;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> | <code>0-bootstrap</code> |
| [organization_policy_data_path](variables.tf#L214) | Path for the data folder used by the organization policies factory. | <code>string</code> | | <code>null</code> | |
| [outputs_location](variables.tf#L220) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | <code>string</code> | | <code>null</code> | |
| [team_folders](variables.tf#L265) | Team folders to be created. Format is described in a code comment. | <code title="map&#40;object&#40;&#123;&#10; descriptive_name &#61; string&#10; group_iam &#61; map&#40;list&#40;string&#41;&#41;&#10; impersonation_groups &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>null</code> | |
| [test_skip_data_sources](variables.tf#L275) | Used when testing to bypass data sources. | <code>bool</code> | | <code>false</code> | |
## Outputs

View File

@ -50,15 +50,13 @@ variable "automation" {
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."
description = "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`."
type = object({
id = string
is_org_level = optional(bool, true)
no_iam = optional(bool, false)
})
validation {
condition = var.billing_account.is_org_level != null
error_message = "Invalid `null` value for `billing_account.is_org_level`."
}
nullable = false
}
variable "cicd_repositories" {

View File

@ -69,8 +69,8 @@ One other design choice worth mentioning here is using a single automation proje
We support three use cases in regards to billing:
- the billing account is part of this same organization, IAM bindings will be set at the organization level
- the billing account is part of a different organization, billing IAM bindings will be set at the organization level in the billing account owning organization
- the billing account is not considered part of an organization (even though it might be), billing IAM bindings are set on the billing account itself
- billing IAM is managed separately, and no bindings should (or can) be set via Terraform, this requires a few extra steps and is definitely not recommended and mainly used for development purposes
For same-organization billing, we configure a custom organization role that can set IAM bindings, via a delegated role grant to limit its scope to the relevant roles.
@ -171,6 +171,18 @@ gcloud beta billing accounts add-iam-policy-binding $FAST_BILLING_ACCOUNT_ID \
--member user:$FAST_BU --role roles/billing.admin
```
#### Preventing creation of billing-related IAM bindings
This configuration is possible but unsupported and only present for development purposes, use at your own risk:
- configure `billing_account.id` as `null` and `billing.no_iam` to `true` in your `tfvars` file
- apply with `terraform apply -target 'module.automation-project.google_project.project[0]'` in addition to the initial user variable
- once Terraform raises an error run `terraform untaint 'module.automation-project.google_project.project[0]'`
- repeat the two steps above for `'module.log-export-project.google_project.project[0]'`
- go through the process to associate the billing account with the two projects
- configure `billing_account.id` with the real billing account id
- resume applying normally
#### Groups
Before the first run, the following IAM groups must exist to allow IAM bindings to be created (actual names are flexible, see the [Customization](#customizations) section):
@ -484,21 +496,21 @@ 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. | <code title="object&#40;&#123;&#10; id &#61; string&#10; is_org_level &#61; optional&#40;bool, true&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | |
| [organization](variables.tf#L196) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | |
| [prefix](variables.tf#L211) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | |
| [bootstrap_user](variables.tf#L29) | Email of the nominal user running this stage for the first time. | <code>string</code> | | <code>null</code> | |
| [cicd_repositories](variables.tf#L35) | CI/CD repository configuration. Identity providers reference keys in the `federated_identity_providers` variable. Set to null to disable, or set individual repositories to null if not needed. | <code title="object&#40;&#123;&#10; bootstrap &#61; optional&#40;object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10; resman &#61; optional&#40;object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [custom_role_names](variables.tf#L81) | Names of custom roles defined at the org level. | <code title="object&#40;&#123;&#10; organization_iam_admin &#61; string&#10; service_project_network_admin &#61; string&#10; tenant_network_admin &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; organization_iam_admin &#61; &#34;organizationIamAdmin&#34;&#10; service_project_network_admin &#61; &#34;serviceProjectNetworkAdmin&#34;&#10; tenant_network_admin &#61; &#34;tenantNetworkAdmin&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [fast_features](variables.tf#L95) | Selective control for top-level FAST features. | <code title="object&#40;&#123;&#10; data_platform &#61; optional&#40;bool, false&#41;&#10; gke &#61; optional&#40;bool, false&#41;&#10; project_factory &#61; optional&#40;bool, false&#41;&#10; sandbox &#61; optional&#40;bool, false&#41;&#10; teams &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [federated_identity_providers](variables.tf#L108) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | <code title="map&#40;object&#40;&#123;&#10; attribute_condition &#61; string&#10; issuer &#61; string&#10; custom_settings &#61; object&#40;&#123;&#10; issuer_uri &#61; string&#10; allowed_audiences &#61; list&#40;string&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [groups](variables.tf#L122) | Group names to grant organization-level permissions. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; gcp-billing-admins &#61; &#34;gcp-billing-admins&#34;,&#10; gcp-devops &#61; &#34;gcp-devops&#34;,&#10; gcp-network-admins &#61; &#34;gcp-network-admins&#34;&#10; gcp-organization-admins &#61; &#34;gcp-organization-admins&#34;&#10; gcp-security-admins &#61; &#34;gcp-security-admins&#34;&#10; gcp-support &#61; &#34;gcp-devops&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [iam](variables.tf#L140) | Organization-level custom IAM settings in role => [principal] format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [iam_additive](variables.tf#L146) | Organization-level custom IAM settings in role => [principal] format for non-authoritative bindings. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [locations](variables.tf#L152) | Optional locations for GCS, BigQuery, and logging buckets created here. | <code title="object&#40;&#123;&#10; bq &#61; string&#10; gcs &#61; string&#10; logging &#61; string&#10; pubsub &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; bq &#61; &#34;EU&#34;&#10; gcs &#61; &#34;EU&#34;&#10; logging &#61; &#34;global&#34;&#10; pubsub &#61; &#91;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [log_sinks](variables.tf#L171) | Org-level log sinks, in name => {type, filter} format. | <code title="map&#40;object&#40;&#123;&#10; filter &#61; string&#10; type &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; audit-logs &#61; &#123;&#10; filter &#61; &#34;logName:&#92;&#34;&#47;logs&#47;cloudaudit.googleapis.com&#37;2Factivity&#92;&#34; OR logName:&#92;&#34;&#47;logs&#47;cloudaudit.googleapis.com&#37;2Fsystem_event&#92;&#34;&#34;&#10; type &#61; &#34;logging&#34;&#10; &#125;&#10; vpc-sc &#61; &#123;&#10; filter &#61; &#34;protoPayload.metadata.&#64;type&#61;&#92;&#34;type.googleapis.com&#47;google.cloud.audit.VpcServiceControlAuditMetadata&#92;&#34;&#34;&#10; type &#61; &#34;logging&#34;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [outputs_location](variables.tf#L205) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | <code>string</code> | | <code>null</code> | |
| [project_parent_ids](variables.tf#L221) | Optional parents for projects created here in folders/nnnnnnn format. Null values will use the organization as parent. | <code title="object&#40;&#123;&#10; automation &#61; string&#10; billing &#61; string&#10; logging &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; automation &#61; null&#10; billing &#61; null&#10; logging &#61; null&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [billing_account](variables.tf#L17) | Billing account id. If billing account is not part of the same org set `is_org_level` to `false`. To disable handling of billing IAM roles set `no_iam` to `true`. | <code title="object&#40;&#123;&#10; id &#61; string&#10; is_org_level &#61; optional&#40;bool, true&#41;&#10; no_iam &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | |
| [organization](variables.tf#L194) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | |
| [prefix](variables.tf#L209) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | |
| [bootstrap_user](variables.tf#L27) | Email of the nominal user running this stage for the first time. | <code>string</code> | | <code>null</code> | |
| [cicd_repositories](variables.tf#L33) | CI/CD repository configuration. Identity providers reference keys in the `federated_identity_providers` variable. Set to null to disable, or set individual repositories to null if not needed. | <code title="object&#40;&#123;&#10; bootstrap &#61; optional&#40;object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10; resman &#61; optional&#40;object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [custom_role_names](variables.tf#L79) | Names of custom roles defined at the org level. | <code title="object&#40;&#123;&#10; organization_iam_admin &#61; string&#10; service_project_network_admin &#61; string&#10; tenant_network_admin &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; organization_iam_admin &#61; &#34;organizationIamAdmin&#34;&#10; service_project_network_admin &#61; &#34;serviceProjectNetworkAdmin&#34;&#10; tenant_network_admin &#61; &#34;tenantNetworkAdmin&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [fast_features](variables.tf#L93) | Selective control for top-level FAST features. | <code title="object&#40;&#123;&#10; data_platform &#61; optional&#40;bool, false&#41;&#10; gke &#61; optional&#40;bool, false&#41;&#10; project_factory &#61; optional&#40;bool, false&#41;&#10; sandbox &#61; optional&#40;bool, false&#41;&#10; teams &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [federated_identity_providers](variables.tf#L106) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | <code title="map&#40;object&#40;&#123;&#10; attribute_condition &#61; string&#10; issuer &#61; string&#10; custom_settings &#61; object&#40;&#123;&#10; issuer_uri &#61; string&#10; allowed_audiences &#61; list&#40;string&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [groups](variables.tf#L120) | Group names to grant organization-level permissions. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; gcp-billing-admins &#61; &#34;gcp-billing-admins&#34;,&#10; gcp-devops &#61; &#34;gcp-devops&#34;,&#10; gcp-network-admins &#61; &#34;gcp-network-admins&#34;&#10; gcp-organization-admins &#61; &#34;gcp-organization-admins&#34;&#10; gcp-security-admins &#61; &#34;gcp-security-admins&#34;&#10; gcp-support &#61; &#34;gcp-devops&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [iam](variables.tf#L138) | Organization-level custom IAM settings in role => [principal] format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [iam_additive](variables.tf#L144) | Organization-level custom IAM settings in role => [principal] format for non-authoritative bindings. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [locations](variables.tf#L150) | Optional locations for GCS, BigQuery, and logging buckets created here. | <code title="object&#40;&#123;&#10; bq &#61; string&#10; gcs &#61; string&#10; logging &#61; string&#10; pubsub &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; bq &#61; &#34;EU&#34;&#10; gcs &#61; &#34;EU&#34;&#10; logging &#61; &#34;global&#34;&#10; pubsub &#61; &#91;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [log_sinks](variables.tf#L169) | Org-level log sinks, in name => {type, filter} format. | <code title="map&#40;object&#40;&#123;&#10; filter &#61; string&#10; type &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; audit-logs &#61; &#123;&#10; filter &#61; &#34;logName:&#92;&#34;&#47;logs&#47;cloudaudit.googleapis.com&#37;2Factivity&#92;&#34; OR logName:&#92;&#34;&#47;logs&#47;cloudaudit.googleapis.com&#37;2Fsystem_event&#92;&#34;&#34;&#10; type &#61; &#34;logging&#34;&#10; &#125;&#10; vpc-sc &#61; &#123;&#10; filter &#61; &#34;protoPayload.metadata.&#64;type&#61;&#92;&#34;type.googleapis.com&#47;google.cloud.audit.VpcServiceControlAuditMetadata&#92;&#34;&#34;&#10; type &#61; &#34;logging&#34;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [outputs_location](variables.tf#L203) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | <code>string</code> | | <code>null</code> | |
| [project_parent_ids](variables.tf#L219) | Optional parents for projects created here in folders/nnnnnnn format. Null values will use the organization as parent. | <code title="object&#40;&#123;&#10; automation &#61; string&#10; billing &#61; string&#10; logging &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; automation &#61; null&#10; billing &#61; null&#10; logging &#61; null&#10;&#125;">&#123;&#8230;&#125;</code> | |
## Outputs

View File

@ -24,13 +24,18 @@ locals {
module.automation-tf-bootstrap-sa.iam_email,
module.automation-tf-resman-sa.iam_email
]
billing_mode = (
var.billing_account.no_iam
? null
: var.billing_account.is_org_level ? "org" : "resource"
)
}
# billing account in same org (IAM is in the organization.tf file)
module "billing-export-project" {
source = "../../../modules/project"
count = var.billing_account.is_org_level ? 1 : 0
count = local.billing_mode == "org" ? 1 : 0
billing_account = var.billing_account.id
name = "billing-exp-0"
parent = coalesce(
@ -52,7 +57,7 @@ module "billing-export-project" {
module "billing-export-dataset" {
source = "../../../modules/bigquery-dataset"
count = var.billing_account.is_org_level ? 1 : 0
count = local.billing_mode == "org" ? 1 : 0
project_id = module.billing-export-project.0.project_id
id = "billing_export"
friendly_name = "Billing export."
@ -63,7 +68,7 @@ module "billing-export-dataset" {
resource "google_billing_account_iam_member" "billing_ext_admin" {
for_each = toset(
!var.billing_account.is_org_level ? local.billing_ext_admins : []
local.billing_mode == "resource" ? local.billing_ext_admins : []
)
billing_account_id = var.billing_account.id
role = "roles/billing.admin"
@ -72,7 +77,7 @@ resource "google_billing_account_iam_member" "billing_ext_admin" {
resource "google_billing_account_iam_member" "billing_ext_cost_manager" {
for_each = toset(
!var.billing_account.is_org_level ? local.billing_ext_admins : []
local.billing_mode == "resource" ? local.billing_ext_admins : []
)
billing_account_id = var.billing_account.id
role = "roles/billing.costsManager"

View File

@ -85,7 +85,7 @@ locals {
# "domain:${var.organization.domain}"
# ]
},
var.billing_account.is_org_level ? {
local.billing_mode == "org" ? {
"roles/billing.admin" = [
local.groups_iam.gcp-billing-admins,
local.groups_iam.gcp-organization-admins,
@ -222,7 +222,7 @@ resource "google_organization_iam_binding" "org_admin_delegated" {
"roles/resourcemanager.organizationViewer",
module.organization.custom_role_id[var.custom_role_names.tenant_network_admin]
],
var.billing_account.is_org_level ? [
local.billing_mode == "org" ? [
"roles/billing.admin",
"roles/billing.costsManager",
"roles/billing.user",

View File

@ -15,15 +15,13 @@
*/
variable "billing_account" {
description = "Billing account id. If billing account is not part of the same org set `is_org_level` to false."
description = "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`."
type = object({
id = string
is_org_level = optional(bool, true)
no_iam = optional(bool, false)
})
validation {
condition = var.billing_account.is_org_level != null
error_message = "Invalid `null` value for `billing_account.is_org_level`."
}
nullable = false
}
variable "bootstrap_user" {

View File

@ -199,19 +199,19 @@ Due to its simplicity, this stage lends itself easily to customizations: adding
| name | description | type | required | default | producer |
|---|---|:---:|:---:|:---:|:---:|
| [automation](variables.tf#L20) | Automation resources created by the bootstrap stage. | <code title="object&#40;&#123;&#10; outputs_bucket &#61; string&#10; project_id &#61; string&#10; project_number &#61; string&#10; federated_identity_pool &#61; string&#10; federated_identity_providers &#61; map&#40;object&#40;&#123;&#10; issuer &#61; string&#10; issuer_uri &#61; string&#10; name &#61; string&#10; principal_tpl &#61; string&#10; principalset_tpl &#61; string&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [billing_account](variables.tf#L38) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | <code title="object&#40;&#123;&#10; id &#61; string&#10; is_org_level &#61; optional&#40;bool, true&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [organization](variables.tf#L193) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [prefix](variables.tf#L217) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>0-bootstrap</code> |
| [cicd_repositories](variables.tf#L51) | CI/CD repository configuration. Identity providers reference keys in the `automation.federated_identity_providers` variable. Set to null to disable, or set individual repositories to null if not needed. | <code title="object&#40;&#123;&#10; data_platform_dev &#61; optional&#40;object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10; data_platform_prod &#61; optional&#40;object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10; gke_dev &#61; optional&#40;object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10; gke_prod &#61; optional&#40;object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10; networking &#61; optional&#40;object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10; project_factory_dev &#61; optional&#40;object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10; project_factory_prod &#61; optional&#40;object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10; security &#61; optional&#40;object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [custom_roles](variables.tf#L133) | Custom roles defined at the org level, in key => id format. | <code title="object&#40;&#123;&#10; service_project_network_admin &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>0-bootstrap</code> |
| [data_dir](variables.tf#L142) | Relative path for the folder storing configuration data. | <code>string</code> | | <code>&#34;data&#34;</code> | |
| [fast_features](variables.tf#L148) | Selective control for top-level FAST features. | <code title="object&#40;&#123;&#10; data_platform &#61; optional&#40;bool, false&#41;&#10; gke &#61; optional&#40;bool, false&#41;&#10; project_factory &#61; optional&#40;bool, false&#41;&#10; sandbox &#61; optional&#40;bool, false&#41;&#10; teams &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | <code>0-0-bootstrap</code> |
| [groups](variables.tf#L162) | Group names to grant organization-level permissions. | <code title="object&#40;&#123;&#10; gcp-devops &#61; optional&#40;string&#41;&#10; gcp-network-admins &#61; optional&#40;string&#41;&#10; gcp-security-admins &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | <code>0-bootstrap</code> |
| [locations](variables.tf#L175) | Optional locations for GCS, BigQuery, and logging buckets created here. | <code title="object&#40;&#123;&#10; bq &#61; string&#10; gcs &#61; string&#10; logging &#61; string&#10; pubsub &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; bq &#61; &#34;EU&#34;&#10; gcs &#61; &#34;EU&#34;&#10; logging &#61; &#34;global&#34;&#10; pubsub &#61; &#91;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> | <code>0-bootstrap</code> |
| [organization_policy_configs](variables.tf#L203) | Organization policies customization. | <code title="object&#40;&#123;&#10; allowed_policy_member_domains &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [outputs_location](variables.tf#L211) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | <code>string</code> | | <code>null</code> | |
| [tag_names](variables.tf#L228) | Customized names for resource management tags. | <code title="object&#40;&#123;&#10; context &#61; string&#10; environment &#61; string&#10; tenant &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; context &#61; &#34;context&#34;&#10; environment &#61; &#34;environment&#34;&#10; tenant &#61; &#34;tenant&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [team_folders](variables.tf#L247) | Team folders to be created. Format is described in a code comment. | <code title="map&#40;object&#40;&#123;&#10; descriptive_name &#61; string&#10; group_iam &#61; map&#40;list&#40;string&#41;&#41;&#10; impersonation_groups &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>null</code> | |
| [billing_account](variables.tf#L38) | Billing account id. If billing account is not part of the same org set `is_org_level` to `false`. To disable handling of billing IAM roles set `no_iam` to `true`. | <code title="object&#40;&#123;&#10; id &#61; string&#10; is_org_level &#61; optional&#40;bool, true&#41;&#10; no_iam &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [organization](variables.tf#L191) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [prefix](variables.tf#L215) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>0-bootstrap</code> |
| [cicd_repositories](variables.tf#L49) | CI/CD repository configuration. Identity providers reference keys in the `automation.federated_identity_providers` variable. Set to null to disable, or set individual repositories to null if not needed. | <code title="object&#40;&#123;&#10; data_platform_dev &#61; optional&#40;object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10; data_platform_prod &#61; optional&#40;object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10; gke_dev &#61; optional&#40;object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10; gke_prod &#61; optional&#40;object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10; networking &#61; optional&#40;object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10; project_factory_dev &#61; optional&#40;object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10; project_factory_prod &#61; optional&#40;object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10; security &#61; optional&#40;object&#40;&#123;&#10; branch &#61; string&#10; identity_provider &#61; string&#10; name &#61; string&#10; type &#61; string&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [custom_roles](variables.tf#L131) | Custom roles defined at the org level, in key => id format. | <code title="object&#40;&#123;&#10; service_project_network_admin &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>0-bootstrap</code> |
| [data_dir](variables.tf#L140) | Relative path for the folder storing configuration data. | <code>string</code> | | <code>&#34;data&#34;</code> | |
| [fast_features](variables.tf#L146) | Selective control for top-level FAST features. | <code title="object&#40;&#123;&#10; data_platform &#61; optional&#40;bool, false&#41;&#10; gke &#61; optional&#40;bool, false&#41;&#10; project_factory &#61; optional&#40;bool, false&#41;&#10; sandbox &#61; optional&#40;bool, false&#41;&#10; teams &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | <code>0-0-bootstrap</code> |
| [groups](variables.tf#L160) | Group names to grant organization-level permissions. | <code title="object&#40;&#123;&#10; gcp-devops &#61; optional&#40;string&#41;&#10; gcp-network-admins &#61; optional&#40;string&#41;&#10; gcp-security-admins &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | <code>0-bootstrap</code> |
| [locations](variables.tf#L173) | Optional locations for GCS, BigQuery, and logging buckets created here. | <code title="object&#40;&#123;&#10; bq &#61; string&#10; gcs &#61; string&#10; logging &#61; string&#10; pubsub &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; bq &#61; &#34;EU&#34;&#10; gcs &#61; &#34;EU&#34;&#10; logging &#61; &#34;global&#34;&#10; pubsub &#61; &#91;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> | <code>0-bootstrap</code> |
| [organization_policy_configs](variables.tf#L201) | Organization policies customization. | <code title="object&#40;&#123;&#10; allowed_policy_member_domains &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [outputs_location](variables.tf#L209) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | <code>string</code> | | <code>null</code> | |
| [tag_names](variables.tf#L226) | Customized names for resource management tags. | <code title="object&#40;&#123;&#10; context &#61; string&#10; environment &#61; string&#10; tenant &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; context &#61; &#34;context&#34;&#10; environment &#61; &#34;environment&#34;&#10; tenant &#61; &#34;tenant&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [team_folders](variables.tf#L245) | Team folders to be created. Format is described in a code comment. | <code title="map&#40;object&#40;&#123;&#10; descriptive_name &#61; string&#10; group_iam &#61; map&#40;list&#40;string&#41;&#41;&#10; impersonation_groups &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>null</code> | |
## Outputs

View File

@ -30,6 +30,11 @@ locals {
local.branch_optional_sa_lists.pf-dev,
local.branch_optional_sa_lists.pf-prod,
)
billing_mode = (
var.billing_account.no_iam
? null
: var.billing_account.is_org_level ? "org" : "resource"
)
}
# billing account in same org (resources is in the organization.tf file)
@ -38,7 +43,7 @@ locals {
resource "google_billing_account_iam_member" "billing_ext_admin" {
for_each = toset(
!var.billing_account.is_org_level ? local.billing_ext_users : []
local.billing_mode == "resource" ? local.billing_ext_users : []
)
billing_account_id = var.billing_account.id
role = "roles/billing.user"
@ -47,7 +52,7 @@ resource "google_billing_account_iam_member" "billing_ext_admin" {
resource "google_billing_account_iam_member" "billing_ext_costsmanager" {
for_each = toset(
!var.billing_account.is_org_level ? local.billing_ext_users : []
local.billing_mode == "resource" ? local.billing_ext_users : []
)
billing_account_id = var.billing_account.id
role = "roles/billing.costsManager"

View File

@ -46,7 +46,7 @@ module "organization" {
module.branch-network-sa.iam_email
]
},
var.billing_account.is_org_level ? {
local.billing_mode == "org" ? {
"roles/billing.costsManager" = concat(
local.branch_optional_sa_lists.pf-dev,
local.branch_optional_sa_lists.pf-prod

View File

@ -37,15 +37,13 @@ variable "automation" {
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."
description = "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`."
type = object({
id = string
is_org_level = optional(bool, true)
no_iam = optional(bool, false)
})
validation {
condition = var.billing_account.is_org_level != null
error_message = "Invalid `null` value for `billing_account.is_org_level`."
}
nullable = false
}
variable "cicd_repositories" {

View File

@ -250,6 +250,20 @@ Variables in this stage -- like most other FAST stages -- are broadly divided in
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.
### Using delayed billing association for projects
This configuration is possible but unsupported and only exists for development purposes, use at your own risk:
- temporarily switch `billing_account.id` to `null` in `globals.auto.tfvars.json`
- for each project resources in the project modules used in this stage (`dev-spoke-project`, `landing-project`, `prod-spoke-project`)
- apply using `-target`, for example
`terraform apply -target 'module.landing-project.google_project.project[0]'`
- untaint the project resource after applying, for example
`terraform untaint 'module.landing-project.google_project.project[0]'`
- go through the process to associate the billing account with the two projects
- switch `billing_account.id` back to the real billing account id
- resume applying normally
### Running the stage
Once provider and variable values are in place and the correct user is configured, the stage can be run:
@ -333,20 +347,19 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS
| [automation](variables.tf#L17) | Automation resources created by the bootstrap stage. | <code title="object&#40;&#123;&#10; outputs_bucket &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [billing_account](variables.tf#L25) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | <code title="object&#40;&#123;&#10; id &#61; string&#10; is_org_level &#61; optional&#40;bool, true&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [folder_ids](variables.tf#L92) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | <code title="object&#40;&#123;&#10; networking &#61; string&#10; networking-dev &#61; string&#10; networking-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>1-resman</code> |
| [organization](variables.tf#L126) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [prefix](variables.tf#L142) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>0-bootstrap</code> |
| [organization](variables.tf#L102) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [prefix](variables.tf#L118) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>0-bootstrap</code> |
| [custom_adv](variables.tf#L38) | Custom advertisement definitions in name => range format. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; cloud_dns &#61; &#34;35.199.192.0&#47;19&#34;&#10; gcp_all &#61; &#34;10.128.0.0&#47;16&#34;&#10; gcp_dev &#61; &#34;10.128.32.0&#47;19&#34;&#10; gcp_landing &#61; &#34;10.128.0.0&#47;19&#34;&#10; gcp_prod &#61; &#34;10.128.64.0&#47;19&#34;&#10; googleapis_private &#61; &#34;199.36.153.8&#47;30&#34;&#10; googleapis_restricted &#61; &#34;199.36.153.4&#47;30&#34;&#10; rfc_1918_10 &#61; &#34;10.0.0.0&#47;8&#34;&#10; rfc_1918_172 &#61; &#34;172.16.0.0&#47;12&#34;&#10; rfc_1918_192 &#61; &#34;192.168.0.0&#47;16&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [custom_roles](variables.tf#L55) | Custom roles defined at the org level, in key => id format. | <code title="object&#40;&#123;&#10; service_project_network_admin &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>0-bootstrap</code> |
| [dns](variables.tf#L64) | Onprem DNS resolvers. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code title="&#123;&#10; onprem &#61; &#91;&#34;10.0.200.3&#34;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [factories_config](variables.tf#L72) | Configuration for network resource factories. | <code title="object&#40;&#123;&#10; data_dir &#61; optional&#40;string, &#34;data&#34;&#41;&#10; firewall_policy_name &#61; optional&#40;string, &#34;factory&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; data_dir &#61; &#34;data&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [l7ilb_subnets](variables.tf#L102) | Subnets used for L7 ILBs. | <code title="object&#40;&#123;&#10; dev &#61; optional&#40;list&#40;object&#40;&#123;&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10; prod &#61; optional&#40;list&#40;object&#40;&#123;&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; dev &#61; &#91;&#10; &#123; ip_cidr_range &#61; &#34;10.128.60.0&#47;24&#34;, region &#61; &#34;primary&#34; &#125;,&#10; &#123; ip_cidr_range &#61; &#34;10.128.61.0&#47;24&#34;, region &#61; &#34;secondary&#34; &#125;&#10; &#93;&#10; prod &#61; &#91;&#10; &#123; ip_cidr_range &#61; &#34;10.128.92.0&#47;24&#34;, region &#61; &#34;primary&#34; &#125;,&#10; &#123; ip_cidr_range &#61; &#34;10.128.93.0&#47;24&#34;, region &#61; &#34;secondary&#34; &#125;&#10; &#93;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [outputs_location](variables.tf#L136) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | <code>string</code> | | <code>null</code> | |
| [outputs_location](variables.tf#L112) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | <code>string</code> | | <code>null</code> | |
| [peering_configs](variables-peerings.tf#L19) | Peering configurations. | <code title="map&#40;object&#40;&#123;&#10; export_local_custom_routes &#61; bool&#10; export_peer_custom_routes &#61; bool&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; dev &#61; &#123;&#10; export_local_custom_routes &#61; true&#10; export_peer_custom_routes &#61; true&#10; &#125;&#10; prod &#61; &#123;&#10; export_local_custom_routes &#61; true&#10; export_peer_custom_routes &#61; true&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [psa_ranges](variables.tf#L153) | IP ranges used for Private Service Access (e.g. CloudSQL). | <code title="object&#40;&#123;&#10; dev &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; routes &#61; object&#40;&#123;&#10; export &#61; bool&#10; import &#61; bool&#10; &#125;&#41;&#10; &#125;&#41;&#10; prod &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; routes &#61; object&#40;&#123;&#10; export &#61; bool&#10; import &#61; bool&#10; &#125;&#41;&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [regions](variables.tf#L190) | Region definitions. | <code title="object&#40;&#123;&#10; primary &#61; string&#10; secondary &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; primary &#61; &#34;europe-west1&#34;&#10; secondary &#61; &#34;europe-west4&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [router_onprem_configs](variables.tf#L202) | Configurations for routers used for onprem connectivity. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; custom &#61; list&#40;string&#41;&#10; default &#61; bool&#10; &#125;&#41;&#10; asn &#61; number&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-primary &#61; &#123;&#10; asn &#61; &#34;65533&#34;&#10; adv &#61; null&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [service_accounts](variables.tf#L220) | Automation service accounts in name => email format. | <code title="object&#40;&#123;&#10; data-platform-dev &#61; string&#10; data-platform-prod &#61; string&#10; gke-dev &#61; string&#10; gke-prod &#61; string&#10; project-factory-dev &#61; string&#10; project-factory-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>1-resman</code> |
| [vpn_onprem_configs](variables.tf#L234) | VPN gateway configuration for onprem interconnection. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; default &#61; bool&#10; custom &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; peer_external_gateway &#61; object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; tunnels &#61; list&#40;object&#40;&#123;&#10; peer_asn &#61; number&#10; peer_external_gateway_interface &#61; number&#10; secret &#61; string&#10; session_range &#61; string&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-primary &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_all&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#34;8.8.8.8&#34;&#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [psa_ranges](variables.tf#L129) | IP ranges used for Private Service Access (e.g. CloudSQL). | <code title="object&#40;&#123;&#10; dev &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; routes &#61; object&#40;&#123;&#10; export &#61; bool&#10; import &#61; bool&#10; &#125;&#41;&#10; &#125;&#41;&#10; prod &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; routes &#61; object&#40;&#123;&#10; export &#61; bool&#10; import &#61; bool&#10; &#125;&#41;&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [regions](variables.tf#L166) | Region definitions. | <code title="object&#40;&#123;&#10; primary &#61; string&#10; secondary &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; primary &#61; &#34;europe-west1&#34;&#10; secondary &#61; &#34;europe-west4&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [router_onprem_configs](variables.tf#L178) | Configurations for routers used for onprem connectivity. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; custom &#61; list&#40;string&#41;&#10; default &#61; bool&#10; &#125;&#41;&#10; asn &#61; number&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-primary &#61; &#123;&#10; asn &#61; &#34;65533&#34;&#10; adv &#61; null&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [service_accounts](variables.tf#L196) | Automation service accounts in name => email format. | <code title="object&#40;&#123;&#10; data-platform-dev &#61; string&#10; data-platform-prod &#61; string&#10; gke-dev &#61; string&#10; gke-prod &#61; string&#10; project-factory-dev &#61; string&#10; project-factory-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>1-resman</code> |
| [vpn_onprem_configs](variables.tf#L210) | VPN gateway configuration for onprem interconnection. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; default &#61; bool&#10; custom &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; peer_external_gateway &#61; object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; tunnels &#61; list&#40;object&#40;&#123;&#10; peer_asn &#61; number&#10; peer_external_gateway_interface &#61; number&#10; secret &#61; string&#10; session_range &#61; string&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-primary &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_all&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#34;8.8.8.8&#34;&#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
## Outputs

View File

@ -3,6 +3,6 @@
region: europe-west1
description: Default subnet for dev Data Platform
ip_cidr_range: 10.128.48.0/24
secondary_ip_range:
secondary_ip_ranges:
pods: 100.128.48.0/20
services: 100.255.48.0/24

View File

@ -3,6 +3,6 @@
region: europe-west1
description: Default subnet for prod gke nodes
ip_cidr_range: 10.64.0.0/24
secondary_ip_range:
secondary_ip_ranges:
pods: 100.64.0.0/16
services: 192.168.1.0/24

View File

@ -16,19 +16,6 @@
# tfdoc:file:description Dev spoke VPC and related resources.
locals {
_l7ilb_subnets_dev = [
for v in var.l7ilb_subnets.dev : merge(v, {
active = true
region = lookup(var.regions, v.region, v.region)
})]
l7ilb_subnets_dev = [
for v in local._l7ilb_subnets_dev : merge(v, {
name = "dev-l7ilb-${local.region_shortnames[v.region]}"
})
]
}
module "dev-spoke-project" {
source = "../../../modules/project"
billing_account = var.billing_account.id
@ -57,13 +44,12 @@ module "dev-spoke-project" {
}
module "dev-spoke-vpc" {
source = "../../../modules/net-vpc"
project_id = module.dev-spoke-project.project_id
name = "dev-spoke-0"
mtu = 1500
data_folder = "${var.factories_config.data_dir}/subnets/dev"
psa_config = try(var.psa_ranges.dev, null)
subnets_proxy_only = local.l7ilb_subnets_dev
source = "../../../modules/net-vpc"
project_id = module.dev-spoke-project.project_id
name = "dev-spoke-0"
mtu = 1500
data_folder = "${var.factories_config.data_dir}/subnets/dev"
psa_config = try(var.psa_ranges.dev, null)
# set explicit routes for googleapis in case the default route is deleted
routes = {
private-googleapis = {

View File

@ -16,19 +16,6 @@
# tfdoc:file:description Production spoke VPC and related resources.
locals {
_l7ilb_subnets_prod = [
for v in var.l7ilb_subnets.prod : merge(v, {
active = true
region = lookup(var.regions, v.region, v.region)
})]
l7ilb_subnets_prod = [
for v in local._l7ilb_subnets_prod : merge(v, {
name = "prod-l7ilb-${local.region_shortnames[v.region]}"
})
]
}
module "prod-spoke-project" {
source = "../../../modules/project"
billing_account = var.billing_account.id
@ -57,13 +44,12 @@ module "prod-spoke-project" {
}
module "prod-spoke-vpc" {
source = "../../../modules/net-vpc"
project_id = module.prod-spoke-project.project_id
name = "prod-spoke-0"
mtu = 1500
data_folder = "${var.factories_config.data_dir}/subnets/prod"
psa_config = try(var.psa_ranges.prod, null)
subnets_proxy_only = local.l7ilb_subnets_prod
source = "../../../modules/net-vpc"
project_id = module.prod-spoke-project.project_id
name = "prod-spoke-0"
mtu = 1500
data_folder = "${var.factories_config.data_dir}/subnets/prod"
psa_config = try(var.psa_ranges.prod, null)
# set explicit routes for googleapis in case the default route is deleted
routes = {
private-googleapis = {

View File

@ -99,30 +99,6 @@ variable "folder_ids" {
})
}
variable "l7ilb_subnets" {
description = "Subnets used for L7 ILBs."
type = object({
dev = optional(list(object({
ip_cidr_range = string
region = string
})), [])
prod = optional(list(object({
ip_cidr_range = string
region = string
})), [])
})
default = {
dev = [
{ ip_cidr_range = "10.128.60.0/24", region = "primary" },
{ ip_cidr_range = "10.128.61.0/24", region = "secondary" }
]
prod = [
{ ip_cidr_range = "10.128.92.0/24", region = "primary" },
{ ip_cidr_range = "10.128.93.0/24", region = "secondary" }
]
}
}
variable "organization" {
# tfdoc:variable:source 0-bootstrap
description = "Organization details."

View File

@ -264,6 +264,20 @@ Variables in this stage -- like most other FAST stages -- are broadly divided in
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.
### Using delayed billing association for projects
This configuration is possible but unsupported and only exists for development purposes, use at your own risk:
- temporarily switch `billing_account.id` to `null` in `globals.auto.tfvars.json`
- for each project resources in the project modules used in this stage (`dev-spoke-project`, `landing-project`, `prod-spoke-project`)
- apply using `-target`, for example
`terraform apply -target 'module.landing-project.google_project.project[0]'`
- untaint the project resource after applying, for example
`terraform untaint 'module.landing-project.google_project.project[0]'`
- go through the process to associate the billing account with the two projects
- switch `billing_account.id` back to the real billing account id
- resume applying normally
### Running the stage
Once provider and variable values are in place and the correct user is configured, the stage can be run:
@ -358,20 +372,19 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS
| [automation](variables.tf#L17) | Automation resources created by the bootstrap stage. | <code title="object&#40;&#123;&#10; outputs_bucket &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [billing_account](variables.tf#L25) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | <code title="object&#40;&#123;&#10; id &#61; string&#10; is_org_level &#61; optional&#40;bool, true&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [folder_ids](variables.tf#L92) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | <code title="object&#40;&#123;&#10; networking &#61; string&#10; networking-dev &#61; string&#10; networking-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>1-resman</code> |
| [organization](variables.tf#L126) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [prefix](variables.tf#L142) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>0-bootstrap</code> |
| [organization](variables.tf#L102) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [prefix](variables.tf#L118) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>0-bootstrap</code> |
| [custom_adv](variables.tf#L38) | Custom advertisement definitions in name => range format. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; cloud_dns &#61; &#34;35.199.192.0&#47;19&#34;&#10; gcp_all &#61; &#34;10.128.0.0&#47;16&#34;&#10; gcp_dev &#61; &#34;10.128.32.0&#47;19&#34;&#10; gcp_landing &#61; &#34;10.128.0.0&#47;19&#34;&#10; gcp_prod &#61; &#34;10.128.64.0&#47;19&#34;&#10; googleapis_private &#61; &#34;199.36.153.8&#47;30&#34;&#10; googleapis_restricted &#61; &#34;199.36.153.4&#47;30&#34;&#10; rfc_1918_10 &#61; &#34;10.0.0.0&#47;8&#34;&#10; rfc_1918_172 &#61; &#34;172.16.0.0&#47;12&#34;&#10; rfc_1918_192 &#61; &#34;192.168.0.0&#47;16&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [custom_roles](variables.tf#L55) | Custom roles defined at the org level, in key => id format. | <code title="object&#40;&#123;&#10; service_project_network_admin &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>0-bootstrap</code> |
| [dns](variables.tf#L64) | Onprem DNS resolvers. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code title="&#123;&#10; onprem &#61; &#91;&#34;10.0.200.3&#34;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [factories_config](variables.tf#L72) | Configuration for network resource factories. | <code title="object&#40;&#123;&#10; data_dir &#61; optional&#40;string, &#34;data&#34;&#41;&#10; firewall_policy_name &#61; optional&#40;string, &#34;factory&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; data_dir &#61; &#34;data&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [l7ilb_subnets](variables.tf#L102) | Subnets used for L7 ILBs. | <code title="object&#40;&#123;&#10; dev &#61; optional&#40;list&#40;object&#40;&#123;&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10; prod &#61; optional&#40;list&#40;object&#40;&#123;&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; dev &#61; &#91;&#10; &#123; ip_cidr_range &#61; &#34;10.128.60.0&#47;24&#34;, region &#61; &#34;primary&#34; &#125;,&#10; &#123; ip_cidr_range &#61; &#34;10.128.61.0&#47;24&#34;, region &#61; &#34;secondary&#34; &#125;&#10; &#93;&#10; prod &#61; &#91;&#10; &#123; ip_cidr_range &#61; &#34;10.128.92.0&#47;24&#34;, region &#61; &#34;primary&#34; &#125;,&#10; &#123; ip_cidr_range &#61; &#34;10.128.93.0&#47;24&#34;, region &#61; &#34;secondary&#34; &#125;&#10; &#93;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [outputs_location](variables.tf#L136) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | <code>string</code> | | <code>null</code> | |
| [psa_ranges](variables.tf#L153) | IP ranges used for Private Service Access (e.g. CloudSQL). | <code title="object&#40;&#123;&#10; dev &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; routes &#61; object&#40;&#123;&#10; export &#61; bool&#10; import &#61; bool&#10; &#125;&#41;&#10; &#125;&#41;&#10; prod &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; routes &#61; object&#40;&#123;&#10; export &#61; bool&#10; import &#61; bool&#10; &#125;&#41;&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [regions](variables.tf#L190) | Region definitions. | <code title="object&#40;&#123;&#10; primary &#61; string&#10; secondary &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; primary &#61; &#34;europe-west1&#34;&#10; secondary &#61; &#34;europe-west4&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [router_onprem_configs](variables.tf#L202) | Configurations for routers used for onprem connectivity. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; custom &#61; list&#40;string&#41;&#10; default &#61; bool&#10; &#125;&#41;&#10; asn &#61; number&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-primary &#61; &#123;&#10; asn &#61; &#34;65533&#34;&#10; adv &#61; null&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [outputs_location](variables.tf#L112) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | <code>string</code> | | <code>null</code> | |
| [psa_ranges](variables.tf#L129) | IP ranges used for Private Service Access (e.g. CloudSQL). | <code title="object&#40;&#123;&#10; dev &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; routes &#61; object&#40;&#123;&#10; export &#61; bool&#10; import &#61; bool&#10; &#125;&#41;&#10; &#125;&#41;&#10; prod &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; routes &#61; object&#40;&#123;&#10; export &#61; bool&#10; import &#61; bool&#10; &#125;&#41;&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [regions](variables.tf#L166) | Region definitions. | <code title="object&#40;&#123;&#10; primary &#61; string&#10; secondary &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; primary &#61; &#34;europe-west1&#34;&#10; secondary &#61; &#34;europe-west4&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [router_onprem_configs](variables.tf#L178) | Configurations for routers used for onprem connectivity. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; custom &#61; list&#40;string&#41;&#10; default &#61; bool&#10; &#125;&#41;&#10; asn &#61; number&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-primary &#61; &#123;&#10; asn &#61; &#34;65533&#34;&#10; adv &#61; null&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [router_spoke_configs](variables-vpn.tf#L18) | Configurations for routers used for internal connectivity. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; custom &#61; list&#40;string&#41;&#10; default &#61; bool&#10; &#125;&#41;&#10; asn &#61; number&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-primary &#61; &#123; asn &#61; &#34;64512&#34;, adv &#61; null &#125;&#10; landing-secondary &#61; &#123; asn &#61; &#34;64512&#34;, adv &#61; null &#125;&#10; spoke-dev-primary &#61; &#123; asn &#61; &#34;64513&#34;, adv &#61; null &#125;&#10; spoke-dev-secondary &#61; &#123; asn &#61; &#34;64513&#34;, adv &#61; null &#125;&#10; spoke-prod-primary &#61; &#123; asn &#61; &#34;64514&#34;, adv &#61; null &#125;&#10; spoke-prod-secondary &#61; &#123; asn &#61; &#34;64514&#34;, adv &#61; null &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [service_accounts](variables.tf#L220) | Automation service accounts in name => email format. | <code title="object&#40;&#123;&#10; data-platform-dev &#61; string&#10; data-platform-prod &#61; string&#10; gke-dev &#61; string&#10; gke-prod &#61; string&#10; project-factory-dev &#61; string&#10; project-factory-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>1-resman</code> |
| [vpn_onprem_configs](variables.tf#L234) | VPN gateway configuration for onprem interconnection. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; default &#61; bool&#10; custom &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; peer_external_gateway &#61; object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; tunnels &#61; list&#40;object&#40;&#123;&#10; peer_asn &#61; number&#10; peer_external_gateway_interface &#61; number&#10; secret &#61; string&#10; session_range &#61; string&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-primary &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_all&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#34;8.8.8.8&#34;&#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [service_accounts](variables.tf#L196) | Automation service accounts in name => email format. | <code title="object&#40;&#123;&#10; data-platform-dev &#61; string&#10; data-platform-prod &#61; string&#10; gke-dev &#61; string&#10; gke-prod &#61; string&#10; project-factory-dev &#61; string&#10; project-factory-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>1-resman</code> |
| [vpn_onprem_configs](variables.tf#L210) | VPN gateway configuration for onprem interconnection. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; default &#61; bool&#10; custom &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; peer_external_gateway &#61; object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; tunnels &#61; list&#40;object&#40;&#123;&#10; peer_asn &#61; number&#10; peer_external_gateway_interface &#61; number&#10; secret &#61; string&#10; session_range &#61; string&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-primary &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_all&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#34;8.8.8.8&#34;&#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [vpn_spoke_configs](variables-vpn.tf#L37) | VPN gateway configuration for spokes. | <code title="map&#40;object&#40;&#123;&#10; default &#61; bool&#10; custom &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-primary &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#34;rfc_1918_10&#34;, &#34;rfc_1918_172&#34;, &#34;rfc_1918_192&#34;&#93;&#10; &#125;&#10; landing-secondary &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#34;rfc_1918_10&#34;, &#34;rfc_1918_172&#34;, &#34;rfc_1918_192&#34;&#93;&#10; &#125;&#10; dev-primary &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#34;gcp_dev&#34;&#93;&#10; &#125;&#10; prod-primary &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#34;gcp_prod&#34;&#93;&#10; &#125;&#10; prod-secondary &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#34;gcp_prod&#34;&#93;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
## Outputs

View File

@ -3,6 +3,6 @@
region: europe-west1
description: Default subnet for dev Data Platform
ip_cidr_range: 10.128.48.0/24
secondary_ip_range:
secondary_ip_ranges:
pods: 100.128.48.0/20
services: 100.255.48.0/24

View File

@ -3,6 +3,6 @@
region: europe-west1
description: Default subnet for prod gke nodes
ip_cidr_range: 10.64.0.0/24
secondary_ip_range:
secondary_ip_ranges:
pods: 100.64.0.0/16
services: 192.168.1.0/24

View File

@ -16,19 +16,6 @@
# tfdoc:file:description Dev spoke VPC and related resources.
locals {
_l7ilb_subnets_dev = [
for v in var.l7ilb_subnets.dev : merge(v, {
active = true
region = lookup(var.regions, v.region, v.region)
})]
l7ilb_subnets_dev = [
for v in local._l7ilb_subnets_dev : merge(v, {
name = "dev-l7ilb-${local.region_shortnames[v.region]}"
})
]
}
module "dev-spoke-project" {
source = "../../../modules/project"
billing_account = var.billing_account.id
@ -57,13 +44,12 @@ module "dev-spoke-project" {
}
module "dev-spoke-vpc" {
source = "../../../modules/net-vpc"
project_id = module.dev-spoke-project.project_id
name = "dev-spoke-0"
mtu = 1500
data_folder = "${var.factories_config.data_dir}/subnets/dev"
psa_config = try(var.psa_ranges.dev, null)
subnets_proxy_only = local.l7ilb_subnets_dev
source = "../../../modules/net-vpc"
project_id = module.dev-spoke-project.project_id
name = "dev-spoke-0"
mtu = 1500
data_folder = "${var.factories_config.data_dir}/subnets/dev"
psa_config = try(var.psa_ranges.dev, null)
# set explicit routes for googleapis in case the default route is deleted
routes = {
private-googleapis = {

View File

@ -16,19 +16,6 @@
# tfdoc:file:description Production spoke VPC and related resources.
locals {
_l7ilb_subnets_prod = [
for v in var.l7ilb_subnets.prod : merge(v, {
active = true
region = lookup(var.regions, v.region, v.region)
})]
l7ilb_subnets_prod = [
for v in local._l7ilb_subnets_prod : merge(v, {
name = "prod-l7ilb-${local.region_shortnames[v.region]}"
})
]
}
module "prod-spoke-project" {
source = "../../../modules/project"
billing_account = var.billing_account.id
@ -57,13 +44,12 @@ module "prod-spoke-project" {
}
module "prod-spoke-vpc" {
source = "../../../modules/net-vpc"
project_id = module.prod-spoke-project.project_id
name = "prod-spoke-0"
mtu = 1500
data_folder = "${var.factories_config.data_dir}/subnets/prod"
psa_config = try(var.psa_ranges.prod, null)
subnets_proxy_only = local.l7ilb_subnets_prod
source = "../../../modules/net-vpc"
project_id = module.prod-spoke-project.project_id
name = "prod-spoke-0"
mtu = 1500
data_folder = "${var.factories_config.data_dir}/subnets/prod"
psa_config = try(var.psa_ranges.prod, null)
# set explicit routes for googleapis in case the default route is deleted
routes = {
private-googleapis = {

View File

@ -99,30 +99,6 @@ variable "folder_ids" {
})
}
variable "l7ilb_subnets" {
description = "Subnets used for L7 ILBs."
type = object({
dev = optional(list(object({
ip_cidr_range = string
region = string
})), [])
prod = optional(list(object({
ip_cidr_range = string
region = string
})), [])
})
default = {
dev = [
{ ip_cidr_range = "10.128.60.0/24", region = "primary" },
{ ip_cidr_range = "10.128.61.0/24", region = "secondary" }
]
prod = [
{ ip_cidr_range = "10.128.92.0/24", region = "primary" },
{ ip_cidr_range = "10.128.93.0/24", region = "secondary" }
]
}
}
variable "organization" {
# tfdoc:variable:source 0-bootstrap
description = "Organization details."

View File

@ -323,6 +323,20 @@ Variables in this stage -- like most other FAST stages -- are broadly divided in
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.
### Using delayed billing association for projects
This configuration is possible but unsupported and only exists for development purposes, use at your own risk:
- temporarily switch `billing_account.id` to `null` in `globals.auto.tfvars.json`
- for each project resources in the project modules used in this stage (`dev-spoke-project`, `landing-project`, `prod-spoke-project`)
- apply using `-target`, for example
`terraform apply -target 'module.landing-project.google_project.project[0]'`
- untaint the project resource after applying, for example
`terraform untaint 'module.landing-project.google_project.project[0]'`
- go through the process to associate the billing account with the two projects
- switch `billing_account.id` back to the real billing account id
- resume applying normally
### Running the stage
Once provider and variable values are in place and the correct user is configured, the stage can be run:
@ -407,20 +421,19 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS
| [automation](variables.tf#L17) | Automation resources created by the bootstrap stage. | <code title="object&#40;&#123;&#10; outputs_bucket &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [billing_account](variables.tf#L25) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | <code title="object&#40;&#123;&#10; id &#61; string&#10; is_org_level &#61; optional&#40;bool, true&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [folder_ids](variables.tf#L97) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | <code title="object&#40;&#123;&#10; networking &#61; string&#10; networking-dev &#61; string&#10; networking-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>1-resman</code> |
| [organization](variables.tf#L133) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [prefix](variables.tf#L149) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>0-bootstrap</code> |
| [organization](variables.tf#L115) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [prefix](variables.tf#L131) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>0-bootstrap</code> |
| [custom_adv](variables.tf#L38) | Custom advertisement definitions in name => range format. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; cloud_dns &#61; &#34;35.199.192.0&#47;19&#34;&#10; gcp_all &#61; &#34;10.128.0.0&#47;16&#34;&#10; gcp_dev_primary &#61; &#34;10.128.128.0&#47;19&#34;&#10; gcp_dev_secondary &#61; &#34;10.128.160.0&#47;19&#34;&#10; gcp_landing_trusted_primary &#61; &#34;10.128.64.0&#47;19&#34;&#10; gcp_landing_trusted_secondary &#61; &#34;10.128.96.0&#47;19&#34;&#10; gcp_landing_untrusted_primary &#61; &#34;10.128.0.0&#47;19&#34;&#10; gcp_landing_untrusted_secondary &#61; &#34;10.128.32.0&#47;19&#34;&#10; gcp_prod_primary &#61; &#34;10.128.192.0&#47;19&#34;&#10; gcp_prod_secondary &#61; &#34;10.128.224.0&#47;19&#34;&#10; googleapis_private &#61; &#34;199.36.153.8&#47;30&#34;&#10; googleapis_restricted &#61; &#34;199.36.153.4&#47;30&#34;&#10; rfc_1918_10 &#61; &#34;10.0.0.0&#47;8&#34;&#10; rfc_1918_172 &#61; &#34;172.16.0.0&#47;12&#34;&#10; rfc_1918_192 &#61; &#34;192.168.0.0&#47;16&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [custom_roles](variables.tf#L60) | Custom roles defined at the org level, in key => id format. | <code title="object&#40;&#123;&#10; service_project_network_admin &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>0-bootstrap</code> |
| [dns](variables.tf#L69) | Onprem DNS resolvers. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code title="&#123;&#10; onprem &#61; &#91;&#34;10.0.200.3&#34;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [factories_config](variables.tf#L77) | Configuration for network resource factories. | <code title="object&#40;&#123;&#10; data_dir &#61; optional&#40;string, &#34;data&#34;&#41;&#10; firewall_policy_name &#61; optional&#40;string, &#34;factory&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; data_dir &#61; &#34;data&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [l7ilb_subnets](variables.tf#L107) | Subnets used for L7 ILBs. | <code title="map&#40;list&#40;object&#40;&#123;&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10;&#125;&#41;&#41;&#41;">map&#40;list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;&#41;</code> | | <code title="&#123;&#10; dev &#61; &#91;&#10; &#123; ip_cidr_range &#61; &#34;10.128.159.0&#47;24&#34;, region &#61; &#34;primary&#34; &#125;,&#10; &#123; ip_cidr_range &#61; &#34;10.128.191.0&#47;24&#34;, region &#61; &#34;secondary&#34; &#125;&#10; &#93;&#10; prod &#61; &#91;&#10; &#123; ip_cidr_range &#61; &#34;10.128.223.0&#47;24&#34;, region &#61; &#34;primary&#34; &#125;,&#10; &#123; ip_cidr_range &#61; &#34;10.128.255.0&#47;24&#34;, region &#61; &#34;secondary&#34; &#125;&#10; &#93;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [onprem_cidr](variables.tf#L125) | Onprem addresses in name => range format. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; main &#61; &#34;10.0.0.0&#47;24&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [outputs_location](variables.tf#L143) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | <code>string</code> | | <code>null</code> | |
| [psa_ranges](variables.tf#L160) | IP ranges used for Private Service Access (e.g. CloudSQL). Ranges is in name => range format. | <code title="object&#40;&#123;&#10; dev &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; routes &#61; object&#40;&#123;&#10; export &#61; bool&#10; import &#61; bool&#10; &#125;&#41;&#10; &#125;&#41;&#10; prod &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; routes &#61; object&#40;&#123;&#10; export &#61; bool&#10; import &#61; bool&#10; &#125;&#41;&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [regions](variables.tf#L181) | Region definitions. | <code title="object&#40;&#123;&#10; primary &#61; string&#10; secondary &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; primary &#61; &#34;europe-west1&#34;&#10; secondary &#61; &#34;europe-west4&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [router_configs](variables.tf#L193) | Configurations for CRs and onprem routers. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; custom &#61; list&#40;string&#41;&#10; default &#61; bool&#10; &#125;&#41;&#10; asn &#61; number&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-trusted-primary &#61; &#123;&#10; asn &#61; &#34;64512&#34;&#10; adv &#61; null&#10; &#125;&#10; landing-trusted-secondary &#61; &#123;&#10; asn &#61; &#34;64512&#34;&#10; adv &#61; null&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [service_accounts](variables.tf#L216) | Automation service accounts in name => email format. | <code title="object&#40;&#123;&#10; data-platform-dev &#61; string&#10; data-platform-prod &#61; string&#10; gke-dev &#61; string&#10; gke-prod &#61; string&#10; project-factory-dev &#61; string&#10; project-factory-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>1-resman</code> |
| [vpn_onprem_configs](variables.tf#L230) | VPN gateway configuration for onprem interconnection. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; default &#61; bool&#10; custom &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; peer_external_gateway &#61; object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; tunnels &#61; list&#40;object&#40;&#123;&#10; peer_asn &#61; number&#10; peer_external_gateway_interface &#61; number&#10; secret &#61; string&#10; session_range &#61; string&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-trusted-primary &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_all&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#34;8.8.8.8&#34;&#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10; landing-trusted-secondary &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_all&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#34;8.8.8.8&#34;&#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [onprem_cidr](variables.tf#L107) | Onprem addresses in name => range format. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; main &#61; &#34;10.0.0.0&#47;24&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [outputs_location](variables.tf#L125) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | <code>string</code> | | <code>null</code> | |
| [psa_ranges](variables.tf#L142) | IP ranges used for Private Service Access (e.g. CloudSQL). Ranges is in name => range format. | <code title="object&#40;&#123;&#10; dev &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; routes &#61; object&#40;&#123;&#10; export &#61; bool&#10; import &#61; bool&#10; &#125;&#41;&#10; &#125;&#41;&#10; prod &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; routes &#61; object&#40;&#123;&#10; export &#61; bool&#10; import &#61; bool&#10; &#125;&#41;&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [regions](variables.tf#L163) | Region definitions. | <code title="object&#40;&#123;&#10; primary &#61; string&#10; secondary &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; primary &#61; &#34;europe-west1&#34;&#10; secondary &#61; &#34;europe-west4&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [router_configs](variables.tf#L175) | Configurations for CRs and onprem routers. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; custom &#61; list&#40;string&#41;&#10; default &#61; bool&#10; &#125;&#41;&#10; asn &#61; number&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-trusted-primary &#61; &#123;&#10; asn &#61; &#34;64512&#34;&#10; adv &#61; null&#10; &#125;&#10; landing-trusted-secondary &#61; &#123;&#10; asn &#61; &#34;64512&#34;&#10; adv &#61; null&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [service_accounts](variables.tf#L198) | Automation service accounts in name => email format. | <code title="object&#40;&#123;&#10; data-platform-dev &#61; string&#10; data-platform-prod &#61; string&#10; gke-dev &#61; string&#10; gke-prod &#61; string&#10; project-factory-dev &#61; string&#10; project-factory-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>1-resman</code> |
| [vpn_onprem_configs](variables.tf#L212) | VPN gateway configuration for onprem interconnection. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; default &#61; bool&#10; custom &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; peer_external_gateway &#61; object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; tunnels &#61; list&#40;object&#40;&#123;&#10; peer_asn &#61; number&#10; peer_external_gateway_interface &#61; number&#10; secret &#61; string&#10; session_range &#61; string&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-trusted-primary &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_all&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#34;8.8.8.8&#34;&#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10; landing-trusted-secondary &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_all&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#34;8.8.8.8&#34;&#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
## Outputs

View File

@ -18,12 +18,6 @@
locals {
custom_roles = coalesce(var.custom_roles, {})
l7ilb_subnets = { for env, v in var.l7ilb_subnets : env => [
for s in v : merge(s, {
active = true
name = "${env}-l7ilb-${s.region}"
})]
}
# combine all regions from variables and subnets
regions = distinct(concat(
values(var.regions),

View File

@ -16,19 +16,6 @@
# tfdoc:file:description Dev spoke VPC and related resources.
locals {
_l7ilb_subnets_dev = [
for v in var.l7ilb_subnets.dev : merge(v, {
active = true
region = lookup(var.regions, v.region, v.region)
})]
l7ilb_subnets_dev = [
for v in local._l7ilb_subnets_dev : merge(v, {
name = "dev-l7ilb-${local.region_shortnames[v.region]}"
})
]
}
module "dev-spoke-project" {
source = "../../../modules/project"
billing_account = var.billing_account.id
@ -63,7 +50,6 @@ module "dev-spoke-vpc" {
data_folder = "${var.factories_config.data_dir}/subnets/dev"
delete_default_routes_on_create = true
psa_config = try(var.psa_ranges.dev, null)
subnets_proxy_only = local.l7ilb_subnets_dev
# Set explicit routes for googleapis; send everything else to NVAs
routes = {
private-googleapis = {

View File

@ -16,19 +16,6 @@
# tfdoc:file:description Production spoke VPC and related resources.
locals {
_l7ilb_subnets_prod = [
for v in var.l7ilb_subnets.prod : merge(v, {
active = true
region = lookup(var.regions, v.region, v.region)
})]
l7ilb_subnets_prod = [
for v in local._l7ilb_subnets_prod : merge(v, {
name = "prod-l7ilb-${local.region_shortnames[v.region]}"
})
]
}
module "prod-spoke-project" {
source = "../../../modules/project"
billing_account = var.billing_account.id
@ -63,7 +50,6 @@ module "prod-spoke-vpc" {
data_folder = "${var.factories_config.data_dir}/subnets/prod"
delete_default_routes_on_create = true
psa_config = try(var.psa_ranges.prod, null)
subnets_proxy_only = local.l7ilb_subnets_prod
# Set explicit routes for googleapis; send everything else to NVAs
routes = {
private-googleapis = {

View File

@ -104,24 +104,6 @@ variable "folder_ids" {
})
}
variable "l7ilb_subnets" {
description = "Subnets used for L7 ILBs."
type = map(list(object({
ip_cidr_range = string
region = string
})))
default = {
dev = [
{ ip_cidr_range = "10.128.159.0/24", region = "primary" },
{ ip_cidr_range = "10.128.191.0/24", region = "secondary" }
]
prod = [
{ ip_cidr_range = "10.128.223.0/24", region = "primary" },
{ ip_cidr_range = "10.128.255.0/24", region = "secondary" }
]
}
}
variable "onprem_cidr" {
description = "Onprem addresses in name => range format."
type = map(string)

View File

@ -212,6 +212,20 @@ Variables in this stage -- like most other FAST stages -- are broadly divided in
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.
### Using delayed billing association for projects
This configuration is possible but unsupported and only exists for development purposes, use at your own risk:
- temporarily switch `billing_account.id` to `null` in `globals.auto.tfvars.json`
- for each project resources in the project modules used in this stage (`dev-spoke-project`, `prod-spoke-project`)
- apply using `-target`, for example
`terraform apply -target 'module.prod-spoke-project.google_project.project[0]'`
- untaint the project resource after applying, for example
`terraform untaint 'module.prod-spoke-project.google_project.project[0]'`
- go through the process to associate the billing account with the two projects
- switch `billing_account.id` back to the real billing account id
- resume applying normally
### Running the stage
Once provider and variable values are in place and the correct user is configured, the stage can be run:
@ -277,19 +291,18 @@ Regions are defined via the `regions` variable which sets up a mapping between t
| [automation](variables.tf#L17) | Automation resources created by the bootstrap stage. | <code title="object&#40;&#123;&#10; outputs_bucket &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [billing_account](variables.tf#L25) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | <code title="object&#40;&#123;&#10; id &#61; string&#10; is_org_level &#61; optional&#40;bool, true&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [folder_ids](variables.tf#L92) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | <code title="object&#40;&#123;&#10; networking &#61; string&#10; networking-dev &#61; string&#10; networking-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>1-resman</code> |
| [organization](variables.tf#L118) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [prefix](variables.tf#L134) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>0-bootstrap</code> |
| [organization](variables.tf#L102) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-bootstrap</code> |
| [prefix](variables.tf#L118) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>0-bootstrap</code> |
| [custom_adv](variables.tf#L38) | Custom advertisement definitions in name => range format. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; cloud_dns &#61; &#34;35.199.192.0&#47;19&#34;&#10; gcp_all &#61; &#34;10.128.0.0&#47;16&#34;&#10; gcp_dev &#61; &#34;10.128.32.0&#47;19&#34;&#10; gcp_prod &#61; &#34;10.128.64.0&#47;19&#34;&#10; googleapis_private &#61; &#34;199.36.153.8&#47;30&#34;&#10; googleapis_restricted &#61; &#34;199.36.153.4&#47;30&#34;&#10; rfc_1918_10 &#61; &#34;10.0.0.0&#47;8&#34;&#10; rfc_1918_172 &#61; &#34;172.16.0.0&#47;12&#34;&#10; rfc_1918_192 &#61; &#34;192.168.0.0&#47;16&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [custom_roles](variables.tf#L54) | Custom roles defined at the org level, in key => id format. | <code title="object&#40;&#123;&#10; service_project_network_admin &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>0-bootstrap</code> |
| [dns](variables.tf#L63) | Onprem DNS resolvers. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code title="&#123;&#10; prod &#61; &#91;&#34;10.0.1.1&#34;&#93;&#10; dev &#61; &#91;&#34;10.0.2.1&#34;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [factories_config](variables.tf#L72) | Configuration for network resource factories. | <code title="object&#40;&#123;&#10; data_dir &#61; optional&#40;string, &#34;data&#34;&#41;&#10; firewall_policy_name &#61; optional&#40;string, &#34;factory&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; data_dir &#61; &#34;data&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [l7ilb_subnets](variables.tf#L102) | Subnets used for L7 ILBs. | <code title="map&#40;list&#40;object&#40;&#123;&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10;&#125;&#41;&#41;&#41;">map&#40;list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;&#41;</code> | | <code title="&#123;&#10; prod &#61; &#91;&#10; &#123; ip_cidr_range &#61; &#34;10.128.92.0&#47;24&#34;, region &#61; &#34;europe-west1&#34; &#125;,&#10; &#93;&#10; dev &#61; &#91;&#10; &#123; ip_cidr_range &#61; &#34;10.128.60.0&#47;24&#34;, region &#61; &#34;europe-west1&#34; &#125;,&#10; &#93;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [outputs_location](variables.tf#L128) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | <code>string</code> | | <code>null</code> | |
| [psa_ranges](variables.tf#L145) | IP ranges used for Private Service Access (e.g. CloudSQL). | <code title="object&#40;&#123;&#10; dev &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; routes &#61; object&#40;&#123;&#10; export &#61; bool&#10; import &#61; bool&#10; &#125;&#41;&#10; &#125;&#41;&#10; prod &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; routes &#61; object&#40;&#123;&#10; export &#61; bool&#10; import &#61; bool&#10; &#125;&#41;&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [regions](variables.tf#L182) | Region definitions. | <code title="object&#40;&#123;&#10; primary &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; primary &#61; &#34;europe-west1&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [router_onprem_configs](variables.tf#L192) | Configurations for routers used for onprem connectivity. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; custom &#61; list&#40;string&#41;&#10; default &#61; bool&#10; &#125;&#41;&#10; asn &#61; number&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; prod-primary &#61; &#123;&#10; asn &#61; &#34;65533&#34;&#10; adv &#61; null&#10; &#125;&#10; dev-primary &#61; &#123;&#10; asn &#61; &#34;65534&#34;&#10; adv &#61; null&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [service_accounts](variables.tf#L215) | Automation service accounts in name => email format. | <code title="object&#40;&#123;&#10; data-platform-dev &#61; string&#10; data-platform-prod &#61; string&#10; project-factory-dev &#61; string&#10; project-factory-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>1-resman</code> |
| [vpn_onprem_configs](variables.tf#L227) | VPN gateway configuration for onprem interconnection. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; default &#61; bool&#10; custom &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; peer_external_gateway &#61; object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; tunnels &#61; list&#40;object&#40;&#123;&#10; peer_asn &#61; number&#10; peer_external_gateway_interface &#61; number&#10; secret &#61; string&#10; session_range &#61; string&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; dev-primary &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_dev&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#34;8.8.8.8&#34;&#93;&#10;&#10;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65544&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65544&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10; prod-primary &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_prod&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#34;8.8.8.8&#34;&#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65543&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65543&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [outputs_location](variables.tf#L112) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | <code>string</code> | | <code>null</code> | |
| [psa_ranges](variables.tf#L129) | IP ranges used for Private Service Access (e.g. CloudSQL). | <code title="object&#40;&#123;&#10; dev &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; routes &#61; object&#40;&#123;&#10; export &#61; bool&#10; import &#61; bool&#10; &#125;&#41;&#10; &#125;&#41;&#10; prod &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; routes &#61; object&#40;&#123;&#10; export &#61; bool&#10; import &#61; bool&#10; &#125;&#41;&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [regions](variables.tf#L166) | Region definitions. | <code title="object&#40;&#123;&#10; primary &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; primary &#61; &#34;europe-west1&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [router_onprem_configs](variables.tf#L176) | Configurations for routers used for onprem connectivity. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; custom &#61; list&#40;string&#41;&#10; default &#61; bool&#10; &#125;&#41;&#10; asn &#61; number&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; prod-primary &#61; &#123;&#10; asn &#61; &#34;65533&#34;&#10; adv &#61; null&#10; &#125;&#10; dev-primary &#61; &#123;&#10; asn &#61; &#34;65534&#34;&#10; adv &#61; null&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [service_accounts](variables.tf#L199) | Automation service accounts in name => email format. | <code title="object&#40;&#123;&#10; data-platform-dev &#61; string&#10; data-platform-prod &#61; string&#10; project-factory-dev &#61; string&#10; project-factory-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>1-resman</code> |
| [vpn_onprem_configs](variables.tf#L211) | VPN gateway configuration for onprem interconnection. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; default &#61; bool&#10; custom &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; peer_external_gateway &#61; object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; tunnels &#61; list&#40;object&#40;&#123;&#10; peer_asn &#61; number&#10; peer_external_gateway_interface &#61; number&#10; secret &#61; string&#10; session_range &#61; string&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; dev-primary &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_dev&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#34;8.8.8.8&#34;&#93;&#10;&#10;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65544&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65544&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10; prod-primary &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_prod&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#34;8.8.8.8&#34;&#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65543&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65543&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
## Outputs

View File

@ -3,6 +3,6 @@
region: europe-west1
description: Default subnet for dev Data Platform
ip_cidr_range: 10.128.48.0/24
secondary_ip_range:
secondary_ip_ranges:
pods: 100.128.48.0/20
services: 100.255.48.0/24

View File

@ -18,19 +18,6 @@
locals {
custom_roles = coalesce(var.custom_roles, {})
_l7ilb_subnets = {
for k, v in var.l7ilb_subnets : k => [
for s in v : merge(s, {
active = true
region = lookup(var.regions, s.region, s.region)
})]
}
l7ilb_subnets = {
for k, v in local._l7ilb_subnets : k => [
for s in v : merge(s, {
name = "${k}-l7ilb-${local.region_shortnames[s.region]}"
})]
}
# combine all regions from variables and subnets
regions = distinct(concat(
values(var.regions),

View File

@ -43,13 +43,12 @@ module "dev-spoke-project" {
}
module "dev-spoke-vpc" {
source = "../../../modules/net-vpc"
project_id = module.dev-spoke-project.project_id
name = "dev-spoke-0"
mtu = 1500
data_folder = "${var.factories_config.data_dir}/subnets/dev"
psa_config = try(var.psa_ranges.dev, null)
subnets_proxy_only = local.l7ilb_subnets.dev
source = "../../../modules/net-vpc"
project_id = module.dev-spoke-project.project_id
name = "dev-spoke-0"
mtu = 1500
data_folder = "${var.factories_config.data_dir}/subnets/dev"
psa_config = try(var.psa_ranges.dev, null)
# set explicit routes for googleapis in case the default route is deleted
routes = {
private-googleapis = {

View File

@ -43,13 +43,12 @@ module "prod-spoke-project" {
}
module "prod-spoke-vpc" {
source = "../../../modules/net-vpc"
project_id = module.prod-spoke-project.project_id
name = "prod-spoke-0"
mtu = 1500
data_folder = "${var.factories_config.data_dir}/subnets/prod"
psa_config = try(var.psa_ranges.prod, null)
subnets_proxy_only = local.l7ilb_subnets.prod
source = "../../../modules/net-vpc"
project_id = module.prod-spoke-project.project_id
name = "prod-spoke-0"
mtu = 1500
data_folder = "${var.factories_config.data_dir}/subnets/prod"
psa_config = try(var.psa_ranges.prod, null)
# set explicit routes for googleapis in case the default route is deleted
routes = {
private-googleapis = {

View File

@ -99,22 +99,6 @@ variable "folder_ids" {
})
}
variable "l7ilb_subnets" {
description = "Subnets used for L7 ILBs."
type = map(list(object({
ip_cidr_range = string
region = string
})))
default = {
prod = [
{ ip_cidr_range = "10.128.92.0/24", region = "europe-west1" },
]
dev = [
{ ip_cidr_range = "10.128.60.0/24", region = "europe-west1" },
]
}
}
variable "organization" {
# tfdoc:variable:source 0-bootstrap
description = "Organization details."

View File

@ -108,6 +108,20 @@ Variables in this stage -- like most other FAST stages -- are broadly divided in
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.
### Using delayed billing association for projects
This configuration is possible but unsupported and only exists for development purposes, use at your own risk:
- temporarily switch `billing_account.id` to `null` in `globals.auto.tfvars.json`
- for each project resources in the project modules used in this stage (`dev-sec-project`, `prod-sec-project`)
- apply using `-target`, for example
`terraform apply -target 'module.prod-sec-project.google_project.project[0]'`
- untaint the project resource after applying, for example
`terraform untaint 'module.prod-sec-project.google_project.project[0]'`
- go through the process to associate the billing account with the two projects
- switch `billing_account.id` back to the real billing account id
- resume applying normally
### Running the stage
Once provider and variable values are in place and the correct user is configured, the stage can be run:

File diff suppressed because one or more lines are too long

View File

@ -59,9 +59,9 @@ resource "google_dataproc_cluster" "cluster" {
dynamic "shielded_instance_config" {
for_each = var.dataproc_config.cluster_config.gce_cluster_config.shielded_instance_config == null ? [] : [""]
content {
enable_secure_boot = var.dataproc_config.cluster_config.gce_cluster_config.shielded_instance_config.value.enable_secure_boot
enable_vtpm = var.dataproc_config.cluster_config.gce_cluster_config.shielded_instance_config.value.enable_vtpm
enable_integrity_monitoring = var.dataproc_config.cluster_config.gce_cluster_config.shielded_instance_config.value.enable_integrity_monitoring
enable_secure_boot = var.dataproc_config.cluster_config.gce_cluster_config.shielded_instance_config.enable_secure_boot
enable_vtpm = var.dataproc_config.cluster_config.gce_cluster_config.shielded_instance_config.enable_vtpm
enable_integrity_monitoring = var.dataproc_config.cluster_config.gce_cluster_config.shielded_instance_config.enable_integrity_monitoring
}
}
}
@ -99,9 +99,9 @@ resource "google_dataproc_cluster" "cluster" {
dynamic "disk_config" {
for_each = var.dataproc_config.cluster_config.worker_config.disk_config == null ? [] : [""]
content {
boot_disk_type = var.dataproc_config.cluster_config.worker_config.disk_config.value.boot_disk_type
boot_disk_size_gb = var.dataproc_config.cluster_config.worker_config.disk_config.value.boot_disk_size_gb
num_local_ssds = var.dataproc_config.cluster_config.worker_config.disk_config.value.num_local_ssds
boot_disk_type = var.dataproc_config.cluster_config.worker_config.disk_config.boot_disk_type
boot_disk_size_gb = var.dataproc_config.cluster_config.worker_config.disk_config.boot_disk_size_gb
num_local_ssds = var.dataproc_config.cluster_config.worker_config.disk_config.num_local_ssds
}
}
image_uri = var.dataproc_config.cluster_config.worker_config.image_uri
@ -165,20 +165,20 @@ resource "google_dataproc_cluster" "cluster" {
dynamic "autoscaling_config" {
for_each = var.dataproc_config.cluster_config.autoscaling_config == null ? [] : [""]
content {
policy_uri = var.dataproc_config.cluster_config.autoscaling_config.value.policy_uri
policy_uri = var.dataproc_config.cluster_config.autoscaling_config.policy_uri
}
}
dynamic "initialization_action" {
for_each = var.dataproc_config.cluster_config.initialization_action == null ? [] : [""]
content {
script = var.dataproc_config.cluster_config.initialization_action.value.script
timeout_sec = var.dataproc_config.cluster_config.initialization_action.value.timeout_sec
script = var.dataproc_config.cluster_config.initialization_action.script
timeout_sec = var.dataproc_config.cluster_config.initialization_action.timeout_sec
}
}
dynamic "encryption_config" {
for_each = var.dataproc_config.cluster_config.encryption_config == null ? [] : [""]
for_each = try(var.dataproc_config.cluster_config.encryption_config.kms_key_name == null ? [] : [""], [])
content {
kms_key_name = var.dataproc_config.cluster_config.encryption_config.value.kms_key_name
kms_key_name = var.dataproc_config.cluster_config.encryption_config.kms_key_name
}
}
dynamic "dataproc_metric_config" {
@ -243,8 +243,8 @@ resource "google_dataproc_cluster" "cluster" {
dynamic "kubernetes_software_config" {
for_each = var.dataproc_config.virtual_cluster_config.kubernetes_cluster_config.kubernetes_software_config == null ? [] : [""]
content {
component_version = var.dataproc_config.virtual_cluster_config.kubernetes_cluster_config.kubernetes_software_config.value.component_version
properties = var.dataproc_config.virtual_cluster_config.kubernetes_cluster_config.kubernetes_software_config.value.properties
component_version = var.dataproc_config.virtual_cluster_config.kubernetes_cluster_config.kubernetes_software_config.component_version
properties = var.dataproc_config.virtual_cluster_config.kubernetes_cluster_config.kubernetes_software_config.properties
}
}

View File

@ -37,6 +37,6 @@ output "instance_names" {
output "name" {
description = "The name of the cluster."
value = google_dataproc_cluster.cluster.cluster_config.0.bucket
value = google_dataproc_cluster.cluster.name
}

View File

@ -84,9 +84,9 @@ variable "dataproc_config" {
}), null)
}), null)
software_config = optional(object({
image_version = string
override_properties = list(map(string))
optional_components = list(string)
image_version = optional(string, null)
override_properties = map(string)
optional_components = optional(list(string), null)
}), null)
security_config = optional(object({
kerberos_config = object({

View File

@ -406,9 +406,11 @@ resource "google_compute_network_peering_routes_config" "gke_master" {
resource "google_pubsub_topic" "notifications" {
count = (
try(var.enable_features.upgrade_notifications.topic_id, null) == null ? 0 : 1
try(var.enable_features.upgrade_notifications, null) != null &&
try(var.enable_features.upgrade_notifications.topic_id, null) == null ? 1 : 0
)
name = "gke-pubsub-notifications"
project = var.project_id
name = "gke-pubsub-notifications"
labels = {
content = "gke-notifications"
}

View File

@ -921,9 +921,9 @@ resource "google_compute_url_map" "default" {
}
dynamic "url_redirect" {
for_each = (
route_rules.value.default_url_redirect == null
route_rules.value.url_redirect == null
? []
: [route_rules.value.default_url_redirect]
: [route_rules.value.url_redirect]
)
content {
host_redirect = url_redirect.value.host

View File

@ -34,6 +34,7 @@ module "vpc" {
```
### Subnet Options
```hcl
module "vpc" {
source = "./fabric/modules/net-vpc"
@ -174,7 +175,7 @@ module "vpc-host" {
ip_cidr_range = "10.0.0.0/24"
name = "subnet-1"
region = "europe-west1"
secondary_ip_range = {
secondary_ip_ranges = {
pods = "172.16.0.0/20"
services = "192.168.0.0/24"
}
@ -305,7 +306,7 @@ module "vpc" {
### Subnet Factory
The `net-vpc` module includes a subnet factory (see [Resource Factories](../../blueprints/factories/)) for the massive creation of subnets leveraging one configuration file per subnet.
The `net-vpc` module includes a subnet factory (see [Resource Factories](../../blueprints/factories/)) for the massive creation of subnets leveraging one configuration file per subnet. The factory also supports proxy-only and PSC subnets via the `purpose` attribute.
```hcl
module "vpc" {
@ -314,7 +315,7 @@ module "vpc" {
name = "my-network"
data_folder = "config/subnets"
}
# tftest modules=1 resources=4 files=subnet-simple,subnet-detailed inventory=factory.yaml
# tftest modules=1 resources=6 files=subnet-simple,subnet-detailed,subnet-proxy,subnet-psc inventory=factory.yaml
```
```yaml
@ -342,6 +343,20 @@ flow_logs: # enable, set to empty map to use defaults
filter_expression: null
```
```yaml
# tftest-file id=subnet-proxy path=config/subnets/subnet-proxy.yaml
region: europe-west4
ip_cidr_range: 10.1.0.0/24
purpose: REGIONAL_MANAGED_PROXY
```
```yaml
# tftest-file id=subnet-psc path=config/subnets/subnet-psc.yaml
region: europe-west4
ip_cidr_range: 10.2.0.0/24
purpose: PRIVATE_SERVICE_CONNECT
```
### Custom Routes
VPC routes can be configured through the `routes` variable.
@ -380,7 +395,6 @@ module "vpc" {
# tftest modules=5 resources=15 inventory=routes.yaml
```
## Variables
| name | description | type | required | default |

View File

@ -34,6 +34,8 @@ locals {
iam_groups = try(v.iam_groups, [])
iam_users = try(v.iam_users, [])
iam_service_accounts = try(v.iam_service_accounts, [])
purpose = try(v.purpose, null)
active = try(v.active, null)
}
}
_factory_subnets_iam = [
@ -45,7 +47,7 @@ locals {
formatlist("user:%s", lookup(v, "iam_users", [])),
formatlist("serviceAccount:%s", lookup(v, "iam_service_accounts", []))
)
}
} if v.purpose == null
]
_subnet_iam_members = flatten([
for subnet, roles in(var.subnet_iam == null ? {} : var.subnet_iam) : [
@ -61,17 +63,17 @@ locals {
local._subnet_iam_members
)
subnets = merge(
{ for subnet in var.subnets : "${subnet.region}/${subnet.name}" => subnet },
local._factory_subnets
{ for s in var.subnets : "${s.region}/${s.name}" => s },
{ for k, v in local._factory_subnets : k => v if v.purpose == null }
)
subnets_proxy_only = merge(
{ for s in var.subnets_proxy_only : "${s.region}/${s.name}" => s },
{ for k, v in local._factory_subnets : k => v if v.purpose == "REGIONAL_MANAGED_PROXY" }
)
subnets_psc = merge(
{ for s in var.subnets_psc : "${s.region}/${s.name}" => s },
{ for k, v in local._factory_subnets : k => v if v.purpose == "PRIVATE_SERVICE_CONNECT" }
)
subnets_proxy_only = {
for subnet in var.subnets_proxy_only :
"${subnet.region}/${subnet.name}" => subnet
}
subnets_psc = {
for subnet in var.subnets_psc :
"${subnet.region}/${subnet.name}" => subnet
}
}
resource "google_compute_subnetwork" "subnetwork" {
@ -120,9 +122,7 @@ resource "google_compute_subnetwork" "proxy_only" {
: each.value.description
)
purpose = "REGIONAL_MANAGED_PROXY"
role = (
each.value.active || each.value.active == null ? "ACTIVE" : "BACKUP"
)
role = each.value.active != false ? "ACTIVE" : "BACKUP"
}
resource "google_compute_subnetwork" "psc" {

View File

@ -169,10 +169,10 @@ module "pubsub" {
| name | description | sensitive |
|---|---|:---:|
| [id](outputs.tf#L17) | Topic id. | |
| [schema](outputs.tf#L25) | Schema resource. | |
| [schema_id](outputs.tf#L30) | Schema resource id. | |
| [subscription_id](outputs.tf#L35) | Subscription ids. | |
| [subscriptions](outputs.tf#L45) | Subscription resources. | |
| [topic](outputs.tf#L53) | Topic resource. | |
| [schema](outputs.tf#L26) | Schema resource. | |
| [schema_id](outputs.tf#L31) | Schema resource id. | |
| [subscription_id](outputs.tf#L36) | Subscription ids. | |
| [subscriptions](outputs.tf#L46) | Subscription resources. | |
| [topic](outputs.tf#L54) | Topic resource. | |
<!-- END TFDOC -->

View File

@ -33,6 +33,7 @@ locals {
options = try(v.options, v, null) == null ? var.defaults : v.options
}
}
topic_id_static = "projects/${var.project_id}/topics/${var.name}"
}
resource "google_pubsub_schema" "default" {

View File

@ -16,8 +16,9 @@
output "id" {
description = "Topic id."
value = google_pubsub_topic.default.id
value = local.topic_id_static
depends_on = [
google_pubsub_topic.default,
google_pubsub_topic_iam_binding.default
]
}

View File

@ -54,8 +54,16 @@ values:
region: europe-west1
role: roles/compute.networkUser
subnetwork: subnet-detailed
module.vpc.google_compute_subnetwork.proxy_only["europe-west4/subnet-proxy"]:
region: europe-west4
ip_cidr_range: 10.1.0.0/24
purpose: REGIONAL_MANAGED_PROXY
module.vpc.google_compute_subnetwork.psc["europe-west4/subnet-psc"]:
region: europe-west4
ip_cidr_range: 10.2.0.0/24
purpose: PRIVATE_SERVICE_CONNECT
counts:
google_compute_network: 1
google_compute_subnetwork: 2
google_compute_subnetwork: 4
google_compute_subnetwork_iam_binding: 1

View File

@ -24,7 +24,12 @@ values:
module.vpc-host.google_compute_shared_vpc_service_project.service_projects["project2"]:
host_project: my-project
service_project: project2
module.vpc-host.google_compute_subnetwork.subnetwork["europe-west1/subnet-1"]: {}
module.vpc-host.google_compute_subnetwork.subnetwork["europe-west1/subnet-1"]:
secondary_ip_range:
- ip_cidr_range: 172.16.0.0/20
range_name: pods
- ip_cidr_range: 192.168.0.0/24
range_name: services
module.vpc-host.google_compute_subnetwork_iam_binding.binding["europe-west1/subnet-1.roles/compute.networkUser"]:
condition: []
members:

View File

@ -15,7 +15,6 @@
# limitations under the License.
import click
import os
import sys
import tempfile
import yaml

View File

@ -60,7 +60,7 @@ FILE_DESC_DEFAULTS = {
}
FILE_RE_MODULES = re.compile(
r'(?sm)module\s*"[^"]+"\s*\{[^\}]*?source\s*=\s*"([^"]+)"')
FILE_RE_RESOURCES = re.compile(r'(?sm)resource\s*"([^"]+)"')
FILE_RE_RESOURCES = re.compile(r'(?sm)resource\s+"([^"]+)"')
HEREDOC_RE = re.compile(r'(?sm)^<<\-?END(\s*.*?)\s*END$')
MARK_BEGIN = '<!-- BEGIN TFDOC -->'
MARK_END = '<!-- END TFDOC -->'

View File

@ -3,6 +3,10 @@ module github.com/GoogleCloudPlatform/cloud-foundation-fabric/tools/tfeditor
go 1.16
require (
github.com/hashicorp/hcl/v2 v2.12.0 // indirect
github.com/zclconf/go-cty v1.8.0 // indirect
github.com/agext/levenshtein v1.2.3 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/hashicorp/hcl/v2 v2.16.1
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/zclconf/go-cty v1.13.0
golang.org/x/text v0.7.0 // indirect
)

View File

@ -1,10 +1,13 @@
github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0=
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -12,40 +15,81 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hashicorp/hcl/v2 v2.12.0 h1:PsYxySWpMD4KPaoJLnsHwtK5Qptvj/4Q6s0t4sUxZf4=
github.com/hashicorp/hcl/v2 v2.12.0/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg=
github.com/hashicorp/hcl/v2 v2.16.1 h1:BwuxEMD/tsYgbhIW7UuI3crjovf3MzuFWiVgiv57iHg=
github.com/hashicorp/hcl/v2 v2.16.1/go.mod h1:JRmR89jycNkrrqnMmvPDMd56n1rQJ2Q6KocSLCMCXng=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
github.com/zclconf/go-cty v1.8.0 h1:s4AvqaeQzJIu3ndv4gVIhplVD0krU+bgrcLSVUnaWuA=
github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
github.com/zclconf/go-cty v1.12.1/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA=
github.com/zclconf/go-cty v1.13.0 h1:It5dfKTTZHe9aeppbNOda3mN7Ag7sg6QkBNm6TkyFa0=
github.com/zclconf/go-cty v1.13.0/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0=
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=