Skip to content

Latest commit

 

History

History
905 lines (667 loc) · 54.4 KB

README-Terraform-Cloud.md

File metadata and controls

905 lines (667 loc) · 54.4 KB

Deploying a Terraform Cloud (TFC) compatible environment

The objective of the instructions below is to configure the infrastructure that allows you to run CI/CD deployments using Terraform Cloud for the Terraform Example Foundation stages (0-bootstrap, 1-org, 2-environments, 3-networks, 4-projects). The infrastructure consists in two Google Cloud Platform projects (prj-b-seed and prj-b-cicd-wif-tfc).

It is a best practice to have two separate projects here (prj-b-seed and prj-b-cicd-wif-tfc) for separation of concerns. On one hand, prj-b-seed has the Service Accounts able to create / modify infrastructure. On the other hand, the authentication infrastructure using Workload identity federation is implemented in prj-b-cicd-wif-tfc. Unlike other deployment methods, Terraform state will be stored in Terraform Cloud instead of in a bucket in GCP.

Note: If you choose to use Terraform Cloud with Agents a private autopilot GKE cluster will be deployed in your prj-b-cicd-wif-tfc GCP project to be used as the Agent.

Requirements

To run the instructions described in this document, install the following:

Also make sure that you have the following:

  • A Terraform Cloud account for your User or Organization.
  • A Terraform Cloud organization.
  • A Terraform Cloud User token or Organization token.
    • Organization token is prefered since the permissions are limited to a single TFC organization.
  • A supported version control system (VCS) provider connected with your Terraform Cloud account.
  • A private repository (or project) in your VCS provider for each one of the stages of Foundation:
  • A Google Cloud organization.
  • A Google Cloud billing account.
  • Cloud Identity or Google Workspace groups for organization and billing admins.
  • For the user who will run the procedures in this document, grant the following roles:
    • The roles/resourcemanager.organizationAdmin role on the Google Cloud organization.
    • The roles/orgpolicy.policyAdmin role on the Google Cloud organization.
    • The roles/resourcemanager.projectCreator role on the Google Cloud organization.
    • The roles/billing.admin role on the billing account.
    • The roles/resourcemanager.folderCreator role.

Instructions

  1. Clone terraform-example-foundation into your local environment.

    git clone https://github.com/terraform-google-modules/terraform-example-foundation.git
  2. Clone all the private repositories (or projects) you created at the same level of the terraform-example-foundation folder. You must be authenticated to the VCS provider. See GitHub authentication or GitLab authentication for more details.

    git clone git@<VCS-SERVICE-PROVIDER>.com:<VCS-OWNER>/<VCS-BOOTSTRAP-REPO>.git gcp-bootstrap
    git clone git@<VCS-SERVICE-PROVIDER>.com:<VCS-OWNER>/<VCS-ORGANIZATION-REPO>.git gcp-org
    git clone git@<VCS-SERVICE-PROVIDER>.com:<VCS-OWNER>/<VCS-ENVIRONMENTS-REPO>.git gcp-environments
    git clone git@<VCS-SERVICE-PROVIDER>.com:<VCS-OWNER>/<VCS-NETWORKS-REPO>.git gcp-networks
    git clone git@<VCS-SERVICE-PROVIDER>.com:<VCS-OWNER>/<VCS-PROJECTS-REPO>.git gcp-projects
  3. The layout should be:

    gcp-bootstrap/
    gcp-org/
    gcp-environments/
    gcp-networks/
    gcp-projects/
    terraform-example-foundation/
  4. In your VCS repositories (or projects) it is expected to have the following branches created. Also, these branches shouldn't be empty, you need at least a single file. Run scripts/git_create_branches_helper.sh script to create these branches with a seed file for each repository automatically.

    • Bootstrap: production
    • Organization: production
    • Environments: development, nonproduction and production
    • Networks: development, nonproduction and production
    • Projects: development, nonproduction and production
    • Note: scripts/git_create_branches_helper.sh script and the following commands assume you are running it from the directory that has all the repos cloned (layout described in the previous step). If you run from another directory, adjust the BASE_PATH variable at the scripts/git_create_branches_helper.sh and adjust in the following commands.
    chmod 755 ./terraform-example-foundation/0-bootstrap/scripts/git_create_branches_helper.sh
    ./terraform-example-foundation/0-bootstrap/scripts/git_create_branches_helper.sh

    You will see some GIT logs related to the branches creation in the console and the message "Branch creation and push completed for all repositories" at the end of the script execution.

  5. Authenticate your Terraform CLI by running the login command and following the instructions provided in the browser tab that should open automatically.

    terraform login

Note: It is required to do this step even if you already have an Organization token in order to generate your User token.

