Skip to content

Commit

Permalink
chore: unify Jenkins documentation in a single file (terraform-google…
Browse files Browse the repository at this point in the history
…-modules#823)

* unify Jenkins documentation in a single file

* use GOOGLE_IMPERSONATE_SERVICE_ACCOUNT for impersonation in jenkinsfile

* fix linting for jenkins code

* reorganize outputs in step 0-bootstrap

* reorganize code for Jenkins toggle

* reorganize jenkins code

* review and lint jenkins READMEs

* fix markdown lint

* use the same number of spaces used in other READMEs
  • Loading branch information
daniel-cit authored Sep 29, 2022
1 parent dca5d1c commit 77be952
Show file tree
Hide file tree
Showing 17 changed files with 1,141 additions and 727 deletions.
1,006 changes: 881 additions & 125 deletions 0-bootstrap/README-Jenkins.md

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion 0-bootstrap/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. |
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
20 changes: 19 additions & 1 deletion 0-bootstrap/cb.tf
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

/**
* Copyright 2022 Google LLC
*
Expand Down Expand Up @@ -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}"
}
60 changes: 60 additions & 0 deletions 0-bootstrap/jenkins.tf.example
Original file line number Diff line number Diff line change
@@ -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}"
}
42 changes: 0 additions & 42 deletions 0-bootstrap/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
# }
56 changes: 28 additions & 28 deletions 0-bootstrap/modules/jenkins-agent/README.md
Original file line number Diff line number Diff line change
@@ -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 `[email protected]` 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 `[email protected]` 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

Expand All @@ -23,9 +24,8 @@ module "jenkins_bootstrap" {
billing_account = "<BILLING_ACCOUNT_ID>"
group_org_admins = "[email protected]"
default_region = "us-central1"
terraform_service_account = "<SERVICE_ACCOUNT_EMAIL>" # normally module.seed_bootstrap.terraform_sa_email
terraform_sa_name = "<SERVICE_ACCOUNT_NAME>" # normally module.seed_bootstrap.terraform_sa_name
terraform_state_bucket = "<GCS_STATE_BUCKET_NAME>" # normally module.seed_bootstrap.gcs_bucket_tfstate
terraform_sa_names = "<SERVICE_ACCOUNT_NAMES>"
terraform_state_bucket = "<GCS_STATE_BUCKET_NAME>"
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"
Expand All @@ -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.

<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
Expand Down Expand Up @@ -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 |
Expand All @@ -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

Expand All @@ -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 <empty> 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" {
Expand Down
4 changes: 2 additions & 2 deletions 0-bootstrap/modules/jenkins-agent/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
}
Expand Down
Loading

0 comments on commit 77be952

Please sign in to comment.