The gcp:cloudbuild/trigger:Trigger resource, part of the Pulumi GCP provider, defines automated build triggers that respond to repository changes, Pub/Sub messages, webhooks, or manual invocation. This guide focuses on four capabilities: repository-based triggers with branch and tag monitoring, event-driven triggers via Pub/Sub and webhooks, inline build definitions vs external config files, and service account customization and secret management.
Triggers reference repositories (Cloud Source Repositories, GitHub, GitLab, Bitbucket), Pub/Sub topics, Secret Manager secrets, and IAM service accounts. The examples are intentionally small. Combine them with your own repository connections, event sources, and build configurations.
Trigger builds from repository branches with external config
Most CI/CD pipelines monitor a repository branch and execute builds when code changes, referencing a cloudbuild.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. When code is pushed to the specified branch, Cloud Build reads the cloudbuild.yaml file from the repository root and executes the defined steps. The substitutions property passes custom variables into the build, accessible as $_FOO and $_BAZ in build steps.
Define build steps inline with artifacts and secrets
Instead of maintaining a separate cloudbuild.yaml file, you can define build logic directly in the trigger configuration.
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, including container images, arguments, and timeouts. The source property specifies where to fetch code (here, from Cloud Storage). The secrets and availableSecrets properties integrate with KMS and Secret Manager for encrypted credentials. The artifacts property defines what to publish after the build completes: container images, Cloud Storage objects, or language-specific packages.
Run builds with a custom service account
Custom service accounts let you apply least-privilege permissions and separate build identities across teams.
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 permissions: serviceAccountUser allows Cloud Build to impersonate the account, and logWriter enables writing to Cloud Logging. The dependsOn ensures IAM bindings exist before creating the trigger.
Trigger builds from Pub/Sub messages
Event-driven architectures can trigger builds in response to messages from other systems rather than repository changes.
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 a message arrives, Cloud Build evaluates the filter expression against the message payload. If the filter matches, Cloud Build fetches the build configuration from the specified gitFileSource and executes it. The sourceToBuild property tells Cloud Build which repository and ref to check out. The substitutions property extracts data from the Pub/Sub message using $(body.message.data.action) syntax.
Trigger builds from webhook requests
External systems can trigger builds by sending HTTP requests to a webhook URL, authenticated using Secret Manager.
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 requires a Secret Manager secret containing the webhook authentication key. The SecretIamPolicy grants Cloud Build’s service account permission to read the secret. When an HTTP request arrives at the webhook URL with the correct secret, Cloud Build fetches the build configuration from gitFileSource and executes it. This pattern integrates with external CI/CD systems or custom automation that can’t use Pub/Sub.
Connect to repositories via Cloud Build v2
Cloud Build v2 connections provide a modern way to integrate with GitHub, GitLab, and other Git providers with fine-grained repository access.
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 Connection resource establishes authentication with GitHub using an App installation. The Repository resource links a specific GitHub repository to the connection. The repositoryEventConfig property monitors the repository for push events matching the branch pattern. This approach replaces the older triggerTemplate pattern and supports more Git providers.
Monitor GitHub Enterprise repositories
Organizations using GitHub Enterprise Server can trigger builds from self-hosted repositories.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const ghe_trigger = new gcp.cloudbuild.Trigger("ghe-trigger", {
name: "ghe-trigger",
location: "us-central1",
github: {
owner: "hashicorp",
name: "terraform-provider-google",
push: {
branch: "^main$",
},
enterpriseConfigResourceName: "projects/123456789/locations/us-central1/githubEnterpriseConfigs/configID",
},
filename: "cloudbuild.yaml",
});
import pulumi
import pulumi_gcp as gcp
ghe_trigger = gcp.cloudbuild.Trigger("ghe-trigger",
name="ghe-trigger",
location="us-central1",
github={
"owner": "hashicorp",
"name": "terraform-provider-google",
"push": {
"branch": "^main$",
},
"enterprise_config_resource_name": "projects/123456789/locations/us-central1/githubEnterpriseConfigs/configID",
},
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, "ghe-trigger", &cloudbuild.TriggerArgs{
Name: pulumi.String("ghe-trigger"),
Location: pulumi.String("us-central1"),
Github: &cloudbuild.TriggerGithubArgs{
Owner: pulumi.String("hashicorp"),
Name: pulumi.String("terraform-provider-google"),
Push: &cloudbuild.TriggerGithubPushArgs{
Branch: pulumi.String("^main$"),
},
EnterpriseConfigResourceName: pulumi.String("projects/123456789/locations/us-central1/githubEnterpriseConfigs/configID"),
},
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 ghe_trigger = new Gcp.CloudBuild.Trigger("ghe-trigger", new()
{
Name = "ghe-trigger",
Location = "us-central1",
Github = new Gcp.CloudBuild.Inputs.TriggerGithubArgs
{
Owner = "hashicorp",
Name = "terraform-provider-google",
Push = new Gcp.CloudBuild.Inputs.TriggerGithubPushArgs
{
Branch = "^main$",
},
EnterpriseConfigResourceName = "projects/123456789/locations/us-central1/githubEnterpriseConfigs/configID",
},
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.TriggerGithubArgs;
import com.pulumi.gcp.cloudbuild.inputs.TriggerGithubPushArgs;
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 ghe_trigger = new Trigger("ghe-trigger", TriggerArgs.builder()
.name("ghe-trigger")
.location("us-central1")
.github(TriggerGithubArgs.builder()
.owner("hashicorp")
.name("terraform-provider-google")
.push(TriggerGithubPushArgs.builder()
.branch("^main$")
.build())
.enterpriseConfigResourceName("projects/123456789/locations/us-central1/githubEnterpriseConfigs/configID")
.build())
.filename("cloudbuild.yaml")
.build());
}
}
resources:
ghe-trigger:
type: gcp:cloudbuild:Trigger
properties:
name: ghe-trigger
location: us-central1
github:
owner: hashicorp
name: terraform-provider-google
push:
branch: ^main$
enterpriseConfigResourceName: projects/123456789/locations/us-central1/githubEnterpriseConfigs/configID
filename: cloudbuild.yaml
The github property with enterpriseConfigResourceName connects to a GitHub Enterprise Server instance. The push property defines which branches to monitor using regular expressions. This configuration requires a separate GitHub Enterprise config resource that defines the server URL and authentication.
Beyond these examples
These snippets focus on specific trigger-level features: repository monitoring, event-driven triggers via Pub/Sub and webhooks, inline build definitions and external config files, and service account customization 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 connected Git providers, Pub/Sub topics for event-driven triggers, Secret Manager secrets for webhooks and build secrets, IAM service accounts and KMS keys, and GitHub Enterprise or Bitbucket Server configurations. They focus on configuring the trigger rather than provisioning everything around it.
To keep things focused, common trigger patterns are omitted, including:
- File path filtering (ignoredFiles, includedFiles)
- Manual approval workflows (approvalConfig)
- Build failure handling (allowFailure, allowExitCodes)
- Developer Connect integration for newer Git providers
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 Configuration & Sources
triggerTemplate (legacy Cloud Source Repositories), github, pubsubConfig, webhookConfig, repositoryEventConfig (Cloud Build v2), bitbucketServerTriggerConfig, or developerConnectEventConfig. These configurations are mutually exclusive; choose one based on your source control system.filename only with triggerTemplate or github triggers. For Pub/Sub, Webhook, or Manual triggers, use gitFileSource instead to specify the build configuration file location.location property, but note that location is immutable after creation.project and location properties are immutable and cannot be changed after trigger creation.Build Definition & Execution
filename to reference a cloudbuild.yaml file, or use build to define the build configuration inline. Either a filename or build template must be provided, but not both.allowFailure: true on a step to let it fail without failing the build, or use allowExitCodes to specify an array of exit codes (e.g., [1, 3]) that won’t cause build failure.approvalConfig with approvalRequired: true. Any user with a Cloud Build Approver role for the project can then approve builds created by this trigger.File Filtering & Source Control
** support. If ignoredFiles is set, changed files must pass that filter first. If includedFiles is also set, at least one file must match an includedFiles glob. If no files pass both filters, the build won’t trigger.Authentication & Permissions
serviceAccount is specified, Cloud Build uses the default service account: [PROJECT_NUM]@system.gserviceaccount.com. You can retrieve this email using the gcp.projects.ServiceIdentity resource.roles/iam.serviceAccountUser and roles/logging.logWriter to the service account. Use dependsOn in your trigger resource to ensure IAM bindings exist before creating the trigger.Advanced Features
substitutions using $(body.message.data.fieldName) syntax (e.g., _ACTION: "$(body.message.data.action)"), then use these substitutions in the filter property for conditional triggering.roles/secretmanager.secretAccessor role to the Cloud Build service account (service-[PROJECT_NUM]@gcp-sa-cloudbuild.iam.gserviceaccount.com), then reference the secret version ID in webhookConfig.secret.Using a different cloud?
Explore integration guides for other cloud providers: