This repo is part of a multi-part guide that shows how to configure and deploy the example.com reference architecture described in Google Cloud security foundations guide. The following table lists the parts of the guide.
| 0-bootstrap | Bootstraps a Google Cloud organization, creating all the required resources and permissions to start using the Cloud Foundation Toolkit (CFT). This step also configures a CI/CD Pipeline for foundations code in subsequent stages. |
| 1-org | Sets up top-level shared folders, networking projects, organization-level logging, and baseline security settings through organizational policies. |
| 2-environments | Sets up development, nonproduction, and production environments within the Google Cloud organization that you've created. |
| 3-networks-svpc | Sets up shared VPCs with default DNS, NAT (optional), Private Service networking, VPC service controls, on-premises Dedicated Interconnect, and baseline firewall rules for each environment. It also sets up the global DNS hub. |
| 3-networks-hub-and-spoke | Sets up shared VPCs with all the default configuration found on step 3-networks-svpc, but here the architecture will be based on the Hub and Spoke network model. It also sets up the global DNS hub |
| 4-projects | Sets up a folder structure, projects, and an application infrastructure pipeline for applications, which are connected as service projects to the shared VPC created in the previous stage. |
| 5-app-infra (this file) | Deploy a simple [Compute Engine](https://cloud.google.com/compute/) instance in one of the business unit projects using the infra pipeline set up in 4-projects. |
For an overview of the architecture and the parts, see the terraform-example-foundation README file.
The purpose of this step is to deploy a simple Compute Engine instance in one of the business unit projects using the infra pipeline set up in 4-projects.
The infra pipeline is created in step 4-projects within the shared env and has a Cloud Build pipeline configured to manage infrastructure within projects.
As part of this deployment, the provided Terraform code automates the provisioning of a secure infrastructure in Google Cloud to run workloads in Confidential Space — based on Confidential VMs with integrity verification, data isolation, trusted image execution, and integration with KMS for encryption and GCS for storage. In addition to this documentation, you can also follow Google's official tutorial to test each step directly: Create your first Confidential Space environment.
To better understand the structure and content of the tokens used in Confidential Space, refer to the reference documentation on Token Claims. This link details the information contained in the tokens and how they are used to ensure the security and integrity of the environment.
This Compute Engine instance is created using the base network from step 3-networks and is used to access private services. For the Confidential Space, a Docker image built in step 4-projects serves as the base for the confidential instance.
There is also a Source Repository configured with build triggers similar to the CI/CD Pipeline setup in 0-bootstrap.
- 0-bootstrap executed successfully.
- 1-org executed successfully.
- 2-environments executed successfully.
- 3-networks executed successfully.
- 4-projects executed successfully.
Please refer to troubleshooting if you run into issues during this step.
Notes:
- For Confidential space, additional firewall rules and directional perimeter rules may be required, depending on the additional workloads to be deployed.
- If you are using MacOS, replace
cp -RTwithcp -Rin the relevant commands. The-Tflag is needed for Linux, but causes problems for MacOS.
-
Clone the
gcp-policiesrepo based on the Terraform output from the0-bootstrapstep. Clone the repo at the same level of theterraform-example-foundationfolder, the following instructions assume this layout. Runterraform output cloudbuild_project_idin the0-bootstrapfolder to get the Cloud Build Project ID.export INFRA_PIPELINE_PROJECT_ID=$(terraform -chdir="gcp-projects/business_unit_1/shared/" output -raw cloudbuild_project_id) echo ${INFRA_PIPELINE_PROJECT_ID} gcloud source repos clone gcp-policies gcp-policies-app-infra --project=${INFRA_PIPELINE_PROJECT_ID}
Note:
gcp-policiesrepo has the same name as the repo created in step1-org. In order to prevent a collision, the previous command will clone this repo in the foldergcp-policies-app-infra. -
Navigate into the repo and copy contents of policy-library to new repo. All subsequent steps assume you are running them from the gcp-policies-app-infra directory. If you run them from another directory, adjust your copy paths accordingly.
cd gcp-policies-app-infra git checkout -b main cp -RT ../terraform-example-foundation/policy-library/ .
-
Commit changes and push your main branch to the new repo.
git add . git commit -m 'Initialize policy library repo' git push --set-upstream origin main
-
Navigate out of the repo.
cd .. -
Clone the
bu1-example-apprepo.gcloud source repos clone bu1-example-app --project=${INFRA_PIPELINE_PROJECT_ID}
-
Navigate into the repo, change to non-main branch and copy contents of foundation to new repo. All subsequent steps assume you are running them from the bu1-example-app directory. If you run them from another directory, adjust your copy paths accordingly.
cd bu1-example-app git checkout -b plan cp -RT ../terraform-example-foundation/5-app-infra/ . cp ../terraform-example-foundation/build/cloudbuild-tf-* . cp ../terraform-example-foundation/build/tf-wrapper.sh . chmod 755 ./tf-wrapper.sh
-
Rename
common.auto.example.tfvarstocommon.auto.tfvars.mv common.auto.example.tfvars common.auto.tfvars
-
Update the file with values from your environment and 0-bootstrap. See any of the business unit 1 envs folders README.md files for additional information on the values in the
common.auto.tfvarsfile.export remote_state_bucket=$(terraform -chdir="../terraform-example-foundation/0-bootstrap/" output -raw projects_gcs_bucket_tfstate) echo "remote_state_bucket = ${remote_state_bucket}" sed -i'' -e "s/REMOTE_STATE_BUCKET/${remote_state_bucket}/" ./common.auto.tfvars
-
Get the
confidential_image_digestvalue from the Docker image created ingcp-projects
export CLOUD_BUILD_PROJECT_ID=$(terraform -chdir="terraform-example-foundation/0-bootstrap/" output -raw cloudbuild_project_id)
echo ${CLOUD_BUILD_PROJECT_ID}
export DEFAULT_REGION=$(terraform -chdir="../gcp-projects/business_unit_1/shared" output -raw default_region)
echo ${DEFAULT_REGION}
export confidential_image_digest=$(gcloud artifacts docker images describe ${DEFAULT_REGION}-docker.pkg.dev/${CLOUD_BUILD_PROJECT_ID}/tf-runners/confidential_space_image:latest --project=${CLOUD_BUILD_PROJECT_ID})
echo "confidential_image_digest = ${confidential_image_digest}"- Update
IMAGE_DIGESTvalue in the filecommon.auto.tfvars.
sed -i'' -e "s/IMAGE_DIGEST/${confidential_image_digest}/" ./common.auto.tfvars-
Commit changes.
git add . git commit -m 'Initialize bu1 example app repo'
-
Push your plan branch to trigger a plan for all environments. Because the plan branch is not a named environment branch, pushing your plan branch triggers terraform plan but not terraform apply. Review the plan output in your Cloud Build project https://console.cloud.google.com/cloud-build/builds;region=DEFAULT_REGION?project=YOUR_INFRA_PIPELINE_PROJECT_ID
git push --set-upstream origin plan
-
Merge changes to development. Because this is a named environment branch, pushing to this branch triggers both terraform plan and terraform apply. Review the apply output in your Cloud Build project https://console.cloud.google.com/cloud-build/builds;region=DEFAULT_REGION?project=YOUR_INFRA_PIPELINE_PROJECT_ID
git checkout -b development git push origin development
-
Merge changes to nonproduction. Because this is a named environment branch, pushing to this branch triggers both terraform plan and terraform apply. Review the apply output in your Cloud Build project https://console.cloud.google.com/cloud-build/builds;region=DEFAULT_REGION?project=YOUR_INFRA_PIPELINE_PROJECT_ID
git checkout -b nonproduction git push origin nonproduction
-
Merge changes to production branch. Because this is a named environment branch, pushing to this branch triggers both terraform plan and terraform apply. Review the apply output in your Cloud Build project https://console.cloud.google.com/cloud-build/builds;region=DEFAULT_REGION?project=YOUR_INFRA_PIPELINE_PROJECT_ID
git checkout -b production git push origin production
-
The next instructions assume that you are at the same level of the
terraform-example-foundationfolder. Change into5-app-infrafolder, copy the Terraform wrapper script and ensure it can be executed.cd terraform-example-foundation/5-app-infra cp ../build/tf-wrapper.sh . chmod 755 ./tf-wrapper.sh
-
Rename
common.auto.example.tfvarsfiles tocommon.auto.tfvars.mv common.auto.example.tfvars common.auto.tfvars
-
Update
common.auto.tfvarsfile with values from your environment. -
Use
terraform outputto get the project backend bucket value from 0-bootstrap.export remote_state_bucket=$(terraform -chdir="../0-bootstrap/" output -raw projects_gcs_bucket_tfstate) echo "remote_state_bucket = ${remote_state_bucket}" sed -i'' -e "s/REMOTE_STATE_BUCKET/${remote_state_bucket}/" ./common.auto.tfvars
-
Provide the user that will be running
./tf-wrapper.shthe Service Account Token Creator role to the bu1 Terraform service account. -
Provide the user permissions to run the terraform locally with the
serviceAccountTokenCreatorpermission.member="user:$(gcloud auth list --filter="status=ACTIVE" --format="value(account)")" echo ${member} project_id=$(terraform -chdir="../4-projects/business_unit_1/shared/" output -raw cloudbuild_project_id) echo ${project_id} terraform_sa=$(terraform -chdir="../4-projects/business_unit_1/shared/" output -json terraform_service_accounts | jq '."bu1-example-app"' --raw-output) echo ${terraform_sa} gcloud iam service-accounts add-iam-policy-binding ${terraform_sa} --project ${project_id} --member="${member}" --role="roles/iam.serviceAccountTokenCreator"
-
Update
backend.tfwith your bucket from the infra pipeline output.export backend_bucket=$(terraform -chdir="../4-projects/business_unit_1/shared/" output -json state_buckets | jq '."bu1-example-app"' --raw-output) echo "backend_bucket = ${backend_bucket}" for i in `find . -name 'backend.tf'`; do sed -i'' -e "s/UPDATE_APP_INFRA_BUCKET/${backend_bucket}/" $i; done
We will now deploy each of our environments (development/production/nonproduction) using this script.
When using Cloud Build or Jenkins as your CI/CD tool, each environment corresponds to a branch in the repository for the 5-app-infra step. Only the corresponding environment is applied.
To use the validate option of the tf-wrapper.sh script, please follow the instructions to install the terraform-tools component.
-
Use
terraform outputto get the Infra Pipeline Project ID from 4-projects output.export INFRA_PIPELINE_PROJECT_ID=$(terraform -chdir="../4-projects/business_unit_1/shared/" output -raw cloudbuild_project_id) echo ${INFRA_PIPELINE_PROJECT_ID} export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=$(terraform -chdir="../4-projects/business_unit_1/shared/" output -json terraform_service_accounts | jq '."bu1-example-app"' --raw-output) echo ${GOOGLE_IMPERSONATE_SERVICE_ACCOUNT}
-
Run
initandplanand review output for environment production../tf-wrapper.sh init production ./tf-wrapper.sh plan production
-
Run
validateand check for violations../tf-wrapper.sh validate production $(pwd)/../policy-library ${INFRA_PIPELINE_PROJECT_ID}
-
Run
applyproduction../tf-wrapper.sh apply production
-
Run
initandplanand review output for environment nonproduction../tf-wrapper.sh init nonproduction ./tf-wrapper.sh plan nonproduction
-
Run
validateand check for violations../tf-wrapper.sh validate nonproduction $(pwd)/../policy-library ${INFRA_PIPELINE_PROJECT_ID}
-
Run
applynonproduction../tf-wrapper.sh apply nonproduction
-
Run
initandplanand review output for environment development../tf-wrapper.sh init development ./tf-wrapper.sh plan development
-
Run
validateand check for violations../tf-wrapper.sh validate development $(pwd)/../policy-library ${INFRA_PIPELINE_PROJECT_ID}
-
Run
applydevelopment../tf-wrapper.sh apply development
If you received any errors or made any changes to the Terraform config or common.auto.tfvars you must re-run ./tf-wrapper.sh plan <env> before running ./tf-wrapper.sh apply <env>.
After executing this stage, unset the GOOGLE_IMPERSONATE_SERVICE_ACCOUNT environment variable.
unset GOOGLE_IMPERSONATE_SERVICE_ACCOUNT