Developer Tools and DevOps
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. See the section bibliography for the reference material consulted while preparing these pages. |
Commands and product statuses on this page were checked against docs.cloud.google.com on 2026-10-01: gcloud CLI 586.x,
Cloud Build and Cloud Deploy as documented there, Skaffold 2.x (skaffold/v4beta13 schema), the
google-github-actions actions at v3, and the Terraform hashicorp/google provider v8. Developer-tool products are
renamed, merged and closed regularly, so confirm availability in the linked pages before designing around any of them.
This page covers the tooling around Google Cloud rather than one runtime service: the IDE and browser environments used
to write Bookshelf (catalog-api, orders-api, notifier), the local emulators used to test it, and the pipelines
that carry a Git push to Cloud Run and GKE. Building and storing images (Artifact Registry, Cloud Build basics, Jib and
buildpacks, the Container Registry shutdown) is in Container Registry and Builds; the
Workload Identity Federation pool and provider setup is in Identity and Access; Terraform
and Infrastructure Manager are in Infrastructure as Code. Pipeline mechanics that are not
specific to Google Cloud (caching, matrix builds, environments) are in
Docker CI/CD with GitHub Actions. The AWS and Azure counterparts are
Developer Tools and DevOps and Developer Tools and DevOps.
Enable the APIs used here:
gcloud services enable workstations.googleapis.com clouddeploy.googleapis.com \
cloudbuild.googleapis.com artifactregistry.googleapis.com run.googleapis.com \
binaryauthorization.googleapis.com containeranalysis.googleapis.com \
iamcredentials.googleapis.com sts.googleapis.com
Derived from Enable and disable services.
Choosing a CI/CD approach
There are two broad options and they mix well: keep the pipeline in the system that already hosts the code (GitHub Actions, GitLab CI/CD, Jenkins) and let it call Google Cloud with short-lived credentials, or use Google’s own services (Cloud Build to build, Cloud Deploy to promote) that run inside the project.
Use the external CI system when the team already lives in it, the build needs nothing from inside a private network, and you want one pipeline definition across clouds. Use Cloud Build when builds must reach private resources (private pools), when you want build provenance generated by the platform, or when the repository connection should be managed in the project. Add Cloud Deploy when promotion between environments, approvals and rollbacks should be tracked as a first-class resource instead of being scripted. Whichever you choose, never create a downloaded service-account JSON key for the pipeline: new organizations block key creation by default, and older guides that store a key as a CI secret are outdated.
Cloud Code and Gemini Code Assist
Cloud Code is a set of plug-ins for Visual Studio Code, the JetBrains IDEs (IntelliJ IDEA, GoLand, PyCharm and others), Cloud Shell Editor and Cloud Workstations. It adds a Google Cloud explorer, project and credential pickers, YAML snippets and schema validation for Kubernetes and Cloud Run files, and an inner development loop that builds with Skaffold, deploys to a local or remote cluster and streams logs. Cloud Code now bundles Gemini Code Assist, Google’s AI coding assistant (chat, code completion and code generation), which has a free tier for individuals and paid editions for organizations whose terms control data use; check the product page for the edition that fits your policy.
Common tasks:
- Run on Kubernetes
-
Builds the image with the builder declared in
skaffold.yaml(Docker, Jib or buildpacks), deploys the manifests to the current kubectl context, forwards ports and rebuilds on save. - Run on Cloud Run Emulator
-
Runs the service locally in a container that mimics Cloud Run (environment, port and service-account behaviour) and can attach a debugger.
- Deploy to Cloud Run
-
Deploys the current folder from the IDE using the same engine as
gcloud run deploy --source. - Explorer views
-
Browse Cloud Run services, GKE clusters, Secret Manager secrets and Cloud Storage buckets without leaving the editor.
Older guides describe Cloud Code for IntelliJ with Skaffold and Jib as the way to iterate on a Spring Boot app on GKE; that flow is still current, only the assistant features are new. Installation is a normal plug-in install from the IDE’s marketplace, followed by Sign in with Google and choosing a project; the IDE then uses Application Default Credentials as described in Getting Started with Google Cloud.
Skaffold for the inner loop
Skaffold is the open-source tool behind both Cloud Code’s loop and Cloud Deploy’s rendering. A minimal configuration for
catalog-api builds with Jib and deploys plain Kubernetes manifests:
apiVersion: skaffold/v4beta13
kind: Config
metadata:
name: catalog-api
build:
artifacts:
- image: catalog-api
jib:
project: com.example.bookshelf:catalog-api
manifests:
rawYaml:
- k8s/deployment.yaml
- k8s/service.yaml
deploy:
kubectl: {}
Derived from Skaffold configuration reference and Cloud Code: deploy a Kubernetes application.
Run it from a terminal with skaffold dev --default-repo=us-central1-docker.pkg.dev/$PROJECT_ID/bookshelf and the schema
version can be upgraded with skaffold fix. The same file is reused unchanged by Cloud Deploy later on this page.
Jib and buildpacks are explained in Container Registry and Builds.
Cloud Shell and Cloud Workstations
Cloud Shell is the free, browser-based terminal with the gcloud CLI, Docker, Git and an editor based on Code OSS already installed. Its limits (50 hours per week, 5 GB of persistent home directory, home deleted after 120 days of inactivity) make it right for experiments and wrong for daily development; it is covered in Getting Started with Google Cloud.
Cloud Workstations is the step up: managed, per-developer development environments that run in your project and VPC. Each workstation is a VM with a persistent home disk and a container image that carries the tools. Developers reach it from a browser (Code OSS or a JetBrains IDE in the browser), from a local VS Code or JetBrains client, or over SSH through a TCP tunnel. Because the environment lives in the project, source code does not need to land on laptops, the network path to private databases is the VPC, and the base image can be standardized.
Three resource levels exist:
- Workstation cluster
-
A regional control plane attached to a VPC network and subnet. It is the unit that incurs an hourly management fee while it exists.
- Workstation configuration
-
A template: machine type, disk, idle and running timeouts, the container image (a Google predefined image such as Code OSS, a JetBrains image, or your own from Artifact Registry), network settings and who may create workstations from it.
- Workstation
-
One developer’s environment created from a configuration. It stops after the idle timeout so that compute billing pauses while the persistent disk remains.
In the Google Cloud console
-
Open Cloud Workstations (enable the API if prompted) and use the project picker to select the project.
-
Choose Cluster management > Create to make a workstation cluster. Name it
bookshelf-ws, pick regionus-central1, and select the VPC network and subnet. Cluster creation takes several minutes. -
Open Workstation configurations > Create. Pick the cluster, a machine type such as
e2-standard-4, the idle timeout, the persistent disk size and the container image, then create it. -
Open Workstations > Create workstation, select the configuration, and create. Use Start and then Launch to open the IDE in the browser; Stop ends compute billing.
-
On the configuration, Permissions controls who may create workstations (
roles/workstations.useron the workstation controls who may use one).
With the gcloud CLI
export REGION=us-central1
gcloud workstations clusters create bookshelf-ws \
--region="$REGION" --network="projects/$PROJECT_ID/global/networks/default" \
--subnetwork="projects/$PROJECT_ID/regions/$REGION/subnetworks/default"
gcloud workstations configs create bookshelf-config \
--cluster=bookshelf-ws --region="$REGION" \
--machine-type=e2-standard-4 --idle-timeout=1200 --pool-size=0 \
--container-predefined-image=codeoss
gcloud workstations create bookshelf-dev \
--cluster=bookshelf-ws --config=bookshelf-config --region="$REGION"
gcloud workstations start bookshelf-dev \
--cluster=bookshelf-ws --config=bookshelf-config --region="$REGION"
Derived from Create a workstation cluster, Create a workstation configuration and gcloud workstations.
To connect from a local IDE or terminal, open a tunnel to the workstation’s SSH port and connect through it:
gcloud workstations start-tcp-tunnel bookshelf-dev 22 \
--cluster=bookshelf-ws --config=bookshelf-config --region="$REGION" \
--local-host-port=localhost:2222
ssh -p 2222 user@localhost
Derived from Connect to a workstation over SSH.
A pool size above zero keeps that many started VMs ready so that workstations launch quickly, at the cost of paying for
idle machines; keep it at zero until startup time hurts. A custom image is the way to preinstall JDK 25, Maven, the
gcloud CLI and company certificates; build it with Cloud Build and push it to the bookshelf repository, then reference
it with --container-custom-image.
A Cloud Build pipeline for Bookshelf
Cloud Build triggers, repository connections, the build service account and cloudbuild.yaml basics are in
Container Registry and Builds. This section adds the step that most pipelines need next:
deploying what was built. The following cloudbuild.yaml builds catalog-api, pushes it and deploys it to Cloud Run
by digest:
steps:
- id: build
name: gcr.io/cloud-builders/docker
args: ['build', '-t', '${_IMAGE}:$SHORT_SHA', '.']
- id: push
name: gcr.io/cloud-builders/docker
args: ['push', '${_IMAGE}:$SHORT_SHA']
- id: deploy
name: gcr.io/google.com/cloudsdktool/google-cloud-cli:slim
entrypoint: gcloud
args:
- run
- deploy
- catalog-api
- --image=${_IMAGE}:$SHORT_SHA
- --region=${_REGION}
substitutions:
_IMAGE: us-central1-docker.pkg.dev/my-project-id/bookshelf/catalog-api
_REGION: us-central1
options:
logging: CLOUD_LOGGING_ONLY
Derived from Deploy to Cloud Run using Cloud Build.
The build service account then needs roles/run.developer on the project and roles/iam.serviceAccountUser on the
service account the Cloud Run service runs as. Grant them on the narrowest resource that works:
BUILDER="serviceAccount:ci-builder@$PROJECT_ID.iam.gserviceaccount.com"
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
--member="$BUILDER" --role=roles/run.developer
gcloud iam service-accounts add-iam-policy-binding \
"catalog-api@$PROJECT_ID.iam.gserviceaccount.com" \
--member="$BUILDER" --role=roles/iam.serviceAccountUser
Derived from Cloud Run IAM roles and Service account permissions.
Deploying straight from a build is fine for a single environment. When several environments, approvals and rollbacks
are involved, hand the image to Cloud Deploy instead of repeating gcloud run deploy in more steps.
Cloud Deploy
Cloud Deploy is a managed continuous delivery service. You describe a delivery pipeline (an ordered list of stages), the targets those stages deploy to (Cloud Run services, GKE clusters, GKE Enterprise or Anthos clusters), and a Skaffold configuration that renders and deploys the manifests. A CI system creates a release, a snapshot of the images and rendered manifests; Cloud Deploy then creates a rollout of that release to the first target and you promote it through the following ones, optionally requiring approval. Rollbacks and the history of what ran where are resources you can list, which is what makes it more than a script.
- Strategies
-
Standard (replace), canary (shift traffic in percentages, with optional verification jobs), and blue-green variants on supported targets. For Cloud Run targets canary uses revision traffic splitting as described in Cloud Run Scaling, Traffic and Jobs; for GKE targets it uses Gateway API or Service-based traffic.
- Execution identity
-
Each target declares the service account that renders and deploys; the default is the Compute Engine default service account, which is broader than needed. Use a dedicated account with
roles/clouddeploy.jobRunner, the runtime role (for exampleroles/run.developer) androles/iam.serviceAccountUseron the runtime identity. - Approvals
-
A target with
requireApproval: truewaits until someone withroles/clouddeploy.approverapproves the rollout.
In the Google Cloud console
-
Open Cloud Deploy (enable the API if prompted). The Delivery pipelines list shows each pipeline with its stages.
-
Pipelines and targets are created from YAML, so the console is mostly for inspection: select
bookshelf-pipeline, open a release, and watch each rollout and its jobs. -
On a rollout waiting for approval choose Approve or Reject; on a succeeded rollout choose Promote to the next target, or Rollback to redeploy the previous release.
-
Targets lists each target and its execution service account.
With the gcloud CLI
Describe a two-stage pipeline (dev then prod) and its Cloud Run targets in one file:
# clouddeploy.yaml
apiVersion: deploy.cloud.google.com/v1
kind: DeliveryPipeline
metadata:
name: bookshelf-pipeline
serialPipeline:
stages:
- targetId: bookshelf-dev
profiles: [dev]
- targetId: bookshelf-prod
profiles: [prod]
strategy:
canary:
runtimeConfig:
cloudRun:
automaticTrafficControl: true
canaryDeployment:
percentages: [25, 50]
verify: false
---
apiVersion: deploy.cloud.google.com/v1
kind: Target
metadata:
name: bookshelf-dev
run:
location: projects/my-project-id/locations/us-central1
executionConfigs:
- usages: [RENDER, DEPLOY]
serviceAccount: deployer@my-project-id.iam.gserviceaccount.com
---
apiVersion: deploy.cloud.google.com/v1
kind: Target
metadata:
name: bookshelf-prod
requireApproval: true
run:
location: projects/my-project-id/locations/us-central1
executionConfigs:
- usages: [RENDER, DEPLOY]
serviceAccount: deployer@my-project-id.iam.gserviceaccount.com
Derived from Deploy an application to Cloud Run and Canary deployment strategy.
The Skaffold file points at a Cloud Run service manifest, with a dev and prod profile that apply patches such as a
different minScale:
# skaffold.yaml
apiVersion: skaffold/v4beta13
kind: Config
metadata:
name: catalog-api
manifests:
rawYaml:
- run.yaml
deploy:
cloudrun: {}
profiles:
- name: dev
patches:
- op: add
path: /manifests/rawYaml/-
value: run-dev-overrides.yaml
- name: prod
patches:
- op: add
path: /manifests/rawYaml/-
value: run-prod-overrides.yaml
Derived from Use Skaffold profiles with Cloud Deploy.
The run.yaml is a Knative-style Cloud Run service whose image field is replaced at render time with the image
passed to the release. Register everything, create a release and drive it forward:
gcloud deploy apply --file=clouddeploy.yaml --region="$REGION"
gcloud deploy releases create "rel-$(date +%Y%m%d-%H%M)" \
--delivery-pipeline=bookshelf-pipeline --region="$REGION" \
--images="catalog-api=$REGION-docker.pkg.dev/$PROJECT_ID/bookshelf/catalog-api@sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
gcloud deploy rollouts list --delivery-pipeline=bookshelf-pipeline \
--release=REL_NAME --region="$REGION"
gcloud deploy releases promote --release=REL_NAME \
--delivery-pipeline=bookshelf-pipeline --region="$REGION"
gcloud deploy rollouts approve ROLLOUT_NAME \
--delivery-pipeline=bookshelf-pipeline --release=REL_NAME --region="$REGION"
Derived from Create a delivery pipeline and targets, Create a release and gcloud deploy.
Replace REL_NAME and ROLLOUT_NAME with the names printed by the previous commands. Passing the digest to
--images makes the release reproducible: the same bytes are deployed to every target.
To invoke Cloud Deploy from Cloud Build, add a final step that runs
gcloud deploy releases create with the freshly pushed image, so Cloud Build owns build and test and Cloud Deploy
owns promotion. For GKE, the target is gke: cluster: projects/my-project-id/locations/us-central1/clusters/bookshelf-gke
and the Skaffold manifests are the Deployment and Service YAML, or a Helm chart or Kustomize overlay, as in
Deploying Microservices on GKE.
GitHub Actions and GitLab CI with Workload Identity Federation
Older recipes for deploying from GitHub Actions or GitLab CI create a service-account key, paste its JSON into a CI secret, and activate it in the job. That approach is outdated and usually fails now: organizations created since 2024-05-03 enable the key-creation constraint by default, and a leaked key is valid until someone notices. The current approach is Workload Identity Federation (WIF): the CI platform issues a short-lived OIDC token for each job, Google Cloud’s Security Token Service exchanges it for a federated token, and the job impersonates a service account. No secret is stored in the CI system.
The pool, provider and roles/iam.workloadIdentityUser binding for GitHub are created in
Identity and Access. What matters for the pipeline is the attribute condition: always
restrict the provider to your repository (and ideally branch or environment) so that other repositories on the same
platform cannot impersonate your service account.
Deploying Bookshelf to Cloud Run from GitHub Actions
The service account deployer needs roles/run.developer, roles/artifactregistry.writer on the bookshelf
repository and roles/iam.serviceAccountUser on the runtime identity of catalog-api.
name: deploy-catalog-api
on:
push:
branches: [main]
permissions:
contents: read
id-token: write
env:
REGION: us-central1
IMAGE: us-central1-docker.pkg.dev/my-project-id/bookshelf/catalog-api
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- id: auth
uses: google-github-actions/auth@v3
with:
workload_identity_provider: projects/<project-number>/locations/global/workloadIdentityPools/github/providers/bookshelf-repo
service_account: deployer@my-project-id.iam.gserviceaccount.com
- uses: google-github-actions/setup-gcloud@v3
- run: gcloud auth configure-docker us-central1-docker.pkg.dev --quiet
- run: |
docker build -t "$IMAGE:${{ github.sha }}" .
docker push "$IMAGE:${{ github.sha }}"
- uses: google-github-actions/deploy-cloudrun@v3
with:
service: catalog-api
region: ${{ env.REGION }}
image: ${{ env.IMAGE }}:${{ github.sha }}
Derived from google-github-actions/auth, google-github-actions/deploy-cloudrun and Cloud Run service identity.
The workflow needs id-token: write or the token cannot be requested. Pin actions to a full commit SHA in regulated
repositories, and use a GitHub environment with required reviewers on the production job so that approvals are
enforced by GitHub as well as by the IAM condition.
Deploying to GKE from GitHub Actions
For GKE, fetch cluster credentials with the same federated identity (the service account also needs
roles/container.developer on the cluster), then apply manifests or run Helm:
- id: auth
uses: google-github-actions/auth@v3
with:
workload_identity_provider: projects/<project-number>/locations/global/workloadIdentityPools/github/providers/bookshelf-repo
service_account: deployer@my-project-id.iam.gserviceaccount.com
- uses: google-github-actions/get-gke-credentials@v3
with:
cluster_name: bookshelf-gke
location: us-central1
- run: |
kubectl set image deployment/catalog-api \
catalog-api="$IMAGE:${{ github.sha }}"
kubectl rollout status deployment/catalog-api --timeout=180s
Derived from google-github-actions/get-gke-credentials and GKE IAM roles.
A private cluster endpoint is not reachable from GitHub-hosted runners; use a self-hosted runner in the VPC, the connect gateway, or let Cloud Deploy perform the rollout and have the workflow only create the release.
GitLab CI
GitLab issues the OIDC token as an id_tokens variable. Create a second pool and provider for GitLab (issuer
https://gitlab.com, or your self-managed URL) and restrict it to the project path:
gcloud iam workload-identity-pools create gitlab --location=global \
--display-name="GitLab CI"
gcloud iam workload-identity-pools providers create-oidc bookshelf-gitlab \
--location=global --workload-identity-pool=gitlab \
--issuer-uri="https://gitlab.com" \
--allowed-audiences="https://iam.googleapis.com/projects/<project-number>/locations/global/workloadIdentityPools/gitlab/providers/bookshelf-gitlab" \
--attribute-mapping="google.subject=assertion.sub,attribute.project_path=assertion.project_path" \
--attribute-condition="assertion.project_path == 'my-group/bookshelf'"
gcloud iam service-accounts add-iam-policy-binding \
"deployer@$PROJECT_ID.iam.gserviceaccount.com" \
--role=roles/iam.workloadIdentityUser \
--member="principalSet://iam.googleapis.com/projects/<project-number>/locations/global/workloadIdentityPools/gitlab/attribute.project_path/my-group/bookshelf"
Derived from Workload Identity Federation with deployment pipelines and GitLab ID token authentication.
Then the job writes the token to a file, builds a credential configuration, and the gcloud CLI uses it:
deploy:
image: gcr.io/google.com/cloudsdktool/google-cloud-cli:slim
id_tokens:
GITLAB_OIDC_TOKEN:
aud: https://iam.googleapis.com/projects/<project-number>/locations/global/workloadIdentityPools/gitlab/providers/bookshelf-gitlab
script:
- echo "${GITLAB_OIDC_TOKEN}" > .ci_job_jwt_file
- gcloud iam workload-identity-pools create-cred-config
projects/<project-number>/locations/global/workloadIdentityPools/gitlab/providers/bookshelf-gitlab
--service-account=deployer@my-project-id.iam.gserviceaccount.com
--output-file=.gcp_temp_cred.json
--credential-source-file=.ci_job_jwt_file
- gcloud auth login --cred-file=.gcp_temp_cred.json
- gcloud run deploy catalog-api --region=us-central1
--image=us-central1-docker.pkg.dev/my-project-id/bookshelf/catalog-api:$CI_COMMIT_SHORT_SHA
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
Derived from GitLab: configure OpenID Connect with Google Cloud and gcloud iam workload-identity-pools create-cred-config.
The credential file contains no secret, only the instructions for exchanging the token file, but it should still not be
committed. Older GitLab guides use the CI_JOB_JWT_V2 variable, which GitLab has removed in favour of id_tokens.
Local development: Functions Framework and emulators
Cloud Run functions (formerly Cloud Functions) are written against the open-source Functions Framework, which also runs locally, so the same code serves requests on a laptop and in production. For Java, add the framework API and run the function with the Maven plug-in:
<dependency>
<groupId>com.google.cloud.functions</groupId>
<artifactId>functions-framework-api</artifactId>
<version>1.1.0</version>
<scope>provided</scope>
</dependency>
<plugin>
<groupId>com.google.cloud.functions</groupId>
<artifactId>function-maven-plugin</artifactId>
<version>0.11.0</version>
<configuration>
<functionTarget>com.example.bookshelf.HelloFunction</functionTarget>
</configuration>
</plugin>
Derived from Functions Framework.
package com.example.bookshelf;
import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
public class HelloFunction implements HttpFunction {
@Override
public void service(HttpRequest request, HttpResponse response) throws Exception {
response.getWriter().write("catalog ok");
}
}
Derived from Write HTTP functions.
Start it with mvn function:run and call http://localhost:8080. The versions above are the ones in the official
sample at the time of writing; use the latest from Maven Central. Event-driven functions (Pub/Sub, Cloud Storage,
Eventarc) are covered in Cloud Run Functions and Events.
Local emulators let tests run without a network connection, a project or a bill. The gcloud CLI ships emulators for
Pub/Sub, Firestore, Bigtable, Datastore and Spanner; Cloud Storage has no official one, and the community
fake-gcs-server fills the gap:
gcloud components install beta pubsub-emulator cloud-firestore-emulator
gcloud beta emulators pubsub start --project=my-project-id --host-port=localhost:8085
# in another terminal
$(gcloud beta emulators pubsub env-init)
gcloud emulators firestore start --host-port=localhost:8086
docker run --rm -p 4443:4443 fsouza/fake-gcs-server -scheme http
Derived from Pub/Sub emulator, Firestore emulator and fake-gcs-server.
The client libraries detect the emulators through environment variables: PUBSUB_EMULATOR_HOST and
FIRESTORE_EMULATOR_HOST make the Java clients talk to localhost with no credentials, and the Cloud Storage client
accepts a custom endpoint through StorageOptions.newBuilder().setHost("http://localhost:4443"). The Spanner, Bigtable
and Datastore emulators follow the same pattern with SPANNER_EMULATOR_HOST, BIGTABLE_EMULATOR_HOST and
DATASTORE_EMULATOR_HOST. Emulators do not enforce IAM, quotas or exact latency, so keep a few integration tests
against real services. For container-based test dependencies see
Testcontainers Fundamentals.
Software supply-chain security
Three services turn a pipeline into an auditable chain from source to running container.
- Artifact Analysis
-
Scans images pushed to Artifact Registry for known vulnerabilities and stores the results and metadata as occurrences. Automatic scanning is switched on by enabling the Container Scanning API (
containerscanning.googleapis.com), as shown in Container Registry and Builds. - SLSA provenance from Cloud Build
-
Cloud Build can generate signed provenance for images it builds, describing the source, builder and build steps. It is requested per build, and the provenance is stored in Artifact Analysis next to the image. It targets the SLSA build track at level 3 for container images built by Cloud Build.
- Binary Authorization
-
A deploy-time policy that admits only images that have required attestations. When the Binary Authorization API is enabled, an attestor named
built-by-cloud-buildis created and Cloud Build attests images it built, so a policy can demand "built by our pipeline" without any key management. It is enforced on Cloud Run, GKE and GKE Enterprise.
In the Google Cloud console
-
Open Security > Binary Authorization (enable the API if prompted).
-
Choose Policy > Edit policy, set the default rule to Require attestations and add the attestor
built-by-cloud-build. -
Set the enforcement mode to Block and audit log, and save. Start with Dry run if existing workloads would be rejected.
-
On a Cloud Run service enable Binary Authorization under security settings so that deployments are checked.
With the gcloud CLI
cat > policy.yaml <<EOF
defaultAdmissionRule:
evaluationMode: REQUIRE_ATTESTATION
enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
requireAttestationsBy:
- projects/$PROJECT_ID/attestors/built-by-cloud-build
globalPolicyEvaluationMode: ENABLE
name: projects/$PROJECT_ID/policy
EOF
gcloud container binauthz policy import policy.yaml
gcloud run deploy catalog-api --region="$REGION" \
--image="$REGION-docker.pkg.dev/$PROJECT_ID/bookshelf/catalog-api:1.0.0" \
--binary-authorization=default
Derived from Binary Authorization overview, Binary Authorization for Cloud Run and View build provenance.
Provenance is requested with options.requestedVerifyOption: VERIFIED in cloudbuild.yaml; with it set, a build
publishes provenance and attestations only when the build is verifiably clean. Pair these controls with the cleanup and
scanning settings in Container Registry and Builds and with image hygiene from
Image Best Practices and Supply-Chain Security.
Infrastructure as code in the pipeline
Pipelines should create the environment as well as deploy to it. Terraform with the hashicorp/google provider (v8) is
the common choice; Infrastructure Manager runs Terraform as a managed service inside Google Cloud, and Deployment
Manager is retired (support ended 2026-04-01 and shutdown is scheduled for 2027-06-30). The setup, remote state
in Cloud Storage, a Bookshelf baseline module and the migration from Deployment Manager are in
Infrastructure as Code.
A typical split keeps infrastructure and application delivery in separate pipelines: a Terraform pipeline runs
terraform plan on pull requests and apply on merge, authenticating through WIF as described above, with a service
account that holds broad infrastructure roles; the application pipeline holds only the narrow deployer roles shown on
this page. The plan output is posted to the pull request for review.
Source repositories
Cloud Source Repositories, Google’s original hosted Git service, has been closed to new customers since 2024-06-17.
Existing customers can keep using it, but Google recommends moving, and older tutorials that begin with
gcloud source repos create or mirror a repository into it are outdated. Where to host code now:
- GitHub, GitLab or Bitbucket
-
Connect them to Cloud Build through a 2nd-generation repository connection; the 1st-generation GitHub App integration is legacy. Connections and triggers are in Container Registry and Builds.
- Developer Connect
-
A Google Cloud service that manages connections to external source providers (GitHub, GitLab and Bitbucket) and links them to build, deploy and Gemini features, so that tools in Google Cloud can read the repository without personal tokens.
- Secure Source Manager
-
A managed, single-tenant Git service in Google Cloud with pull requests, issues and built-in integrations, for organizations that need source hosting inside their own security boundary. It is billed per instance while it exists, so create one only if you need it.
To migrate an existing Cloud Source Repositories repository, list it, clone it, create the destination repository on the new host and push every ref:
gcloud source repos list
gcloud source repos clone bookshelf --project="$PROJECT_ID"
cd bookshelf
git remote add github git@github.com:my-org/bookshelf.git
git push github --all
git push github --tags
Derived from Cloud Source Repositories release notes and gcloud source repos.
Git fundamentals and GitHub workflows are in Git & GitHub.
Pricing model, limits and launch stages
- Pricing
-
Cloud Code is free; Gemini Code Assist has a free tier for individuals and paid per-user editions. Cloud Workstations bills the cluster management fee per hour, the VM compute while a workstation runs (including idle pool VMs), and the persistent disks. Cloud Build bills per build minute by machine type with a free monthly allowance for the default machine. Cloud Deploy bills per active delivery pipeline per month, with a free allowance; rendering and deployment run in Cloud Build and are billed there. Binary Authorization and Artifact Analysis scanning are billed per policy evaluation or scanned image. Check the dated pricing pages linked below, because rates change.
- Cost drivers and controls
-
Workstations left running (set short idle and running timeouts), workstation clusters that nobody uses, long or uncached builds, large pools of ready VMs, and scanning of images that are never deployed.
- Limits
-
Cloud Build and Cloud Deploy have per-region quotas for concurrent builds, pipelines, targets and releases; GitHub Actions and GitLab runners have their own limits. See the quota pages in the references.
- Launch stages and dated changes
-
Cloud Source Repositories closed to new customers on 2024-06-17. Container Registry shut down on 2025-03-18. Deployment Manager support ends 2026-04-01. Cloud Build 1st-generation repository integrations are legacy. Gemini Cloud Assist and some Gemini Code Assist agent features have been in Preview; check the release notes for the status of any feature you depend on.
Troubleshooting
Unable to acquire impersonated credentialsorPERMISSION_DENIEDfromgoogle-github-actions/auth-
The workflow lacks
id-token: write, the provider’s attribute condition does not match the repository or branch, or the service account is missingroles/iam.workloadIdentityUserfor that principal. Compare the token claims with the condition. The caller does not have permissiononiam.serviceAccounts.actAs-
The deployer needs
roles/iam.serviceAccountUseron the identity the Cloud Run service or Cloud Deploy target runs as. - Cloud Deploy release fails at render
-
The Skaffold configuration or profile names do not match the pipeline stage, or the execution service account cannot read the Skaffold source bucket. Read the render log in the release page.
- Rollout waits forever
-
The target has
requireApproval: true. Approve it withgcloud deploy rollouts approve. - Binary Authorization denies a deployment
-
The image has no attestation from the required attestor, usually because it was not built by Cloud Build or was pushed by another tool. The audit log entry lists the missing attestor.
- Workstation will not start
-
The cluster subnet lacks Private Google Access or required firewall rules, or the machine type is out of stock in the zone. Check the workstation’s logs and the cluster’s network settings.
gcloud source repos createfails with an access error-
The project is not an existing Cloud Source Repositories customer; the service is closed to new customers.
Clean up
Delete in dependency order. A throwaway project can be deleted instead, which removes everything after a 30-day recovery window.
gcloud workstations delete bookshelf-dev --cluster=bookshelf-ws --config=bookshelf-config --region="$REGION"
gcloud workstations configs delete bookshelf-config --cluster=bookshelf-ws --region="$REGION"
gcloud workstations clusters delete bookshelf-ws --region="$REGION"
gcloud deploy delete --file=clouddeploy.yaml --region="$REGION" --force
gcloud iam workload-identity-pools providers delete bookshelf-gitlab \
--location=global --workload-identity-pool=gitlab
gcloud iam workload-identity-pools delete gitlab --location=global
gcloud run services delete catalog-api --region="$REGION"
Derived from Delete a workstation and
gcloud deploy delete. Workload Identity pools are soft
deleted and can be restored for 30 days; their names cannot be reused until they are purged. Also reset the Binary
Authorization policy to allow all (gcloud container binauthz policy import with the default policy) if it was
changed, remove the GitHub environment and repository variables you created, and revoke the Cloud Build GitHub App
installation in GitHub. See
Shutting down projects.