Deploying step 0-bootstrap

  1. Navigate into the repo. All subsequent steps assume you are running them from the gcp-bootstrap directory. If you run them from another directory, adjust your copy paths accordingly.

    cd gcp-bootstrap
  2. change to a nonproduction branch.

    git checkout -b plan
  3. Copy contents of foundation to new repo (modify accordingly based on your current directory).

    mkdir -p envs/shared
    cp -RT ../terraform-example-foundation/0-bootstrap/ ./envs/shared
    cd ./envs/shared
  4. In the versions file ./versions.tf un-comment the tfe required provider

  5. In the variables file ./variables.tf un-comment variables in the section Specific to tfc_bootstrap

  6. In the outputs file ./outputs.tf Comment-out outputs in the section Specific to cloudbuild_module

  7. In the outputs file ./outputs.tf un-comment outputs in the section Specific to tfc_bootstrap

    1. If you want to use Terraform Cloud with Agents, in addition to Specific to tfc_bootstrap, un-comment outputs in the section Specific to tfc_bootstrap with Terraform Cloud Agents and update enable_tfc_cloud_agents to true variable at terraform.tfvars
  8. Rename file ./cb.tf to ./cb.tf.example

    mv ./cb.tf ./cb.tf.example
  9. Rename file .terraform_cloud.tf.example to ./terraform_cloud.tf

    mv ./terraform_cloud.tf.example ./terraform_cloud.tf
  10. Rename file terraform.example.tfvars to terraform.tfvars

    mv ./terraform.example.tfvars ./terraform.tfvars
  11. Update the file terraform.tfvars with values from your Google Cloud environment

  12. Update the file terraform.tfvars with values from your VCS repositories at tfc_bootstrap section

  13. Update the file terraform.tfvars with values from your Terraform Cloud organization at tfc_bootstrap section

    1. If you want to use Terraform Cloud with Agents update enable_tfc_cloud_agents variable at tfc_bootstrap section
  14. To prevent saving the tfc_token in plain text in the terraform.tfvars file, export the Terraform Cloud token as an environment variable:

    export TF_VAR_tfc_token=YOUR-TFC-TOKEN
  15. To prevent saving the vcs_oauth_token_id in plain text in the terraform.tfvars file, export the OAuth Token ID as an environment variable:

    export TF_VAR_vcs_oauth_token_id=YOUR-VCS-OAUTH-TOKEN-ID
  16. Run terraform version to get the version of your TF and export it as environment variables. terraform_version variable will be used by the tfe_workspace resource in order to set the version of the TF in TFC workspaces. This is important so the state migration (from your local to TFC) works.

    TF_VAR_tfc_terraform_version=$(terraform --version -json | jq '.terraform_version' | sed 's/"//g')
    export TF_VAR_tfc_terraform_version
    echo "TF Version = ${TF_VAR_tfc_terraform_version}"

    Note: You may need to install jq, if your OS doesn't have built-in. An alternative would be run terraform --version and manually copy the number version output to be set at the environment variable.

  17. Use the helper script validate-requirements.sh to validate your environment:

    ../../../terraform-example-foundation/scripts/validate-requirements.sh  -o <ORGANIZATION_ID> -b <BILLING_ACCOUNT_ID> -u <END_USER_EMAIL> -e

    Note: The script is not able to validate if the user is in a Cloud Identity or Google Workspace group with the required roles.

  18. Run terraform init and terraform plan and review the output.

    terraform init
    terraform plan -input=false -out bootstrap.tfplan
  19. Run terraform apply.

    terraform apply bootstrap.tfplan
  20. If you set enable_tfc_cloud_agents variable to true on terraform.tfvars in order to use Terraform Cloud with Agents you need to run these additional steps. If not, you should skip it.

    1. In provider.tf file, un-comment kubernetes provider section;
    2. In terraform_cloud.tf file, un-comment providers block at tfc_agent_gke module;
    3. Run terraform plan -input=false -out bootstrap_2.tfplan
    4. Run terraform apply bootstrap_2.tfplan
  21. Run terraform output to get the email address of the terraform service accounts that will be used to run manual steps for shared environments in steps 3-networks-dual-svpc, 3-networks-hub-and-spoke, and 4-projects.

    export network_step_sa=$(terraform output -raw networks_step_terraform_service_account_email)
    export projects_step_sa=$(terraform output -raw projects_step_terraform_service_account_email)
    
    echo "network step service account = ${network_step_sa}"
    echo "projects step service account = ${projects_step_sa}"
  22. Run terraform output to get the ID of your CI/CD project:

    export cicd_project_id=$(terraform output -raw cicd_project_id)
    echo "CI/CD Project ID = ${cicd_project_id}"
  23. Run terraform output to get the name of the TFC organization and export it as environment variables. TF_CLOUD_ORGANIZATION variable will be used by the cloud block in order to move the local Terraform's state to TFC and TF_VAR_tfc_org_name will be used to run manual steps for shared environments in steps 3-networks-dual-svpc, 3-networks-hub-and-spoke, and 4-projects

    export TF_CLOUD_ORGANIZATION=$(terraform output -raw tfc_org_name)
    export TF_VAR_tfc_org_name=$TF_CLOUD_ORGANIZATION
    echo "TFC Organization = ${TF_CLOUD_ORGANIZATION}"
  24. You need to rename the following files in order to configure the foundation steps for TFC:

    • backend.tf to backend.tf.gcs.example and backend.tf.cloud.example to backend.tf in order to define TFC workspace configuration and store Terraform's state in TFC.
    • remote.tf to remote.tf.gcs.example and remote.tf.cloud.example to remote.tf in order to retrieve the state outputs from workspace in TFC .
    • Note: You need to do this renaming in all the steps. You can run scripts/set-tfc-backend-and-remote.sh script to do the renaming for all the steps automatically.
    mv backend.tf.cloud.example backend.tf
    cd ../../../
    chmod 775 ./terraform-example-foundation/scripts/set-tfc-backend-and-remote.sh
    ./terraform-example-foundation/scripts/set-tfc-backend-and-remote.sh
    cd ./gcp-bootstrap/envs/shared
  25. Re-run terraform init. When you're prompted, agree to copy Terraform state to Terraform Cloud.

    terraform init
  26. (Optional) Run terraform plan to verify that state is configured correctly. You should see no changes from the previous state.

  27. Save the Terraform configuration to gcp-bootstrap VCS repository:

    cd ../..
    git add .
    git commit -m 'Initialize bootstrap repo'
    git push --set-upstream origin plan
  28. Open a pull request (for GitHub) or a merge request (for GitLab) from the plan branch to the production branch and review the output.

  29. The pull request (or merge request) will trigger a Terraform Cloud speculative plan in the production environment.

  30. Review the speculative plan output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/0-shared/runs under Run List item. The output should be Your infrastructure matches the configuration since we applied the 0-bootstrap locally.

  31. If the speculative plan is successful, merge the pull request in to the production branch.

  32. The merge will trigger a Terraform Cloud Plan and Apply run, that will run the plan again for the terraform configuration for the production environment.

  33. Before moving to the next step, go back to the parent directory.

    cd ..

