The gcp:cloudbuild/trigger:Trigger resource, part of the Pulumi GCP provider, defines automated build triggers that respond to repository events, Pub/Sub messages, or webhook requests. This guide focuses on five capabilities: repository branch monitoring with build files, inline build definitions, event-driven triggers via Pub/Sub and webhooks, Cloud Build v2 connections, and custom service account configuration.
Triggers reference repositories, Pub/Sub topics, Secret Manager secrets, and service accounts that must exist separately. The examples are intentionally small. Combine them with your own repository connections, IAM roles, and event sources.
Trigger builds from repository branches with a build file
Most CI/CD pipelines monitor a repository branch and execute builds when code changes, reading build instructions from a YAML file in the repository.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const filename_trigger = new gcp.cloudbuild.Trigger("filename-trigger", {
location: "us-central1",
triggerTemplate: {
branchName: "main",
repoName: "my-repo",
},
substitutions: {
_FOO: "bar",
_BAZ: "qux",
},
filename: "cloudbuild.yaml",
});
import pulumi
import pulumi_gcp as gcp
filename_trigger = gcp.cloudbuild.Trigger("filename-trigger",
location="us-central1",
trigger_template={
"branch_name": "main",
"repo_name": "my-repo",
},
substitutions={
"_FOO": "bar",
"_BAZ": "qux",
},
filename="cloudbuild.yaml")
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/cloudbuild"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := cloudbuild.NewTrigger(ctx, "filename-trigger", &cloudbuild.TriggerArgs{
Location: pulumi.String("us-central1"),
TriggerTemplate: &cloudbuild.TriggerTriggerTemplateArgs{
BranchName: pulumi.String("main"),
RepoName: pulumi.String("my-repo"),
},
Substitutions: pulumi.StringMap{
"_FOO": pulumi.String("bar"),
"_BAZ": pulumi.String("qux"),
},
Filename: pulumi.String("cloudbuild.yaml"),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
return await Deployment.RunAsync(() =>
{
var filename_trigger = new Gcp.CloudBuild.Trigger("filename-trigger", new()
{
Location = "us-central1",
TriggerTemplate = new Gcp.CloudBuild.Inputs.TriggerTriggerTemplateArgs
{
BranchName = "main",
RepoName = "my-repo",
},
Substitutions =
{
{ "_FOO", "bar" },
{ "_BAZ", "qux" },
},
Filename = "cloudbuild.yaml",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.cloudbuild.Trigger;
import com.pulumi.gcp.cloudbuild.TriggerArgs;
import com.pulumi.gcp.cloudbuild.inputs.TriggerTriggerTemplateArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var filename_trigger = new Trigger("filename-trigger", TriggerArgs.builder()
.location("us-central1")
.triggerTemplate(TriggerTriggerTemplateArgs.builder()
.branchName("main")
.repoName("my-repo")
.build())
.substitutions(Map.ofEntries(
Map.entry("_FOO", "bar"),
Map.entry("_BAZ", "qux")
))
.filename("cloudbuild.yaml")
.build());
}
}
resources:
filename-trigger:
type: gcp:cloudbuild:Trigger
properties:
location: us-central1
triggerTemplate:
branchName: main
repoName: my-repo
substitutions:
_FOO: bar
_BAZ: qux
filename: cloudbuild.yaml
The triggerTemplate property defines which repository and branch to monitor. The branchName field accepts regular expressions (here, “main” matches exactly). When commits land on the matching branch, Cloud Build reads the filename property to locate build instructions. The substitutions property passes key-value pairs into the build as environment variables.
Define build steps inline without a repository file
Some workflows define build logic directly in the trigger rather than reading from a repository file, enabling centralized build management.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const build_trigger = new gcp.cloudbuild.Trigger("build-trigger", {
name: "my-trigger",
location: "global",
triggerTemplate: {
branchName: "main",
repoName: "my-repo",
},
build: {
steps: [
{
name: "gcr.io/cloud-builders/gcloud",
args: [
"storage",
"cp",
"gs://mybucket/remotefile.zip",
"localfile.zip",
],
timeout: "120s",
secretEnvs: ["MY_SECRET"],
},
{
name: "ubuntu",
script: "echo hello",
},
],
source: {
storageSource: {
bucket: "mybucket",
object: "source_code.tar.gz",
},
},
tags: [
"build",
"newFeature",
],
substitutions: {
_FOO: "bar",
_BAZ: "qux",
},
queueTtl: "20s",
logsBucket: "gs://mybucket/logs",
secrets: [{
kmsKeyName: "projects/myProject/locations/global/keyRings/keyring-name/cryptoKeys/key-name",
secretEnv: {
PASSWORD: "ZW5jcnlwdGVkLXBhc3N3b3JkCg==",
},
}],
availableSecrets: {
secretManagers: [{
env: "MY_SECRET",
versionName: "projects/myProject/secrets/mySecret/versions/latest",
}],
},
artifacts: {
images: ["gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA"],
objects: {
location: "gs://bucket/path/to/somewhere/",
paths: ["path"],
},
npmPackages: [{
packagePath: "package.json",
repository: "https://us-west1-npm.pkg.dev/myProject/quickstart-nodejs-repo",
}],
pythonPackages: [{
paths: ["dist/*"],
repository: "https://us-west1-python.pkg.dev/myProject/quickstart-python-repo",
}],
mavenArtifacts: [{
repository: "https://us-west1-maven.pkg.dev/myProject/quickstart-java-repo",
path: "/workspace/my-app/target/my-app-1.0.SNAPSHOT.jar",
artifactId: "my-app",
groupId: "com.mycompany.app",
version: "1.0",
}],
},
options: {
sourceProvenanceHashes: ["MD5"],
requestedVerifyOption: "VERIFIED",
machineType: "N1_HIGHCPU_8",
diskSizeGb: 100,
substitutionOption: "ALLOW_LOOSE",
dynamicSubstitutions: true,
logStreamingOption: "STREAM_OFF",
workerPool: "pool",
logging: "LEGACY",
envs: ["ekey = evalue"],
secretEnvs: ["secretenv = svalue"],
volumes: [{
name: "v1",
path: "v1",
}],
},
},
});
import pulumi
import pulumi_gcp as gcp
build_trigger = gcp.cloudbuild.Trigger("build-trigger",
name="my-trigger",
location="global",
trigger_template={
"branch_name": "main",
"repo_name": "my-repo",
},
build={
"steps": [
{
"name": "gcr.io/cloud-builders/gcloud",
"args": [
"storage",
"cp",
"gs://mybucket/remotefile.zip",
"localfile.zip",
],
"timeout": "120s",
"secret_envs": ["MY_SECRET"],
},
{
"name": "ubuntu",
"script": "echo hello",
},
],
"source": {
"storage_source": {
"bucket": "mybucket",
"object": "source_code.tar.gz",
},
},
"tags": [
"build",
"newFeature",
],
"substitutions": {
"_FOO": "bar",
"_BAZ": "qux",
},
"queue_ttl": "20s",
"logs_bucket": "gs://mybucket/logs",
"secrets": [{
"kms_key_name": "projects/myProject/locations/global/keyRings/keyring-name/cryptoKeys/key-name",
"secret_env": {
"PASSWORD": "ZW5jcnlwdGVkLXBhc3N3b3JkCg==",
},
}],
"available_secrets": {
"secret_managers": [{
"env": "MY_SECRET",
"version_name": "projects/myProject/secrets/mySecret/versions/latest",
}],
},
"artifacts": {
"images": ["gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA"],
"objects": {
"location": "gs://bucket/path/to/somewhere/",
"paths": ["path"],
},
"npm_packages": [{
"package_path": "package.json",
"repository": "https://us-west1-npm.pkg.dev/myProject/quickstart-nodejs-repo",
}],
"python_packages": [{
"paths": ["dist/*"],
"repository": "https://us-west1-python.pkg.dev/myProject/quickstart-python-repo",
}],
"maven_artifacts": [{
"repository": "https://us-west1-maven.pkg.dev/myProject/quickstart-java-repo",
"path": "/workspace/my-app/target/my-app-1.0.SNAPSHOT.jar",
"artifact_id": "my-app",
"group_id": "com.mycompany.app",
"version": "1.0",
}],
},
"options": {
"source_provenance_hashes": ["MD5"],
"requested_verify_option": "VERIFIED",
"machine_type": "N1_HIGHCPU_8",
"disk_size_gb": 100,
"substitution_option": "ALLOW_LOOSE",
"dynamic_substitutions": True,
"log_streaming_option": "STREAM_OFF",
"worker_pool": "pool",
"logging": "LEGACY",
"envs": ["ekey = evalue"],
"secret_envs": ["secretenv = svalue"],
"volumes": [{
"name": "v1",
"path": "v1",
}],
},
})
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/cloudbuild"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := cloudbuild.NewTrigger(ctx, "build-trigger", &cloudbuild.TriggerArgs{
Name: pulumi.String("my-trigger"),
Location: pulumi.String("global"),
TriggerTemplate: &cloudbuild.TriggerTriggerTemplateArgs{
BranchName: pulumi.String("main"),
RepoName: pulumi.String("my-repo"),
},
Build: &cloudbuild.TriggerBuildArgs{
Steps: cloudbuild.TriggerBuildStepArray{
&cloudbuild.TriggerBuildStepArgs{
Name: pulumi.String("gcr.io/cloud-builders/gcloud"),
Args: pulumi.StringArray{
pulumi.String("storage"),
pulumi.String("cp"),
pulumi.String("gs://mybucket/remotefile.zip"),
pulumi.String("localfile.zip"),
},
Timeout: pulumi.String("120s"),
SecretEnvs: pulumi.StringArray{
pulumi.String("MY_SECRET"),
},
},
&cloudbuild.TriggerBuildStepArgs{
Name: pulumi.String("ubuntu"),
Script: pulumi.String("echo hello"),
},
},
Source: &cloudbuild.TriggerBuildSourceArgs{
StorageSource: &cloudbuild.TriggerBuildSourceStorageSourceArgs{
Bucket: pulumi.String("mybucket"),
Object: pulumi.String("source_code.tar.gz"),
},
},
Tags: pulumi.StringArray{
pulumi.String("build"),
pulumi.String("newFeature"),
},
Substitutions: pulumi.StringMap{
"_FOO": pulumi.String("bar"),
"_BAZ": pulumi.String("qux"),
},
QueueTtl: pulumi.String("20s"),
LogsBucket: pulumi.String("gs://mybucket/logs"),
Secrets: cloudbuild.TriggerBuildSecretArray{
&cloudbuild.TriggerBuildSecretArgs{
KmsKeyName: pulumi.String("projects/myProject/locations/global/keyRings/keyring-name/cryptoKeys/key-name"),
SecretEnv: pulumi.StringMap{
"PASSWORD": pulumi.String("ZW5jcnlwdGVkLXBhc3N3b3JkCg=="),
},
},
},
AvailableSecrets: &cloudbuild.TriggerBuildAvailableSecretsArgs{
SecretManagers: cloudbuild.TriggerBuildAvailableSecretsSecretManagerArray{
&cloudbuild.TriggerBuildAvailableSecretsSecretManagerArgs{
Env: pulumi.String("MY_SECRET"),
VersionName: pulumi.String("projects/myProject/secrets/mySecret/versions/latest"),
},
},
},
Artifacts: &cloudbuild.TriggerBuildArtifactsArgs{
Images: pulumi.StringArray{
pulumi.String("gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA"),
},
Objects: &cloudbuild.TriggerBuildArtifactsObjectsArgs{
Location: pulumi.String("gs://bucket/path/to/somewhere/"),
Paths: pulumi.StringArray{
pulumi.String("path"),
},
},
NpmPackages: cloudbuild.TriggerBuildArtifactsNpmPackageArray{
&cloudbuild.TriggerBuildArtifactsNpmPackageArgs{
PackagePath: pulumi.String("package.json"),
Repository: pulumi.String("https://us-west1-npm.pkg.dev/myProject/quickstart-nodejs-repo"),
},
},
PythonPackages: cloudbuild.TriggerBuildArtifactsPythonPackageArray{
&cloudbuild.TriggerBuildArtifactsPythonPackageArgs{
Paths: pulumi.StringArray{
pulumi.String("dist/*"),
},
Repository: pulumi.String("https://us-west1-python.pkg.dev/myProject/quickstart-python-repo"),
},
},
MavenArtifacts: cloudbuild.TriggerBuildArtifactsMavenArtifactArray{
&cloudbuild.TriggerBuildArtifactsMavenArtifactArgs{
Repository: pulumi.String("https://us-west1-maven.pkg.dev/myProject/quickstart-java-repo"),
Path: pulumi.String("/workspace/my-app/target/my-app-1.0.SNAPSHOT.jar"),
ArtifactId: pulumi.String("my-app"),
GroupId: pulumi.String("com.mycompany.app"),
Version: pulumi.String("1.0"),
},
},
},
Options: &cloudbuild.TriggerBuildOptionsArgs{
SourceProvenanceHashes: pulumi.StringArray{
pulumi.String("MD5"),
},
RequestedVerifyOption: pulumi.String("VERIFIED"),
MachineType: pulumi.String("N1_HIGHCPU_8"),
DiskSizeGb: pulumi.Int(100),
SubstitutionOption: pulumi.String("ALLOW_LOOSE"),
DynamicSubstitutions: pulumi.Bool(true),
LogStreamingOption: pulumi.String("STREAM_OFF"),
WorkerPool: pulumi.String("pool"),
Logging: pulumi.String("LEGACY"),
Envs: pulumi.StringArray{
pulumi.String("ekey = evalue"),
},
SecretEnvs: pulumi.StringArray{
pulumi.String("secretenv = svalue"),
},
Volumes: cloudbuild.TriggerBuildOptionsVolumeArray{
&cloudbuild.TriggerBuildOptionsVolumeArgs{
Name: pulumi.String("v1"),
Path: pulumi.String("v1"),
},
},
},
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
return await Deployment.RunAsync(() =>
{
var build_trigger = new Gcp.CloudBuild.Trigger("build-trigger", new()
{
Name = "my-trigger",
Location = "global",
TriggerTemplate = new Gcp.CloudBuild.Inputs.TriggerTriggerTemplateArgs
{
BranchName = "main",
RepoName = "my-repo",
},
Build = new Gcp.CloudBuild.Inputs.TriggerBuildArgs
{
Steps = new[]
{
new Gcp.CloudBuild.Inputs.TriggerBuildStepArgs
{
Name = "gcr.io/cloud-builders/gcloud",
Args = new[]
{
"storage",
"cp",
"gs://mybucket/remotefile.zip",
"localfile.zip",
},
Timeout = "120s",
SecretEnvs = new[]
{
"MY_SECRET",
},
},
new Gcp.CloudBuild.Inputs.TriggerBuildStepArgs
{
Name = "ubuntu",
Script = "echo hello",
},
},
Source = new Gcp.CloudBuild.Inputs.TriggerBuildSourceArgs
{
StorageSource = new Gcp.CloudBuild.Inputs.TriggerBuildSourceStorageSourceArgs
{
Bucket = "mybucket",
Object = "source_code.tar.gz",
},
},
Tags = new[]
{
"build",
"newFeature",
},
Substitutions =
{
{ "_FOO", "bar" },
{ "_BAZ", "qux" },
},
QueueTtl = "20s",
LogsBucket = "gs://mybucket/logs",
Secrets = new[]
{
new Gcp.CloudBuild.Inputs.TriggerBuildSecretArgs
{
KmsKeyName = "projects/myProject/locations/global/keyRings/keyring-name/cryptoKeys/key-name",
SecretEnv =
{
{ "PASSWORD", "ZW5jcnlwdGVkLXBhc3N3b3JkCg==" },
},
},
},
AvailableSecrets = new Gcp.CloudBuild.Inputs.TriggerBuildAvailableSecretsArgs
{
SecretManagers = new[]
{
new Gcp.CloudBuild.Inputs.TriggerBuildAvailableSecretsSecretManagerArgs
{
Env = "MY_SECRET",
VersionName = "projects/myProject/secrets/mySecret/versions/latest",
},
},
},
Artifacts = new Gcp.CloudBuild.Inputs.TriggerBuildArtifactsArgs
{
Images = new[]
{
"gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA",
},
Objects = new Gcp.CloudBuild.Inputs.TriggerBuildArtifactsObjectsArgs
{
Location = "gs://bucket/path/to/somewhere/",
Paths = new[]
{
"path",
},
},
NpmPackages = new[]
{
new Gcp.CloudBuild.Inputs.TriggerBuildArtifactsNpmPackageArgs
{
PackagePath = "package.json",
Repository = "https://us-west1-npm.pkg.dev/myProject/quickstart-nodejs-repo",
},
},
PythonPackages = new[]
{
new Gcp.CloudBuild.Inputs.TriggerBuildArtifactsPythonPackageArgs
{
Paths = new[]
{
"dist/*",
},
Repository = "https://us-west1-python.pkg.dev/myProject/quickstart-python-repo",
},
},
MavenArtifacts = new[]
{
new Gcp.CloudBuild.Inputs.TriggerBuildArtifactsMavenArtifactArgs
{
Repository = "https://us-west1-maven.pkg.dev/myProject/quickstart-java-repo",
Path = "/workspace/my-app/target/my-app-1.0.SNAPSHOT.jar",
ArtifactId = "my-app",
GroupId = "com.mycompany.app",
Version = "1.0",
},
},
},
Options = new Gcp.CloudBuild.Inputs.TriggerBuildOptionsArgs
{
SourceProvenanceHashes = new[]
{
"MD5",
},
RequestedVerifyOption = "VERIFIED",
MachineType = "N1_HIGHCPU_8",
DiskSizeGb = 100,
SubstitutionOption = "ALLOW_LOOSE",
DynamicSubstitutions = true,
LogStreamingOption = "STREAM_OFF",
WorkerPool = "pool",
Logging = "LEGACY",
Envs = new[]
{
"ekey = evalue",
},
SecretEnvs = new[]
{
"secretenv = svalue",
},
Volumes = new[]
{
new Gcp.CloudBuild.Inputs.TriggerBuildOptionsVolumeArgs
{
Name = "v1",
Path = "v1",
},
},
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.cloudbuild.Trigger;
import com.pulumi.gcp.cloudbuild.TriggerArgs;
import com.pulumi.gcp.cloudbuild.inputs.TriggerTriggerTemplateArgs;
import com.pulumi.gcp.cloudbuild.inputs.TriggerBuildArgs;
import com.pulumi.gcp.cloudbuild.inputs.TriggerBuildSourceArgs;
import com.pulumi.gcp.cloudbuild.inputs.TriggerBuildSourceStorageSourceArgs;
import com.pulumi.gcp.cloudbuild.inputs.TriggerBuildAvailableSecretsArgs;
import com.pulumi.gcp.cloudbuild.inputs.TriggerBuildArtifactsArgs;
import com.pulumi.gcp.cloudbuild.inputs.TriggerBuildArtifactsObjectsArgs;
import com.pulumi.gcp.cloudbuild.inputs.TriggerBuildOptionsArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var build_trigger = new Trigger("build-trigger", TriggerArgs.builder()
.name("my-trigger")
.location("global")
.triggerTemplate(TriggerTriggerTemplateArgs.builder()
.branchName("main")
.repoName("my-repo")
.build())
.build(TriggerBuildArgs.builder()
.steps(
TriggerBuildStepArgs.builder()
.name("gcr.io/cloud-builders/gcloud")
.args(
"storage",
"cp",
"gs://mybucket/remotefile.zip",
"localfile.zip")
.timeout("120s")
.secretEnvs("MY_SECRET")
.build(),
TriggerBuildStepArgs.builder()
.name("ubuntu")
.script("echo hello")
.build())
.source(TriggerBuildSourceArgs.builder()
.storageSource(TriggerBuildSourceStorageSourceArgs.builder()
.bucket("mybucket")
.object("source_code.tar.gz")
.build())
.build())
.tags(
"build",
"newFeature")
.substitutions(Map.ofEntries(
Map.entry("_FOO", "bar"),
Map.entry("_BAZ", "qux")
))
.queueTtl("20s")
.logsBucket("gs://mybucket/logs")
.secrets(TriggerBuildSecretArgs.builder()
.kmsKeyName("projects/myProject/locations/global/keyRings/keyring-name/cryptoKeys/key-name")
.secretEnv(Map.of("PASSWORD", "ZW5jcnlwdGVkLXBhc3N3b3JkCg=="))
.build())
.availableSecrets(TriggerBuildAvailableSecretsArgs.builder()
.secretManagers(TriggerBuildAvailableSecretsSecretManagerArgs.builder()
.env("MY_SECRET")
.versionName("projects/myProject/secrets/mySecret/versions/latest")
.build())
.build())
.artifacts(TriggerBuildArtifactsArgs.builder()
.images("gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA")
.objects(TriggerBuildArtifactsObjectsArgs.builder()
.location("gs://bucket/path/to/somewhere/")
.paths("path")
.build())
.npmPackages(TriggerBuildArtifactsNpmPackageArgs.builder()
.packagePath("package.json")
.repository("https://us-west1-npm.pkg.dev/myProject/quickstart-nodejs-repo")
.build())
.pythonPackages(TriggerBuildArtifactsPythonPackageArgs.builder()
.paths("dist/*")
.repository("https://us-west1-python.pkg.dev/myProject/quickstart-python-repo")
.build())
.mavenArtifacts(TriggerBuildArtifactsMavenArtifactArgs.builder()
.repository("https://us-west1-maven.pkg.dev/myProject/quickstart-java-repo")
.path("/workspace/my-app/target/my-app-1.0.SNAPSHOT.jar")
.artifactId("my-app")
.groupId("com.mycompany.app")
.version("1.0")
.build())
.build())
.options(TriggerBuildOptionsArgs.builder()
.sourceProvenanceHashes("MD5")
.requestedVerifyOption("VERIFIED")
.machineType("N1_HIGHCPU_8")
.diskSizeGb(100)
.substitutionOption("ALLOW_LOOSE")
.dynamicSubstitutions(true)
.logStreamingOption("STREAM_OFF")
.workerPool("pool")
.logging("LEGACY")
.envs("ekey = evalue")
.secretEnvs("secretenv = svalue")
.volumes(TriggerBuildOptionsVolumeArgs.builder()
.name("v1")
.path("v1")
.build())
.build())
.build())
.build());
}
}
resources:
build-trigger:
type: gcp:cloudbuild:Trigger
properties:
name: my-trigger
location: global
triggerTemplate:
branchName: main
repoName: my-repo
build:
steps:
- name: gcr.io/cloud-builders/gcloud
args:
- storage
- cp
- gs://mybucket/remotefile.zip
- localfile.zip
timeout: 120s
secretEnvs:
- MY_SECRET
- name: ubuntu
script: echo hello
source:
storageSource:
bucket: mybucket
object: source_code.tar.gz
tags:
- build
- newFeature
substitutions:
_FOO: bar
_BAZ: qux
queueTtl: 20s
logsBucket: gs://mybucket/logs
secrets:
- kmsKeyName: projects/myProject/locations/global/keyRings/keyring-name/cryptoKeys/key-name
secretEnv:
PASSWORD: ZW5jcnlwdGVkLXBhc3N3b3JkCg==
availableSecrets:
secretManagers:
- env: MY_SECRET
versionName: projects/myProject/secrets/mySecret/versions/latest
artifacts:
images:
- gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA
objects:
location: gs://bucket/path/to/somewhere/
paths:
- path
npmPackages:
- packagePath: package.json
repository: https://us-west1-npm.pkg.dev/myProject/quickstart-nodejs-repo
pythonPackages:
- paths:
- dist/*
repository: https://us-west1-python.pkg.dev/myProject/quickstart-python-repo
mavenArtifacts:
- repository: https://us-west1-maven.pkg.dev/myProject/quickstart-java-repo
path: /workspace/my-app/target/my-app-1.0.SNAPSHOT.jar
artifactId: my-app
groupId: com.mycompany.app
version: '1.0'
options:
sourceProvenanceHashes:
- MD5
requestedVerifyOption: VERIFIED
machineType: N1_HIGHCPU_8
diskSizeGb: 100
substitutionOption: ALLOW_LOOSE
dynamicSubstitutions: true
logStreamingOption: STREAM_OFF
workerPool: pool
logging: LEGACY
envs:
- ekey = evalue
secretEnvs:
- secretenv = svalue
volumes:
- name: v1
path: v1
The build property contains the complete build definition. The steps array defines each build operation: the name specifies a container image, args provides command arguments, and timeout limits execution time. The source property points to a Cloud Storage object containing source code. The secrets and availableSecrets properties integrate with KMS and Secret Manager for encrypted values. The artifacts property specifies where to publish build outputs (container images, language packages, or arbitrary objects).
Trigger builds from Pub/Sub messages
Event-driven architectures use Pub/Sub to coordinate builds across services, allowing external systems to trigger builds by publishing messages.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const mytopic = new gcp.pubsub.Topic("mytopic", {name: "my-topic"});
const pubsub_config_trigger = new gcp.cloudbuild.Trigger("pubsub-config-trigger", {
location: "us-central1",
name: "pubsub-trigger",
description: "acceptance test example pubsub build trigger",
pubsubConfig: {
topic: mytopic.id,
},
sourceToBuild: {
uri: "https://hashicorp/terraform-provider-google-beta",
ref: "refs/heads/main",
repoType: "GITHUB",
},
gitFileSource: {
path: "cloudbuild.yaml",
uri: "https://hashicorp/terraform-provider-google-beta",
revision: "refs/heads/main",
repoType: "GITHUB",
},
substitutions: {
_ACTION: "$(body.message.data.action)",
},
filter: "_ACTION.matches('INSERT')",
});
import pulumi
import pulumi_gcp as gcp
mytopic = gcp.pubsub.Topic("mytopic", name="my-topic")
pubsub_config_trigger = gcp.cloudbuild.Trigger("pubsub-config-trigger",
location="us-central1",
name="pubsub-trigger",
description="acceptance test example pubsub build trigger",
pubsub_config={
"topic": mytopic.id,
},
source_to_build={
"uri": "https://hashicorp/terraform-provider-google-beta",
"ref": "refs/heads/main",
"repo_type": "GITHUB",
},
git_file_source={
"path": "cloudbuild.yaml",
"uri": "https://hashicorp/terraform-provider-google-beta",
"revision": "refs/heads/main",
"repo_type": "GITHUB",
},
substitutions={
"_ACTION": "$(body.message.data.action)",
},
filter="_ACTION.matches('INSERT')")
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/cloudbuild"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/pubsub"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
mytopic, err := pubsub.NewTopic(ctx, "mytopic", &pubsub.TopicArgs{
Name: pulumi.String("my-topic"),
})
if err != nil {
return err
}
_, err = cloudbuild.NewTrigger(ctx, "pubsub-config-trigger", &cloudbuild.TriggerArgs{
Location: pulumi.String("us-central1"),
Name: pulumi.String("pubsub-trigger"),
Description: pulumi.String("acceptance test example pubsub build trigger"),
PubsubConfig: &cloudbuild.TriggerPubsubConfigArgs{
Topic: mytopic.ID(),
},
SourceToBuild: &cloudbuild.TriggerSourceToBuildArgs{
Uri: pulumi.String("https://hashicorp/terraform-provider-google-beta"),
Ref: pulumi.String("refs/heads/main"),
RepoType: pulumi.String("GITHUB"),
},
GitFileSource: &cloudbuild.TriggerGitFileSourceArgs{
Path: pulumi.String("cloudbuild.yaml"),
Uri: pulumi.String("https://hashicorp/terraform-provider-google-beta"),
Revision: pulumi.String("refs/heads/main"),
RepoType: pulumi.String("GITHUB"),
},
Substitutions: pulumi.StringMap{
"_ACTION": pulumi.String("$(body.message.data.action)"),
},
Filter: pulumi.String("_ACTION.matches('INSERT')"),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
return await Deployment.RunAsync(() =>
{
var mytopic = new Gcp.PubSub.Topic("mytopic", new()
{
Name = "my-topic",
});
var pubsub_config_trigger = new Gcp.CloudBuild.Trigger("pubsub-config-trigger", new()
{
Location = "us-central1",
Name = "pubsub-trigger",
Description = "acceptance test example pubsub build trigger",
PubsubConfig = new Gcp.CloudBuild.Inputs.TriggerPubsubConfigArgs
{
Topic = mytopic.Id,
},
SourceToBuild = new Gcp.CloudBuild.Inputs.TriggerSourceToBuildArgs
{
Uri = "https://hashicorp/terraform-provider-google-beta",
Ref = "refs/heads/main",
RepoType = "GITHUB",
},
GitFileSource = new Gcp.CloudBuild.Inputs.TriggerGitFileSourceArgs
{
Path = "cloudbuild.yaml",
Uri = "https://hashicorp/terraform-provider-google-beta",
Revision = "refs/heads/main",
RepoType = "GITHUB",
},
Substitutions =
{
{ "_ACTION", "$(body.message.data.action)" },
},
Filter = "_ACTION.matches('INSERT')",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.pubsub.Topic;
import com.pulumi.gcp.pubsub.TopicArgs;
import com.pulumi.gcp.cloudbuild.Trigger;
import com.pulumi.gcp.cloudbuild.TriggerArgs;
import com.pulumi.gcp.cloudbuild.inputs.TriggerPubsubConfigArgs;
import com.pulumi.gcp.cloudbuild.inputs.TriggerSourceToBuildArgs;
import com.pulumi.gcp.cloudbuild.inputs.TriggerGitFileSourceArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var mytopic = new Topic("mytopic", TopicArgs.builder()
.name("my-topic")
.build());
var pubsub_config_trigger = new Trigger("pubsub-config-trigger", TriggerArgs.builder()
.location("us-central1")
.name("pubsub-trigger")
.description("acceptance test example pubsub build trigger")
.pubsubConfig(TriggerPubsubConfigArgs.builder()
.topic(mytopic.id())
.build())
.sourceToBuild(TriggerSourceToBuildArgs.builder()
.uri("https://hashicorp/terraform-provider-google-beta")
.ref("refs/heads/main")
.repoType("GITHUB")
.build())
.gitFileSource(TriggerGitFileSourceArgs.builder()
.path("cloudbuild.yaml")
.uri("https://hashicorp/terraform-provider-google-beta")
.revision("refs/heads/main")
.repoType("GITHUB")
.build())
.substitutions(Map.of("_ACTION", "$(body.message.data.action)"))
.filter("_ACTION.matches('INSERT')")
.build());
}
}
resources:
mytopic:
type: gcp:pubsub:Topic
properties:
name: my-topic
pubsub-config-trigger:
type: gcp:cloudbuild:Trigger
properties:
location: us-central1
name: pubsub-trigger
description: acceptance test example pubsub build trigger
pubsubConfig:
topic: ${mytopic.id}
sourceToBuild:
uri: https://hashicorp/terraform-provider-google-beta
ref: refs/heads/main
repoType: GITHUB
gitFileSource:
path: cloudbuild.yaml
uri: https://hashicorp/terraform-provider-google-beta
revision: refs/heads/main
repoType: GITHUB
substitutions:
_ACTION: $(body.message.data.action)
filter: _ACTION.matches('INSERT')
The pubsubConfig property connects the trigger to a Pub/Sub topic. When messages arrive, Cloud Build evaluates the filter expression against message attributes. The sourceToBuild property specifies which repository and ref to build. The gitFileSource property locates the build file within that repository. The substitutions property extracts values from the Pub/Sub message body using JSONPath expressions like $(body.message.data.action).
Connect to repositories via Cloud Build v2 connections
Cloud Build v2 connections provide a unified way to authenticate with GitHub, GitLab, and other Git providers, replacing legacy trigger templates.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const my_connection = new gcp.cloudbuildv2.Connection("my-connection", {
location: "us-central1",
name: "my-connection",
githubConfig: {
appInstallationId: 123123,
authorizerCredential: {
oauthTokenSecretVersion: "projects/my-project/secrets/github-pat-secret/versions/latest",
},
},
});
const my_repository = new gcp.cloudbuildv2.Repository("my-repository", {
name: "my-repo",
parentConnection: my_connection.id,
remoteUri: "https://github.com/myuser/my-repo.git",
});
const repo_trigger = new gcp.cloudbuild.Trigger("repo-trigger", {
location: "us-central1",
repositoryEventConfig: {
repository: my_repository.id,
push: {
branch: "feature-.*",
},
},
filename: "cloudbuild.yaml",
});
import pulumi
import pulumi_gcp as gcp
my_connection = gcp.cloudbuildv2.Connection("my-connection",
location="us-central1",
name="my-connection",
github_config={
"app_installation_id": 123123,
"authorizer_credential": {
"oauth_token_secret_version": "projects/my-project/secrets/github-pat-secret/versions/latest",
},
})
my_repository = gcp.cloudbuildv2.Repository("my-repository",
name="my-repo",
parent_connection=my_connection.id,
remote_uri="https://github.com/myuser/my-repo.git")
repo_trigger = gcp.cloudbuild.Trigger("repo-trigger",
location="us-central1",
repository_event_config={
"repository": my_repository.id,
"push": {
"branch": "feature-.*",
},
},
filename="cloudbuild.yaml")
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/cloudbuild"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/cloudbuildv2"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
my_connection, err := cloudbuildv2.NewConnection(ctx, "my-connection", &cloudbuildv2.ConnectionArgs{
Location: pulumi.String("us-central1"),
Name: pulumi.String("my-connection"),
GithubConfig: &cloudbuildv2.ConnectionGithubConfigArgs{
AppInstallationId: pulumi.Int(123123),
AuthorizerCredential: &cloudbuildv2.ConnectionGithubConfigAuthorizerCredentialArgs{
OauthTokenSecretVersion: pulumi.String("projects/my-project/secrets/github-pat-secret/versions/latest"),
},
},
})
if err != nil {
return err
}
my_repository, err := cloudbuildv2.NewRepository(ctx, "my-repository", &cloudbuildv2.RepositoryArgs{
Name: pulumi.String("my-repo"),
ParentConnection: my_connection.ID(),
RemoteUri: pulumi.String("https://github.com/myuser/my-repo.git"),
})
if err != nil {
return err
}
_, err = cloudbuild.NewTrigger(ctx, "repo-trigger", &cloudbuild.TriggerArgs{
Location: pulumi.String("us-central1"),
RepositoryEventConfig: &cloudbuild.TriggerRepositoryEventConfigArgs{
Repository: my_repository.ID(),
Push: &cloudbuild.TriggerRepositoryEventConfigPushArgs{
Branch: pulumi.String("feature-.*"),
},
},
Filename: pulumi.String("cloudbuild.yaml"),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
return await Deployment.RunAsync(() =>
{
var my_connection = new Gcp.CloudBuildV2.Connection("my-connection", new()
{
Location = "us-central1",
Name = "my-connection",
GithubConfig = new Gcp.CloudBuildV2.Inputs.ConnectionGithubConfigArgs
{
AppInstallationId = 123123,
AuthorizerCredential = new Gcp.CloudBuildV2.Inputs.ConnectionGithubConfigAuthorizerCredentialArgs
{
OauthTokenSecretVersion = "projects/my-project/secrets/github-pat-secret/versions/latest",
},
},
});
var my_repository = new Gcp.CloudBuildV2.Repository("my-repository", new()
{
Name = "my-repo",
ParentConnection = my_connection.Id,
RemoteUri = "https://github.com/myuser/my-repo.git",
});
var repo_trigger = new Gcp.CloudBuild.Trigger("repo-trigger", new()
{
Location = "us-central1",
RepositoryEventConfig = new Gcp.CloudBuild.Inputs.TriggerRepositoryEventConfigArgs
{
Repository = my_repository.Id,
Push = new Gcp.CloudBuild.Inputs.TriggerRepositoryEventConfigPushArgs
{
Branch = "feature-.*",
},
},
Filename = "cloudbuild.yaml",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.cloudbuildv2.Connection;
import com.pulumi.gcp.cloudbuildv2.ConnectionArgs;
import com.pulumi.gcp.cloudbuildv2.inputs.ConnectionGithubConfigArgs;
import com.pulumi.gcp.cloudbuildv2.inputs.ConnectionGithubConfigAuthorizerCredentialArgs;
import com.pulumi.gcp.cloudbuildv2.Repository;
import com.pulumi.gcp.cloudbuildv2.RepositoryArgs;
import com.pulumi.gcp.cloudbuild.Trigger;
import com.pulumi.gcp.cloudbuild.TriggerArgs;
import com.pulumi.gcp.cloudbuild.inputs.TriggerRepositoryEventConfigArgs;
import com.pulumi.gcp.cloudbuild.inputs.TriggerRepositoryEventConfigPushArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var my_connection = new Connection("my-connection", ConnectionArgs.builder()
.location("us-central1")
.name("my-connection")
.githubConfig(ConnectionGithubConfigArgs.builder()
.appInstallationId(123123)
.authorizerCredential(ConnectionGithubConfigAuthorizerCredentialArgs.builder()
.oauthTokenSecretVersion("projects/my-project/secrets/github-pat-secret/versions/latest")
.build())
.build())
.build());
var my_repository = new Repository("my-repository", RepositoryArgs.builder()
.name("my-repo")
.parentConnection(my_connection.id())
.remoteUri("https://github.com/myuser/my-repo.git")
.build());
var repo_trigger = new Trigger("repo-trigger", TriggerArgs.builder()
.location("us-central1")
.repositoryEventConfig(TriggerRepositoryEventConfigArgs.builder()
.repository(my_repository.id())
.push(TriggerRepositoryEventConfigPushArgs.builder()
.branch("feature-.*")
.build())
.build())
.filename("cloudbuild.yaml")
.build());
}
}
resources:
my-connection:
type: gcp:cloudbuildv2:Connection
properties:
location: us-central1
name: my-connection
githubConfig:
appInstallationId: 123123
authorizerCredential:
oauthTokenSecretVersion: projects/my-project/secrets/github-pat-secret/versions/latest
my-repository:
type: gcp:cloudbuildv2:Repository
properties:
name: my-repo
parentConnection: ${["my-connection"].id}
remoteUri: https://github.com/myuser/my-repo.git
repo-trigger:
type: gcp:cloudbuild:Trigger
properties:
location: us-central1
repositoryEventConfig:
repository: ${["my-repository"].id}
push:
branch: feature-.*
filename: cloudbuild.yaml
The repositoryEventConfig property uses a Cloud Build v2 Repository resource, which itself references a Connection. The push property defines branch patterns using regular expressions. This approach centralizes authentication in the Connection resource rather than duplicating credentials across triggers.
Run builds with a custom service account
Production builds often need specific IAM permissions beyond the default Cloud Build service account.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const project = gcp.organizations.getProject({});
const cloudbuildServiceAccount = new gcp.serviceaccount.Account("cloudbuild_service_account", {accountId: "cloud-sa"});
const actAs = new gcp.projects.IAMMember("act_as", {
project: project.then(project => project.projectId),
role: "roles/iam.serviceAccountUser",
member: pulumi.interpolate`serviceAccount:${cloudbuildServiceAccount.email}`,
});
const logsWriter = new gcp.projects.IAMMember("logs_writer", {
project: project.then(project => project.projectId),
role: "roles/logging.logWriter",
member: pulumi.interpolate`serviceAccount:${cloudbuildServiceAccount.email}`,
});
const service_account_trigger = new gcp.cloudbuild.Trigger("service-account-trigger", {
triggerTemplate: {
branchName: "main",
repoName: "my-repo",
},
serviceAccount: cloudbuildServiceAccount.id,
filename: "cloudbuild.yaml",
}, {
dependsOn: [
actAs,
logsWriter,
],
});
import pulumi
import pulumi_gcp as gcp
project = gcp.organizations.get_project()
cloudbuild_service_account = gcp.serviceaccount.Account("cloudbuild_service_account", account_id="cloud-sa")
act_as = gcp.projects.IAMMember("act_as",
project=project.project_id,
role="roles/iam.serviceAccountUser",
member=cloudbuild_service_account.email.apply(lambda email: f"serviceAccount:{email}"))
logs_writer = gcp.projects.IAMMember("logs_writer",
project=project.project_id,
role="roles/logging.logWriter",
member=cloudbuild_service_account.email.apply(lambda email: f"serviceAccount:{email}"))
service_account_trigger = gcp.cloudbuild.Trigger("service-account-trigger",
trigger_template={
"branch_name": "main",
"repo_name": "my-repo",
},
service_account=cloudbuild_service_account.id,
filename="cloudbuild.yaml",
opts = pulumi.ResourceOptions(depends_on=[
act_as,
logs_writer,
]))
package main
import (
"fmt"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/cloudbuild"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/projects"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/serviceaccount"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
project, err := organizations.LookupProject(ctx, &organizations.LookupProjectArgs{}, nil)
if err != nil {
return err
}
cloudbuildServiceAccount, err := serviceaccount.NewAccount(ctx, "cloudbuild_service_account", &serviceaccount.AccountArgs{
AccountId: pulumi.String("cloud-sa"),
})
if err != nil {
return err
}
actAs, err := projects.NewIAMMember(ctx, "act_as", &projects.IAMMemberArgs{
Project: pulumi.String(project.ProjectId),
Role: pulumi.String("roles/iam.serviceAccountUser"),
Member: cloudbuildServiceAccount.Email.ApplyT(func(email string) (string, error) {
return fmt.Sprintf("serviceAccount:%v", email), nil
}).(pulumi.StringOutput),
})
if err != nil {
return err
}
logsWriter, err := projects.NewIAMMember(ctx, "logs_writer", &projects.IAMMemberArgs{
Project: pulumi.String(project.ProjectId),
Role: pulumi.String("roles/logging.logWriter"),
Member: cloudbuildServiceAccount.Email.ApplyT(func(email string) (string, error) {
return fmt.Sprintf("serviceAccount:%v", email), nil
}).(pulumi.StringOutput),
})
if err != nil {
return err
}
_, err = cloudbuild.NewTrigger(ctx, "service-account-trigger", &cloudbuild.TriggerArgs{
TriggerTemplate: &cloudbuild.TriggerTriggerTemplateArgs{
BranchName: pulumi.String("main"),
RepoName: pulumi.String("my-repo"),
},
ServiceAccount: cloudbuildServiceAccount.ID(),
Filename: pulumi.String("cloudbuild.yaml"),
}, pulumi.DependsOn([]pulumi.Resource{
actAs,
logsWriter,
}))
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
return await Deployment.RunAsync(() =>
{
var project = Gcp.Organizations.GetProject.Invoke();
var cloudbuildServiceAccount = new Gcp.ServiceAccount.Account("cloudbuild_service_account", new()
{
AccountId = "cloud-sa",
});
var actAs = new Gcp.Projects.IAMMember("act_as", new()
{
Project = project.Apply(getProjectResult => getProjectResult.ProjectId),
Role = "roles/iam.serviceAccountUser",
Member = cloudbuildServiceAccount.Email.Apply(email => $"serviceAccount:{email}"),
});
var logsWriter = new Gcp.Projects.IAMMember("logs_writer", new()
{
Project = project.Apply(getProjectResult => getProjectResult.ProjectId),
Role = "roles/logging.logWriter",
Member = cloudbuildServiceAccount.Email.Apply(email => $"serviceAccount:{email}"),
});
var service_account_trigger = new Gcp.CloudBuild.Trigger("service-account-trigger", new()
{
TriggerTemplate = new Gcp.CloudBuild.Inputs.TriggerTriggerTemplateArgs
{
BranchName = "main",
RepoName = "my-repo",
},
ServiceAccount = cloudbuildServiceAccount.Id,
Filename = "cloudbuild.yaml",
}, new CustomResourceOptions
{
DependsOn =
{
actAs,
logsWriter,
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetProjectArgs;
import com.pulumi.gcp.serviceaccount.Account;
import com.pulumi.gcp.serviceaccount.AccountArgs;
import com.pulumi.gcp.projects.IAMMember;
import com.pulumi.gcp.projects.IAMMemberArgs;
import com.pulumi.gcp.cloudbuild.Trigger;
import com.pulumi.gcp.cloudbuild.TriggerArgs;
import com.pulumi.gcp.cloudbuild.inputs.TriggerTriggerTemplateArgs;
import com.pulumi.resources.CustomResourceOptions;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
final var project = OrganizationsFunctions.getProject(GetProjectArgs.builder()
.build());
var cloudbuildServiceAccount = new Account("cloudbuildServiceAccount", AccountArgs.builder()
.accountId("cloud-sa")
.build());
var actAs = new IAMMember("actAs", IAMMemberArgs.builder()
.project(project.projectId())
.role("roles/iam.serviceAccountUser")
.member(cloudbuildServiceAccount.email().applyValue(_email -> String.format("serviceAccount:%s", _email)))
.build());
var logsWriter = new IAMMember("logsWriter", IAMMemberArgs.builder()
.project(project.projectId())
.role("roles/logging.logWriter")
.member(cloudbuildServiceAccount.email().applyValue(_email -> String.format("serviceAccount:%s", _email)))
.build());
var service_account_trigger = new Trigger("service-account-trigger", TriggerArgs.builder()
.triggerTemplate(TriggerTriggerTemplateArgs.builder()
.branchName("main")
.repoName("my-repo")
.build())
.serviceAccount(cloudbuildServiceAccount.id())
.filename("cloudbuild.yaml")
.build(), CustomResourceOptions.builder()
.dependsOn(
actAs,
logsWriter)
.build());
}
}
resources:
service-account-trigger:
type: gcp:cloudbuild:Trigger
properties:
triggerTemplate:
branchName: main
repoName: my-repo
serviceAccount: ${cloudbuildServiceAccount.id}
filename: cloudbuild.yaml
options:
dependsOn:
- ${actAs}
- ${logsWriter}
cloudbuildServiceAccount:
type: gcp:serviceaccount:Account
name: cloudbuild_service_account
properties:
accountId: cloud-sa
actAs:
type: gcp:projects:IAMMember
name: act_as
properties:
project: ${project.projectId}
role: roles/iam.serviceAccountUser
member: serviceAccount:${cloudbuildServiceAccount.email}
logsWriter:
type: gcp:projects:IAMMember
name: logs_writer
properties:
project: ${project.projectId}
role: roles/logging.logWriter
member: serviceAccount:${cloudbuildServiceAccount.email}
variables:
project:
fn::invoke:
function: gcp:organizations:getProject
arguments: {}
The serviceAccount property specifies which identity runs the build. The IAMMember resources grant necessary roles: roles/iam.serviceAccountUser allows Cloud Build to impersonate the account, and roles/logging.logWriter enables log output. The dependsOn ensures IAM bindings exist before creating the trigger.
Accept webhook requests with secret validation
External systems can trigger builds via HTTP webhooks, with Secret Manager providing the shared secret for request validation.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const webhookTriggerSecretKey = new gcp.secretmanager.Secret("webhook_trigger_secret_key", {
secretId: "webhook-trigger-secret-key",
replication: {
userManaged: {
replicas: [{
location: "us-central1",
}],
},
},
});
const webhookTriggerSecretKeyData = new gcp.secretmanager.SecretVersion("webhook_trigger_secret_key_data", {
secret: webhookTriggerSecretKey.id,
secretData: "secretkeygoeshere",
});
const project = gcp.organizations.getProject({});
const secretAccessor = project.then(project => gcp.organizations.getIAMPolicy({
bindings: [{
role: "roles/secretmanager.secretAccessor",
members: [`serviceAccount:service-${project.number}@gcp-sa-cloudbuild.iam.gserviceaccount.com`],
}],
}));
const policy = new gcp.secretmanager.SecretIamPolicy("policy", {
project: webhookTriggerSecretKey.project,
secretId: webhookTriggerSecretKey.secretId,
policyData: secretAccessor.then(secretAccessor => secretAccessor.policyData),
});
const webhook_config_trigger = new gcp.cloudbuild.Trigger("webhook-config-trigger", {
name: "webhook-trigger",
description: "acceptance test example webhook build trigger",
webhookConfig: {
secret: webhookTriggerSecretKeyData.id,
},
sourceToBuild: {
uri: "https://hashicorp/terraform-provider-google-beta",
ref: "refs/heads/main",
repoType: "GITHUB",
},
gitFileSource: {
path: "cloudbuild.yaml",
uri: "https://hashicorp/terraform-provider-google-beta",
revision: "refs/heads/main",
repoType: "GITHUB",
},
});
import pulumi
import pulumi_gcp as gcp
webhook_trigger_secret_key = gcp.secretmanager.Secret("webhook_trigger_secret_key",
secret_id="webhook-trigger-secret-key",
replication={
"user_managed": {
"replicas": [{
"location": "us-central1",
}],
},
})
webhook_trigger_secret_key_data = gcp.secretmanager.SecretVersion("webhook_trigger_secret_key_data",
secret=webhook_trigger_secret_key.id,
secret_data="secretkeygoeshere")
project = gcp.organizations.get_project()
secret_accessor = gcp.organizations.get_iam_policy(bindings=[{
"role": "roles/secretmanager.secretAccessor",
"members": [f"serviceAccount:service-{project.number}@gcp-sa-cloudbuild.iam.gserviceaccount.com"],
}])
policy = gcp.secretmanager.SecretIamPolicy("policy",
project=webhook_trigger_secret_key.project,
secret_id=webhook_trigger_secret_key.secret_id,
policy_data=secret_accessor.policy_data)
webhook_config_trigger = gcp.cloudbuild.Trigger("webhook-config-trigger",
name="webhook-trigger",
description="acceptance test example webhook build trigger",
webhook_config={
"secret": webhook_trigger_secret_key_data.id,
},
source_to_build={
"uri": "https://hashicorp/terraform-provider-google-beta",
"ref": "refs/heads/main",
"repo_type": "GITHUB",
},
git_file_source={
"path": "cloudbuild.yaml",
"uri": "https://hashicorp/terraform-provider-google-beta",
"revision": "refs/heads/main",
"repo_type": "GITHUB",
})
package main
import (
"fmt"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/cloudbuild"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/secretmanager"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
webhookTriggerSecretKey, err := secretmanager.NewSecret(ctx, "webhook_trigger_secret_key", &secretmanager.SecretArgs{
SecretId: pulumi.String("webhook-trigger-secret-key"),
Replication: &secretmanager.SecretReplicationArgs{
UserManaged: &secretmanager.SecretReplicationUserManagedArgs{
Replicas: secretmanager.SecretReplicationUserManagedReplicaArray{
&secretmanager.SecretReplicationUserManagedReplicaArgs{
Location: pulumi.String("us-central1"),
},
},
},
},
})
if err != nil {
return err
}
webhookTriggerSecretKeyData, err := secretmanager.NewSecretVersion(ctx, "webhook_trigger_secret_key_data", &secretmanager.SecretVersionArgs{
Secret: webhookTriggerSecretKey.ID(),
SecretData: pulumi.String("secretkeygoeshere"),
})
if err != nil {
return err
}
project, err := organizations.LookupProject(ctx, &organizations.LookupProjectArgs{}, nil)
if err != nil {
return err
}
secretAccessor, err := organizations.LookupIAMPolicy(ctx, &organizations.LookupIAMPolicyArgs{
Bindings: []organizations.GetIAMPolicyBinding{
{
Role: "roles/secretmanager.secretAccessor",
Members: []string{
fmt.Sprintf("serviceAccount:service-%v@gcp-sa-cloudbuild.iam.gserviceaccount.com", project.Number),
},
},
},
}, nil)
if err != nil {
return err
}
_, err = secretmanager.NewSecretIamPolicy(ctx, "policy", &secretmanager.SecretIamPolicyArgs{
Project: webhookTriggerSecretKey.Project,
SecretId: webhookTriggerSecretKey.SecretId,
PolicyData: pulumi.String(secretAccessor.PolicyData),
})
if err != nil {
return err
}
_, err = cloudbuild.NewTrigger(ctx, "webhook-config-trigger", &cloudbuild.TriggerArgs{
Name: pulumi.String("webhook-trigger"),
Description: pulumi.String("acceptance test example webhook build trigger"),
WebhookConfig: &cloudbuild.TriggerWebhookConfigArgs{
Secret: webhookTriggerSecretKeyData.ID(),
},
SourceToBuild: &cloudbuild.TriggerSourceToBuildArgs{
Uri: pulumi.String("https://hashicorp/terraform-provider-google-beta"),
Ref: pulumi.String("refs/heads/main"),
RepoType: pulumi.String("GITHUB"),
},
GitFileSource: &cloudbuild.TriggerGitFileSourceArgs{
Path: pulumi.String("cloudbuild.yaml"),
Uri: pulumi.String("https://hashicorp/terraform-provider-google-beta"),
Revision: pulumi.String("refs/heads/main"),
RepoType: pulumi.String("GITHUB"),
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
return await Deployment.RunAsync(() =>
{
var webhookTriggerSecretKey = new Gcp.SecretManager.Secret("webhook_trigger_secret_key", new()
{
SecretId = "webhook-trigger-secret-key",
Replication = new Gcp.SecretManager.Inputs.SecretReplicationArgs
{
UserManaged = new Gcp.SecretManager.Inputs.SecretReplicationUserManagedArgs
{
Replicas = new[]
{
new Gcp.SecretManager.Inputs.SecretReplicationUserManagedReplicaArgs
{
Location = "us-central1",
},
},
},
},
});
var webhookTriggerSecretKeyData = new Gcp.SecretManager.SecretVersion("webhook_trigger_secret_key_data", new()
{
Secret = webhookTriggerSecretKey.Id,
SecretData = "secretkeygoeshere",
});
var project = Gcp.Organizations.GetProject.Invoke();
var secretAccessor = Gcp.Organizations.GetIAMPolicy.Invoke(new()
{
Bindings = new[]
{
new Gcp.Organizations.Inputs.GetIAMPolicyBindingInputArgs
{
Role = "roles/secretmanager.secretAccessor",
Members = new[]
{
$"serviceAccount:service-{project.Apply(getProjectResult => getProjectResult.Number)}@gcp-sa-cloudbuild.iam.gserviceaccount.com",
},
},
},
});
var policy = new Gcp.SecretManager.SecretIamPolicy("policy", new()
{
Project = webhookTriggerSecretKey.Project,
SecretId = webhookTriggerSecretKey.SecretId,
PolicyData = secretAccessor.Apply(getIAMPolicyResult => getIAMPolicyResult.PolicyData),
});
var webhook_config_trigger = new Gcp.CloudBuild.Trigger("webhook-config-trigger", new()
{
Name = "webhook-trigger",
Description = "acceptance test example webhook build trigger",
WebhookConfig = new Gcp.CloudBuild.Inputs.TriggerWebhookConfigArgs
{
Secret = webhookTriggerSecretKeyData.Id,
},
SourceToBuild = new Gcp.CloudBuild.Inputs.TriggerSourceToBuildArgs
{
Uri = "https://hashicorp/terraform-provider-google-beta",
Ref = "refs/heads/main",
RepoType = "GITHUB",
},
GitFileSource = new Gcp.CloudBuild.Inputs.TriggerGitFileSourceArgs
{
Path = "cloudbuild.yaml",
Uri = "https://hashicorp/terraform-provider-google-beta",
Revision = "refs/heads/main",
RepoType = "GITHUB",
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.secretmanager.Secret;
import com.pulumi.gcp.secretmanager.SecretArgs;
import com.pulumi.gcp.secretmanager.inputs.SecretReplicationArgs;
import com.pulumi.gcp.secretmanager.inputs.SecretReplicationUserManagedArgs;
import com.pulumi.gcp.secretmanager.SecretVersion;
import com.pulumi.gcp.secretmanager.SecretVersionArgs;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetProjectArgs;
import com.pulumi.gcp.organizations.inputs.GetIAMPolicyArgs;
import com.pulumi.gcp.secretmanager.SecretIamPolicy;
import com.pulumi.gcp.secretmanager.SecretIamPolicyArgs;
import com.pulumi.gcp.cloudbuild.Trigger;
import com.pulumi.gcp.cloudbuild.TriggerArgs;
import com.pulumi.gcp.cloudbuild.inputs.TriggerWebhookConfigArgs;
import com.pulumi.gcp.cloudbuild.inputs.TriggerSourceToBuildArgs;
import com.pulumi.gcp.cloudbuild.inputs.TriggerGitFileSourceArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var webhookTriggerSecretKey = new Secret("webhookTriggerSecretKey", SecretArgs.builder()
.secretId("webhook-trigger-secret-key")
.replication(SecretReplicationArgs.builder()
.userManaged(SecretReplicationUserManagedArgs.builder()
.replicas(SecretReplicationUserManagedReplicaArgs.builder()
.location("us-central1")
.build())
.build())
.build())
.build());
var webhookTriggerSecretKeyData = new SecretVersion("webhookTriggerSecretKeyData", SecretVersionArgs.builder()
.secret(webhookTriggerSecretKey.id())
.secretData("secretkeygoeshere")
.build());
final var project = OrganizationsFunctions.getProject(GetProjectArgs.builder()
.build());
final var secretAccessor = OrganizationsFunctions.getIAMPolicy(GetIAMPolicyArgs.builder()
.bindings(GetIAMPolicyBindingArgs.builder()
.role("roles/secretmanager.secretAccessor")
.members(String.format("serviceAccount:service-%s@gcp-sa-cloudbuild.iam.gserviceaccount.com", project.number()))
.build())
.build());
var policy = new SecretIamPolicy("policy", SecretIamPolicyArgs.builder()
.project(webhookTriggerSecretKey.project())
.secretId(webhookTriggerSecretKey.secretId())
.policyData(secretAccessor.policyData())
.build());
var webhook_config_trigger = new Trigger("webhook-config-trigger", TriggerArgs.builder()
.name("webhook-trigger")
.description("acceptance test example webhook build trigger")
.webhookConfig(TriggerWebhookConfigArgs.builder()
.secret(webhookTriggerSecretKeyData.id())
.build())
.sourceToBuild(TriggerSourceToBuildArgs.builder()
.uri("https://hashicorp/terraform-provider-google-beta")
.ref("refs/heads/main")
.repoType("GITHUB")
.build())
.gitFileSource(TriggerGitFileSourceArgs.builder()
.path("cloudbuild.yaml")
.uri("https://hashicorp/terraform-provider-google-beta")
.revision("refs/heads/main")
.repoType("GITHUB")
.build())
.build());
}
}
resources:
webhookTriggerSecretKey:
type: gcp:secretmanager:Secret
name: webhook_trigger_secret_key
properties:
secretId: webhook-trigger-secret-key
replication:
userManaged:
replicas:
- location: us-central1
webhookTriggerSecretKeyData:
type: gcp:secretmanager:SecretVersion
name: webhook_trigger_secret_key_data
properties:
secret: ${webhookTriggerSecretKey.id}
secretData: secretkeygoeshere
policy:
type: gcp:secretmanager:SecretIamPolicy
properties:
project: ${webhookTriggerSecretKey.project}
secretId: ${webhookTriggerSecretKey.secretId}
policyData: ${secretAccessor.policyData}
webhook-config-trigger:
type: gcp:cloudbuild:Trigger
properties:
name: webhook-trigger
description: acceptance test example webhook build trigger
webhookConfig:
secret: ${webhookTriggerSecretKeyData.id}
sourceToBuild:
uri: https://hashicorp/terraform-provider-google-beta
ref: refs/heads/main
repoType: GITHUB
gitFileSource:
path: cloudbuild.yaml
uri: https://hashicorp/terraform-provider-google-beta
revision: refs/heads/main
repoType: GITHUB
variables:
project:
fn::invoke:
function: gcp:organizations:getProject
arguments: {}
secretAccessor:
fn::invoke:
function: gcp:organizations:getIAMPolicy
arguments:
bindings:
- role: roles/secretmanager.secretAccessor
members:
- serviceAccount:service-${project.number}@gcp-sa-cloudbuild.iam.gserviceaccount.com
The webhookConfig property references a Secret Manager secret version containing the shared secret. Cloud Build validates incoming webhook requests against this secret. The SecretIamPolicy grants the Cloud Build service account permission to read the secret. The sourceToBuild and gitFileSource properties specify which repository and build file to use when the webhook fires.
Beyond these examples
These snippets focus on specific trigger-level features: repository event sources, inline build definitions and repository-based build files, and service account configuration and secret management. They’re intentionally minimal rather than full CI/CD pipelines.
The examples may reference pre-existing infrastructure such as Cloud Source Repositories or external Git repositories, Pub/Sub topics for event-driven triggers, Secret Manager secrets and KMS keys, service accounts with appropriate IAM roles, and Cloud Build v2 connections for modern Git integrations. They focus on configuring the trigger rather than provisioning the surrounding infrastructure.
To keep things focused, common trigger patterns are omitted, including:
- Manual approval workflows (approvalConfig)
- File path filtering (includedFiles, ignoredFiles)
- Build log integration with GitHub (includeBuildLogs)
- Bitbucket Server and GitHub Enterprise configurations
- Developer Connect event configurations
- Build failure handling (allowFailure, allowExitCodes)
These omissions are intentional: the goal is to illustrate how each trigger feature is wired, not provide drop-in CI/CD modules. See the Cloud Build Trigger resource reference for all available configuration options.
Let's configure GCP Cloud Build Triggers
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Trigger Sources & Configuration
triggerTemplate, github, pubsubConfig, webhookConfig, sourceToBuild, repositoryEventConfig, bitbucketServerTriggerConfig, or developerConnectEventConfig. These are mutually exclusive and cannot be combined.filename only with triggerTemplate or github triggers. For Pub/Sub, Webhook, or Manual triggers, you must use gitFileSource.path instead to specify the build configuration file.triggerTemplate is the legacy configuration for Cloud Source Repositories, while repositoryEventConfig is the newer approach that works with Cloud Build v2 repositories (GitHub, GitLab, etc.) created via gcp.cloudbuildv2.Repository.location is immutable after creation, so choose carefully.Service Accounts & Permissions
roles/iam.serviceAccountUser and roles/logging.logWriter roles. Grant these permissions before creating the trigger using dependsOn to ensure proper ordering.gcp.projects.ServiceIdentity resource to retrieve the Cloud Build service account email used in jobs.serviceAccount is set, the standard Cloud Build service account ([PROJECT_NUM]@system.gserviceaccount.com) is used for all operations.Build Definition & Execution
filename to reference a cloudbuild.yaml file, or use build to define steps inline. You cannot use both simultaneously.allowFailure: true on the step to continue execution even if it fails, or use allowExitCodes to specify which exit codes (e.g., [1, 3]) should be treated as success.substitutions as key-value pairs. User-defined substitutions must start with an underscore (e.g., _FOO, _BAZ). Built-in substitutions like $PROJECT_ID, $REPO_NAME, and $COMMIT_SHA are also available.Advanced Features
roles/secretmanager.secretAccessor to the Cloud Build service account (service-[PROJECT_NUM]@gcp-sa-cloudbuild.iam.gserviceaccount.com), then reference the secret version in webhookConfig.secret.ignoredFiles and includedFiles with file glob patterns (supporting **). If a file change matches ignoredFiles, it won’t trigger a build. If includedFiles is set, at least one changed file must match to trigger a build.approvalConfig with approvalRequired: true. Any user with a Cloud Build Approver role for the project can then approve builds before they execute.Using a different cloud?
Explore integration guides for other cloud providers: