11.3 Builders, Substitutions, Triggers, and IAM
Key Takeaways
Custom substitution names begin with an underscore.
Approval gates apply before the triggered build executes, not to an arbitrary middle step.
Build identity, deployer and runtime service account need separate least-privilege permissions.
Standard Builders vs. Custom Builders
Cloud Build does not require proprietary DSLs; any Docker container image can serve as a builder image.
Standard Cloud Builders
Google Cloud publishes and maintains open-source, builder container images; permissions come from the build identity known as Standard Cloud Builders. Historically hosted on gcr.io/cloud-builders/ and modern mirrors, these images come pre-installed with essential toolchains:
docker: Docker CLI client configured to interact with the host Docker daemon.gcloud: Google Cloud SDK CLI for managing cloud resources, IAM, and deployments.kubectl: Kubernetes command-line tool configured to deploy workloads to GKE.git: Git source control client for shallow clones, branch checkouts, and submodule initialization.gsutil: Cloud Storage CLI tool for staging build tarballs and static website assets.- Language Runtimes:
golang,npm,mvn,gradle,python,bazel.
Custom Builders
When your pipeline requires specialized toolchains not covered by standard builders (such as Terraform, Helm, Ansible, Trivy, or custom compilation scripts), you can create and execute a Custom Builder.
A custom builder is simply any public or private container image containing your required executable:
- If public (e.g., Docker Hub or Quay), specify the full image URI in the
namefield (e.g.,hashicorp/terraform:1.7). - If private, build the image in an initial build step, push it to Artifact Registry, and call it in subsequent pipeline steps.
steps:
# Using a custom builder from Docker Hub to run Helm lint
- name: 'alpine/helm:3.14.0'
args: ['lint', './helm/payment-service']
Substitutions and Variables
Substitutions allow developers to parameterize cloudbuild.yaml configurations across multiple deployment environments, branches, and repositories without hardcoding strings.
Built-In Substitutions
Cloud Build provides variables according to invocation context; trigger-only variables can be empty for manual builds:
| Variable Name | Description | Example Value |
|---|---|---|
$PROJECT_ID | The Google Cloud project ID where the build is running | corp-finance-prod |
$BUILD_ID | The unique UUID identifier assigned to the build execution | 3d4f8a7e-1234-5678-abcd-ef0123456789 |
$COMMIT_SHA | The complete 40-character Git commit hash triggering the build | 7c1b2f4893708a984f1839201a0d829374019283 |
$SHORT_SHA | The first seven characters of the commit SHA | 7c1b2f4 |
$BRANCH_NAME | The Git branch name (available on branch triggers) | main or feature/login |
$TAG_NAME | The Git tag name (available on tag triggers) | v1.2.0 |
$REPO_NAME | The name of the source repository | payment-service |
User-Defined Substitutions & The Underscore Rule
Developers can define custom substitution keys under the substitutions block.
Exam Trap: All user-defined substitution variables MUST begin with an underscore (e.g.,
$_ENVIRONMENT,$_CLUSTER_NAME,$_RELEASE_TAG). If you declare a custom substitution variable that does not start with an underscore (such asENVIRONMENT: staging), Cloud Build will reject the configuration with a schema validation error before the build executes.
Substitution Handling Options
For a manually submitted build, Cloud Build normally operates in strict matching mode (substitutionOption: 'MUST_MATCH'). If your YAML template references a substitution variable that is not supplied by the trigger, or if a supplied substitution is never referenced in the configuration, Cloud Build halts execution with an error.
To allow optional parameters across dynamic multi-branch workflows, specify:
options:
substitutionOption: 'ALLOW_LOOSE'
Triggered builds always use ALLOW_LOOSE. Several built-in source variables have empty defaults even without it; user-defined missing substitution handling must not be mistaken for a required value being validated. Explicitly validate required configuration inside the pipeline.
Build Triggers and Automation
Build Triggers connect source code repositories directly to Cloud Build, automatically launching builds upon incoming webhook events or code repository lifecycle actions.
Supported Source Repositories
Cloud Build natively connects to:
- GitHub & GitHub Enterprise: Via the official Google Cloud Build GitHub App.
- Bitbucket Cloud & Server: Via secure OAuth and API tokens.
- GitLab: Via 2nd gen developer connections.
- Cloud Source Repositories: Google Cloud's private Git repository service.
- Webhook Triggers: Arbitrary HTTP targets triggered by third-party systems (such as Jenkins, Spinnaker, or custom Slack bots) using the documented webhook-secret authentication mechanism.
Trigger Filters and Matchers
- Branch Name / Tag Regex: Specify regex patterns (such as
^main$or^v[0-9]+\.[0-9]+\.[0-9]+$) to restrict triggers to production releases or release candidate branches. - File Inclusion and Exclusion (
includedFiles/ignoredFiles): In mono-repositories containing multiple microservices, code modifications to one service should not trigger rebuilds of all other services. Use glob patterns (such asservices/order-api/**) to scope execution:
## Trigger specification snippet
filename: 'services/order-api/cloudbuild.yaml'
includedFiles:
- 'services/order-api/**'
ignoredFiles:
- 'services/order-api/docs/**'
- 'services/order-api/*.md'
Manual Approval Gates
Approval applies to the entire triggered build before it executes, not to an arbitrary middle step. Separate CI and production-deployment builds when tests and artifact creation must finish before a reviewer approves deployment. Give approvers the appropriate role and keep approval, build, and runtime identities distinct. A build that pauses before execution has not yet run its tests.
Service Account Permissions & Least Privilege
When Cloud Build executes, it acts under the identity of a service account. Understanding this identity model is crucial for resolving common permission failures on the exam.
Legacy Default vs. User-Managed Service Accounts
- Legacy Default Cloud Build Account: Format
[PROJECT_NUMBER]@cloudbuild.gserviceaccount.com. The selected default account depends on project history and organization policy; newer projects commonly use the Compute Engine default service account. The legacy account can have the Cloud Build Service Account role, whose role is not an explicit deny: its actual permissions and any additional grants determine access. Do not assume it is selected or sufficiently privileged for a particular deployment. - Modern Best Practice (User-Managed Service Accounts): Create a dedicated, user-managed service account (e.g.,
sa-cloudbuild-runner@my-project.iam.gserviceaccount.com) and associate it directly with the Build Trigger. This adheres to the Principle of Least Privilege and isolates CI/CD permissions per pipeline.
Required IAM Roles for Common Deployment Targets
If your pipeline deploys to Cloud Run or GKE, the Cloud Build service account requires specific IAM grants:
| Deployment Target | Required IAM Role Grants | Purpose |
|---|---|---|
| Cloud Run | roles/run.developer (Run Admin only for needed additional operations) | Permission to deploy new revisions and route traffic |
| Cloud Run Service Account | roles/iam.serviceAccountUser | Permission for Cloud Build to "act as" the runtime service account assigned to the Cloud Run service |
| Google Kubernetes Engine | roles/container.developer | Permission to apply Kubernetes manifests and update deployments |
| Artifact Registry | roles/artifactregistry.writer | Permission to push Docker images and package manifests |
| Secret Manager | roles/secretmanager.secretAccessor | Permission to retrieve API keys, tokens, and deployment credentials |
Exam Trap: When Cloud Build fails with
Error: Permission 'iam.serviceAccounts.actAs' denied on service account [RUNTIME_SA], grantingroles/run.developer(or Run Admin only when the deployment also needs its additional permissions) is insufficient. Cloud Build must also be grantedroles/iam.serviceAccountUseron the specific runtime service account that Cloud Run uses, allowing the build process to bind that identity to the newly deployed container revision.
Official references checked October 18, 2026: Google Cloud 1.
A developer writes a custom cloudbuild.yaml file and defines a user-specified variable under the 'substitutions' block as 'ENVIRONMENT: production'. When pushing the commit to trigger a build, Cloud Build fails immediately with an error before executing any pipeline steps. What is the cause of this failure?
Custom substitutions must be stored in Secret Manager rather than declared in cloudbuild.yaml
Cloud Build requires all substitution values to be specified entirely in lowercase alphanumeric characters
User-defined substitution variable keys must begin with an underscore, such as '_ENVIRONMENT'
The Cloud Build service account lacks the roles/cloudbuild.builds.editor IAM permission to read substitutions
Sections you finish are checked off in the contents.