Note: After the deploy, to prevent the project quota error described in the Troubleshooting guide, we recommend that you request 50 additional projects for the projects step service account created in this step.

Deploying step 1-org

  1. Navigate into the repo. All subsequent steps assume you are running them from the gcp-org directory. If you run them from another directory, adjust your copy paths accordingly.

    cd gcp-org
  2. change to a nonproduction branch.

    git checkout -b plan
  3. Copy contents of foundation to new repo.

    cp -RT ../terraform-example-foundation/1-org/ .
  4. Rename ./envs/shared/terraform.example.tfvars to ./envs/shared/terraform.tfvars

    mv ./envs/shared/terraform.example.tfvars ./envs/shared/terraform.tfvars
  5. Update the file envs/shared/terraform.tfvars with values from your GCP environment. See the shared folder README.md for additional information on the values in the terraform.tfvars file.

  6. Un-comment the variable create_access_context_manager_access_policy = false if your organization already has an Access Context Manager Policy.

    export ORGANIZATION_ID=$(terraform -chdir="../gcp-bootstrap/envs/shared" output -json common_config | jq '.org_id' --raw-output)
    
    export ACCESS_CONTEXT_MANAGER_ID=$(gcloud access-context-manager policies list --organization ${ORGANIZATION_ID} --format="value(name)")
    
    echo "access_context_manager_policy_id = ${ACCESS_CONTEXT_MANAGER_ID}"
    
    if [ ! -z "${ACCESS_CONTEXT_MANAGER_ID}" ]; then sed -i "s=//create_access_context_manager_access_policy=create_access_context_manager_access_policy=" ./envs/shared/terraform.tfvars; fi
  7. Check if a Security Command Center Notification with the default name, scc-notify, already exists in your organization.

    export ORG_STEP_SA=$(terraform -chdir="../gcp-bootstrap/envs/shared" output -raw organization_step_terraform_service_account_email)
    
    gcloud scc notifications describe "scc-notify" --format="value(name)" --organization=${ORGANIZATION_ID} --impersonate-service-account=${ORG_STEP_SA}
  8. If the notification exists the output will be:

    organizations/ORGANIZATION_ID/notificationConfigs/scc-notify
    
  9. If the notification does not exist the output will be:

    ERROR: (gcloud.scc.notifications.describe) NOT_FOUND: Requested entity was not found.
    
  10. If the notification exists, choose a different value for the scc_notification_name variable in the ./envs/shared/terraform.tfvars file.

  11. Commit changes.

    git add .
    git commit -m 'Initialize org repo'
  12. Push your plan branch.

    git push --set-upstream origin plan
  13. Open a pull request (for GitHub) or a merge request (for GitLab) from the plan branch to the production branch and review the output.

  14. The pull request (or merge request) will trigger a Terraform Cloud speculative plan in the production environment.

  15. Review the speculative plan output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/1-shared/runs under Run List item.

  16. If the speculative plan is successful, merge the pull request in to the production branch.

  17. The merge will trigger a Terraform Cloud Plan and Apply run, that will run the plan and apply the terraform configuration for the production environment. You need to approve the apply in the Runs menu.

  18. Review apply output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/1-shared/runs under Run List item.

  19. Before moving to the next step, go back to the parent directory.

    cd ..

