Prepare and test your skills
Prepare and test your skills
Design and Optimize Build Configurations and Artifacts is the practice of defining an efficient cloudbuild.yaml pipeline in Cloud Build (opens in a new tab) that tests code, builds container images, and distributes versioned outputs to Artifact Registry. A build configuration declares an ordered list of steps, where each step runs in a builder container with shared source and workspace. Substitutions make the same file reusable across projects, regions, and commits, and the resulting image tag becomes the traceable link between a source commit and what is deployed.
A cloudbuild.yaml build configuration in Cloud Build defines work as an ordered sequence of steps that share the build workspace. Each step specifies a builder image in name, a unique id, an optional entrypoint to override the image default, args passed to the container, and optional env values such as cluster location. Data flows from the fetched source into the first step, then from step to step in declared order, so a later step can use files created by an earlier step.
The following builders divide continuous integration and delivery work by tool:
| Builder image | Role in the pipeline |
|---|---|
python:3.13-slim | Run unit tests and install dependencies |
gcr.io/cloud-builders/docker | Build container images and push images to Artifact Registry |
gcr.io/cloud-builders/gcloud | Clone source repositories and generate manifests with shell commands |
gcr.io/cloud-builders/kubectl | Apply Kubernetes manifests to a Google Kubernetes Engine cluster |
gcr.io/cloud-builders/git | Fetch branches, copy manifests, and push commits between branches |
A minimal continuous integration pipeline runs three steps in strict order:
pip install flask && python test_app.py -v in python:3.13-slim with entrypoint: /bin/sh.docker build -t us-central1-docker.pkg.dev/$PROJECT_ID/my-repository/hello-cloudbuild:$SHORT_SHA . in gcr.io/cloud-builders/docker.docker push us-central1-docker.pkg.dev/$PROJECT_ID/my-repository/hello-cloudbuild:$SHORT_SHA in gcr.io/cloud-builders/docker.The extended pipeline that connects integration to delivery adds three more steps after the push. First the Clone env repository step clones hello-cloudbuild-env and checks out the candidate branch, then the Generate manifest step renders kubernetes.yaml.tpl with sed into hello-cloudbuild-env/kubernetes.yaml, and only after that the Push manifest step commits and pushes to origin candidate. The delivery configuration then applies that manifest and copies it to production, so the continuous integration workspace output becomes the continuous delivery trigger input.
Build artifacts in Cloud Build are the versioned outputs kept for deployment, and container images are stored in Artifact Registry repositories. The image path encodes its destination, including region, project, repository, image, and tag, such as us-central1-docker.pkg.dev/${PROJECT_ID}/my-repository/hello-cloudbuild:${COMMIT_ID}. Tagging with the short commit SHA, such as $SHORT_SHA or $(git rev-parse --short=7 HEAD), ties each stored image to the exact commit that produced it.
Images reach Artifact Registry through two paths that depend on the same tagging model. The gcloud builds submit --tag="us-central1-docker.pkg.dev/${PROJECT_ID}/my-repository/hello-cloudbuild:${COMMIT_ID}" . command builds from a Dockerfile in the current directory and streams logs to the terminal, with the new image then visible in Artifact Registry. The declarative path uses the Docker builder Build and Push steps, so every trigger run rebuilds and pushes without manual commands. Distribution depends on identity and permissions: the Cloud Build service account ${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com needs roles/artifactregistry.writer to push, while a dedicated publisher such as azure-pipelines-publisher@$PROD_PROJECT_ID.iam.gserviceaccount.com needs the Writer role when an external system publishes.
Substitutions in Cloud Build are variables replaced at build time that keep one cloudbuild.yaml reusable across environments. Default substitutions such as $PROJECT_ID, $SHORT_SHA, and $COMMIT_SHA are automatically replaced by Cloud Build, so the same Build and Push steps produce a correctly namespaced and uniquely tagged image on every commit. Custom substitutions start with an underscore and are passed in at trigger creation or invocation time.
A manual trigger example passes four custom values with the --substitutions flag:
$_REGION for the Artifact Registry region$_AR_REPO_NAME for the Artifact Registry repository name, such as custom-images$_AR_IMAGE_NAME for the container image name, such as code-oss-java$_IMAGE_DIR for the directory containing the Dockerfile to buildThe trigger also binds execution to source and configuration through flags such as --repo, --repo-type=GITHUB, --branch=main, and --build-config=labs/cloudbuild-scheduled-jobs/code-oss-java/cloudbuild.yaml. Because the build configuration file references the substitution variables instead of hard-coded values, the same file builds different images and regions by changing only the trigger invocation.
Exam tip: A build trigger ignores the service account specified inside the build config file and uses the trigger's service account, so grant push, deploy, and repository-write roles to the trigger identity.
Linking build output to deployment targets in Cloud Build means the image tag generated during integration is written into a Kubernetes manifest that delivery then applies. The Generate manifest step substitutes ${PROJECT_ID} and ${SHORT_SHA} into kubernetes.yaml.tpl to produce a manifest that points at us-central1-docker.pkg.dev/$PROJECT_ID/my-repository/hello-cloudbuild:${SHORT_SHA}. The Push manifest step commits that file with the source commit, author, and image tag in the message and pushes it to the candidate branch of hello-cloudbuild-env, which starts the delivery build.
The delivery build depends on the manifest pushed by integration and on cluster credentials. The Deploy step runs kubectl apply -f kubernetes.yaml with env values CLOUDSDK_COMPUTE_REGION=us-central1 and CLOUDSDK_CONTAINER_CLUSTER=hello-cloudbuild to update the cluster. Only after a successful apply, the Copy to production branch step fetches origin production, checks out kubernetes.yaml from $COMMIT_SHA, commits it, and pushes to origin production. The delivery service account therefore also needs Kubernetes Engine Developer access to deploy and Source Repository Writer access on hello-cloudbuild-env to complete the promotion from candidate to production.
Cloud Build (opens in a new tab) triggers automate continuous integration by starting a build when a defined event occurs in a connected source repository. A trigger binds a source, an event filter, a cloudbuild.yaml configuration file, substitution values, and a service account into one repeatable pipeline. When code is pushed to the monitored branch, data flows from the repository event to the trigger, from the trigger to the build steps, and from the build steps to deployment targets such as APIs or artifact stores.
Source integration connects Cloud Build to Cloud Source Repositories, GitHub, GitLab, or Bitbucket so pushes can start builds. Cloud Source Repositories are managed with gcloud source repos create, gcloud source repos clone, and gcloud source repos delete, while external Git repositories are linked with Connect repository or Link repository using Cloud Build repositories (2nd gen) or Developer Connect connection links. A typical bootstrap copies code from an upstream Git URL such as https://github.com/GoogleCloudPlatform/cloud-build-samples.git into a newly created Cloud Source Repository, commits with a configured git identity, and pushes to establish the monitored codebase. Once linked, the trigger's Source section points to that specific connected repository.
A Cloud Build trigger is an event-driven alternative to time-based schedules that compiles and invokes work immediately after a commit. During trigger creation the builder selects a region, selects an event for the trigger, selects the connected repository, and sets a branch filter such as the repository's main branch. Only pushes that match the branch filter start a build, and the build can read the matched branch through the built-in $BRANCH_NAME variable. Committing and pushing the cloudbuild.yaml file to the monitored branch is therefore both the test method and the normal operating path.
Substitution variables parameterize one cloudbuild.yaml file so the same build definition works across repositories, regions, and environments. User-defined substitutions must start with an underscore, are declared in a substitutions: block with defaults, and are overridden with concrete values in the trigger's Substitution variables section:
_DATAFORM_REPO_ID: target repository ID with no default and required in the trigger_RELEASE_CONFIG_ID: target release configuration ID with no default and required in the trigger_WORKFLOW_CONFIG_ID: target workflow configuration ID with no default and required in the trigger_DATAFORM_LOCATION: repository region with a default such as us-central1 that can be overridden_PROJECT_ID: build project ID that automatically uses the build's projectInside bash build steps, references use $$ escaping such as $${_PROJECT_ID} or $$TOKEN so Cloud Build substitutes first and bash expands second. The Configuration section of the trigger selects the YAML or JSON build file and its location, for example /cloudbuild.yaml, and the build steps then use the resolved values to construct resource names and API endpoints.
Exam tip: A single $ like $BRANCH_NAME is a built-in Cloud Build variable, _NAME like _DATAFORM_REPO_ID is a user substitution set in the trigger, and $$NAME inside a bash step prevents early expansion so the value survives into the script.
The trigger's service account determines which Google Cloud APIs the build can call. Production triggers select a custom service account with least-privilege roles rather than relying on the default Cloud Build service account, and required APIs such as cloudbuild.googleapis.com and sourcerepo.googleapis.com are enabled with gcloud services enable. Supporting grants include roles/editor or narrower build roles and roles/source.admin for repository management on the build service account PROJECT_NUM@cloudbuild.gserviceaccount.com. Creating the trigger follows an ordered workflow:
Testing a trigger validates the full path from source event through build steps to downstream invocation. The test starts when the cloudbuild.yaml file is committed and pushed to the monitored branch, which causes the trigger to start a build that appears on the Build History page. A multi-step build then executes in order: first it creates a compilation result via API, then it parses the returned name, then it patches the release configuration to mark that result live, and only after that it launches the workflow invocation. Successful completion is confirmed when the downstream system shows a new compilation result marked live and a new workflow invocation in its execution logs.
Integrating security and compliance in Cloud Build (opens in a new tab) continuous integration pipelines means every build validates what it produces and limits what it can access. Vulnerability scanning checks container artifacts after they are pushed, Binary Authorization blocks deployment unless a trusted attestation exists, least-privilege service accounts restrict build permissions, and infrastructure as code validation checks Terraform against organization policy before resources change.
Vulnerability scanning in Cloud Build relies on Artifact Registry storing the artifact and Artifact Analysis scanning it for known issues. The flow runs from the build to Artifact Registry to Artifact Analysis, and results appear in the Security insights side panel on the Build History page. Scanning requires enabling the Container Scanning API (containerscanning.googleapis.com) and storing the build artifact in Artifact Registry, after which scanning starts automatically and can take several minutes depending on image size.
The Security insights panel for a build shows peer security signals for the selected artifact:
Viewing insights requires Cloud Build Viewer (roles/cloudbuild.builds.viewer) to see the build and Container Analysis Occurrences Viewer (roles/containeranalysis.occurrences.viewer) to see vulnerabilities and dependency data. When Artifact Registry and Artifact Analysis run in different projects, the Container Analysis Occurrences Viewer role must be granted in the project where Artifact Analysis runs.
Binary Authorization is the deployment gate that allows a cluster or service to run only images that carry a signed attestation from a trusted authority. In a Cloud Build pipeline, the build evaluates supply-chain checks, such as OpenSSF Scorecard policy, and only when the checks pass does it sign an attestation with Cloud Key Management Service (Cloud KMS); the later deployment policy then verifies that signature before admitting the image. A failing image still builds successfully but produces no attestation, so a policy violation such as an unpinned Debian 10 dependency or a compiled binary checked into source prevents deployment.
Setting up an attestor depends on Cloud KMS for signing and Binary Authorization for verification:
roles/cloudkms.signer).scorecard-attestor-key-ring in location global and key scorecard-attestor-key with purpose asymmetric-signing and algorithm rsa-sign-pkcs1-2048-sha256.SHA256 and key name projects/PROJECT_ID/locations/global/keyRings/scorecard-attestor-key-ring/cryptoKeys/scorecard-attestor-key/cryptoKeyVersions/1.scorecard-attestor with attestation authority note scorecard-attestation and a description of the checks it certifies.Exam tip: Binary Authorization does not scan for vulnerabilities; Artifact Analysis scanning produces findings, while a Binary Authorization attestation proves a policy check passed and was signed by Cloud KMS.
Least-privilege identity in Cloud Build means each build runs as a user-specified service account (opens in a new tab) that holds only the roles needed for that pipeline. If no account is specified, Cloud Build may automatically select a default service account with unnecessarily broad access, such as access to source repositories and any Cloud Storage bucket in the project. Creating a dedicated account per task, for example one account only for building and pushing images to Artifact Registry, reduces the impact of misconfiguration or a compromised build step.
The choice between identities changes both permission scope and operational requirements:
| Identity | Permission scope | Log storage requirement |
|---|---|---|
| Default Cloud Build service account | Broad by default, may include source and storage access beyond the build need | Can use the default Cloud Storage logs bucket |
| User-specified service account | Scoped by administrator through the Cloud Build Permissions settings page and IAM grants for only required APIs and resources | Must store logs in Cloud Logging or a user-created Cloud Storage bucket, not the default bucket |
Granting access starts on the Cloud Build Settings Permissions page, where the administrator selects the service account and enables only needed roles, then adds any missing roles on the IAM page. Private and sensitive connections follow the same model: a build that reads from Secure Source Manager over Private Service Connect with private pools grants that account only roles/privateca.auditor for the CA certificate, roles/securesourcemanager.instanceAccessor for the instance, and roles/securesourcemanager.repoReader for repositories. The underlying Cloud Build service agent (service-PROJECT_NUMBER@gcp-sa-cloudbuild.iam.gserviceaccount.com) must retain roles/cloudbuild.serviceAgent, and trigger identities need cloudbuild.builds.create, while deployment to managed services also requires iam.serviceAccounts.actAs on the target runtime service account.
Infrastructure as code validation in Cloud Build checks Terraform resource definitions in the build against the same controls applied to live Google Cloud resources. A build config instructs Cloud Build to evaluate the proposed configuration against organization policies and Security Health Analytics detectors defined through a Security Command Center security posture. This shifts compliance left, so a Terraform change that violates policy fails during continuous integration instead of after deployment.
Validation depends on Security Command Center Premium or Enterprise tier activated at the organization level, which enables the securityposture.googleapis.com and securitycentermanagement.googleapis.com APIs. The validating identity needs organization-level roles for posture evaluation and evidence handling, including Security Posture Shift-Left Validator, Log Writer, Storage Writer, and Storage Reader. Policies themselves are defined as organization policies and Security Health Analytics detectors, typically authored and deployed as a security posture, while pipelines that promote integrations also enable the Secret Manager API (secretmanager.googleapis.com) so credentials and environment-specific config values are managed outside hardcoded build files.
cloudbuild.yaml file defines an ordered sequence of builder steps that share source and workspace, where data flows from the fetched source through Test, Build, and Push steps to Artifact Registry.$SHORT_SHA or $COMMIT_SHA ties each Artifact Registry image to the exact commit that produced it, and the tag is written into Kubernetes manifests to link integration output to deployment.A minimal continuous integration pipeline in Cloud Build runs Test, Build, and Push steps in strict order that share the build workspace. The Test step runs unit tests in the python:3.13-slim builder, while the Build and Push steps use the gcr.io/cloud-builders/docker builder to build the container image tagged with the short commit SHA and push it to Artifact Registry. Data flows from the fetched source into the first step and then from step to step in declared order so later steps can use files created by earlier steps.
Substitutions in Cloud Build are variables replaced at build time that keep one cloudbuild.yaml file reusable across projects, regions, and commits. Default substitutions such as $PROJECT_ID, $SHORT_SHA, and $COMMIT_SHA are automatically replaced by Cloud Build so the same Build and Push steps produce a correctly namespaced and uniquely tagged image on every commit. Custom substitutions start with an underscore and are passed in at trigger creation or invocation time, so changing only the trigger values builds different images and regions.
Artifact Analysis scanning produces vulnerability findings for artifacts stored in Artifact Registry that appear in the Security insights side panel on the Build History page. Binary Authorization does not scan for vulnerabilities and instead blocks deployment unless the image carries a signed attestation proving supply-chain checks passed and were signed with Cloud Key Management Service (Cloud KMS). A failing image still builds successfully but produces no attestation, so a policy violation prevents deployment even though the build completed.
A Cloud Build trigger binds a connected source repository, an event filter, a cloudbuild.yaml configuration file, substitution values, and a service account into one repeatable pipeline. Only pushes that match the branch filter such as the main branch start a build, and the build can read the matched branch through the built-in $BRANCH_NAME variable. Committing and pushing the cloudbuild.yaml file to the monitored branch both tests the trigger and follows the normal operating path.
Your team is building a continuous integration pipeline in Cloud Build to package and publish Python libraries to Google Cloud Artifact Registry across different deployment environments (such as staging and production).
You need to create a reusable cloudbuild.yaml configuration that dynamically targets different repository locations and names without hardcoding values, while relying on Cloud Build's default variables for the project ID.
Which configuration and submission command should you use?
Last updated