Configure and submit an Image Builder pipeline programmatically by using the Google Cloud CLI or Terraform. Configuring your pipeline programmatically lets you define infrastructure settings, base OS images, customization actions, and validation tests in declarative configuration files.
Before you begin
- Complete the environment setup steps in Prepare your environment.
- If you intend to deploy your pipeline using Terraform or automate builds
from a repository, connect your GitHub, GitLab, or Bitbucket repository by using
Cloud Build repositories (
2nd gen) or Developer Connect connection links. - If you plan to use Terraform, install the Terraform CLI version 1.3 or later.
-
If you haven't already, set up authentication.
Authentication verifies your identity for access to Google Cloud services and APIs. To run
code or samples from a local development environment, you can authenticate to
Compute Engine by selecting one of the following options:
Select the tab for how you plan to use the samples on this page:
gcloud
-
Install the Google Cloud CLI, and then sign in to the gcloud CLI with your federated identity. After signing in, initialize the Google Cloud CLI by running the following command:
gcloud init - Set a default region and zone.
Terraform
To use the Terraform samples on this page in a local development environment, install and initialize the gcloud CLI, and then set up Application Default Credentials with your user credentials.
-
Install the Google Cloud CLI.
-
Configure the gcloud CLI to use your federated identity.
For more information, see Sign in to the gcloud CLI with your federated identity.
-
Create local authentication credentials for your user account:
gcloud auth application-default login
If an authentication error is returned, and you are using an external identity provider (IdP), confirm that you have signed in to the gcloud CLI with your federated identity.
For more information, see Set up authentication for a local development environment.
-
Required roles
To get the permissions that you need to create and submit image customization pipelines using the Google Cloud CLI or Terraform, ask your administrator to grant you the following IAM roles on your project:
- Cloud Build Editor (
roles/cloudbuild.builds.editor) - Service Account User (
roles/iam.serviceAccountUser)
For more information about granting roles, see Manage access to projects, folders, and organizations.
You might also be able to get the required permissions through custom roles or other predefined roles.
Create configuration files
To configure your pipeline by using the gcloud CLI or Terraform, create two configuration files:
imagebuilder.yaml: defines the customization recipe for the image, including the base OS image, worker VM infrastructure settings, target image output details, and the sequential customization steps such as running shell scripts, transferring files, or performing reboots.cloudbuild.yaml: orchestrates the build process steps in Cloud Build, including parsing, validation, building, testing, and publishing the custom OS image.
Create the image configuration file
To specify your image configuration, create a file named imagebuilder.yaml
in your local directory. For a complete list of all supported schema fields and
customization actions,
see Customization recipe schema
and Supported customization actions.
The following sample imagebuilder.yaml file
configures a pipeline that builds a customized Ubuntu 22.04 LTS image using an
e2-standard-4 worker VM in your
specified region and zone, and performs a system package update.
apiVersion: imagebuilder.gcp.com/v1 kind: OSImageCustomization metadata: name: customized-ubuntu-baseline description: "Ubuntu 22.04 LTS custom OS baseline image" infrastructureConfig: machineType: e2-standard-4 zone: ZONE debug: false source: imageFamily: projects/ubuntu-os-cloud/global/images/family/ubuntu-2204-lts destinations: - diskImage: name: custom-ubuntu-v1 family: custom-ubuntu-family project: PROJECT_ID storageLocations: - REGION spec: config: skipSystemTests: false steps: - name: "System Package Update" action: Shell inputs: command: "apt-get update -y && apt-get upgrade -y"
Replace the following placeholder values:
PROJECT_ID: your project ID.REGION: the target image storage location, for example,us-east1oreurope-west1. Ensure that you meet the following regional and zonal requirements:- Image Builder is only supported in regions where Cloud Build is available.
- The worker VM
ZONEmust be located within your specifiedREGION. - To minimize network latency and prevent cross-region egress charges, ensure that your worker VM zone, Cloud Storage staging bucket, Artifact Registry repository, and target image storage location are co-located in the same region.
ZONE: a zone located within your specifiedREGION, for example,us-east1-boreurope-west1-b.
Create the orchestrator build file
Create a file named cloudbuild.yaml in the same directory. This file calls the
Image Builder container steps to build, validate, and publish the
custom OS image.
substitutions: _GCS_WORKDIR: 'gs://STAGING_BUCKET_NAME/workdir/' _IMAGE_BUILDER_CONFIG_PATH: 'imagebuilder.yaml' _SERVICE_ACCOUNT: 'projects/PROJECT_ID/serviceAccounts/SERVICE_ACCOUNT_EMAIL' _IMAGE_OUTPUT_PATH: 'image-builder/binaryOut' _ARTIFACT_REGISTRY_RESOURCE_URI: 'projects/PROJECT_ID/locations/REGION/repositories/REPOSITORY_NAME/packages/PACKAGE_NAME/versions/v${BUILD_ID}' steps: # Step 1: Parse configs and run OS customization on worker VM - name: 'REGION-docker.pkg.dev/image-builder-official/release/builder:stable' script: | #!/usr/bin/env bash /build id: 'imagebuilder-customize' results: - name: image_builder_telemetry_metrics - name: base_image attestationType: "https://cloudbuild.googleapis.com/attestations/build_content_restrictions" attestationContent: base_image # Step 2: Validate by running system boot checks on a test VM - name: 'REGION-docker.pkg.dev/image-builder-official/release/validator:stable' script: | #!/usr/bin/env bash /validate id: 'imagebuilder-validate' results: - name: image_builder_telemetry_metrics # Step 3: Register image in Compute Engine and upload tar files to Artifact Registry - name: 'REGION-docker.pkg.dev/image-builder-official/release/builder:stable' script: | #!/usr/bin/env bash /publish id: 'imagebuilder-publish' results: - name: image_builder_telemetry_metrics options: automapSubstitutions: true requestedVerifyOption: VERIFIED substitutionOption: ALLOW_LOOSE dynamicSubstitutions: true logging: CLOUD_LOGGING_ONLY artifacts: generic_artifacts: - folder: '${_IMAGE_OUTPUT_PATH}' registry_path: '${_ARTIFACT_REGISTRY_RESOURCE_URI}' timeout: '3600s'
Replace the following placeholder values:
STAGING_BUCKET_NAME: an existing Cloud Storage bucket in your project to use as a temporary staging workspace. If you don't have a bucket, you can create one by runninggcloud storage buckets create gs://STAGING_BUCKET_NAME. If you deploy your pipeline using Terraform, Terraform creates this bucket automatically.PROJECT_ID: your Google Cloud project ID.REGION: the Google Cloud region for you Artifact Registry repository, for example,us-east1oreurope-west1.SERVICE_ACCOUNT_EMAIL: the email for the service account that you configured with the required IAM permissions.REPOSITORYandPACKAGE: the target repository and package name created in Artifact Registry. To set up the Artifact Registry registry, see Configure Artifact Registry.
Create and submit the build pipeline
To run your image customization pipeline, submit a build by using the gcloud CLI or deploy your pipeline by using Terraform. Select one of the following tabs:
gcloud
To deploy and run your image customization pipeline, from your local terminal
directory containing both configuration files, run the
gcloud builds submit command:
gcloud builds submit . \
--config=cloudbuild.yaml \
--project=PROJECT_ID \
--service-account="projects/PROJECT_ID/serviceAccounts/SERVICE_ACCOUNT_EMAIL"
Replace the following:
PROJECT_ID: your Google Cloud project ID.SERVICE_ACCOUNT_EMAIL: the email address of the service account configured with the required IAM permissions.
This command uploads your customization workspace, registers the Cloud Build execution, and launches the orchestration containers.
Terraform
To provision the infrastructure required to automatically build and validate customized OS images, you can use Terraform. This Terraform configuration completes the following tasks:
- Enables required Google Cloud APIs.
- Creates a dedicated Cloud Storage bucket
(
workdir_bucket) to store temporary logs and build artifacts. - Configures a Cloud Build trigger
(
image_builder_trigger) linked to your Developer Connect GitHub repository connection.
Create your Terraform configuration files
To organize and deploy your pipeline infrastructure by using Terraform, complete the following steps:
Create a dedicated directory on your local workstation or CI/CD environment, separate from your application repository, and change into it:
mkdir terraform-image-builder && cd terraform-image-builder
In this directory, create the following five Terraform configuration files:
terraform.tfvars: sets values for project-specific variables.main.tf: provisions resources in your Google Cloud project, including enabling required APIs, creating the Cloud Storage staging bucket (workdir_bucket), and deploying the Cloud Build trigger (image_builder_trigger).outputs.tf: defines output values displayed in your terminal after deployment, such as the trigger ID and staging bucket name.providers.tf: specifies the required Terraform version (>= 1.3) and configures the Google Cloud provider (hashicorp/google).variables.tf: defines input variables, default values, and validation rules for the deployment.
Select one of the following tabs to view and copy each file's configuration into your local directory:
terraform.tfvarsThis file specifies parameter values for your environment for the declared variables:
project_id = "PROJECT_ID" builder_service_account = "SERVICE_ACCOUNT_EMAIL" github_repo_name = "projects/PROJECT_ID/locations/LOCATION/connections/CONNECTION/repositories/REPO_NAME" region = "REGION" trigger_name = "TRIGGER_NAME" cloudbuild_yaml_path = "CLOUDBUILD_YAML_PATH" image_builder_config_path = "RECIPE_PATH" gcs_lifecycle_age_days = LIFECYCLE_DAYS ar_repository_id = "REPOSITORY_NAME" ar_package_name = "PACKAGE_NAME"
Replace the following placeholders for your pre-existing resources:
PROJECT_ID: your existing Google Cloud project ID.SERVICE_ACCOUNT_EMAIL: the email address of your build service account configured in Configure Image Builder service account.LOCATION,CONNECTION, andREPO_NAME: your Developer Connect host region, connection name, and repository link configured in Connect a repository.CLOUDBUILD_YAML_PATH: the relative path to yourcloudbuild.yamlfile in your local directory. You don't need to checkcloudbuild.yamlinto your Git repository.RECIPE_PATH: the relative path to yourimagebuilder.yamlcustomization recipe file checked into your Git repository.REPOSITORY_NAME: the existing generic Artifact Registry repository created in Configure Artifact Registry.
Replace the following placeholders for the resources that Terraform creates:
REGION: the target Google Cloud region where Terraform provisions the Cloud Storage staging bucket and Cloud Build trigger, for example,us-central1.TRIGGER_NAME: the name for the new Cloud Build repository trigger created by Terraform, for example,git-push-os-builder.LIFECYCLE_DAYS: the retention period in days before intermediate artifacts in the Cloud Storage staging bucket created by Terraform are automatically deleted, for example,30.PACKAGE_NAME: the name that you want Terraform to use for the package created inside your Artifact Registry repository. This package stores published OS image versions, for example,ubuntu-custom.
main.tfThis file declares the core infrastructure resources and data sources for the deployment:
# Main resource configurations for Image Builder. # 1. Enable Required APIs resource "google_project_service" "apis" { for_each = toset([ "compute.googleapis.com", "cloudbuild.googleapis.com", "artifactregistry.googleapis.com", "serviceusage.googleapis.com", "cloudresourcemanager.googleapis.com", "iam.googleapis.com", "storage.googleapis.com" ]) project = var.project_id service = each.key disable_on_destroy = false } # 2. Project data source to retrieve Project Number data "google_project" "project" { project_id = var.project_id depends_on = [google_project_service.apis] } locals { builder_sa = var.builder_service_account } # 3. Storage Bucket for Image Builder Workdir resource "google_storage_bucket" "workdir_bucket" { name = var.gcs_bucket_name != "" ? var.gcs_bucket_name : "${var.project_id}-vm-builder-workdir" project = var.project_id location = var.region force_destroy = true uniform_bucket_level_access = true lifecycle_rule { action { type = "Delete" } condition { age = var.gcs_lifecycle_age_days } } depends_on = [google_project_service.apis] } # 4. Cloud Build Trigger resource "google_cloudbuild_trigger" "image_builder_trigger" { name = var.trigger_name location = var.region project = var.project_id description = "Trigger that runs Image Builder customization" service_account = var.builder_service_account != "" ? "projects/${var.project_id}/serviceAccounts/${var.builder_service_account}" : null repository_event_config { repository = replace(var.github_repo_name, "gitRepositoryLinks", "repositories") push { branch = "^main$" } } filename = var.cloudbuild_yaml_path substitutions = { _GCS_WORKDIR = "gs://${google_storage_bucket.workdir_bucket.name}/workdir/" _SERVICE_ACCOUNT = "projects/${var.project_id}/serviceAccounts/${local.builder_sa}" _IMAGE_OUTPUT_PATH = "image-builder/binaryOut" _PROJECT_ID = var.project_id _LOCATION = var.region _REPOSITORY_NAME = var.ar_repository_id _PACKAGE_NAME = var.ar_package_name _IMAGE_BUILDER_CONFIG_PATH = var.image_builder_config_path _ARTIFACT_REGISTRY_RESOURCE_URI = "projects/${var.project_id}/locations/${var.region}/repositories/${var.ar_repository_id}/packages/${var.ar_package_name}/versions/v$${BUILD_ID}" } depends_on = [ google_project_service.apis ] }outputs.tfThis file defines the output attributes returned to your terminal after deployment:
output "builder_service_account" { value = local.builder_sa description = "The email representation of the resolved Image Builder service account." } output "workdir_bucket" { value = google_storage_bucket.workdir_bucket.name description = "The name of the storage workdir bucket." } output "artifact_registry_repository" { value = "projects/${var.project_id}/locations/${var.region}/repositories/${var.ar_repository_id}" description = "The fully qualified resource path of the Artifact Registry repository." } output "cloud_build_trigger_id" { value = google_cloudbuild_trigger.image_builder_trigger.trigger_id description = "The unique ID for the created Cloud Build Trigger." }providers.tfThis file configures the required Terraform version and region settings:
terraform { required_version = ">= 1.3" required_providers { google = { source = "hashicorp/google" version = ">= 5.0, < 7.0" } } } provider "google" { project = var.project_id region = var.region }variables.tfThis file declares all required and optional input variables and validation rules:
variable "project_id" { type = string description = "The target Project ID where resources will be created." validation { condition = can(regex("^[a-z0-9-]{6,30}$", var.project_id)) error_message = "The project_id must consist of lowercase letters, numbers, and hyphens, and be between 6 and 30 characters." } } variable "region" { type = string default = "us-central1" description = "Location used for cloud build triggers, storage buckets, and artifact registry." } variable "github_repo_name" { type = string default = "" description = "Developer Connect github repository details, format: projects/PROJECT_ID/locations/LOCATION/connections/CONNECTION/repositories/REPO_LINK" validation { condition = can(regex("^projects/[^/]+/locations/[^/]+/connections/[^/]+/(gitRepositoryLinks|repositories)/[^/]+$", var.github_repo_name)) error_message = "The github_repo_name must follow either the Cloud Build v2 repository link format (using '/repositories/') or the Developer Connect resource format (using '/gitRepositoryLinks/')." } } variable "builder_service_account" { type = string default = "" description = "The email representation of the pre-existing Image Builder service account. If omitted, the default Cloud Build service account will be used." } variable "gcs_bucket_name" { type = string default = "" description = "Custom name for the workdir storage bucket. If left empty, a default name using the project ID will be constructed." } variable "gcs_lifecycle_age_days" { type = number default = 30 description = "The number of days after which temporary logs and artifacts in the storage workdir bucket are deleted." } variable "ar_repository_id" { type = string default = "vm-images" description = "The repository ID for the generic Artifact Registry hosting the final OS image tarballs." } variable "ar_package_name" { type = string default = "image-builder" description = "The package name under which the generic OS image artifact will be registered in Artifact Registry." } variable "trigger_name" { type = string default = "custom-os-image-builder" description = "The name of the Cloud Build trigger." } variable "cloudbuild_yaml_path" { type = string default = "cloudbuild.yaml" description = "The path to the cloudbuild.yaml configuration file relative to the repository root." } variable "image_builder_config_path" { type = string default = "imagebuilder.yaml" description = "The path to the imagebuilder.yaml configuration file relative to the repository root." }To deploy the configurations, run the following commands in the directory containing your Terraform files:
- Initialize the directory:
terraform init
- Validate the syntax:
terraform validate
- Preview the deployment:
terraform plan
- Apply the configuration:
terraform apply
- Initialize the directory:
Verify and monitor the build
To track the progress of your build pipeline, complete the following steps:
In the Google Cloud console, go to the Cloud Build page.
In the navigation menu, click History to view active or completed jobs.
In the Builds list, click the Build ID of your build to inspect container run logs. The logs display steps being performed inside the worker VM such as system package updates or custom shell commands, followed by validation test results from the test VM, and final output registration.
What's next
- To configure and launch recurring or automated builds from the command line:
- Verify image provenance