Deploying step 2-environments

  1. Navigate into the repo. All subsequent steps assume you are running them from the gcp-environments directory. If you run them from another directory, adjust your copy paths accordingly.

    cd gcp-environments
  2. change to a nonproduction branch.

    git checkout -b plan
  3. Copy contents of foundation to new repo.

    cp -RT ../terraform-example-foundation/2-environments/ .
  4. Rename terraform.example.tfvars to terraform.tfvars.

    mv terraform.example.tfvars terraform.tfvars
  5. Update the file with values from your GCP environment. See any of the envs folder README.md files for additional information on the values in the terraform.tfvars file.

  6. Commit changes.

    git add .
    git commit -m 'Initialize environments repo'
  7. Push your plan branch.

    git push --set-upstream origin plan
  8. Open a pull request (for GitHub) or a merge request (for GitLab) from the plan branch to the development branch and review the output.

  9. The pull request (or merge request) will trigger a Terraform Cloud speculative plan in the development environment.

  10. Review the speculative plan output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/2-development/runs under Run List item.

  11. If the speculative plan is successful, merge the pull request in to the development branch.

  12. The merge will trigger a Terraform Cloud Plan and Apply run, that will run the plan and apply the terraform configuration for the development environment. You need to approve the apply in the Runs menu.

  13. Review apply output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/2-development/runs under Run List item.

  14. If the TFC Apply is successful, you can open the pull request (or merge request) for the next environment.

  15. Open a pull request (for GitHub) or a merge request (for GitLab) from the development branch to the nonproduction branch and review the output.

  16. The pull request (or merge request) will trigger a Terraform Cloud speculative plan in the nonproduction environment.

  17. Review the speculative plan output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/2-nonproduction/runs under Run List item.

  18. If the speculative plan is successful, merge the pull request in to the nonproduction branch.

  19. The merge will trigger a Terraform Cloud Plan and Apply run, that will run the plan and apply the terraform configuration for the nonproduction environment. You need to approve the apply in the Runs menu.

  20. Review apply output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/2-nonproduction/runs under Run List item.

  21. If the TFC Apply is successful, you can open the pull request (or merge request) for the next environment.

  22. Open a pull request (for GitHub) or a merge request (for GitLab) from the nonproduction branch to the production branch and review the output.

  23. The pull request (or merge request) will trigger a Terraform Cloud speculative plan in the production environment.

  24. Review the speculative plan output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/2-production/runs under Run List item.

  25. If the speculative plan is successful, merge the pull request in to the production branch.

  26. The merge will trigger a Terraform Cloud Plan and Apply run, that will run the plan and apply the terraform configuration for the production environment. You need to approve the apply in the Runs menu.

  27. Review apply output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/2-production/runs under Run List item.

  28. You can now move to the instructions in the network stage. To use the Dual Shared VPC network mode go to Deploying step 3-networks-dual-svpc, or go to Deploying step 3-networks-hub-and-spoke to use the Hub and Spoke network mode.

  29. Before moving to the next step, go back to the parent directory.

    cd ..

Deploying step 3-networks-dual-svpc

