diff --git a/0-bootstrap/README-Jenkins.md b/0-bootstrap/README-Jenkins.md index 7daef6261..0c8d25be3 100644 --- a/0-bootstrap/README-Jenkins.md +++ b/0-bootstrap/README-Jenkins.md @@ -1,30 +1,31 @@ # 0-bootstrap - deploying a Jenkins-compatible environment -The purpose of this step is to bootstrap a GCP organization, creating all the required resources & permissions to start using the Cloud Foundation Toolkit (CFT). This step also guides you on how to configure a CICD project to host a Jenkins Agent, which connects to your existing Jenkins Controller infrastructure & your own Git repos (which might live on-prem). The Jenkins Agent will run [CI/CD Pipelines](/docs/GLOSSARY.md#foundation-cicd-pipeline) for foundations code in subsequent stages. +The purpose of this step is to bootstrap a GCP organization, creating all the required resources & permissions to start using the Cloud Foundation Toolkit (CFT). This step also guides you on how to configure a CI/CD project to host a Jenkins Agent, which connects to your existing Jenkins Controller infrastructure & your own Git repos (which might live on-prem). The Jenkins Agent will run [CI/CD Pipelines](../docs/GLOSSARY.md#foundation-cicd-pipeline) for foundations code in subsequent stages. -Another CICD option is to use Cloud Build & Cloud Source Repos. If you don't have a Jenkins implementation and don't want one, then we recommend you to [use the Cloud Build module](./README.md) instead. +Another CI/CD option is to use Cloud Build & Cloud Source Repos. If you don't have a Jenkins implementation and don't want one, then we recommend you to [use the Cloud Build module](./README.md#deploying-with-cloud-build) instead. ## Overview -The objective of the instructions below is to configure the infrastructure that allows you to run CICD deployments for the next stages (`1-org, 2-environments, 3-networks, 4-projects`) using Jenkins. The infrastructure consists in two Google Cloud Platform projects (`prj-b-seed` and `prj-b-cicd`) and VPN configuration to connect to your on-prem environment. +The objective of the instructions below is to configure the infrastructure that allows you to run CI/CD deployments for the next stages (`1-org, 2-environments, 3-networks, 4-projects`) using Jenkins. The infrastructure consists in two Google Cloud Platform projects (`prj-b-seed` and `prj-b-cicd`) and VPN configuration to connect to your on-prem environment. It is a best practice to have two separate projects here (`prj-b-seed` and `prj-b-cicd`) for separation of concerns. On one hand, `prj-b-seed` stores terraform state and has the Service Account able to create / modify infrastructure. On the other hand, the deployment of that infrastructure is coordinated by Jenkins, which is implemented in `prj-b-cicd` and connected to your Controller on-prem. **After following the instructions below, you will have:** + - The `prj-b-seed` project, which contains: - - Terraform state bucket - - Custom Service Account used by Terraform to create new resources in GCP + - Terraform state bucket. + - Custom Service Accounts used by Terraform to create new resources in GCP. - The `prj-b-cicd` project, which contains: - GCE Instance for the Jenkins Agent, connected to your current Jenkins Controller using SSH. - - VPC to connect the Jenkins GCE Instance to - - FW rules to allow communication over port 22 - - VPN connection with on-prem (or where ever your Jenkins Controller is located) + - VPC to connect the Jenkins GCE Instance to. + - FW rules to allow communication over port 22. + - VPN connection with on-prem (or where ever your Jenkins Controller is located). - Custom service account `sa-jenkins-agent-gce@prj-b-cicd-xxxx.iam.gserviceaccount.com` for the GCE instance. - - This service account is granted the access to generate tokens on the Terraform custom service account in the `prj-b-seed` project + - This service account is granted the access to generate tokens on the Terraform custom service accounts in the `prj-b-seed` project. -- **Note: these instructions do not indicate how to create a Jenkins Controller.** To deploy a Jenkins Controller, you should follow [Jenkins Architecture](https://www.jenkins.io/doc/book/architecting-for-scale/) recommendations. +- **Note: these instructions do not indicate how to create a Jenkins Controller.** To deploy a Jenkins Controller, you should follow [Jenkins Architecture](https://www.jenkins.io/doc/book/scaling/architecting-for-scale/) recommendations. -**If you don't have a Jenkins implementation and don't want one**, then we recommend you to [use the Cloud Build module](./README.md) instead. +**If you don't have a Jenkins implementation and don't want one**, then we recommend you to [use the Cloud Build module](./README.md#deploying-with-cloud-build) instead. ## Requirements @@ -38,168 +39,923 @@ commands. The `-T` flag is needed for Linux, but causes problems for MacOS. ## Instructions You arrived to these instructions because you are using the `jenkins_bootstrap` to run the 0-bootstrap step instead of `cloudbuild_bootstrap`. Please follow the indications below: + - Make sure you cover all the [requirements](./modules/jenkins-agent/README.md#Requirements) of Software, Infrastructure and Permissions before following the instructions below. ### I. Setup your environment - - Required information: - - Access to the Jenkins Controller host to run `ssh-keygen` command - - Access to the Jenkins Controller Web UI - - [SSH Agent Jenkins plugin](https://plugins.jenkins.io/ssh-agent) installed in your Jenkins Controller - - Private IP address for the Jenkins Agent: usually assigned by your network administrator. You will use this IP for the GCE instance that will be created in the `prj-b-cicd` GCP Project in step [II. Create the SEED and CICD projects using Terraform](#II-Create-the-SEED-and-CICD-projects-using-Terraform). - - Access to create five Git repositories, one for each directory in this [monorepo](https://github.com/terraform-google-modules/terraform-example-foundation) (`0-bootstrap, 1-org, 2-environments, 3-networks, 4-projects`). These are usually private repositories that might be on-prem. + +- Required information: + - Access to the Jenkins Controller host to run `ssh-keygen` command + - Access to the Jenkins Controller Web UI + - [SSH Agent Jenkins plugin](https://plugins.jenkins.io/ssh-agent) installed in your Jenkins Controller + - Private IP address for the Jenkins Agent: usually assigned by your network administrator. You will use this IP for the GCE instance that will be created in the `prj-b-cicd` GCP Project in step [II. Create the SEED and CI/CD projects using Terraform](#ii-create-the-seed-and-cicd-projects-using-terraform). + - Access to create five Git repositories, one for each directory in this [monorepo](https://github.com/terraform-google-modules/terraform-example-foundation) (`0-bootstrap, 1-org, 2-environments, 3-networks, 4-projects`). These are usually private repositories that might be on-prem. 1. Generate a SSH key pair. In the Jenkins Controller host, use the `ssh-keygen` command to generate a SSH key pair. - You will need this key pair to enable authentication between the Controller and Agent. Although the key pair can be generated in any linux machine, it is recommended not to copy the secret private key from one host to another, so you probably want to do this in the Jenkins Controller host command line. - - - Note the `ssh-keygen` command uses the `-N` option to protect the private key with a password. In this example, we are using `-N "my-password"`. This is important because you will need both, the private key and the password when configuring the SSH Agent in you Jenkins Controller Web UI. - ``` - SSH_LOCAL_CONFIG_DIR="$HOME/.ssh" - JENKINS_USER="jenkins" - JENKINS_AGENT_NAME="AgentGCE1" - SSH_KEY_FILE_PATH="$SSH_LOCAL_CONFIG_DIR/$JENKINS_USER-${JENKINS_AGENT_NAME}_rsa" - - mkdir "$SSH_LOCAL_CONFIG_DIR" - ssh-keygen -t rsa -m PEM -N "my-password" -C $JENKINS_USER -f $SSH_KEY_FILE_PATH - cat $SSH_KEY_FILE_PATH - ``` - - - You will see an output similar to this: - ``` - -----BEGIN RSA PRIVATE KEY----- - copy your private key - from BEGIN to END - And configure a new - Jenkins Agent in the Web UI - -----END RSA PRIVATE KEY----- - ``` + - Note the `ssh-keygen` command uses the `-N` option to protect the private key with a password. In this example, we are using `-N "my-password"`. This is important because you will need both, the private key and the password when configuring the SSH Agent in you Jenkins Controller Web UI. + + ```bash + SSH_LOCAL_CONFIG_DIR="$HOME/.ssh" + JENKINS_USER="jenkins" + JENKINS_AGENT_NAME="AgentGCE1" + SSH_KEY_FILE_PATH="$SSH_LOCAL_CONFIG_DIR/$JENKINS_USER-${JENKINS_AGENT_NAME}_rsa" + mkdir "$SSH_LOCAL_CONFIG_DIR" + ssh-keygen -t rsa -m PEM -N "my-password" -C $JENKINS_USER -f $SSH_KEY_FILE_PATH + cat $SSH_KEY_FILE_PATH + ``` + + - You will see an output similar to this: + + ```text + -----BEGIN RSA PRIVATE KEY----- + copy your private key + from BEGIN to END + And configure a new + Jenkins Agent in the Web UI + -----END RSA PRIVATE KEY----- + ``` 1. Configure a new SSH Jenkins Agent in the Jenkins Controller’s Web UI. You need the following information: - - [SSH Agent Jenkins plugin](https://plugins.jenkins.io/ssh-agent/) installed in your Controller - - SSH private key you just generated in the previous step - - Passphrase that protects the private key (the one you used in the `-N` option) - - Jenkins Agent’s private IP address (usually assigned by your Network Administrator. In the provided examples this IP is "172.16.1.6"). This private IP will be reachable through the VPN connection that you will create later. + - [SSH Agent Jenkins plugin](https://plugins.jenkins.io/ssh-agent/) installed in your Controller + - SSH private key you just generated in the previous step + - Passphrase that protects the private key (the one you used in the `-N` option) + - Jenkins Agent’s private IP address (usually assigned by your Network Administrator. In the provided examples this IP is "172.16.1.6"). This private IP will be reachable through the VPN connection that you will create later. 1. Create five individual Git repositories in your Git server (This might be a task delegated to your infrastructure team) - - Note that although this infrastructure code is distributed to you as a [monorepo](https://github.com/terraform-google-modules/terraform-example-foundation), you will store the code in five different repositories, one for each directory: - ``` - ./0-bootstrap - ./1-org - ./2-environments - ./3-networks - ./4-projects - ``` - - For simplicity, let's name your five repositories as follows: - ``` - YOUR_NEW_REPO-0-bootstrap - YOUR_NEW_REPO-1-org - YOUR_NEW_REPO-2-environments - YOUR_NEW_REPO-3-networks - YOUR_NEW_REPO-4-projects - ``` - - **Note:** Towards the end of these instructions, you will configure your Jenkins Controller with **new automatic pipelines only for the following repositories:** - ``` - YOUR_NEW_REPO-1-org - YOUR_NEW_REPO-2-environments - YOUR_NEW_REPO-3-networks - YOUR_NEW_REPO-4-projects - ``` - - **Note: there is no automatic pipeline needed for `YOUR_NEW_REPO-0-bootstrap`** - - In this 0-bootstrap section we only work with your new repository that is a copy of the directory `./0-bootstrap` (`YOUR_NEW_REPO-0-bootstrap`) - -1. Clone this mono-repository with `git clone https://github.com/terraform-google-modules/terraform-example-foundation` -1. Clone the repository you created to host the `0-bootstrap` directory with `git clone ` -1. Navigate into the freshly cloned repo `cd ` and change to a new branch `git checkout -b my-0-bootstrap` -1. Copy contents of foundation to new repo `cp -RT ../terraform-example-foundation/0-bootstrap/ .` (modify accordingly based on your current directory). + - Note that although this infrastructure code is distributed to you as a [monorepo](https://github.com/terraform-google-modules/terraform-example-foundation), you will store the code in five different repositories, one for each directory: + + ```text + ./0-bootstrap + ./1-org + ./2-environments + ./3-networks + ./4-projects + ``` + + - For simplicity, let's name your five repositories as follows: + + ```text + YOUR_NEW_REPO-0-bootstrap + YOUR_NEW_REPO-1-org + YOUR_NEW_REPO-2-environments + YOUR_NEW_REPO-3-networks + YOUR_NEW_REPO-4-projects + ``` + + - **Note:** Towards the end of these instructions, you will configure your Jenkins Controller with **new automatic pipelines only for the following repositories:** + + ```text + YOUR_NEW_REPO-1-org + YOUR_NEW_REPO-2-environments + YOUR_NEW_REPO-3-networks + YOUR_NEW_REPO-4-projects + ``` + + - **Note: there is no automatic pipeline needed for `YOUR_NEW_REPO-0-bootstrap`** + - In this 0-bootstrap section we only work with your new repository that is a copy of the directory `./0-bootstrap` (`YOUR_NEW_REPO-0-bootstrap`) + +1. Clone this mono-repository with: + + ```bash + git clone https://github.com/terraform-google-modules/terraform-example-foundation + ``` + +1. Clone the repository you created to host the `0-bootstrap` directory with: + + ```bash + git clone 0-bootstrap + ``` + +1. Navigate into the freshly cloned repo and change to a new branch + + ```bash + cd 0-bootstrap + git checkout -b my-0-bootstrap + ``` + +1. Copy contents of foundation to new repo (modify accordingly based on your current directory). + + ```bash + cp -RT ../terraform-example-foundation/0-bootstrap/ . + ``` + 1. Activate the Jenkins module and disable the Cloud Build module. This implies manually editing the following files: - 1. Comment-out the `cloudbuild_bootstrap` module in `./main.tf` - 1. Comment-out the `cloudbuild_bootstrap` outputs in `./outputs.tf` - 1. Un-comment the `jenkins_bootstrap` module in `./main.tf` - 1. Un-comment the `jenkins_bootstrap` variables in `./variables.tf` - 1. Un-comment the `jenkins_bootstrap` outputs in `./outputs.tf` + 1. Rename file `./cb.tf` to `./cb.tf.example` + + ```bash + mv ./cb.tf ./cb.tf.example + ``` + + 1. Comment-out the `cloudbuild_bootstrap` outputs in `./outputs.tf` + 1. Rename file `./jenkins.tf.example` to `./jenkins.tf` + + ```bash + mv ./jenkins.tf.example ./jenkins.tf + ``` + + 1. Un-comment the `jenkins_bootstrap` variables in `./variables.tf` + 1. Un-comment the `jenkins_bootstrap` outputs in `./outputs.tf` 1. Rename `terraform.example.tfvars` to `terraform.tfvars` and update the file with values from your environment. - - One of the value to supply (variable `jenkins_agent_gce_ssh_pub_key`) is the **public SSH key** you generated in the first step. - - **Note: this is not the secret private key**. The public SSH key can be in your repository code. - 1. Show the public key using `cat "${SSH_KEY_FILE_PATH}.pub"`, you will copy / paste it in the `terraform.tfvars` file (variable `jenkins_agent_gce_ssh_pub_key`). - 1. Provide the rest of the values needed in `terraform.tfvars` -1. Commit changes with `git add .` and `git commit -m 'Your message - Bootstrap configuration using jenkins_module'` -1. Push my-0-bootstrap branch to your repository YOUR_NEW_REPO-0-bootstrap with `git push --set-upstream origin my-0-bootstrap` + ```bash + mv ./terraform.example.tfvars ./terraform.tfvars + ``` + +1. One of the value to supply (variable `jenkins_agent_gce_ssh_pub_key`) is the **public SSH key** you generated in the first step. + - **Note: this is not the secret private key**. The public SSH key can be in your repository code. +1. Show the public key using + + ```bash + cat "${SSH_KEY_FILE_PATH}.pub" + ``` + +1. you will copy / paste it in the `terraform.tfvars` file (variable `jenkins_agent_gce_ssh_pub_key`). +1. Provide the rest of the values needed in `terraform.tfvars` + +1. Commit changes: -### II. Create the SEED and CICD projects using Terraform + ```bash + git add . + git commit -m 'Your message - Bootstrap configuration using jenkins_module' + ``` + +1. Push my-0-bootstrap branch to your repository YOUR_NEW_REPO-0-bootstrap with + + ```bash + git push --set-upstream origin my-0-bootstrap + ``` + +### II. Create the SEED and CI/CD projects using Terraform - Required information: - - Terraform version 0.13.7 - See [Requirements](#requirements) section for more details. + - Terraform version 1.0.0 - See [Requirements](#requirements) section for more details. - The `terraform.tfvars` file with all the necessary values. -1. Get the appropriate credentials: run the following command with an account that has the [necessary permissions](./modules/jenkins-agent/README.md#Permissions). - ``` - gcloud auth application-default login - ``` - 1. Open the link in your browser and accept. +1. Get the appropriate credentials: run the following command with an account that has the [necessary permissions](./modules/jenkins-agent/README.md#permissions). + + ```bash + gcloud auth application-default login + ``` + +1. Open the link in your browser and accept. 1. Run terraform commands. - - After the credentials are configured, we will create the `prj-b-seed` project (which contains the GCS state bucket and Terraform custom service account) and the `prj-b-cicd` project (which contains the Jenkins Agent, its custom service account and where we will add VPN configuration) - - **WARNING: Make sure you have commented-out the `cloudbuild_bootstrap` module and enabled the `jenkins_bootstrap` module in the `./main.tf` file** - - **Use Terraform 0.13.7** to run the terraform script with the commands below - ``` - terraform init - terraform plan - terraform apply - ``` - - The Terraform script will take about 10 to 15 minutes. Once it finishes, note that communication between on-prem and the `prj-b-cicd` project won’t happen yet - you will configure the VPN network connectivity in step [III. Create VPN connection](#III-Create-VPN-connection). + - After the credentials are configured, we will create the `prj-b-seed` project (which contains the GCS state bucket and Terraform custom service account) and the `prj-b-cicd` project (which contains the Jenkins Agent, its custom service account and where we will add VPN configuration) + - **WARNING: Make sure you have commented-out the `cloudbuild_bootstrap` module and enabled the `jenkins_bootstrap` module in the `./main.tf` file** + - **Use Terraform 1.0.0** to run the terraform script with the commands below + + ```bash + terraform init + terraform plan + terraform apply + ``` + + - The Terraform script will take about 10 to 15 minutes. Once it finishes, note that communication between on-prem and the `prj-b-cicd` project won’t happen yet - you will configure the VPN network connectivity in step [III. Create VPN connection](#iii-configure-vpn-connection). 1. Move Terraform State to the GCS bucket created in the Seed Project - 1. Run `terraform output gcs_bucket_tfstate` to get the tfstate bucket name 1. Rename `backend.tf.example` to `backend.tf` - 1. Edit file `backend.tf` and replace `UPDATE_ME` with the tfstate bucket name + + ```bash + mv backend.tf.example backend.tf + ``` + + 1. Update `backend.tf` with your tfstate bucket name + + ```bash + export backend_bucket=$(terraform output -raw gcs_bucket_tfstate) + echo "backend_bucket = ${backend_bucket}" + for i in `find -name 'backend.tf'`; do sed -i "s/UPDATE_ME/${backend_bucket}/" $i; done + ``` 1. Re-run `terraform init` and agree to copy state to gcs when prompted - - (Optional) Run `terraform apply` to verify state is configured correctly. You can confirm the terraform state is now in that bucket by visiting the bucket url in your Seed Project. -1. Commit changes with `git add backend.tf` and `git commit -m 'Your message - Terraform Backend configuration using GCS'` -1. Push my-0-bootstrap branch to your repository YOUR_NEW_REPO-0-bootstrap with `git push` + ```bash + terraform init + ``` + + - (Optional) Run `terraform apply` to verify state is configured correctly. You can confirm the terraform state is now in that bucket by visiting the bucket url in your Seed Project. + +1. Commit changes + + ```bash + git add backend.tf + git commit -m 'Your message - Terraform Backend configuration using GCS' + ``` + +1. Push my-0-bootstrap branch to your repository YOUR_NEW_REPO-0-bootstrap with + + ```bash + git push + ``` ### III. Configure VPN connection Here you will configure a VPN Network tunnel to enable connectivity between the `prj-b-cicd` project and your on-prem environment. Learn more about [a VPN tunnel in GCP](https://cloud.google.com/network-connectivity/docs/vpn/how-to). + - Required information: - - On-prem VPN public IP Address - - Jenkins Controller’s network CIDR (the example code uses "10.1.0.0/24") - - Jenkins Agent network CIDR (the example code uses "172.16.1.0/24") - - VPN PSK (pre-shared secret key) + - On-prem VPN public IP Address + - Jenkins Controller’s network CIDR (the example code uses "10.1.0.0/24") + - Jenkins Agent network CIDR (the example code uses "172.16.1.0/24") + - VPN PSK (pre-shared secret key) 1. Check in the `prj-b-cicd` project for the VPN gateway static IP addresses which have been reserved. These addresses are required by the Network Administrator for the configuration of the on-prem side of the VPN tunnels to GCP. - - - Assuming your network administrator already configured the on-prem end of the VPN, the CICD end of the VPN might show the message `First Handshake` for around 5 minutes. - - When the VPN is ready, the status will show `Tunnel is up and running`. At this point, your Jenkins Controller (on-prem) and Jenkins Agent (in `prj-b-cicd` project) must have network connectivity through the VPN. + 1. Assuming your network administrator already configured the on-prem end of the VPN, the CI/CD end of the VPN might show the message `First Handshake` for around 5 minutes. + 1. When the VPN is ready, the status will show `Tunnel is up and running`. At this point, your Jenkins Controller (on-prem) and Jenkins Agent (in `prj-b-cicd` project) must have network connectivity through the VPN. 1. Test a pipeline using the Jenkins Controller Web UI: - 1. Make sure your [SSH Agent](https://plugins.jenkins.io/ssh-agent) is online and troubleshoot network connectivity if needed. - 1. Test that your Jenkins Controller can deploy a [pipeline](https://www.jenkins.io/doc/book/pipeline/getting-started/) to the Jenkins Agent located in the `prj-b-cicd` project (you can test this by running with a simple `echo "Hello World"` pipeline build). + 1. Make sure your [SSH Agent](https://plugins.jenkins.io/ssh-agent) is online and troubleshoot network connectivity if needed. + 1. Test that your Jenkins Controller can deploy a [pipeline](https://www.jenkins.io/doc/book/pipeline/getting-started/) to the Jenkins Agent located in the `prj-b-cicd` project (you can test this by running with a simple `echo "Hello World"` pipeline build). ### IV. Configure the Git repositories and Multibranch Pipelines in your Jenkins Controller - **Note:** this section is considered out of the scope of this document. Since there are multiple options on how to configure the Git repositories and **Multibranch Pipeline** in your Jenkins Controller, here we can only provide some guidance that you should keep in mind while completing this step. Visit the [Jenkins website](https://jenkins.io) for more information, there are plenty of Jenkins Plugins that could help with the task. - - You need to configure a **"Multibranch Pipeline"**. Note that the `Jenkinsfile` and `tf-wrapper.sh` files use the `$BRANCH_NAME` environment variable. **the `$BRANCH_NAME` variable is only available in Jenkins' Multibranch Pipelines**. - - + - You need to configure a **"Multibranch Pipeline"**. Note that the `Jenkinsfile` and `tf-wrapper.sh` files use the `$BRANCH_NAME` environment variable. **the `$BRANCH_NAME` variable is only available in Jenkins' Multibranch Pipelines**. - **Jenkinsfile:** A [Jenkinsfile](../build/Jenkinsfile) has been included which closely aligns with the Cloud Build pipeline. Additionally, the stage `TF wait for approval` which lets you confirm via Jenkins UI before proceeding with `terraform apply` has been disabled by default. It can be enabled by un-commenting that stage in the file. 1. Create Multibranch pipelines for your new repos (`YOUR_NEW_REPO-1-org, YOUR_NEW_REPO-2-environments, YOUR_NEW_REPO-3-networks, YOUR_NEW_REPO-4-projects`). - - **DO NOT configure an automatic pipeline for your `YOUR_NEW_REPO-0-bootstrap` repository** + - **DO NOT configure an automatic pipeline for your `YOUR_NEW_REPO-0-bootstrap` repository** 1. In your Jenkins Controller Web UI, **create Multibranch Pipelines only for the following repositories:** - ``` - YOUR_NEW_REPO-1-org - YOUR_NEW_REPO-2-environments - YOUR_NEW_REPO-3-networks - YOUR_NEW_REPO-4-projects - ``` + + ```text + YOUR_NEW_REPO-1-org + YOUR_NEW_REPO-2-environments + YOUR_NEW_REPO-3-networks + YOUR_NEW_REPO-4-projects + ``` + 1. Assuming your new Git repositories are private, you may need to configure new credentials In your Jenkins Controller web UI, so it can connect to the repositories. 1. You will also want to configure automatic triggers in each one of the Jenkins Multibranch Pipelines, unless you want to run the pipelines manually from the Jenkins Web UI after each commit to your repositories. -1. You can now move to the instructions in the step [1-org](../1-org/README.md). +1. You can now move to the instructions for step 1-org. + +## Deploying step 1-org + +1. Clone the repo you created manually in 0-bootstrap instructions. + + ```bash + git clone + ``` + +1. Navigate into the repo and change to a non-production branch. All subsequent + steps assume you are running them from the directory. If + you run them from another directory, adjust your copy paths accordingly. + + ```bash + cd YOUR_NEW_REPO_CLONE-1-org + git checkout -b plan + ``` + +1. Copy contents of foundation to new repo. + + ```bash + cp -RT ../terraform-example-foundation/1-org/ . + cp -RT ../terraform-example-foundation/policy-library/ ./policy-library + cp ../terraform-example-foundation/build/Jenkinsfile . + cp ../terraform-example-foundation/build/tf-wrapper.sh . + chmod 755 ./tf-wrapper.sh + ``` + +1. Update the variables located in the `environment {}` section of the `Jenkinsfile` with values from 0-bootstrap: + + ```text + _TF_SA_EMAIL + _STATE_BUCKET_NAME + _PROJECT_ID (the CI/CD project ID) + ``` + +1. You can re-run `terraform output` in the 0-bootstrap directory to find these values. + + ```bash + BACKEND_STATE_BUCKET_NAME=$(terraform -chdir="../0-bootstrap/" output -raw gcs_bucket_tfstate) + echo "_STATE_BUCKET_NAME = ${BACKEND_STATE_BUCKET_NAME}" + sed -i "s/STATE_BUCKET_NAME/${BACKEND_STATE_BUCKET_NAME}/" ./Jenkinsfile + + TERRAFORM_SA_EMAIL=$(terraform -chdir="../0-bootstrap/" output -raw organization_step_terraform_service_account_email) + echo "_TF_SA_EMAIL = ${TERRAFORM_SA_EMAIL}" + sed -i "s/TERRAFORM_SA_EMAIL/${TERRAFORM_SA_EMAIL}/" ./Jenkinsfile + + CICD_PROJECT_ID=$(terraform -chdir="../0-bootstrap/" output -raw cicd_project_id) + echo "_PROJECT_ID = ${CICD_PROJECT_ID}" + sed -i "s/CICD_PROJECT_ID/${CICD_PROJECT_ID}/" ./Jenkinsfile + ``` + +1. Check if your organization already has an Access Context Manager Policy. + + ```bash + export ORGANIZATION_ID=$(terraform -chdir="../0-bootstrap/" output -json common_config | jq '.org_id' | tr -d '"') + 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}" + ``` + +1. Rename `./envs/shared/terraform.example.tfvars` to `./envs/shared/terraform.tfvars` and update the file with values from your environment and 0-bootstrap. + + ```bash + mv ./envs/shared/terraform.example.tfvars ./envs/shared/terraform.tfvars + ``` + +1. You can re-run `terraform output` in the 0-bootstrap directory to find these values. If the previous step showed a numeric value, make sure to un-comment the variable `create_access_context_manager_access_policy = false`. See the shared folder [README.md](../1-org/envs/shared/README.md) for additional information on the values in the `terraform.tfvars` file. + + ```bash + export backend_bucket=$(terraform -chdir="../0-bootstrap/" output -raw gcs_bucket_tfstate) + echo "backend_bucket = ${backend_bucket}" + sed -i "s/TERRAFORM_STATE_BUCKET/${backend_bucket}/" ./envs/shared/terraform.tfvars + ``` + +1. Commit changes. + + ```bash + git add . + git commit -m 'Your message' + ``` + +1. Push your plan branch. + - Assuming you configured an automatic trigger in your Jenkins Controller (see [Jenkins sub-module README](../0-bootstrap/modules/jenkins-agent)), this will trigger a plan. You can also trigger a Jenkins job manually. Given the many options to do this in Jenkins, it is out of the scope of this document see [Jenkins website](https://www.jenkins.io) for more details. + + ```bash + git push --set-upstream origin plan + ``` + +1. Review the plan output in your Controller's web UI. +1. Merge changes to production branch. + + ```bash + git checkout -b production + git push origin production + ``` + +1. Review the apply output in your Controller's web UI. (you might want to use the option to "Scan Multibranch Pipeline Now" in your Jenkins Controller UI). + +## Deploying step 2-environments + +1. Clone the repo you created manually in 0-bootstrap. + + ```bash + git clone + ``` + +1. Navigate into the repo and change to a non-production branch. All subsequent + steps assume you are running them from the directory. If + you run them from another directory, adjust your copy paths accordingly. + + ```bash + cd YOUR_NEW_REPO_CLONE-2-environments + git checkout -b plan + ``` + +1. Copy contents of foundation to new repo. + + ```bash + cp -RT ../terraform-example-foundation/2-environments/ . + cp -RT ../terraform-example-foundation/policy-library/ ./policy-library + cp ../terraform-example-foundation/build/Jenkinsfile . + cp ../terraform-example-foundation/build/tf-wrapper.sh . + chmod 755 ./tf-wrapper.sh + ``` + +1. Update the variables located in the `environment {}` section of the `Jenkinsfile` with values from your environment: + + ```text + _TF_SA_EMAIL + _STATE_BUCKET_NAME + _PROJECT_ID (the CI/CD project ID) + ``` + +1. You can re-run `terraform output` in the 0-bootstrap directory to find these values. + + ```bash + BACKEND_STATE_BUCKET_NAME=$(terraform -chdir="../0-bootstrap/" output -raw gcs_bucket_tfstate) + echo "_STATE_BUCKET_NAME = ${BACKEND_STATE_BUCKET_NAME}" + sed -i "s/STATE_BUCKET_NAME/${BACKEND_STATE_BUCKET_NAME}/" ./Jenkinsfile + + TERRAFORM_SA_EMAIL=$(terraform -chdir="../0-bootstrap/" output -raw environment_step_terraform_service_account_email) + echo "_TF_SA_EMAIL = ${TERRAFORM_SA_EMAIL}" + sed -i "s/TERRAFORM_SA_EMAIL/${TERRAFORM_SA_EMAIL}/" ./Jenkinsfile + + CICD_PROJECT_ID=$(terraform -chdir="../0-bootstrap/" output -raw cicd_project_id) + echo "_PROJECT_ID = ${CICD_PROJECT_ID}" + sed -i "s/CICD_PROJECT_ID/${CICD_PROJECT_ID}/" ./Jenkinsfile + ``` + +1. Rename `terraform.example.tfvars` to `terraform.tfvars` and update the file with values from your environment and 0-bootstrap. + + ```bash + mv terraform.example.tfvars terraform.tfvars + ``` + +1. You can re-run `terraform output` in the 0-bootstrap directory to find these values. See any of the envs folder [README.md](../2-environments/envs/production/README.md) files for additional information on the values in the `terraform.tfvars` file. + + ```bash + export backend_bucket=$(terraform -chdir="../0-bootstrap/" output -raw gcs_bucket_tfstate) + echo "backend_bucket = ${backend_bucket}" + sed -i "s/TERRAFORM_STATE_BUCKET/${backend_bucket}/" ./terraform.tfvars + ``` + +1. Commit changes. + + ```bash + git add . + git commit -m 'Your message' + ``` + +1. Push your plan branch. + + ```bash + git push --set-upstream origin plan + ``` + + - Assuming you configured an automatic trigger in your Jenkins Controller (see [Jenkins sub-module README](../0-bootstrap/modules/jenkins-agent/README.md)), this will trigger a plan. You can also trigger a Jenkins job manually. Given the many options to do this in Jenkins, it is out of the scope of this document see [Jenkins website](https://www.jenkins.io) for more details. +1. Review the plan output in your Controller's web UI. +1. Merge changes to development. + + ```bash + git checkout -b development + git push origin development + ``` + +1. Review the apply output in your Controller's web UI (you might want to use the option to "Scan Multibranch Pipeline Now" in your Jenkins Controller UI). +1. Merge changes to non-production with. + + ```bash + git checkout -b non-production + git push origin non-production + ``` + +1. Review the apply output in your Controller's web UI (you might want to use the option to "Scan Multibranch Pipeline Now" in your Jenkins Controller UI). +1. Merge changes to production branch. + + ```bash + git checkout -b production + git push origin production + ``` + +1. Review the apply output in your Controller's web UI (you might want to use the option to "Scan Multibranch Pipeline Now" in your Jenkins Controller UI). +1. You can now move to the instructions in the next step, go to [Deploying step 3-networks-dual-svpc](#deploying-step-3-networks-dual-svpc) to use the Dual Shared VPC mode, or go to [Deploying step 3-networks-hub-and-spoke](#deploying-step-3-networks-hub-and-spoke) to use the Hub and Spoke network mode. + +## Deploying step 3-networks-dual-svpc + +1. Clone the repo you created manually in 0-bootstrap. + + ```bash + git clone + ``` + +1. Navigate into the repo and change to a non-production branch. All subsequent + steps assume you are running them from the directory. If + you run them from another directory, adjust your copy paths accordingly. + + ```bash + cd YOUR_NEW_REPO_CLONE-3-networks + git checkout -b plan + ``` + +1. Copy contents of foundation to new repo. + + ```bash + cp -RT ../terraform-example-foundation/3-networks-dual-svpc/ . + cp -RT ../terraform-example-foundation/policy-library/ ./policy-library + cp ../terraform-example-foundation/build/Jenkinsfile . + cp ../terraform-example-foundation/build/tf-wrapper.sh . + chmod 755 ./tf-wrapper.sh + ``` + +1. Update the variables located in the `environment {}` section of the `Jenkinsfile` with values from your environment: + + ```text + _TF_SA_EMAIL + _STATE_BUCKET_NAME + _PROJECT_ID (the CI/CD project ID) + ``` + +1. You can re-run `terraform output` in the 0-bootstrap directory to find these values. + + ```bash + BACKEND_STATE_BUCKET_NAME=$(terraform -chdir="../0-bootstrap/" output -raw gcs_bucket_tfstate) + echo "_STATE_BUCKET_NAME = ${BACKEND_STATE_BUCKET_NAME}" + sed -i "s/STATE_BUCKET_NAME/${BACKEND_STATE_BUCKET_NAME}/" ./Jenkinsfile + + TERRAFORM_SA_EMAIL=$(terraform -chdir="../0-bootstrap/" output -raw networks_step_terraform_service_account_email) + echo "_TF_SA_EMAIL = ${TERRAFORM_SA_EMAIL}" + sed -i "s/TERRAFORM_SA_EMAIL/${TERRAFORM_SA_EMAIL}/" ./Jenkinsfile + + CICD_PROJECT_ID=$(terraform -chdir="../0-bootstrap/" output -raw cicd_project_id) + echo "_PROJECT_ID = ${CICD_PROJECT_ID}" + sed -i "s/CICD_PROJECT_ID/${CICD_PROJECT_ID}/" ./Jenkinsfile + ``` + +1. 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`. + + ```bash + 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 + ``` + +1. Update `common.auto.tfvars` file with values from your environment and bootstrap. See any of the envs folder [README.md](../3-networks-dual-svpc/envs/production/README.md) files for additional information on the values in the `common.auto.tfvars` file. +1. Update `shared.auto.tfvars` file with the `target_name_server_addresses`. +1. Update `access_context.auto.tfvars` file with the `access_context_manager_policy_id`. +1. Use `terraform output` to get the backend bucket and networks step Terraform Service Account values from 0-bootstrap output. + + ```bash + export ORGANIZATION_ID=$(terraform -chdir="../0-bootstrap/" output -json common_config | jq '.org_id' | tr -d '"') + 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 + + export backend_bucket=$(terraform -chdir="../0-bootstrap/" output -raw gcs_bucket_tfstate) + echo "backend_bucket = ${backend_bucket}" + sed -i "s/TERRAFORM_STATE_BUCKET/${backend_bucket}/" ./common.auto.tfvars + + export NETWORKS_STEP_TERRAFORM_SERVICE_ACCOUNT_EMAIL=$(terraform -chdir="../0-bootstrap/" output -raw networks_step_terraform_service_account_email) + echo "terraform_service_account = ${NETWORKS_STEP_TERRAFORM_SERVICE_ACCOUNT_EMAIL}" + sed -i "s/NETWORKS_STEP_TERRAFORM_SERVICE_ACCOUNT_EMAIL/${NETWORKS_STEP_TERRAFORM_SERVICE_ACCOUNT_EMAIL}/" ./common.auto.tfvars + ``` + +1. Commit changes. + + ```bash + git add . + git commit -m 'Your message' + ``` + +1. You must manually plan and apply the `shared` environment (only once) since the `development`, `non-production` and `production` environments depend on it. +1. To use the `validate` option of the `tf-wrapper.sh` script, please follow the [instructions](https://cloud.google.com/docs/terraform/policy-validation/validate-policies#install) to install the terraform-tools component. +1. Also update `backend.tf` with your backend bucket from 0-bootstrap output. + + ```bash + for i in `find -name 'backend.tf'`; do sed -i "s/UPDATE_ME/${backend_bucket}/" $i; done + ``` + +1. Use `terraform output` to get the Cloud Build project ID and the networks step Terraform Service Account from 0-bootstrap output. An environment variable `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` will be set using the Terraform Service Account to enable impersonation. + + ```bash + export CICD_PROJECT_ID=$(terraform -chdir="../0-bootstrap/" output -raw cicd_project_id) + echo ${CICD_PROJECT_ID} + + export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=$(terraform -chdir="../0-bootstrap/" output -raw networks_step_terraform_service_account_email) + echo ${GOOGLE_IMPERSONATE_SERVICE_ACCOUNT} + ``` + +1. Run `init` and `plan` and review output for environment shared. + + ```bash + ./tf-wrapper.sh init shared + ./tf-wrapper.sh plan shared + ``` + +1. Run `validate` and check for violations. + + ```bash + ./tf-wrapper.sh validate shared $(pwd)/../policy-library ${CICD_PROJECT_ID} + ``` + +1. Run `apply` shared. + + ```bash + ./tf-wrapper.sh apply shared + ``` + +1. Push your plan branch. + + ```bash + git push --set-upstream origin plan + ``` + + - Assuming you configured an automatic trigger in your Jenkins Controller (see [Jenkins sub-module README](../0-bootstrap/modules/jenkins-agent)), this will trigger a plan. You can also trigger a Jenkins job manually. Given the many options to do this in Jenkins, it is out of the scope of this document see [Jenkins website](https://www.jenkins.io) for more details. +1. Review the plan output in your Controller's web UI. +1. Merge changes to production branch. + + ```bash + git checkout -b production + git push origin production + ``` + +1. Review the apply output in your Controller's web UI (you might want to use the option to "Scan Multibranch Pipeline Now" in your Jenkins Controller UI). +1. After production has been applied, apply development and non-production. +1. Merge changes to development + + ```bash + git checkout -b development + git push origin development + ``` + +1. Review the apply output in your Controller's web UI (you might want to use the option to "Scan Multibranch Pipeline Now" in your Jenkins Controller UI). +1. Merge changes to non-production. + + ```bash + git checkout -b non-production + git push origin non-production + ``` + +1. Review the apply output in your Controller's web UI (you might want to use the option to "Scan Multibranch Pipeline Now" in your Jenkins Controller UI). + +## Deploying step 3-networks-hub-and-spoke + +1. Clone the repo you created manually in 0-bootstrap. + + ```bash + git clone + ``` + +1. Navigate into the repo and change to a non-production branch. All subsequent + steps assume you are running them from the directory. If + you run them from another directory, adjust your copy paths accordingly. + + ```bash + cd YOUR_NEW_REPO_CLONE-3-networks + git checkout -b plan + ``` + +1. Copy contents of foundation to new repo. + + ```bash + cp -RT ../terraform-example-foundation/3-networks-hub-and-spoke/ . + cp -RT ../terraform-example-foundation/policy-library/ ./policy-library + cp ../terraform-example-foundation/build/Jenkinsfile . + cp ../terraform-example-foundation/build/tf-wrapper.sh . + chmod 755 ./tf-wrapper.sh + ``` + +1. Update the variables located in the `environment {}` section of the `Jenkinsfile` with values from your environment: + + ```text + _TF_SA_EMAIL + _STATE_BUCKET_NAME + _PROJECT_ID (the CI/CD project ID) + ``` + +1. You can re-run `terraform output` in the 0-bootstrap directory to find these values. + + ```bash + BACKEND_STATE_BUCKET_NAME=$(terraform -chdir="../0-bootstrap/" output -raw gcs_bucket_tfstate) + echo "_STATE_BUCKET_NAME = ${BACKEND_STATE_BUCKET_NAME}" + sed -i "s/STATE_BUCKET_NAME/${BACKEND_STATE_BUCKET_NAME}/" ./Jenkinsfile + + TERRAFORM_SA_EMAIL=$(terraform -chdir="../0-bootstrap/" output -raw networks_step_terraform_service_account_email) + echo "_TF_SA_EMAIL = ${TERRAFORM_SA_EMAIL}" + sed -i "s/TERRAFORM_SA_EMAIL/${TERRAFORM_SA_EMAIL}/" ./Jenkinsfile + + CICD_PROJECT_ID=$(terraform -chdir="../0-bootstrap/" output -raw cicd_project_id) + echo "_PROJECT_ID = ${CICD_PROJECT_ID}" + sed -i "s/CICD_PROJECT_ID/${CICD_PROJECT_ID}/" ./Jenkinsfile + ``` + +1. 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`. + + ```bash + 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 + ``` + +1. Update `common.auto.tfvars` file with values from your environment and bootstrap. See any of the envs folder [README.md](../3-networks-hub-and-spoke/envs/production/README.md) files for additional information on the values in the `common.auto.tfvars` file. +1. Update `shared.auto.tfvars` file with the `target_name_server_addresses`. +1. Update `access_context.auto.tfvars` file with the `access_context_manager_policy_id`. +1. Use `terraform output` to get the backend bucket and networks step Terraform Service Account values from 0-bootstrap output. + + ```bash + export ORGANIZATION_ID=$(terraform -chdir="../0-bootstrap/" output -json common_config | jq '.org_id' | tr -d '"') + 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 + + export backend_bucket=$(terraform -chdir="../0-bootstrap/" output -raw gcs_bucket_tfstate) + echo "backend_bucket = ${backend_bucket}" + sed -i "s/TERRAFORM_STATE_BUCKET/${backend_bucket}/" ./common.auto.tfvars + + export NETWORKS_STEP_TERRAFORM_SERVICE_ACCOUNT_EMAIL=$(terraform -chdir="../0-bootstrap/" output -raw networks_step_terraform_service_account_email) + echo "terraform_service_account = ${NETWORKS_STEP_TERRAFORM_SERVICE_ACCOUNT_EMAIL}" + sed -i "s/NETWORKS_STEP_TERRAFORM_SERVICE_ACCOUNT_EMAIL/${NETWORKS_STEP_TERRAFORM_SERVICE_ACCOUNT_EMAIL}/" ./common.auto.tfvars + ``` + +1. Commit changes. + + ```bash + git add . + git commit -m 'Your message' + ``` + +1. You must manually plan and apply the `shared` environment (only once) since the `development`, `non-production` and `production` environments depend on it. +1. To use the `validate` option of the `tf-wrapper.sh` script, please follow the [instructions](https://cloud.google.com/docs/terraform/policy-validation/validate-policies#install) to install the terraform-tools component. +1. Also update `backend.tf` with your backend bucket from 0-bootstrap output. + + ```bash + for i in `find -name 'backend.tf'`; do sed -i "s/UPDATE_ME/${backend_bucket}/" $i; done + ``` + +1. Use `terraform output` to get the Cloud Build project ID and the networks step Terraform Service Account from 0-bootstrap output. An environment variable `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` will be set using the Terraform Service Account to enable impersonation. + + ```bash + export CICD_PROJECT_ID=$(terraform -chdir="../0-bootstrap/" output -raw cicd_project_id) + echo ${CICD_PROJECT_ID} + + export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=$(terraform -chdir="../0-bootstrap/" output -raw networks_step_terraform_service_account_email) + echo ${GOOGLE_IMPERSONATE_SERVICE_ACCOUNT} + ``` + +1. Run `init` and `plan` and review output for environment shared. + + ```bash + ./tf-wrapper.sh init shared + ./tf-wrapper.sh plan shared + ``` + +1. Run `validate` and check for violations. + + ```bash + ./tf-wrapper.sh validate shared $(pwd)/../policy-library ${CICD_PROJECT_ID} + ``` + +1. Run `apply` shared. + + ```bash + ./tf-wrapper.sh apply shared + ``` + +1. Push your plan branch. + + ```bash + git push --set-upstream origin plan + ``` + + - Assuming you configured an automatic trigger in your Jenkins Controller (see [Jenkins sub-module README](../0-bootstrap/modules/jenkins-agent)), this will trigger a plan. You can also trigger a Jenkins job manually. Given the many options to do this in Jenkins, it is out of the scope of this document see [Jenkins website](https://www.jenkins.io) for more details. +1. Review the plan output in your Controller's web UI. +1. Merge changes to production branch. + + ```bash + git checkout -b production + git push origin production + ``` + +1. Review the apply output in your Controller's web UI (you might want to use the option to "Scan Multibranch Pipeline Now" in your Jenkins Controller UI). +1. After production has been applied, apply development and non-production. +1. Merge changes to development + + ```bash + git checkout -b development + git push origin development + ``` + +1. Review the apply output in your Controller's web UI (you might want to use the option to "Scan Multibranch Pipeline Now" in your Jenkins Controller UI). +1. Merge changes to non-production. + + ```bash + git checkout -b non-production + git push origin non-production + ``` + +1. Review the apply output in your Controller's web UI (you might want to use the option to "Scan Multibranch Pipeline Now" in your Jenkins Controller UI). + +## Deploying step 4-projects + +1. Clone the repo you created manually in 0-bootstrap. + + ```bash + git clone + ``` + +1. Navigate into the repo and change to a non-production branch. All subsequent + steps assume you are running them from the directory. If + you run them from another directory, adjust your copy paths accordingly. + + ```bash + cd YOUR_NEW_REPO_CLONE-4-projects + git checkout -b plan + ``` + +1. Copy contents of foundation to new repo. + + ```bash + cp -RT ../terraform-example-foundation/4-projects/ . + cp -RT ../terraform-example-foundation/policy-library/ ./policy-library + cp ../terraform-example-foundation/build/Jenkinsfile . + cp ../terraform-example-foundation/build/tf-wrapper.sh . + chmod 755 ./tf-wrapper.sh + ``` + +1. Update the variables located in the `environment {}` section of the `Jenkinsfile` with values from your environment: + + ```text + _TF_SA_EMAIL + _STATE_BUCKET_NAME + _PROJECT_ID (the CI/CD project ID) + ``` + +1. You can re-run `terraform output` in the 0-bootstrap directory to find these values. + + ```bash + BACKEND_STATE_BUCKET_NAME=$(terraform -chdir="../0-bootstrap/" output -raw gcs_bucket_tfstate) + echo "_STATE_BUCKET_NAME = ${BACKEND_STATE_BUCKET_NAME}" + sed -i "s/STATE_BUCKET_NAME/${BACKEND_STATE_BUCKET_NAME}/" ./Jenkinsfile + + TERRAFORM_SA_EMAIL=$(terraform -chdir="../0-bootstrap/" output -raw projects_step_terraform_service_account_email) + echo "_TF_SA_EMAIL = ${TERRAFORM_SA_EMAIL}" + sed -i "s/TERRAFORM_SA_EMAIL/${TERRAFORM_SA_EMAIL}/" ./Jenkinsfile + + CICD_PROJECT_ID=$(terraform -chdir="../0-bootstrap/" output -raw cicd_project_id) + echo "_PROJECT_ID = ${CICD_PROJECT_ID}" + sed -i "s/CICD_PROJECT_ID/${CICD_PROJECT_ID}/" ./Jenkinsfile + ``` + +1. Rename `auto.example.tfvars` files to `auto.tfvars`. + + ```bash + 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 non-production.auto.example.tfvars non-production.auto.tfvars + mv production.auto.example.tfvars production.auto.tfvars + ``` + +1. See any of the envs folder [README.md](../4-projects/business_unit_1/production/README.md) files for additional information on the values in the `common.auto.tfvars`, `development.auto.tfvars`, `non-production.auto.tfvars`, and `production.auto.tfvars` files. +1. See any of the shared folder [README.md](../4-projects/business_unit_1/shared/README.md) files for additional information on the values in the `shared.auto.tfvars` file. +1. Use `terraform output` to get the backend bucket value from 0-bootstrap output. + + ```bash + export backend_bucket=$(terraform -chdir="../0-bootstrap/" output -raw gcs_bucket_tfstate) + echo "backend_bucket = ${backend_bucket}" + sed -i "s/TERRAFORM_STATE_BUCKET/${backend_bucket}/" ./common.auto.tfvars + ``` + +1. Commit changes. + + ```bash + git add . + git commit -m 'Your message' + ``` + +1. Also update `backend.tf` with your backend bucket from 0-bootstrap output. + + ```bash + for i in `find -name 'backend.tf'`; do sed -i "s/UPDATE_ME/${backend_bucket}/" $i; done + ``` + +1. You need to manually plan and apply only once the `shared` environments since `development`, `non-production`, and `production` depend on it. +1. Use `terraform output` to get the Cloud Build project ID and the projects step Terraform Service Account from 0-bootstrap output. An environment variable `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` will be set using the Terraform Service Account to enable impersonation. + + ```bash + export CICD_PROJECT_ID=$(terraform -chdir="../0-bootstrap/" output -raw cicd_project_id) + echo ${CICD_PROJECT_ID} + + export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=$(terraform -chdir="../0-bootstrap/" output -raw projects_step_terraform_service_account_email) + echo ${GOOGLE_IMPERSONATE_SERVICE_ACCOUNT} + ``` + +1. Run `init` and `plan` and review output for environments `shared`. + + ```bash + ./tf-wrapper.sh init shared + ./tf-wrapper.sh plan shared + ``` + +1. Run `validate` and check for violations. + + ```bash + ./tf-wrapper.sh validate shared $(pwd)/../policy-library ${CICD_PROJECT_ID} + ``` + +1. Run `apply` shared. + + ```bash + ./tf-wrapper.sh apply shared + ``` + +1. Push your plan branch. + + ```bash + git push --set-upstream origin plan + ``` + + - Assuming you configured an automatic trigger in your Jenkins Controller (see [Jenkins sub-module README](../0-bootstrap/modules/jenkins-agent/)), this will trigger a plan. You can also trigger a Jenkins job manually. Given the many options to do this in Jenkins, it is out of the scope of this document see [Jenkins website](https://www.jenkins.io) for more details. +1. Review the plan output in your Controller's web UI. +1. Merge changes to production branch. + + ```bash + git checkout -b production + git push origin production + ``` + +1. Review the apply output in your Controller's web UI (you might want to use the option to "Scan Multibranch Pipeline Now" in your Jenkins Controller UI). +1. After production has been applied, apply development. +1. Merge changes to development branch. + + ```bash + git checkout -b development + git push origin development + ``` + +1. Review the apply output in your Controller's web UI (you might want to use the option to "Scan Multibranch Pipeline Now" in your Jenkins Controller UI). +1. After development has been applied, apply non-production. +1. Merge changes to non-production branch. + + ```bash + git checkout -b non-production + git push origin non-production + ``` + +1. Review the apply output in your Controller's web UI (you might want to use the option to "Scan Multibranch Pipeline Now" in your Jenkins Controller UI). ## Contributing diff --git a/0-bootstrap/README.md b/0-bootstrap/README.md index fd9c356d3..80cf01abb 100644 --- a/0-bootstrap/README.md +++ b/0-bootstrap/README.md @@ -216,6 +216,5 @@ the following steps: | projects\_step\_terraform\_service\_account\_email | Projects Step Terraform Account | | required\_groups | List of Google Groups created that are required by the Example Foundation steps. | | seed\_project\_id | Project where service accounts and core APIs will be enabled. | -| terraform\_sa\_name | Fully qualified name for privileged service account for Terraform. | diff --git a/0-bootstrap/cb.tf b/0-bootstrap/cb.tf index 743aa8dee..41a66556a 100644 --- a/0-bootstrap/cb.tf +++ b/0-bootstrap/cb.tf @@ -1,4 +1,3 @@ - /** * Copyright 2022 Google LLC * @@ -169,3 +168,22 @@ module "tf_workspace" { ] } + +resource "google_artifact_registry_repository_iam_member" "terraform_sa_artifact_registry_reader" { + for_each = local.granular_sa + + project = module.tf_source.cloudbuild_project_id + location = var.default_region + repository = local.gar_repository + role = "roles/artifactregistry.reader" + member = "serviceAccount:${google_service_account.terraform-env-sa[each.key].email}" +} + +resource "google_sourcerepo_repository_iam_member" "member" { + for_each = local.granular_sa + + project = module.tf_source.cloudbuild_project_id + repository = module.tf_source.csr_repos["gcp-policies"].name + role = "roles/viewer" + member = "serviceAccount:${google_service_account.terraform-env-sa[each.key].email}" +} diff --git a/0-bootstrap/jenkins.tf.example b/0-bootstrap/jenkins.tf.example new file mode 100644 index 000000000..9216ab226 --- /dev/null +++ b/0-bootstrap/jenkins.tf.example @@ -0,0 +1,60 @@ +/** + * 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 + * + * 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 { + sa_names = { for k, v in google_service_account.terraform-env-sa : k => v.id } +} + +module "jenkins_bootstrap" { + source = "./modules/jenkins-agent" + org_id = var.org_id + folder_id = google_folder.bootstrap.id + billing_account = var.billing_account + group_org_admins = local.group_org_admins + default_region = var.default_region + terraform_sa_names = local.sa_names + terraform_state_bucket = module.seed_bootstrap.gcs_bucket_tfstate + sa_enable_impersonation = true + jenkins_controller_subnetwork_cidr_range = var.jenkins_controller_subnetwork_cidr_range + jenkins_agent_gce_subnetwork_cidr_range = var.jenkins_agent_gce_subnetwork_cidr_range + jenkins_agent_gce_private_ip_address = var.jenkins_agent_gce_private_ip_address + nat_bgp_asn = var.nat_bgp_asn + jenkins_agent_sa_email = var.jenkins_agent_sa_email + jenkins_agent_gce_ssh_pub_key = var.jenkins_agent_gce_ssh_pub_key + vpn_shared_secret = var.vpn_shared_secret + on_prem_vpn_public_ip_address = var.on_prem_vpn_public_ip_address + on_prem_vpn_public_ip_address2 = var.on_prem_vpn_public_ip_address2 + router_asn = var.router_asn + bgp_peer_asn = var.bgp_peer_asn + tunnel0_bgp_peer_address = var.tunnel0_bgp_peer_address + tunnel0_bgp_session_range = var.tunnel0_bgp_session_range + tunnel1_bgp_peer_address = var.tunnel1_bgp_peer_address + tunnel1_bgp_session_range = var.tunnel1_bgp_session_range +} + +resource "google_organization_iam_member" "org_jenkins_sa_browser" { + count = var.parent_folder == "" ? 1 : 0 + org_id = var.org_id + role = "roles/browser" + member = "serviceAccount:${module.jenkins_bootstrap.jenkins_agent_sa_email}" +} + +resource "google_folder_iam_member" "folder_jenkins_sa_browser" { + count = var.parent_folder != "" ? 1 : 0 + folder = var.parent_folder + role = "roles/browser" + member = "serviceAccount:${module.jenkins_bootstrap.jenkins_agent_sa_email}" +} diff --git a/0-bootstrap/main.tf b/0-bootstrap/main.tf index 3b685f160..3bc6f06bd 100644 --- a/0-bootstrap/main.tf +++ b/0-bootstrap/main.tf @@ -98,45 +98,3 @@ module "seed_bootstrap" { sa_org_iam_permissions = [] } -## Un-comment the jenkins_bootstrap module and its outputs if you want to use Jenkins instead of Cloud Build -# module "jenkins_bootstrap" { -# source = "./modules/jenkins-agent" -# org_id = var.org_id -# folder_id = google_folder.bootstrap.id -# billing_account = var.billing_account -# group_org_admins = local.group_org_admins -# default_region = var.default_region -# terraform_service_account = module.seed_bootstrap.terraform_sa_email -# terraform_sa_name = module.seed_bootstrap.terraform_sa_name -# terraform_state_bucket = module.seed_bootstrap.gcs_bucket_tfstate -# sa_enable_impersonation = true -# jenkins_controller_subnetwork_cidr_range = var.jenkins_controller_subnetwork_cidr_range -# jenkins_agent_gce_subnetwork_cidr_range = var.jenkins_agent_gce_subnetwork_cidr_range -# jenkins_agent_gce_private_ip_address = var.jenkins_agent_gce_private_ip_address -# nat_bgp_asn = var.nat_bgp_asn -# jenkins_agent_sa_email = var.jenkins_agent_sa_email -# jenkins_agent_gce_ssh_pub_key = var.jenkins_agent_gce_ssh_pub_key -# vpn_shared_secret = var.vpn_shared_secret -# on_prem_vpn_public_ip_address = var.on_prem_vpn_public_ip_address -# on_prem_vpn_public_ip_address2 = var.on_prem_vpn_public_ip_address2 -# router_asn = var.router_asn -# bgp_peer_asn = var.bgp_peer_asn -# tunnel0_bgp_peer_address = var.tunnel0_bgp_peer_address -# tunnel0_bgp_session_range = var.tunnel0_bgp_session_range -# tunnel1_bgp_peer_address = var.tunnel1_bgp_peer_address -# tunnel1_bgp_session_range = var.tunnel1_bgp_session_range -# } - -# resource "google_organization_iam_member" "org_jenkins_sa_browser" { -# count = var.parent_folder == "" ? 1 : 0 -# org_id = var.org_id -# role = "roles/browser" -# member = "serviceAccount:${module.jenkins_bootstrap.jenkins_agent_sa_email}" -# } - -# resource "google_folder_iam_member" "folder_jenkins_sa_browser" { -# count = var.parent_folder != "" ? 1 : 0 -# folder = var.parent_folder -# role = "roles/browser" -# member = "serviceAccount:${module.jenkins_bootstrap.jenkins_agent_sa_email}" -# } diff --git a/0-bootstrap/modules/jenkins-agent/README.md b/0-bootstrap/modules/jenkins-agent/README.md index a87c95180..33ac34c90 100644 --- a/0-bootstrap/modules/jenkins-agent/README.md +++ b/0-bootstrap/modules/jenkins-agent/README.md @@ -1,15 +1,16 @@ -## Overview +# Overview + +The objective of this module is to deploy a Google Cloud Platform project `prj-b-cicd` to host a Jenkins Agent that can connect with your current Jenkins Controller on-prem. This module is a replica of the deprecated [cloudbuild module](https://github.com/terraform-google-modules/terraform-google-bootstrap/tree/master/modules/cloudbuild), but re-purposed to use Jenkins instead. This module creates: -The objective of this module is to deploy a Google Cloud Platform project `prj-b-cicd` to host a Jenkins Agent that can connect with your current Jenkins Controller on-prem. This module is a replica of the [cloudbuild module](https://github.com/terraform-google-modules/terraform-google-bootstrap/tree/master/modules/cloudbuild), but re-purposed to use Jenkins instead. This module creates: - The `prj-b-cicd` project, which includes: - - GCE Instance for the Jenkins Agent, which you will configure to connect to your current Jenkins Controller using SSH. - - VPC to connect the Jenkins GCE Instance to - - FW rules to allow communication over port 22 - - VPN connection with on-prem (or where ever your Jenkins Controller is located) - - Custom service account `sa-jenkins-agent-gce@prj-b-cicd-xxxx.iam.gserviceaccount.com` for the GCE instance. This service account is granted the access to generate tokens on the provided Terraform custom service account + - GCE Instance for the Jenkins Agent, which you will configure to connect to your current Jenkins Controller using SSH. + - VPC to connect the Jenkins GCE Instance to + - FW rules to allow communication over port 22 + - VPN connection with on-prem (or where ever your Jenkins Controller is located) + - Custom service account `sa-jenkins-agent-gce@prj-b-cicd-xxxx.iam.gserviceaccount.com` for the GCE instance. This service account is granted the access to generate tokens on the provided Terraform custom service account Please note this module does not include an option to create a Jenkins Controller. To deploy a Jenkins Controller, you should follow one of the available user guides about [Jenkins in GCP](https://cloud.google.com/jenkins). -**If you don't have a Jenkins implementation and don't want one**, then we recommend you to [use the Cloud Build module](../../README.md) instead. +**If you don't have a Jenkins implementation and don't want one**, then we recommend you to [use the Cloud Build module](../../README.md#deploying-with-cloud-build) instead. ## Usage @@ -23,9 +24,8 @@ module "jenkins_bootstrap" { billing_account = "" group_org_admins = "gcp-organization-admins@example.com" default_region = "us-central1" - terraform_service_account = "" # normally module.seed_bootstrap.terraform_sa_email - terraform_sa_name = "" # normally module.seed_bootstrap.terraform_sa_name - terraform_state_bucket = "" # normally module.seed_bootstrap.gcs_bucket_tfstate + terraform_sa_names = "" + terraform_state_bucket = "" sa_enable_impersonation = true jenkins_controller_subnetwork_cidr_range = ["10.1.0.6/32"] jenkins_agent_gce_subnetwork_cidr_range = "172.16.1.0/24" @@ -43,7 +43,7 @@ module "jenkins_bootstrap" { 1. Creates a GCE Instance to run the Jenkins Agent with SSH access using the supplied public key 1. Creates a Service Account (`jenkins_agent_sa_email`) to run the Jenkins Agent GCE instance 1. Creates a GCS bucket for Jenkins Artifacts using `project_prefix` -1. Allows `jenkins_agent_sa_email` service account permissions to impersonate terraform service account (which exists in the `seed` project) using `sa_enable_impersonation` and supplied value for `terraform_sa_name` +1. Allows `jenkins_agent_sa_email` service account permissions to impersonate terraform service account (which exists in the `seed` project) using `sa_enable_impersonation` and supplied value for `terraform_sa_names` 1. Adds Cloud NAT for the Agent to be able to download updates and necessary binaries. @@ -76,11 +76,10 @@ module "jenkins_bootstrap" { | service\_account\_prefix | Name prefix to use for service accounts. | `string` | `"sa"` | no | | storage\_bucket\_labels | Labels to apply to the storage bucket. | `map(string)` | `{}` | no | | storage\_bucket\_prefix | Name prefix to use for storage buckets. | `string` | `"bkt"` | no | -| terraform\_sa\_name | Fully-qualified name of the Terraform Service Account. It must be supplied by the Seed Project | `string` | n/a | yes | -| terraform\_service\_account | Email for Terraform Service Account. It must be supplied by the Seed Project | `string` | n/a | yes | +| terraform\_sa\_names | Fully-qualified name of the Terraform Service Accounts. It must be supplied by the Seed Project | `map(string)` | n/a | yes | | terraform\_state\_bucket | Default state bucket, used in Cloud Build substitutions. It must be supplied by the Seed Project | `string` | n/a | yes | -| terraform\_version | Default terraform version. | `string` | `"0.13.7"` | no | -| terraform\_version\_sha256sum | sha256sum for default terraform version. | `string` | `"4a52886e019b4fdad2439da5ff43388bbcc6cce9784fde32c53dcd0e28ca9957"` | no | +| terraform\_version | Default terraform version. | `string` | `"1.0.0"` | no | +| terraform\_version\_sha256sum | sha256sum for default terraform version. | `string` | `"8be33cc3be8089019d95eb8f546f35d41926e7c1e5deff15792e969dde573eb5"` | no | | tunnel0\_bgp\_peer\_address | BGP peer address for tunnel 0 | `string` | n/a | yes | | tunnel0\_bgp\_session\_range | BGP session range for tunnel 0 | `string` | n/a | yes | | tunnel1\_bgp\_peer\_address | BGP peer address for tunnel 1 | `string` | n/a | yes | @@ -104,20 +103,20 @@ module "jenkins_bootstrap" { ### Software -- [gcloud sdk](https://cloud.google.com/sdk/install) >= 206.0.0 -- [Terraform](https://www.terraform.io/downloads.html) = 0.13.7 - - The scripts in this codebase use Terraform v0.13.7. You should use the same version in the manual steps to avoid [Terraform State Snapshot Lock](https://github.com/hashicorp/terraform/issues/23290) errors caused by differences in terraform versions. +- [gcloud sdk](https://cloud.google.com/sdk/install) >= 393.0.0 +- [Terraform](https://www.terraform.io/downloads.html) = 1.0.0 + - The scripts in this codebase use Terraform v1.0.0. You should use the same version in the manual steps to avoid [Terraform State Snapshot Lock](https://github.com/hashicorp/terraform/issues/23290) errors caused by differences in terraform versions. ### Infrastructure - - **Jenkins Controller:** You need a Jenkins Controller, since this module does not include an option to create one. To deploy a Jenkins Controller, you should follow one of the available user guides about [Jenkins in GCP](https://cloud.google.com/jenkins). If you don't have a Jenkins implementation and don't want one, then we recommend you to [use the Cloud Build module](../../README.md) instead. +- **Jenkins Controller:** You need a Jenkins Controller, since this module does not include an option to create one. To deploy a Jenkins Controller, you should follow one of the available user guides about [Jenkins in GCP](https://cloud.google.com/jenkins). If you don't have a Jenkins implementation and don't want one, then we recommend you to [use the Cloud Build module](../../README.md#deploying-with-cloud-build) instead. - - **VPN Connectivity with on-prem:** Once you run this module, a Jenkins Agent is created in the CICD project in GCP. Please add VPN connectivity manually by following our user guide about [how to deploy a VPN tunnel in GCP](https://cloud.google.com/network-connectivity/docs/vpn/how-to). This VPN is necessary to allow communication between the Jenkins Controller (on prem or in a cloud environment) with the Jenkins Agent in the CICD project. +- **VPN Connectivity with on-prem:** Once you run this module, a Jenkins Agent is created in the CI/CD project in GCP. Please add VPN connectivity manually by following our user guide about [how to deploy a VPN tunnel in GCP](https://cloud.google.com/network-connectivity/docs/vpn/how-to/adding-a-tunnel). This VPN is necessary to allow communication between the Jenkins Controller (on prem or in a cloud environment) with the Jenkins Agent in the CI/CD project. - - **Binaries and packages for the Jenkins Agent:** The Jenkins Agent is a new GCE instance created by this module. After creation, the startup script needs to fetch several binaries for later use, during pipelines execution. These binaries include `java`, `terraform` and any other binary you use in your own scripts. You have several options to make these binaries and libraries available to the Jenkins Agent: - - allow the Jenkins Agent Internet access (ideally through Cloud NAT, implemented by default). - - allow the Jenkins Agent access to local package repositories on your premises, ideally through the VPN connection. - - preparing a golden image for the Jenkins Agent (and assign the image to the `jenkins_agent_gce_instance.boot_disk.initialize_params.image` terraform variable). You can create the golden images with tools like Packer. Although, you might still need network access to download dependencies while running a pipeline. +- **Binaries and packages for the Jenkins Agent:** The Jenkins Agent is a new GCE instance created by this module. After creation, the startup script needs to fetch several binaries for later use, during pipelines execution. These binaries include `java`, `terraform` and any other binary you use in your own scripts. You have several options to make these binaries and libraries available to the Jenkins Agent: + - allow the Jenkins Agent Internet access (ideally through Cloud NAT, implemented by default). + - allow the Jenkins Agent access to local package repositories on your premises, ideally through the VPN connection. + - preparing a golden image for the Jenkins Agent (and assign the image to the `jenkins_agent_gce_instance.boot_disk.initialize_params.image` terraform variable). You can create the golden images with tools like Packer. Although, you might still need network access to download dependencies while running a pipeline. ### Permissions @@ -128,19 +127,20 @@ An account that has the following [permissions](https://github.com/terraform-goo - `roles/resourcemanager.projectCreator` on GCP Organization or folder This is especially important as you might face one of the errors below: -``` + +```text Error: google: could not find default credentials. See https://developers.google.com/accounts/docs/application-default-credentials for more information. on line 0: (source code not available) ``` -``` +```text Error: Error setting billing account "aaaaaa-bbbbbb-cccccc" for project "projects/prj-jenkins-dc3a": googleapi: Error 400: Precondition check failed., failedPrecondition on .terraform/modules/jenkins/terraform-google-project-factory-7.1.0/modules/core_project_factory/main.tf line 96, in resource "google_project" "main": 96: resource "google_project" "main" { ``` -``` +```text Error: failed pre-requisites: missing permission on "billingAccounts/aaaaaa-bbbbbb-cccccc": billing.resourceAssociations.create on .terraform/modules/jenkins/terraform-google-project-factory-7.1.0/modules/core_project_factory/main.tf line 96, in resource "google_project" "main": 96: resource "google_project" "main" { diff --git a/0-bootstrap/modules/jenkins-agent/main.tf b/0-bootstrap/modules/jenkins-agent/main.tf index 8ec8d9774..4a74fe09c 100644 --- a/0-bootstrap/modules/jenkins-agent/main.tf +++ b/0-bootstrap/modules/jenkins-agent/main.tf @@ -237,9 +237,9 @@ resource "google_storage_bucket_iam_member" "jenkins_artifacts_iam" { // Allow the Jenkins Agent (GCE Instance) custom Service Account to impersonate the Terraform Service Account resource "google_service_account_iam_member" "jenkins_terraform_sa_impersonate_permissions" { - count = local.impersonation_enabled_count + for_each = var.sa_enable_impersonation ? var.terraform_sa_names : {} - service_account_id = var.terraform_sa_name + service_account_id = each.value role = "roles/iam.serviceAccountTokenCreator" member = "serviceAccount:${google_service_account.jenkins_agent_gce_sa.email}" } diff --git a/0-bootstrap/modules/jenkins-agent/variables.tf b/0-bootstrap/modules/jenkins-agent/variables.tf index 91de56f13..33691f9f3 100644 --- a/0-bootstrap/modules/jenkins-agent/variables.tf +++ b/0-bootstrap/modules/jenkins-agent/variables.tf @@ -141,14 +141,10 @@ variable "tunnel1_bgp_session_range" { /* ---------------------------------------- Specific to Seed Project ---------------------------------------- */ -variable "terraform_service_account" { - description = "Email for Terraform Service Account. It must be supplied by the Seed Project" - type = string -} -variable "terraform_sa_name" { - description = "Fully-qualified name of the Terraform Service Account. It must be supplied by the Seed Project" - type = string +variable "terraform_sa_names" { + description = "Fully-qualified name of the Terraform Service Accounts. It must be supplied by the Seed Project" + type = map(string) } variable "terraform_state_bucket" { @@ -224,11 +220,11 @@ variable "folder_id" { variable "terraform_version" { description = "Default terraform version." type = string - default = "0.13.7" + default = "1.0.0" } variable "terraform_version_sha256sum" { description = "sha256sum for default terraform version." type = string - default = "4a52886e019b4fdad2439da5ff43388bbcc6cce9784fde32c53dcd0e28ca9957" + default = "8be33cc3be8089019d95eb8f546f35d41926e7c1e5deff15792e969dde573eb5" } diff --git a/0-bootstrap/outputs.tf b/0-bootstrap/outputs.tf index 1ec1e2a74..acefe5038 100644 --- a/0-bootstrap/outputs.tf +++ b/0-bootstrap/outputs.tf @@ -39,11 +39,6 @@ output "organization_step_terraform_service_account_email" { value = google_service_account.terraform-env-sa["org"].email } -output "terraform_sa_name" { - description = "Fully qualified name for privileged service account for Terraform." - value = module.seed_bootstrap.terraform_sa_name -} - output "gcs_bucket_tfstate" { description = "Bucket used for storing terraform state for Foundations Pipelines in Seed Project." value = module.seed_bootstrap.gcs_bucket_tfstate @@ -63,10 +58,30 @@ output "common_config" { } } +output "group_org_admins" { + description = "Google Group for GCP Organization Administrators." + value = var.groups.create_groups == true ? module.required_group["group_org_admins"].id : var.group_org_admins +} + +output "group_billing_admins" { + description = "Google Group for GCP Billing Administrators." + value = var.groups.create_groups == true ? module.required_group["group_billing_admins"].id : var.group_billing_admins +} + +output "required_groups" { + description = "List of Google Groups created that are required by the Example Foundation steps." + value = var.groups.create_groups == true ? module.required_group : {} +} + +output "optional_groups" { + description = "List of Google Groups created that are optional to the Example Foundation steps." + value = var.groups.create_groups == true ? module.optional_group : {} +} + /* ---------------------------------------- Specific to cloudbuild_module ---------------------------------------- */ -// Comment-out the cloudbuild_bootstrap module and its outputs if you want to use Jenkins instead of Cloud Build +# Comment-out the cloudbuild_bootstrap module and its outputs if you want to use Jenkins instead of Cloud Build output "cloudbuild_project_id" { description = "Project where CloudBuild configuration and terraform container image will reside." value = module.tf_source.cloudbuild_project_id @@ -82,61 +97,36 @@ output "csr_repos" { value = module.tf_source.csr_repos } -output "group_org_admins" { - description = "Google Group for GCP Organization Administrators." - value = var.groups.create_groups == true ? module.required_group["group_org_admins"].id : var.group_org_admins -} - -output "group_billing_admins" { - description = "Google Group for GCP Billing Administrators." - value = var.groups.create_groups == true ? module.required_group["group_billing_admins"].id : var.group_billing_admins -} - /* ---------------------------------------- Specific to jenkins_bootstrap module ---------------------------------------- */ -//// Un-comment the jenkins_bootstrap module and its outputs if you want to use Jenkins instead of Cloud Build -//output "cicd_project_id" { -// description = "Project where the [CI/CD Pipeline](/docs/GLOSSARY.md#foundation-cicd-pipeline) (Jenkins Agents and terraform builder container image) reside." -// value = module.jenkins_bootstrap.cicd_project_id -//} -// -//output "jenkins_agent_gce_instance_id" { -// description = "Jenkins Agent GCE Instance id." -// value = module.jenkins_bootstrap.jenkins_agent_gce_instance_id -//} -// -//output "jenkins_agent_vpc_id" { -// description = "Jenkins Agent VPC name." -// value = module.jenkins_bootstrap.jenkins_agent_vpc_id -//} -// -//output "jenkins_agent_sa_email" { -// description = "Email for privileged custom service account for Jenkins Agent GCE instance." -// value = module.jenkins_bootstrap.jenkins_agent_sa_email -//} -// -//output "jenkins_agent_sa_name" { -// description = "Fully qualified name for privileged custom service account for Jenkins Agent GCE instance." -// value = module.jenkins_bootstrap.jenkins_agent_sa_name -//} -// -//output "gcs_bucket_jenkins_artifacts" { -// description = "Bucket used to store Jenkins artifacts in Jenkins project." -// value = module.jenkins_bootstrap.gcs_bucket_jenkins_artifacts -//} - - -/* ---------------------------------------- - Specific to Google Groups creation module - ---------------------------------------- */ - -output "required_groups" { - description = "List of Google Groups created that are required by the Example Foundation steps." - value = var.groups.create_groups == true ? module.required_group : {} -} - -output "optional_groups" { - description = "List of Google Groups created that are optional to the Example Foundation steps." - value = var.groups.create_groups == true ? module.optional_group : {} -} +# # Un-comment the jenkins_bootstrap module and its outputs if you want to use Jenkins instead of Cloud Build +# output "cicd_project_id" { +# description = "Project where the [CI/CD Pipeline](/docs/GLOSSARY.md#foundation-cicd-pipeline) (Jenkins Agents and terraform builder container image) reside." +# value = module.jenkins_bootstrap.cicd_project_id +# } + +# output "jenkins_agent_gce_instance_id" { +# description = "Jenkins Agent GCE Instance id." +# value = module.jenkins_bootstrap.jenkins_agent_gce_instance_id +# } + +# output "jenkins_agent_vpc_id" { +# description = "Jenkins Agent VPC name." +# value = module.jenkins_bootstrap.jenkins_agent_vpc_id +# } + +# output "jenkins_agent_sa_email" { +# description = "Email for privileged custom service account for Jenkins Agent GCE instance." +# value = module.jenkins_bootstrap.jenkins_agent_sa_email +# } + +# output "jenkins_agent_sa_name" { +# description = "Fully qualified name for privileged custom service account for Jenkins Agent GCE instance." +# value = module.jenkins_bootstrap.jenkins_agent_sa_name +# } + +# output "gcs_bucket_jenkins_artifacts" { +# description = "Bucket used to store Jenkins artifacts in Jenkins project." +# value = module.jenkins_bootstrap.gcs_bucket_jenkins_artifacts +# } diff --git a/0-bootstrap/sa.tf b/0-bootstrap/sa.tf index 1aad703cb..6b86541e2 100644 --- a/0-bootstrap/sa.tf +++ b/0-bootstrap/sa.tf @@ -152,22 +152,3 @@ resource "google_billing_account_iam_member" "billing_admin_user" { google_billing_account_iam_member.tf_billing_user ] } - -resource "google_artifact_registry_repository_iam_member" "terraform_sa_artifact_registry_reader" { - for_each = local.granular_sa - - project = module.tf_source.cloudbuild_project_id - location = var.default_region - repository = local.gar_repository - role = "roles/artifactregistry.reader" - member = "serviceAccount:${google_service_account.terraform-env-sa[each.key].email}" -} - -resource "google_sourcerepo_repository_iam_member" "member" { - for_each = local.granular_sa - - project = module.tf_source.cloudbuild_project_id - repository = module.tf_source.csr_repos["gcp-policies"].name - role = "roles/viewer" - member = "serviceAccount:${google_service_account.terraform-env-sa[each.key].email}" -} diff --git a/0-bootstrap/variables.tf b/0-bootstrap/variables.tf index effdb2aae..9557e7daa 100644 --- a/0-bootstrap/variables.tf +++ b/0-bootstrap/variables.tf @@ -82,88 +82,6 @@ variable "bucket_force_destroy" { default = false } -/* ---------------------------------------- - Specific to jenkins_bootstrap module - ---------------------------------------- */ - -# # Un-comment the jenkins_bootstrap module and its outputs if you want to use Jenkins instead of Cloud Build -# variable "jenkins_agent_gce_subnetwork_cidr_range" { -# description = "The subnetwork to which the Jenkins Agent will be connected to (in CIDR range 0.0.0.0/0)" -# type = string -# } - -# variable "jenkins_agent_gce_private_ip_address" { -# description = "The private IP Address of the Jenkins Agent. This IP Address must be in the CIDR range of `jenkins_agent_gce_subnetwork_cidr_range` and be reachable through the VPN that exists between on-prem (Jenkins Controller) and GCP (CICD Project, where the Jenkins Agent is located)." -# type = string -# } - -# variable "jenkins_agent_gce_ssh_pub_key" { -# description = "SSH public key needed by the Jenkins Agent GCE Instance. The Jenkins Controller holds the SSH private key. The correct format is `'ssh-rsa [KEY_VALUE] [USERNAME]'`" -# type = string -# } - -# variable "jenkins_agent_sa_email" { -# description = "Email for Jenkins Agent service account." -# type = string -# default = "jenkins-agent-gce" -# } - -# variable "jenkins_controller_subnetwork_cidr_range" { -# description = "A list of CIDR IP ranges of the Jenkins Controller in the form ['0.0.0.0/0']. Usually only one IP in the form '0.0.0.0/32'. Needed to create a FW rule that allows communication with the Jenkins Agent GCE Instance." -# type = list(string) -# } - -# variable "nat_bgp_asn" { -# type = number -# description = "BGP ASN for NAT cloud route. This is needed to allow the Jenkins Agent to download packages and updates from the internet without having an external IP address." -# } - -# variable "vpn_shared_secret" { -# description = "The shared secret used in the VPN" -# type = string -# } - -# variable "on_prem_vpn_public_ip_address" { -# description = "The public IP Address of the Jenkins Controller." -# type = string -# } - -# variable "on_prem_vpn_public_ip_address2" { -# description = "The second public IP Address of the Jenkins Controller." -# type = string -# } - -# variable "router_asn" { -# type = number -# description = "BGP ASN for cloud routes." -# default = "64515" -# } - -# variable "bgp_peer_asn" { -# type = number -# description = "BGP ASN for cloud routes." -# } - -# variable "tunnel0_bgp_peer_address" { -# type = string -# description = "BGP session address for tunnel 0" -# } - -# variable "tunnel0_bgp_session_range" { -# type = string -# description = "BGP session range for tunnel 0" -# } - -# variable "tunnel1_bgp_peer_address" { -# type = string -# description = "BGP session address for tunnel 1" -# } - -# variable "tunnel1_bgp_session_range" { -# type = string -# description = "BGP session range for tunnel 1" -# } - /* ---------------------------------------- Specific to Groups creation ---------------------------------------- */ @@ -245,3 +163,85 @@ variable "initial_group_config" { type = string default = "WITH_INITIAL_OWNER" } + +/* ---------------------------------------- + Specific to jenkins_bootstrap module + ---------------------------------------- */ + +# # Un-comment the jenkins_bootstrap module and its outputs if you want to use Jenkins instead of Cloud Build +# variable "jenkins_agent_gce_subnetwork_cidr_range" { +# description = "The subnetwork to which the Jenkins Agent will be connected to (in CIDR range 0.0.0.0/0)" +# type = string +# } + +# variable "jenkins_agent_gce_private_ip_address" { +# description = "The private IP Address of the Jenkins Agent. This IP Address must be in the CIDR range of `jenkins_agent_gce_subnetwork_cidr_range` and be reachable through the VPN that exists between on-prem (Jenkins Controller) and GCP (CICD Project, where the Jenkins Agent is located)." +# type = string +# } + +# variable "jenkins_agent_gce_ssh_pub_key" { +# description = "SSH public key needed by the Jenkins Agent GCE Instance. The Jenkins Controller holds the SSH private key. The correct format is `'ssh-rsa [KEY_VALUE] [USERNAME]'`" +# type = string +# } + +# variable "jenkins_agent_sa_email" { +# description = "Email for Jenkins Agent service account." +# type = string +# default = "jenkins-agent-gce" +# } + +# variable "jenkins_controller_subnetwork_cidr_range" { +# description = "A list of CIDR IP ranges of the Jenkins Controller in the form ['0.0.0.0/0']. Usually only one IP in the form '0.0.0.0/32'. Needed to create a FW rule that allows communication with the Jenkins Agent GCE Instance." +# type = list(string) +# } + +# variable "nat_bgp_asn" { +# type = number +# description = "BGP ASN for NAT cloud route. This is needed to allow the Jenkins Agent to download packages and updates from the internet without having an external IP address." +# } + +# variable "vpn_shared_secret" { +# description = "The shared secret used in the VPN" +# type = string +# } + +# variable "on_prem_vpn_public_ip_address" { +# description = "The public IP Address of the Jenkins Controller." +# type = string +# } + +# variable "on_prem_vpn_public_ip_address2" { +# description = "The second public IP Address of the Jenkins Controller." +# type = string +# } + +# variable "router_asn" { +# type = number +# description = "BGP ASN for cloud routes." +# default = "64515" +# } + +# variable "bgp_peer_asn" { +# type = number +# description = "BGP ASN for cloud routes." +# } + +# variable "tunnel0_bgp_peer_address" { +# type = string +# description = "BGP session address for tunnel 0" +# } + +# variable "tunnel0_bgp_session_range" { +# type = string +# description = "BGP session range for tunnel 0" +# } + +# variable "tunnel1_bgp_peer_address" { +# type = string +# description = "BGP session address for tunnel 1" +# } + +# variable "tunnel1_bgp_session_range" { +# type = string +# description = "BGP session range for tunnel 1" +# } diff --git a/1-org/README.md b/1-org/README.md index fc3f8c813..6be292ca0 100644 --- a/1-org/README.md +++ b/1-org/README.md @@ -204,66 +204,7 @@ to run the command as the Terraform Service Account. ### Deploying with Jenkins -1. Clone the repo you created manually in 0-bootstrap. - ``` - git clone - ``` -1. Navigate into the repo and change to a non-production branch. All subsequent - steps assume you are running them from the directory. If - you run them from another directory, adjust your copy paths accordingly. - ``` - cd YOUR_NEW_REPO_CLONE-1-org - git checkout -b plan - ``` -1. Copy contents of foundation to new repo. - ``` - cp -RT ../terraform-example-foundation/1-org/ . - ``` -1. Copy contents of policy-library to new repo. - ``` - cp -RT ../terraform-example-foundation/policy-library/ ./policy-library - ``` -1. Copy the Jenkinsfile script to the root of your new repository.\ - - ``` - cp ../terraform-example-foundation/build/Jenkinsfile . - ``` -1. Update the variables located in the `environment {}` section of the `Jenkinsfile` with values from your environment: - ``` - _TF_SA_EMAIL - _STATE_BUCKET_NAME - _PROJECT_ID (the CI/CD project ID) - ``` -1. Copy Terraform wrapper script to the root of your new repository. - ``` - cp ../terraform-example-foundation/build/tf-wrapper.sh . - ``` -1. Ensure wrapper script can be executed. - ``` - chmod 755 ./tf-wrapper.sh - ``` -1. Check if your organization already has an Access Context Manager Policy. - ``` - gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format="value(name)" - ``` -1. Rename `./envs/shared/terraform.example.tfvars` to `./envs/shared/terraform.tfvars` and update the file with values from your environment and bootstrap. You can re-run `terraform output` in the 0-bootstrap directory to find these values. Make sure that `default_region` is set to a valid [BigQuery dataset region](https://cloud.google.com/bigquery/docs/locations). Also, if the previous step showed a numeric value, make sure to un-comment the variable `create_access_context_manager_access_policy = false`. See the shared folder [README.md](./envs/shared/README.md) for additional information on the values in the `terraform.tfvars` file. -1. Commit changes. - ``` - git add . - git commit -m 'Your message' - ``` -1. Push your plan branch. - - Assuming you configured an automatic trigger in your Jenkins Controller (see [Jenkins sub-module README](../0-bootstrap/modules/jenkins-agent)), this will trigger a plan. You can also trigger a Jenkins job manually. Given the many options to do this in Jenkins, it is out of the scope of this document see [Jenkins website](https://www.jenkins.io) for more details. - ``` - git push --set-upstream origin plan - ``` -1. Review the plan output in your Controller's web UI. -1. Merge changes to production branch. - ``` - git checkout -b production - git push origin production - ``` -1. Review the apply output in your Controller's web UI. (you might want to use the option to "Scan Multibranch Pipeline Now" in your Jenkins Controller UI). +See `0-bootstrap` [README-Jenkins.md](../0-bootstrap/README-Jenkins.md#deploying-step-1-org) ### Running Terraform locally diff --git a/2-environments/README.md b/2-environments/README.md index eb86bbdea..f0227b268 100644 --- a/2-environments/README.md +++ b/2-environments/README.md @@ -141,70 +141,7 @@ commands. The `-T` flag is needed for Linux, but causes problems for MacOS. ### Deploying with Jenkins -1. Clone the repo you created manually in 0-bootstrap. - ``` - git clone - ``` -1. Navigate into the repo and change to a non-production branch. 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 YOUR_NEW_REPO_CLONE-2-environments - git checkout -b plan - ``` -1. Copy contents of foundation to new repo. - ``` - cp -RT ../terraform-example-foundation/2-environments/ . - ``` -1. Copy the Jenkinsfile script to the root of your new repository. - ``` - cp ../terraform-example-foundation/build/Jenkinsfile . - ``` -1. Update the variables located in the `environment {}` section of the `Jenkinsfile` with values from your environment: - ``` - _TF_SA_EMAIL - _STATE_BUCKET_NAME - _PROJECT_ID (the CI/CD project ID) - ``` -1. Copy Terraform wrapper script to the root of your new repository. - ``` - cp ../terraform-example-foundation/build/tf-wrapper.sh . - ``` -1. Ensure wrapper script can be executed. - ``` - chmod 755 ./tf-wrapper.sh - ``` -1. Rename `terraform.example.tfvars` to `terraform.tfvars` and update the file with values from your environment and bootstrap (you can re-run `terraform output` in the 0-bootstrap directory to find these values). See any of the envs folder [README.md](./envs/production/README.md#inputs) files for additional information on the values in the `terraform.tfvars` file. -1. Commit changes. - ``` - git add . - git commit -m 'Your message' - ``` -1. Push your plan branch. - ``` - git push --set-upstream origin plan - ``` - - Assuming you configured an automatic trigger in your Jenkins Controller (see [Jenkins sub-module README](../0-bootstrap/modules/jenkins-agent/README.md)), this will trigger a plan. You can also trigger a Jenkins job manually. Given the many options to do this in Jenkins, it is out of the scope of this document see [Jenkins website](https://www.jenkins.io) for more details. -1. Review the plan output in your Controller's web UI. -1. Merge changes to development. - ``` - git checkout -b development - git push origin development - ``` -1. Review the apply output in your Controller's web UI (you might want to use the option to "Scan Multibranch Pipeline Now" in your Jenkins Controller UI). -1. Merge changes to non-production with. - ``` - git checkout -b non-production - git push origin non-production - ``` -1. Review the apply output in your Controller's web UI (you might want to use the option to "Scan Multibranch Pipeline Now" in your Jenkins Controller UI). -1. Merge changes to production branch. - ``` - git checkout -b production - git push origin production - ``` -1. Review the apply output in your Controller's web UI (you might want to use the option to "Scan Multibranch Pipeline Now" in your Jenkins Controller UI). +See `0-bootstrap` [README-Jenkins.md](../0-bootstrap/README-Jenkins.md#deploying-step-2-environments) ### Run Terraform locally diff --git a/3-networks-dual-svpc/README.md b/3-networks-dual-svpc/README.md index 1ec15ed2c..5beeabc86 100644 --- a/3-networks-dual-svpc/README.md +++ b/3-networks-dual-svpc/README.md @@ -199,80 +199,7 @@ If you are not able to use Dedicated or Partner Interconnect, you can also use a ### Deploying with Jenkins -1. Clone the repo you created manually in 0-bootstrap. - ``` - git clone - ``` -1. Navigate into the repo and change to a non-production branch. All subsequent - steps assume you are running them from the directory. If - you run them from another directory, adjust your copy paths accordingly. - ``` - cd YOUR_NEW_REPO_CLONE-3-networks - git checkout -b plan - ``` -1. Copy contents of foundation to new repo. - ``` - cp -RT ../terraform-example-foundation/3-networks-dual-svpc/ . - ``` -1. Copy the Jenkinsfile script to the root of your new repository. - ``` - cp ../terraform-example-foundation/build/Jenkinsfile . - ``` -1. Update the variables located in the `environment {}` section of the `Jenkinsfile` with values from your environment: - ``` - _TF_SA_EMAIL - _STATE_BUCKET_NAME - _PROJECT_ID (the CI/CD project ID) - ``` -1. Copy Terraform wrapper script to the root of your new repository. - ``` - cp ../terraform-example-foundation/build/tf-wrapper.sh . - ``` -1. Ensure wrapper script can be executed. - ``` - chmod 755 ./tf-wrapper.sh - ``` -1. Rename `common.auto.example.tfvars` to `common.auto.tfvars` and update the file with values from your environment and bootstrap. See any of the envs folder [README.md](./envs/production/README.md) files for additional information on the values in the `common.auto.tfvars` file. -1. Rename `shared.auto.example.tfvars` to `shared.auto.tfvars` and update the file with the `target_name_server_addresses`. -1. Rename `access_context.auto.example.tfvars` to `access_context.auto.tfvars` and update the file with the `access_context_manager_policy_id`. -1. Commit changes. - ``` - git add . - git commit -m 'Your message' - ``` -1. You must manually plan and apply the `shared` environment (only once) since the `development`, `non-production` and `production` environments depend on it. - 1. Run `cd ./envs/shared/`. - 1. Update `backend.tf` with your bucket name from the bootstrap step. - 1. Export the network (`terraform-net-sa`) service account for impersonation `export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=""` - 1. Run `terraform init`. - 1. Run `terraform plan` and review output. - 1. Run `terraform apply`. - 1. If you would like the bucket to be replaced by Cloud Build at run time, change the bucket name back to `UPDATE_ME`. -1. Push your plan branch. - ``` - git push --set-upstream origin plan - ``` - - Assuming you configured an automatic trigger in your Jenkins Controller (see [Jenkins sub-module README](../0-bootstrap/modules/jenkins-agent)), this will trigger a plan. You can also trigger a Jenkins job manually. Given the many options to do this in Jenkins, it is out of the scope of this document see [Jenkins website](https://www.jenkins.io) for more details. -1. Review the plan output in your Controller's web UI. -1. Merge changes to production branch. - ``` - git checkout -b production - git push origin production - ``` -1. Review the apply output in your Controller's web UI (you might want to use the option to "Scan Multibranch Pipeline Now" in your Jenkins Controller UI). -1. After production has been applied, apply development and non-production. -1. Merge changes to development - ``` - git checkout -b development - git push origin development - ``` -1. Review the apply output in your Controller's web UI (you might want to use the option to "Scan Multibranch Pipeline Now" in your Jenkins Controller UI). -1. Merge changes to non-production. - ``` - git checkout -b non-production - git push origin non-production - ``` -1. Review the apply output in your Controller's web UI (you might want to use the option to "Scan Multibranch Pipeline Now" in your Jenkins Controller UI). +See `0-bootstrap` [README-Jenkins.md](../0-bootstrap/README-Jenkins.md#deploying-step-3-networks-dual-svpc) ### Run Terraform locally diff --git a/3-networks-hub-and-spoke/README.md b/3-networks-hub-and-spoke/README.md index 6d2d0c7a8..41f010ab9 100644 --- a/3-networks-hub-and-spoke/README.md +++ b/3-networks-hub-and-spoke/README.md @@ -201,80 +201,7 @@ If you are not able to use Dedicated or Partner Interconnect, you can also use a ### Deploying with Jenkins -1. Clone the repo you created manually in 0-bootstrap. - ``` - git clone - ``` -1. Navigate into the repo and change to a non-production branch. All subsequent - steps assume you are running them from the directory. If - you run them from another directory, adjust your copy paths accordingly. - ``` - cd YOUR_NEW_REPO_CLONE-3-networks - git checkout -b plan - ``` -1. Copy contents of foundation to new repo. - ``` - cp -RT ../terraform-example-foundation/3-networks-hub-and-spoke/ . - ``` -1. Copy the Jenkinsfile script to the root of your new repository. - ``` - cp ../terraform-example-foundation/build/Jenkinsfile . - ``` -1. Update the variables located in the `environment {}` section of the `Jenkinsfile` with values from your environment: - ``` - _TF_SA_EMAIL - _STATE_BUCKET_NAME - _PROJECT_ID (the CI/CD project ID) - ``` -1. Copy Terraform wrapper script to the root of your new repository. - ``` - cp ../terraform-example-foundation/build/tf-wrapper.sh . - ``` -1. Ensure wrapper script can be executed. - ``` - chmod 755 ./tf-wrapper.sh - ``` -1. Rename `common.auto.example.tfvars` to `common.auto.tfvars` and update the file with values from your environment and bootstrap. See any of the envs folder [README.md](./envs/production/README.md) files for additional information on the values in the `common.auto.tfvars` file. -1. Rename `shared.auto.example.tfvars` to `shared.auto.tfvars` and update the file with the `target_name_server_addresses`. -1. Rename `access_context.auto.example.tfvars` to `access_context.auto.tfvars` and update the file with the `access_context_manager_policy_id`. -1. Commit changes. - ``` - git add . - git commit -m 'Your message' - ``` -1. You must manually plan and apply the `shared` environment (only once) since the `development`, `non-production` and `production` environments depend on it. - 1. Run `cd ./envs/shared/`. - 1. Update `backend.tf` with your bucket name from the bootstrap step. - 1. Export the network (`terraform-net-sa`) service account for impersonation `export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=""` - 1. Run `terraform init`. - 1. Run `terraform plan` and review output. - 1. Run `terraform apply`. - 1. If you would like the bucket to be replaced by Cloud Build at run time, change the bucket name back to `UPDATE_ME`. -1. Push your plan branch. - ``` - git push --set-upstream origin plan - ``` - - Assuming you configured an automatic trigger in your Jenkins Controller (see [Jenkins sub-module README](../0-bootstrap/modules/jenkins-agent)), this will trigger a plan. You can also trigger a Jenkins job manually. Given the many options to do this in Jenkins, it is out of the scope of this document see [Jenkins website](https://www.jenkins.io) for more details. -1. Review the plan output in your Controller's web UI. -1. Merge changes to production branch. - ``` - git checkout -b production - git push origin production - ``` -1. Review the apply output in your Controller's web UI (you might want to use the option to "Scan Multibranch Pipeline Now" in your Jenkins Controller UI). -1. After production has been applied, apply development and non-production. -1. Merge changes to development - ``` - git checkout -b development - git push origin development - ``` -1. Review the apply output in your Controller's web UI (you might want to use the option to "Scan Multibranch Pipeline Now" in your Jenkins Controller UI). -1. Merge changes to non-production. - ``` - git checkout -b non-production - git push origin non-production - ``` -1. Review the apply output in your Controller's web UI (you might want to use the option to "Scan Multibranch Pipeline Now" in your Jenkins Controller UI). +See `0-bootstrap` [README-Jenkins.md](../0-bootstrap/README-Jenkins.md#deploying-step-3-networks-hub-and-spoke) ### Run Terraform locally diff --git a/4-projects/README.md b/4-projects/README.md index 6831c8010..286c5d006 100644 --- a/4-projects/README.md +++ b/4-projects/README.md @@ -161,88 +161,7 @@ commands. The `-T` flag is needed for Linux, but causes problems for MacOS. ### Deploying with Jenkins -1. Clone the repo you created manually in 0-bootstrap. - ``` - git clone - ``` -1. Navigate into the repo and change to a non-production branch. All subsequent - steps assume you are running them from the directory. If - you run them from another directory, adjust your copy paths accordingly. - ``` - cd YOUR_NEW_REPO_CLONE-4-projects - git checkout -b plan - ``` -1. Copy contents of foundation to new repo. - ``` - cp -RT ../terraform-example-foundation/4-projects/ . - ``` -1. Copy the Jenkinsfile script to the root of your new repository. - ``` - cp ../terraform-example-foundation/build/Jenkinsfile . - ``` -1. Update the variables located in the `environment {}` section of the `Jenkinsfile` with values from your environment: - ``` - _TF_SA_EMAIL - _STATE_BUCKET_NAME - _PROJECT_ID (the CI/CD project ID) - ``` -1. Copy Terraform wrapper script to the root of your new repository. - ``` - cp ../terraform-example-foundation/build/tf-wrapper.sh . - ``` -1. Ensure wrapper script can be executed. - ``` - chmod 755 ./tf-wrapper.sh - ``` -1. Rename `common.auto.example.tfvars` to `common.auto.tfvars` and update the file with values from your environment and bootstrap. -1. Rename `shared.auto.example.tfvars` to `shared.auto.tfvars` and update the file with values from your environment and bootstrap. -1. Rename `development.auto.example.tfvars` to `development.auto.tfvars` and update the file with the `perimeter_name` that starts with `sp_d_shared_restricted`. -1. Rename `non-production.auto.example.tfvars` to `non-production.auto.tfvars` and update the file with the `perimeter_name` that starts with `sp_n_shared_restricted`. -1. Rename `production.auto.example.tfvars` to `production.auto.tfvars` and update the file with the `perimeter_name` that starts with `sp_p_shared_restricted`. -1. Rename `access_context.auto.example.tfvars` to `access_context.auto.tfvars` and update the file with the `access_context_manager_policy_id`. -1. You need to manually plan and apply only once the `business_unit_1/shared` environment since `development`, `non-production`, and `production` depend on it. - 1. Run `cd ./business_unit_1/shared/`. - 1. Update `backend.tf` with your bucket name from the 0-bootstrap step. - 1. Export the projects (`terraform-proj-sa`) service account for impersonation `export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=""` - 1. Run `terraform init`. - 1. Run `terraform plan` and review output. - 1. Run `terraform apply`. - 1. Run `terraform output cloudbuild_sa` to get the cloud build service account from the apply step. - 1. If you would like the bucket to be replaced by cloud build at run time, change the bucket name back to `UPDATE_ME` -1. Once you have done the instructions for the `business_unit_1`, you need to repeat same steps for `business_unit_2` folder. -1. Rename `business_unit_1.auto.example.tfvars` to `business_unit_1.auto.tfvars` and update the file with the `app_infra_pipeline_cloudbuild_sa` which is the output of `cloudbuild_sa` from `business_unit_1/shared` steps. -1. Rename `business_unit_2.auto.example.tfvars` to `business_unit_2.auto.tfvars` and update the file with the `app_infra_pipeline_cloudbuild_sa` which is the output of `cloudbuild_sa` from `business_unit_2/shared` steps. -1. Commit changes. - ``` - git add . - git commit -m 'Your message' - ``` -1. Push your plan branch. - ``` - git push --set-upstream origin plan - ``` - - Assuming you configured an automatic trigger in your Jenkins Controller (see [Jenkins sub-module README](../0-bootstrap/modules/jenkins-agent)), this will trigger a plan. You can also trigger a Jenkins job manually. Given the many options to do this in Jenkins, it is out of the scope of this document see [Jenkins website](https://www.jenkins.io) for more details. -1. Review the plan output in your Controller's web UI. -1. Merge changes to production branch. - ``` - git checkout -b production - git push origin production - ``` -1. Review the apply output in your Controller's web UI (you might want to use the option to "Scan Multibranch Pipeline Now" in your Jenkins Controller UI). -1. After production has been applied, apply development. -1. Merge changes to development branch. - ``` - git checkout -b development - git push origin development - ``` -1. Review the apply output in your Controller's web UI (you might want to use the option to "Scan Multibranch Pipeline Now" in your Jenkins Controller UI). -1. After development has been applied, apply non-production. -1. Merge changes to non-production branch. - ``` - git checkout -b non-production - git push origin non-production - ``` -1. Review the apply output in your Controller's web UI (you might want to use the option to "Scan Multibranch Pipeline Now" in your Jenkins Controller UI). +See `0-bootstrap` [README-Jenkins.md](../0-bootstrap/README-Jenkins.md#deploying-step-4-projects) ### Run Terraform locally diff --git a/build/Jenkinsfile b/build/Jenkinsfile index 7bb705717..708a66410 100644 --- a/build/Jenkinsfile +++ b/build/Jenkinsfile @@ -5,9 +5,9 @@ pipeline { environment { _POLICY_REPO="policy-library" - _TF_SA_EMAIL="" - _STATE_BUCKET_NAME="" - _PROJECT_ID="" + _TF_SA_EMAIL="TERRAFORM_SA_EMAIL" + _STATE_BUCKET_NAME="BACKEND_STATE_BUCKET_NAME" + _PROJECT_ID="CICD_PROJECT_ID" } stages { @@ -33,6 +33,7 @@ pipeline { } steps { sh ''' + export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=${_TF_SA_EMAIL} ./tf-wrapper.sh plan_validate_all ${BRANCH_NAME} ${WORKSPACE}/${_POLICY_REPO} ${_PROJECT_ID} ''' } @@ -47,6 +48,7 @@ pipeline { } steps { sh ''' + export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=${_TF_SA_EMAIL} ./tf-wrapper.sh init $BRANCH_NAME ''' } @@ -61,6 +63,7 @@ pipeline { } steps { sh ''' + export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=${_TF_SA_EMAIL} ./tf-wrapper.sh plan $BRANCH_NAME ''' } @@ -75,6 +78,7 @@ pipeline { } steps { sh ''' + export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=${_TF_SA_EMAIL} ./tf-wrapper.sh validate ${BRANCH_NAME} ${WORKSPACE}/${_POLICY_REPO} ${_PROJECT_ID} ''' } @@ -103,6 +107,7 @@ pipeline { } steps { sh ''' + export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=${_TF_SA_EMAIL} ./tf-wrapper.sh apply $BRANCH_NAME ''' }