Note: For all purposes we treat shared environment as production environment due to the possible impacts into production. So 3-production TFC workspace have a Run Trigger sourcing 3-shared TFC workspace, which means that every time you successfully run an apply job in 3-shared TFC workspace, a Plan and apply job will be triggered automatically for 3-production TFC workspace. (All the applies will continue requiring manual approvals in TFC console).

  1. Navigate into the repo. All subsequent steps assume you are running them from the gcp-networks directory. If you run them from another directory, adjust your copy paths accordingly.

    cd gcp-networks
  2. change to a nonproduction branch.

    git checkout -b plan
  3. Copy contents of foundation to new repo.

    cp -RT ../terraform-example-foundation/3-networks-dual-svpc/ .
    cp -RT ../terraform-example-foundation/policy-library/ ./policy-library
    cp ../terraform-example-foundation/build/tf-wrapper.sh .
    chmod 755 ./tf-wrapper.sh
  4. Rename common.auto.example.tfvars to common.auto.tfvars, rename shared.auto.example.tfvars to shared.auto.tfvars and rename access_context.auto.example.tfvars to access_context.auto.tfvars.

    mv common.auto.example.tfvars common.auto.tfvars
    mv shared.auto.example.tfvars shared.auto.tfvars
    mv access_context.auto.example.tfvars access_context.auto.tfvars
  5. Update the file shared.auto.tfvars with the values for the target_name_server_addresses.

  6. Update the file access_context.auto.tfvars with the organization's access_context_manager_policy_id.

    export ORGANIZATION_ID=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -json common_config | jq '.org_id' --raw-output)
    
    export ACCESS_CONTEXT_MANAGER_ID=$(gcloud access-context-manager policies list --organization ${ORGANIZATION_ID} --format="value(name)")
    
    echo "access_context_manager_policy_id = ${ACCESS_CONTEXT_MANAGER_ID}"
    
    sed -i "s/ACCESS_CONTEXT_MANAGER_ID/${ACCESS_CONTEXT_MANAGER_ID}/" ./access_context.auto.tfvars
  7. Update common.auto.tfvars file with values from your GCP environment. See any of the envs folder README.md files for additional information on the values in the common.auto.tfvars file.

  8. You must add your user email in the variable perimeter_additional_members to be able to see the resources created in the restricted project.

  9. You must manually plan and apply the shared environment from your (only once) since the development, nonproduction and production environments depend on it.

  10. In order to manually run the apply for shared workspace from your local we need to temporary unset the TFC backend by renaming envs/shared/backend.tf to envs/shared/backend.tf.temporary_disabled.

    mv envs/shared/backend.tf envs/shared/backend.tf.temporary_disabled
  11. Use terraform output to get the CI/CD project ID and the networks step Terraform Service Account from gcp-bootstrap output.

  12. The CI/CD project ID will be used in the validation of the Terraform configuration

    export CICD_PROJECT_ID=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw cicd_project_id)
    echo ${CICD_PROJECT_ID}
  13. Use terraform output to get the name of the TFC organization from gcp-bootstrap output and export it as environment variables. The TFC organization will be used during the manual apply process by tfe_outputs resource in order to grab the outputs from previous steps.

    export TF_VAR_tfc_org_name=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw tfc_org_name)
    echo "TFC Organization = ${TF_VAR_tfc_org_name}"
  14. The networks step Terraform Service Account will be used for Service Account impersonation in the following steps. An environment variable GOOGLE_IMPERSONATE_SERVICE_ACCOUNT will be set with the Terraform Service Account to enable impersonation.

    export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw networks_step_terraform_service_account_email)
    echo ${GOOGLE_IMPERSONATE_SERVICE_ACCOUNT}
  15. Run init and plan and review output for environment shared.

    ./tf-wrapper.sh init shared
    ./tf-wrapper.sh plan shared
  16. To use the validate option of the tf-wrapper.sh script, please follow the instructions to install the terraform-tools component.

  17. Run validate and check for violations.

    ./tf-wrapper.sh validate shared $(pwd)/policy-library ${CICD_PROJECT_ID}
  18. Run apply shared.

    ./tf-wrapper.sh apply shared

    Note: Because we are running an apply locally instead of in the TFC workspace, this apply to shared won't be triggerring the TFC Run Trigger for 3-production TFC workspace.

  19. In order to set the TFC backend for shared workspace we now can rename envs/shared/backend.tf.temporary_disabled to envs/shared/backend.tf and run terraform init. When you're prompted, agree to copy Terraform state to Terraform Cloud.

    cd envs/shared/
    mv backend.tf.temporary_disabled backend.tf
    terraform init
    cd ../..
  20. Commit changes

    git add .
    git commit -m 'Initialize networks repo'
  21. Push your plan branch.

    git push --set-upstream origin plan
  22. Open a pull request (for GitHub) or a merge request (for GitLab) from the plan branch to the development branch and review the output.

  23. The pull request (or merge request) will trigger a Terraform Cloud speculative plan in the development environment.

  24. Review the speculative plan output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/3-development/runs under Run List item.

  25. If the speculative plan is successful, merge the pull request in to the development branch.

  26. The merge will trigger a Terraform Cloud Plan and Apply run, that will run the plan and apply the terraform configuration for the development environment. You need to approve the apply in the Runs menu.

  27. Review apply output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/3-development/runs under Run List item.

  28. If the TFC Apply is successful, you can open the pull request (or merge request) for the next environment.

  29. Open a pull request (for GitHub) or a merge request (for GitLab) from the development branch to the nonproduction branch and review the output.

  30. The pull request (or merge request) will trigger a Terraform Cloud speculative plan in the nonproduction environment.

  31. Review the speculative plan output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/3-nonproduction/runs under Run List item.

  32. If the speculative plan is successful, merge the pull request in to the nonproduction branch.

  33. The merge will trigger a Terraform Cloud Plan and Apply run, that will run the plan and apply the terraform configuration for the nonproduction environment. You need to approve the apply in the Runs menu.

  34. Review apply output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/3-nonproduction/runs under Run List item.

  35. If the TFC Apply is successful, you can open the pull request (or merge request) for the next environment.

  36. Open a pull request (for GitHub) or a merge request (for GitLab) from the nonproduction branch to the production branch and review the output.

  37. The pull request (or merge request) will trigger a Terraform Cloud speculative plan in the production environment.

  38. Review the speculative plan output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/3-production/runs under Run List item.

  39. If the speculative plan is successful, merge the pull request in to the production branch.

  40. The merge will trigger a Terraform Cloud Plan and Apply run, that will run the plan and apply the terraform configuration for the production environment. You need to approve the apply in the Runs menu.

  41. Review apply output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/3-production/runs under Run List item.

  42. Before executing the next steps, unset the GOOGLE_IMPERSONATE_SERVICE_ACCOUNT environment variable.

    unset GOOGLE_IMPERSONATE_SERVICE_ACCOUNT
  43. Before moving to the next step, go back to the parent directory.

    cd ..
  44. You can now move to the instructions in the 4-projects stage.

Deploying step 3-networks-hub-and-spoke

Note: For all purposes we treat shared environment as production environment due to the possible impacts into production. So 3-production TFC workspace have a Run Trigger sourcing 3-shared TFC workspace, which means that every time you successfully run an apply job in 3-shared TFC workspace, a Plan and apply job will be triggered automatically for 3-production TFC workspace. (All the applies will continue requiring manual approvals in TFC console).

  1. Navigate into the repo. All subsequent steps assume you are running them from the gcp-networks directory. If you run them from another directory, adjust your copy paths accordingly.

    cd gcp-networks
  2. change to a nonproduction branch.

    git checkout -b plan
  3. Copy contents of foundation to new repo.

    cp -RT ../terraform-example-foundation/3-networks-hub-and-spoke/ .
    cp -RT ../terraform-example-foundation/policy-library/ ./policy-library
    cp ../terraform-example-foundation/build/tf-wrapper.sh .
    chmod 755 ./tf-wrapper.sh
  4. Rename common.auto.example.tfvars to common.auto.tfvars, rename shared.auto.example.tfvars to shared.auto.tfvars and rename access_context.auto.example.tfvars to access_context.auto.tfvars.

    mv common.auto.example.tfvars common.auto.tfvars
    mv shared.auto.example.tfvars shared.auto.tfvars
    mv access_context.auto.example.tfvars access_context.auto.tfvars
  5. Update common.auto.tfvars file with values from your GCP environment. See any of the envs folder README.md files for additional information on the values in the common.auto.tfvars file.

  6. You must add your user email in the variable perimeter_additional_members to be able to see the resources created in the restricted project.

  7. You must manually plan and apply the shared environment (only once) since the development, nonproduction and production environments depend on it.

  8. In order to manually run the apply for shared workspace from your local we need to temporary unset the TFC backend by renaming envs/shared/backend.tf to envs/shared/backend.tf.temporary_disabled.

    mv envs/shared/backend.tf envs/shared/backend.tf.temporary_disabled
  9. Use terraform output to get the CI/CD project ID and the networks step Terraform Service Account from gcp-bootstrap output.

  10. The CI/CD project ID will be used in the validation of the Terraform configuration

    export CICD_PROJECT_ID=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw cicd_project_id)
    echo ${CICD_PROJECT_ID}
  11. Use terraform output to get the name of the TFC organization from gcp-bootstrap output and export it as environment variables. The TFC organization will be used during the manual apply process by tfe_outputs resource in order to grab the outputs from previous steps.

    export TF_VAR_tfc_org_name=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw tfc_org_name)
    echo "TFC Organization = ${TF_VAR_tfc_org_name}"
  12. The networks step Terraform Service Account will be used for Service Account impersonation in the following steps. An environment variable GOOGLE_IMPERSONATE_SERVICE_ACCOUNT will be set with the Terraform Service Account to enable impersonation.

    export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw networks_step_terraform_service_account_email)
    echo ${GOOGLE_IMPERSONATE_SERVICE_ACCOUNT}
  13. Run init and plan and review output for environment shared.

    ./tf-wrapper.sh init shared
    ./tf-wrapper.sh plan shared
  14. To use the validate option of the tf-wrapper.sh script, please follow the instructions to install the terraform-tools component.

  15. Run validate and check for violations.

    ./tf-wrapper.sh validate shared $(pwd)/policy-library ${CICD_PROJECT_ID}
  16. Run apply shared.

    ./tf-wrapper.sh apply shared

    Note: Because we are running an apply locally instead of in the TFC workspace, this apply to shared won't be triggerring the TFC Run Trigger for 3-production TFC workspace.

  17. In order to set the TFC backend for shared workspace we now can rename envs/shared/backend.tf.temporary_disabled to envs/shared/backend.tf and run terraform init. When you're prompted, agree to copy Terraform state to Terraform Cloud.

    cd envs/shared/
    mv backend.tf.temporary_disabled backend.tf
    terraform init
    cd ../..
  18. Commit changes

    git add .
    git commit -m 'Initialize networks repo'
  19. Push your plan branch.

    git push --set-upstream origin plan
  20. Open a pull request (for GitHub) or a merge request (for GitLab) from the plan branch to the development branch and review the output.

  21. The pull request (or merge request) will trigger a Terraform Cloud speculative plan in the development environment.

  22. Review the speculative plan output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/3-development/runs under Run List item.

  23. If the speculative plan is successful, merge the pull request in to the development branch.

  24. The merge will trigger a Terraform Cloud Plan and Apply run, that will run the plan and apply the terraform configuration for the development environment. You need to approve the apply in the Runs menu.

  25. Review apply output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/3-development/runs under Run List item.

  26. If the TFC Apply is successful, you can open the pull request (or merge request) for the next environment.

  27. Open a pull request (for GitHub) or a merge request (for GitLab) from the development branch to the nonproduction branch and review the output.

  28. The pull request (or merge request) will trigger a Terraform Cloud speculative plan in the nonproduction environment.

  29. Review the speculative plan output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/3-nonproduction/runs under Run List item.

  30. If the speculative plan is successful, merge the pull request in to the nonproduction branch.

  31. The merge will trigger a Terraform Cloud Plan and Apply run, that will run the plan and apply the terraform configuration for the nonproduction environment. You need to approve the apply in the Runs menu.

  32. Review apply output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/3-nonproduction/runs under Run List item.

  33. If the TFC Apply is successful, you can open the pull request (or merge request) for the next environment.

  34. Open a pull request (for GitHub) or a merge request (for GitLab) from the nonproduction branch to the production branch and review the output.

  35. The pull request (or merge request) will trigger a Terraform Cloud speculative plan in the production environment.

  36. Review the speculative plan output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/3-production/runs under Run List item.

  37. If the speculative plan is successful, merge the pull request in to the production branch.

  38. The merge will trigger a Terraform Cloud Plan and Apply run, that will run the plan and apply the terraform configuration for the production environment. You need to approve the apply in the Runs menu.

  39. Review apply output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/3-production/runs under Run List item.

  40. Before executing the next steps, unset the GOOGLE_IMPERSONATE_SERVICE_ACCOUNT environment variable.

    unset GOOGLE_IMPERSONATE_SERVICE_ACCOUNT
  41. Before moving to the next step, go back to the parent directory.

    cd ..
  42. You can now move to the instructions in the 4-projects stage.

Deploying step 4-projects

Note: For all purposes we treat shared environment as production environment due to the possible impacts into production. So 4-<business_unit>-production TFC workspace have a Run Trigger sourcing 4-<business_unit>-shared TFC workspace, which means that every time you successfully run an apply job in 4-<business_unit>-shared TFC workspace, a Plan and apply job will be triggered automatically for 4-<business_unit>-production TFC workspace. (All the applies will continue requiring manual approvals in TFC console).

  1. Navigate into the repo. All subsequent steps assume you are running them from the gcp-projects directory. If you run them from another directory, adjust your copy paths accordingly.

    cd gcp-projects
  2. change to a nonproduction branch.

    git checkout -b plan
  3. Copy contents of foundation to new repo.

    cp -RT ../terraform-example-foundation/4-projects/ .
    cp -RT ../terraform-example-foundation/policy-library/ ./policy-library
    cp ../terraform-example-foundation/build/tf-wrapper.sh .
    chmod 755 ./tf-wrapper.sh
  4. Rename auto.example.tfvars files to auto.tfvars.

    mv common.auto.example.tfvars common.auto.tfvars
    mv shared.auto.example.tfvars shared.auto.tfvars
    mv development.auto.example.tfvars development.auto.tfvars
    mv nonproduction.auto.example.tfvars nonproduction.auto.tfvars
    mv production.auto.example.tfvars production.auto.tfvars
  5. See any of the envs folder README.md files for additional information on the values in the common.auto.tfvars, development.auto.tfvars, nonproduction.auto.tfvars, and production.auto.tfvars files.

  6. See any of the shared folder README.md files for additional information on the values in the shared.auto.tfvars file.

  7. You need to manually plan and apply only once the business_unit_1/shared and business_unit_2/shared environments since development, nonproduction, and production depend on them.

  8. In order to manually run the apply for shared workspace from your local we need to temporary unset the TFC backend by renaming envs/shared/backend.tf to envs/shared/backend.tf.temporary_disabled.

    mv business_unit_1/shared/backend.tf business_unit_1/shared/backend.tf.temporary_disabled
    mv business_unit_2/shared/backend.tf business_unit_2/shared/backend.tf.temporary_disabled
  9. Use terraform output to get the CI/CD project ID and the projects step Terraform Service Account from gcp-bootstrap output.

  10. The CI/CD project ID will be used in the validation of the Terraform configuration

    export CICD_PROJECT_ID=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw cicd_project_id)
    echo ${CICD_PROJECT_ID}
  11. Use terraform output to get the name of the TFC organization from gcp-bootstrap output and export it as environment variables. The TFC organization will be used during the manual apply process by tfe_outputs resource in order to grab the outputs from previous steps.

    export TF_VAR_tfc_org_name=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw tfc_org_name)
    echo "TFC Organization = ${TF_VAR_tfc_org_name}"
  12. The projects step Terraform Service Account will be used for Service Account impersonation in the following steps. An environment variable GOOGLE_IMPERSONATE_SERVICE_ACCOUNT will be set with the Terraform Service Account to enable impersonation.

    export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=$(terraform -chdir="../gcp-bootstrap/envs/shared/" output -raw projects_step_terraform_service_account_email)
    echo ${GOOGLE_IMPERSONATE_SERVICE_ACCOUNT}
  13. Run init and plan and review output for environment shared.

    ./tf-wrapper.sh init shared
    ./tf-wrapper.sh plan shared
  14. To use the validate option of the tf-wrapper.sh script, please follow the instructions to install the terraform-tools component.

  15. Run validate and check for violations.

    ./tf-wrapper.sh validate shared $(pwd)/policy-library ${CICD_PROJECT_ID}
  16. Run apply shared.

    ./tf-wrapper.sh apply shared

    Note: Because we are running an apply locally instead of in the TFC workspace, this apply to shared won't be triggerring the TFC Run Trigger for 4-<business_unit>-production TFC workspace.

  17. In order to set the TFC backend for shared workspace we now can rename envs/shared/backend.tf.temporary_disabled to envs/shared/backend.tf and run terraform init. When you're prompted, agree to copy Terraform state to Terraform Cloud.

    mv business_unit_1/shared/backend.tf.temporary_disabled business_unit_1/shared/backend.tf
    mv business_unit_2/shared/backend.tf.temporary_disabled business_unit_2/shared/backend.tf
    terraform -chdir="business_unit_1/shared/" init
    terraform -chdir="business_unit_2/shared/" init
  18. Commit changes

    git add .
    git commit -m 'Initialize networks repo'
  19. Push your plan branch.

    git push --set-upstream origin plan
  20. Open a pull request (for GitHub) or a merge request (for GitLab) from the plan branch to the development branch and review the output.

  21. The pull request (or merge request) will trigger a Terraform Cloud speculative plan in the development environment.

  22. Review the speculative plan output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/4-development/runs under Run List item.

  23. If the speculative plan is successful, merge the pull request in to the development branch.

  24. The merge will trigger a Terraform Cloud Plan and Apply run, that will run the plan and apply the terraform configuration for the development environment. You need to approve the apply in the Runs menu.

  25. Review apply output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/4-development/runs under Run List item.

  26. If the TFC Apply is successful, you can open the pull request (or merge request) for the next environment.

  27. Open a pull request (for GitHub) or a merge request (for GitLab) from the development branch to the nonproduction branch and review the output.

  28. The pull request (or merge request) will trigger a Terraform Cloud speculative plan in the nonproduction environment.

  29. Review the speculative plan output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/4-nonproduction/runs under Run List item.

  30. If the speculative plan is successful, merge the pull request in to the nonproduction branch.

  31. The merge will trigger a Terraform Cloud Plan and Apply run, that will run the plan and apply the terraform configuration for the nonproduction environment. You need to approve the apply in the Runs menu.

  32. Review apply output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/4-nonproduction/runs under Run List item.

  33. If the TFC Apply is successful, you can open the pull request (or merge request) for the next environment.

  34. Open a pull request (for GitHub) or a merge request (for GitLab) from the nonproduction branch to the production branch and review the output.

  35. The pull request (or merge request) will trigger a Terraform Cloud speculative plan in the production environment.

  36. Review the speculative plan output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/4-production/runs under Run List item.

  37. If the speculative plan is successful, merge the pull request in to the production branch.

  38. The merge will trigger a Terraform Cloud Plan and Apply run, that will run the plan and apply the terraform configuration for the production environment. You need to approve the apply in the Runs menu.

  39. Review apply output in Terraform Cloud https://app.terraform.io/app/TFC-ORGANIZATION-NAME/workspaces/4-production/runs under Run List item.

  40. Unset the GOOGLE_IMPERSONATE_SERVICE_ACCOUNT environment variable.

    unset GOOGLE_IMPERSONATE_SERVICE_ACCOUNT