Configure GCP Cloud Build Connections

The gcp:cloudbuildv2/connection:Connection resource, part of the Pulumi GCP provider, establishes authenticated connections between Cloud Build and source control platforms: GitHub, GitHub Enterprise, Bitbucket, or GitLab. This guide focuses on three capabilities: GitHub.com authentication via GitHub Apps, GitHub Enterprise Server with private keys and webhooks, and Secret Manager integration for credential storage.

Connections depend on GitHub App installations, Secret Manager secrets containing credentials, and IAM policies granting the Cloud Build service account access to those secrets. The examples are intentionally small. Combine them with your own repository configurations and build triggers.

Connect to GitHub with an app installation

Teams using GitHub.com for source control establish connections that Cloud Build uses to access repositories. The GitHub App installation model provides scoped access without requiring personal access tokens.

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: "tf-test-connection",
    githubConfig: {
        appInstallationId: 0,
        authorizerCredential: {
            oauthTokenSecretVersion: "projects/gcb-terraform-creds/secrets/github-pat/versions/1",
        },
    },
});
import pulumi
import pulumi_gcp as gcp

my_connection = gcp.cloudbuildv2.Connection("my-connection",
    location="us-central1",
    name="tf-test-connection",
    github_config={
        "app_installation_id": 0,
        "authorizer_credential": {
            "oauth_token_secret_version": "projects/gcb-terraform-creds/secrets/github-pat/versions/1",
        },
    })
package main

import (
	"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 {
		_, err := cloudbuildv2.NewConnection(ctx, "my-connection", &cloudbuildv2.ConnectionArgs{
			Location: pulumi.String("us-central1"),
			Name:     pulumi.String("tf-test-connection"),
			GithubConfig: &cloudbuildv2.ConnectionGithubConfigArgs{
				AppInstallationId: pulumi.Int(0),
				AuthorizerCredential: &cloudbuildv2.ConnectionGithubConfigAuthorizerCredentialArgs{
					OauthTokenSecretVersion: pulumi.String("projects/gcb-terraform-creds/secrets/github-pat/versions/1"),
				},
			},
		})
		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 = "tf-test-connection",
        GithubConfig = new Gcp.CloudBuildV2.Inputs.ConnectionGithubConfigArgs
        {
            AppInstallationId = 0,
            AuthorizerCredential = new Gcp.CloudBuildV2.Inputs.ConnectionGithubConfigAuthorizerCredentialArgs
            {
                OauthTokenSecretVersion = "projects/gcb-terraform-creds/secrets/github-pat/versions/1",
            },
        },
    });

});
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 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("tf-test-connection")
            .githubConfig(ConnectionGithubConfigArgs.builder()
                .appInstallationId(0)
                .authorizerCredential(ConnectionGithubConfigAuthorizerCredentialArgs.builder()
                    .oauthTokenSecretVersion("projects/gcb-terraform-creds/secrets/github-pat/versions/1")
                    .build())
                .build())
            .build());

    }
}
resources:
  my-connection:
    type: gcp:cloudbuildv2:Connection
    properties:
      location: us-central1
      name: tf-test-connection
      githubConfig:
        appInstallationId: 0
        authorizerCredential:
          oauthTokenSecretVersion: projects/gcb-terraform-creds/secrets/github-pat/versions/1

The githubConfig property configures GitHub.com authentication. The appInstallationId identifies your GitHub App installation, and authorizerCredential.oauthTokenSecretVersion points to the OAuth token stored in Secret Manager. Cloud Build uses this token to authenticate API requests to GitHub.

Connect to GitHub Enterprise with webhook secrets

Organizations running GitHub Enterprise Server need connections that authenticate to self-hosted instances using private keys and webhook secrets for event delivery.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
import * as std from "@pulumi/std";

const private_key_secret = new gcp.secretmanager.Secret("private-key-secret", {
    secretId: "ghe-pk-secret",
    replication: {
        auto: {},
    },
});
const private_key_secret_version = new gcp.secretmanager.SecretVersion("private-key-secret-version", {
    secret: private_key_secret.id,
    secretData: std.file({
        input: "private-key.pem",
    }).then(invoke => invoke.result),
});
const webhook_secret_secret = new gcp.secretmanager.Secret("webhook-secret-secret", {
    secretId: "github-token-secret",
    replication: {
        auto: {},
    },
});
const webhook_secret_secret_version = new gcp.secretmanager.SecretVersion("webhook-secret-secret-version", {
    secret: webhook_secret_secret.id,
    secretData: "<webhook-secret-data>",
});
const p4sa_secretAccessor = gcp.organizations.getIAMPolicy({
    bindings: [{
        role: "roles/secretmanager.secretAccessor",
        members: ["serviceAccount:service-123456789@gcp-sa-cloudbuild.iam.gserviceaccount.com"],
    }],
});
const policy_pk = new gcp.secretmanager.SecretIamPolicy("policy-pk", {
    secretId: private_key_secret.secretId,
    policyData: p4sa_secretAccessor.then(p4sa_secretAccessor => p4sa_secretAccessor.policyData),
});
const policy_whs = new gcp.secretmanager.SecretIamPolicy("policy-whs", {
    secretId: webhook_secret_secret.secretId,
    policyData: p4sa_secretAccessor.then(p4sa_secretAccessor => p4sa_secretAccessor.policyData),
});
const my_connection = new gcp.cloudbuildv2.Connection("my-connection", {
    location: "us-central1",
    name: "my-terraform-ghe-connection",
    githubEnterpriseConfig: {
        hostUri: "https://ghe.com",
        privateKeySecretVersion: private_key_secret_version.id,
        webhookSecretSecretVersion: webhook_secret_secret_version.id,
        appId: 200,
        appSlug: "gcb-app",
        appInstallationId: 300,
    },
}, {
    dependsOn: [
        policy_pk,
        policy_whs,
    ],
});
import pulumi
import pulumi_gcp as gcp
import pulumi_std as std

private_key_secret = gcp.secretmanager.Secret("private-key-secret",
    secret_id="ghe-pk-secret",
    replication={
        "auto": {},
    })
private_key_secret_version = gcp.secretmanager.SecretVersion("private-key-secret-version",
    secret=private_key_secret.id,
    secret_data=std.file(input="private-key.pem").result)
webhook_secret_secret = gcp.secretmanager.Secret("webhook-secret-secret",
    secret_id="github-token-secret",
    replication={
        "auto": {},
    })
webhook_secret_secret_version = gcp.secretmanager.SecretVersion("webhook-secret-secret-version",
    secret=webhook_secret_secret.id,
    secret_data="<webhook-secret-data>")
p4sa_secret_accessor = gcp.organizations.get_iam_policy(bindings=[{
    "role": "roles/secretmanager.secretAccessor",
    "members": ["serviceAccount:service-123456789@gcp-sa-cloudbuild.iam.gserviceaccount.com"],
}])
policy_pk = gcp.secretmanager.SecretIamPolicy("policy-pk",
    secret_id=private_key_secret.secret_id,
    policy_data=p4sa_secret_accessor.policy_data)
policy_whs = gcp.secretmanager.SecretIamPolicy("policy-whs",
    secret_id=webhook_secret_secret.secret_id,
    policy_data=p4sa_secret_accessor.policy_data)
my_connection = gcp.cloudbuildv2.Connection("my-connection",
    location="us-central1",
    name="my-terraform-ghe-connection",
    github_enterprise_config={
        "host_uri": "https://ghe.com",
        "private_key_secret_version": private_key_secret_version.id,
        "webhook_secret_secret_version": webhook_secret_secret_version.id,
        "app_id": 200,
        "app_slug": "gcb-app",
        "app_installation_id": 300,
    },
    opts = pulumi.ResourceOptions(depends_on=[
            policy_pk,
            policy_whs,
        ]))
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/cloudbuildv2"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/secretmanager"
	"github.com/pulumi/pulumi-std/sdk/go/std"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		private_key_secret, err := secretmanager.NewSecret(ctx, "private-key-secret", &secretmanager.SecretArgs{
			SecretId: pulumi.String("ghe-pk-secret"),
			Replication: &secretmanager.SecretReplicationArgs{
				Auto: &secretmanager.SecretReplicationAutoArgs{},
			},
		})
		if err != nil {
			return err
		}
		invokeFile, err := std.File(ctx, &std.FileArgs{
			Input: "private-key.pem",
		}, nil)
		if err != nil {
			return err
		}
		private_key_secret_version, err := secretmanager.NewSecretVersion(ctx, "private-key-secret-version", &secretmanager.SecretVersionArgs{
			Secret:     private_key_secret.ID(),
			SecretData: pulumi.String(invokeFile.Result),
		})
		if err != nil {
			return err
		}
		webhook_secret_secret, err := secretmanager.NewSecret(ctx, "webhook-secret-secret", &secretmanager.SecretArgs{
			SecretId: pulumi.String("github-token-secret"),
			Replication: &secretmanager.SecretReplicationArgs{
				Auto: &secretmanager.SecretReplicationAutoArgs{},
			},
		})
		if err != nil {
			return err
		}
		webhook_secret_secret_version, err := secretmanager.NewSecretVersion(ctx, "webhook-secret-secret-version", &secretmanager.SecretVersionArgs{
			Secret:     webhook_secret_secret.ID(),
			SecretData: pulumi.String("<webhook-secret-data>"),
		})
		if err != nil {
			return err
		}
		p4sa_secretAccessor, err := organizations.LookupIAMPolicy(ctx, &organizations.LookupIAMPolicyArgs{
			Bindings: []organizations.GetIAMPolicyBinding{
				{
					Role: "roles/secretmanager.secretAccessor",
					Members: []string{
						"serviceAccount:service-123456789@gcp-sa-cloudbuild.iam.gserviceaccount.com",
					},
				},
			},
		}, nil)
		if err != nil {
			return err
		}
		policy_pk, err := secretmanager.NewSecretIamPolicy(ctx, "policy-pk", &secretmanager.SecretIamPolicyArgs{
			SecretId:   private_key_secret.SecretId,
			PolicyData: pulumi.String(p4sa_secretAccessor.PolicyData),
		})
		if err != nil {
			return err
		}
		policy_whs, err := secretmanager.NewSecretIamPolicy(ctx, "policy-whs", &secretmanager.SecretIamPolicyArgs{
			SecretId:   webhook_secret_secret.SecretId,
			PolicyData: pulumi.String(p4sa_secretAccessor.PolicyData),
		})
		if err != nil {
			return err
		}
		_, err = cloudbuildv2.NewConnection(ctx, "my-connection", &cloudbuildv2.ConnectionArgs{
			Location: pulumi.String("us-central1"),
			Name:     pulumi.String("my-terraform-ghe-connection"),
			GithubEnterpriseConfig: &cloudbuildv2.ConnectionGithubEnterpriseConfigArgs{
				HostUri:                    pulumi.String("https://ghe.com"),
				PrivateKeySecretVersion:    private_key_secret_version.ID(),
				WebhookSecretSecretVersion: webhook_secret_secret_version.ID(),
				AppId:                      pulumi.Int(200),
				AppSlug:                    pulumi.String("gcb-app"),
				AppInstallationId:          pulumi.Int(300),
			},
		}, pulumi.DependsOn([]pulumi.Resource{
			policy_pk,
			policy_whs,
		}))
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
using Std = Pulumi.Std;

return await Deployment.RunAsync(() => 
{
    var private_key_secret = new Gcp.SecretManager.Secret("private-key-secret", new()
    {
        SecretId = "ghe-pk-secret",
        Replication = new Gcp.SecretManager.Inputs.SecretReplicationArgs
        {
            Auto = null,
        },
    });

    var private_key_secret_version = new Gcp.SecretManager.SecretVersion("private-key-secret-version", new()
    {
        Secret = private_key_secret.Id,
        SecretData = Std.File.Invoke(new()
        {
            Input = "private-key.pem",
        }).Apply(invoke => invoke.Result),
    });

    var webhook_secret_secret = new Gcp.SecretManager.Secret("webhook-secret-secret", new()
    {
        SecretId = "github-token-secret",
        Replication = new Gcp.SecretManager.Inputs.SecretReplicationArgs
        {
            Auto = null,
        },
    });

    var webhook_secret_secret_version = new Gcp.SecretManager.SecretVersion("webhook-secret-secret-version", new()
    {
        Secret = webhook_secret_secret.Id,
        SecretData = "<webhook-secret-data>",
    });

    var p4sa_secretAccessor = Gcp.Organizations.GetIAMPolicy.Invoke(new()
    {
        Bindings = new[]
        {
            new Gcp.Organizations.Inputs.GetIAMPolicyBindingInputArgs
            {
                Role = "roles/secretmanager.secretAccessor",
                Members = new[]
                {
                    "serviceAccount:service-123456789@gcp-sa-cloudbuild.iam.gserviceaccount.com",
                },
            },
        },
    });

    var policy_pk = new Gcp.SecretManager.SecretIamPolicy("policy-pk", new()
    {
        SecretId = private_key_secret.SecretId,
        PolicyData = p4sa_secretAccessor.Apply(p4sa_secretAccessor => p4sa_secretAccessor.Apply(getIAMPolicyResult => getIAMPolicyResult.PolicyData)),
    });

    var policy_whs = new Gcp.SecretManager.SecretIamPolicy("policy-whs", new()
    {
        SecretId = webhook_secret_secret.SecretId,
        PolicyData = p4sa_secretAccessor.Apply(p4sa_secretAccessor => p4sa_secretAccessor.Apply(getIAMPolicyResult => getIAMPolicyResult.PolicyData)),
    });

    var my_connection = new Gcp.CloudBuildV2.Connection("my-connection", new()
    {
        Location = "us-central1",
        Name = "my-terraform-ghe-connection",
        GithubEnterpriseConfig = new Gcp.CloudBuildV2.Inputs.ConnectionGithubEnterpriseConfigArgs
        {
            HostUri = "https://ghe.com",
            PrivateKeySecretVersion = private_key_secret_version.Id,
            WebhookSecretSecretVersion = webhook_secret_secret_version.Id,
            AppId = 200,
            AppSlug = "gcb-app",
            AppInstallationId = 300,
        },
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            policy_pk,
            policy_whs,
        },
    });

});
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.SecretReplicationAutoArgs;
import com.pulumi.gcp.secretmanager.SecretVersion;
import com.pulumi.gcp.secretmanager.SecretVersionArgs;
import com.pulumi.std.StdFunctions;
import com.pulumi.std.inputs.FileArgs;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetIAMPolicyArgs;
import com.pulumi.gcp.secretmanager.SecretIamPolicy;
import com.pulumi.gcp.secretmanager.SecretIamPolicyArgs;
import com.pulumi.gcp.cloudbuildv2.Connection;
import com.pulumi.gcp.cloudbuildv2.ConnectionArgs;
import com.pulumi.gcp.cloudbuildv2.inputs.ConnectionGithubEnterpriseConfigArgs;
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) {
        var private_key_secret = new Secret("private-key-secret", SecretArgs.builder()
            .secretId("ghe-pk-secret")
            .replication(SecretReplicationArgs.builder()
                .auto(SecretReplicationAutoArgs.builder()
                    .build())
                .build())
            .build());

        var private_key_secret_version = new SecretVersion("private-key-secret-version", SecretVersionArgs.builder()
            .secret(private_key_secret.id())
            .secretData(StdFunctions.file(FileArgs.builder()
                .input("private-key.pem")
                .build()).result())
            .build());

        var webhook_secret_secret = new Secret("webhook-secret-secret", SecretArgs.builder()
            .secretId("github-token-secret")
            .replication(SecretReplicationArgs.builder()
                .auto(SecretReplicationAutoArgs.builder()
                    .build())
                .build())
            .build());

        var webhook_secret_secret_version = new SecretVersion("webhook-secret-secret-version", SecretVersionArgs.builder()
            .secret(webhook_secret_secret.id())
            .secretData("<webhook-secret-data>")
            .build());

        final var p4sa-secretAccessor = OrganizationsFunctions.getIAMPolicy(GetIAMPolicyArgs.builder()
            .bindings(GetIAMPolicyBindingArgs.builder()
                .role("roles/secretmanager.secretAccessor")
                .members("serviceAccount:service-123456789@gcp-sa-cloudbuild.iam.gserviceaccount.com")
                .build())
            .build());

        var policy_pk = new SecretIamPolicy("policy-pk", SecretIamPolicyArgs.builder()
            .secretId(private_key_secret.secretId())
            .policyData(p4sa_secretAccessor.policyData())
            .build());

        var policy_whs = new SecretIamPolicy("policy-whs", SecretIamPolicyArgs.builder()
            .secretId(webhook_secret_secret.secretId())
            .policyData(p4sa_secretAccessor.policyData())
            .build());

        var my_connection = new Connection("my-connection", ConnectionArgs.builder()
            .location("us-central1")
            .name("my-terraform-ghe-connection")
            .githubEnterpriseConfig(ConnectionGithubEnterpriseConfigArgs.builder()
                .hostUri("https://ghe.com")
                .privateKeySecretVersion(private_key_secret_version.id())
                .webhookSecretSecretVersion(webhook_secret_secret_version.id())
                .appId(200)
                .appSlug("gcb-app")
                .appInstallationId(300)
                .build())
            .build(), CustomResourceOptions.builder()
                .dependsOn(                
                    policy_pk,
                    policy_whs)
                .build());

    }
}
resources:
  private-key-secret:
    type: gcp:secretmanager:Secret
    properties:
      secretId: ghe-pk-secret
      replication:
        auto: {}
  private-key-secret-version:
    type: gcp:secretmanager:SecretVersion
    properties:
      secret: ${["private-key-secret"].id}
      secretData:
        fn::invoke:
          function: std:file
          arguments:
            input: private-key.pem
          return: result
  webhook-secret-secret:
    type: gcp:secretmanager:Secret
    properties:
      secretId: github-token-secret
      replication:
        auto: {}
  webhook-secret-secret-version:
    type: gcp:secretmanager:SecretVersion
    properties:
      secret: ${["webhook-secret-secret"].id}
      secretData: <webhook-secret-data>
  policy-pk:
    type: gcp:secretmanager:SecretIamPolicy
    properties:
      secretId: ${["private-key-secret"].secretId}
      policyData: ${["p4sa-secretAccessor"].policyData}
  policy-whs:
    type: gcp:secretmanager:SecretIamPolicy
    properties:
      secretId: ${["webhook-secret-secret"].secretId}
      policyData: ${["p4sa-secretAccessor"].policyData}
  my-connection:
    type: gcp:cloudbuildv2:Connection
    properties:
      location: us-central1
      name: my-terraform-ghe-connection
      githubEnterpriseConfig:
        hostUri: https://ghe.com
        privateKeySecretVersion: ${["private-key-secret-version"].id}
        webhookSecretSecretVersion: ${["webhook-secret-secret-version"].id}
        appId: 200
        appSlug: gcb-app
        appInstallationId: 300
    options:
      dependsOn:
        - ${["policy-pk"]}
        - ${["policy-whs"]}
variables:
  p4sa-secretAccessor:
    fn::invoke:
      function: gcp:organizations:getIAMPolicy
      arguments:
        bindings:
          - role: roles/secretmanager.secretAccessor
            members:
              - serviceAccount:service-123456789@gcp-sa-cloudbuild.iam.gserviceaccount.com

The githubEnterpriseConfig property configures self-hosted GitHub Enterprise. The hostUri specifies your GitHub Enterprise URL, while privateKeySecretVersion and webhookSecretSecretVersion reference secrets stored in Secret Manager. The example creates secrets, loads credentials from local files, and configures IAM policies to grant the Cloud Build service account access. The appId, appSlug, and appInstallationId identify your GitHub Enterprise App configuration.

Connect to GitHub with a personal access token

Some workflows use personal access tokens stored in Secret Manager rather than GitHub App installations, providing an alternative authentication method for GitHub.com.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
import * as std from "@pulumi/std";

const github_token_secret = new gcp.secretmanager.Secret("github-token-secret", {
    secretId: "github-token-secret",
    replication: {
        auto: {},
    },
});
const github_token_secret_version = new gcp.secretmanager.SecretVersion("github-token-secret-version", {
    secret: github_token_secret.id,
    secretData: std.file({
        input: "my-github-token.txt",
    }).then(invoke => invoke.result),
});
const p4sa_secretAccessor = gcp.organizations.getIAMPolicy({
    bindings: [{
        role: "roles/secretmanager.secretAccessor",
        members: ["serviceAccount:service-123456789@gcp-sa-cloudbuild.iam.gserviceaccount.com"],
    }],
});
const policy = new gcp.secretmanager.SecretIamPolicy("policy", {
    secretId: github_token_secret.secretId,
    policyData: p4sa_secretAccessor.then(p4sa_secretAccessor => p4sa_secretAccessor.policyData),
});
const my_connection = new gcp.cloudbuildv2.Connection("my-connection", {
    location: "us-central1",
    name: "my-connection",
    githubConfig: {
        appInstallationId: 123123,
        authorizerCredential: {
            oauthTokenSecretVersion: github_token_secret_version.id,
        },
    },
});
import pulumi
import pulumi_gcp as gcp
import pulumi_std as std

github_token_secret = gcp.secretmanager.Secret("github-token-secret",
    secret_id="github-token-secret",
    replication={
        "auto": {},
    })
github_token_secret_version = gcp.secretmanager.SecretVersion("github-token-secret-version",
    secret=github_token_secret.id,
    secret_data=std.file(input="my-github-token.txt").result)
p4sa_secret_accessor = gcp.organizations.get_iam_policy(bindings=[{
    "role": "roles/secretmanager.secretAccessor",
    "members": ["serviceAccount:service-123456789@gcp-sa-cloudbuild.iam.gserviceaccount.com"],
}])
policy = gcp.secretmanager.SecretIamPolicy("policy",
    secret_id=github_token_secret.secret_id,
    policy_data=p4sa_secret_accessor.policy_data)
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": github_token_secret_version.id,
        },
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/cloudbuildv2"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/secretmanager"
	"github.com/pulumi/pulumi-std/sdk/go/std"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		github_token_secret, err := secretmanager.NewSecret(ctx, "github-token-secret", &secretmanager.SecretArgs{
			SecretId: pulumi.String("github-token-secret"),
			Replication: &secretmanager.SecretReplicationArgs{
				Auto: &secretmanager.SecretReplicationAutoArgs{},
			},
		})
		if err != nil {
			return err
		}
		invokeFile, err := std.File(ctx, &std.FileArgs{
			Input: "my-github-token.txt",
		}, nil)
		if err != nil {
			return err
		}
		github_token_secret_version, err := secretmanager.NewSecretVersion(ctx, "github-token-secret-version", &secretmanager.SecretVersionArgs{
			Secret:     github_token_secret.ID(),
			SecretData: pulumi.String(invokeFile.Result),
		})
		if err != nil {
			return err
		}
		p4sa_secretAccessor, err := organizations.LookupIAMPolicy(ctx, &organizations.LookupIAMPolicyArgs{
			Bindings: []organizations.GetIAMPolicyBinding{
				{
					Role: "roles/secretmanager.secretAccessor",
					Members: []string{
						"serviceAccount:service-123456789@gcp-sa-cloudbuild.iam.gserviceaccount.com",
					},
				},
			},
		}, nil)
		if err != nil {
			return err
		}
		_, err = secretmanager.NewSecretIamPolicy(ctx, "policy", &secretmanager.SecretIamPolicyArgs{
			SecretId:   github_token_secret.SecretId,
			PolicyData: pulumi.String(p4sa_secretAccessor.PolicyData),
		})
		if err != nil {
			return err
		}
		_, 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: github_token_secret_version.ID(),
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
using Std = Pulumi.Std;

return await Deployment.RunAsync(() => 
{
    var github_token_secret = new Gcp.SecretManager.Secret("github-token-secret", new()
    {
        SecretId = "github-token-secret",
        Replication = new Gcp.SecretManager.Inputs.SecretReplicationArgs
        {
            Auto = null,
        },
    });

    var github_token_secret_version = new Gcp.SecretManager.SecretVersion("github-token-secret-version", new()
    {
        Secret = github_token_secret.Id,
        SecretData = Std.File.Invoke(new()
        {
            Input = "my-github-token.txt",
        }).Apply(invoke => invoke.Result),
    });

    var p4sa_secretAccessor = Gcp.Organizations.GetIAMPolicy.Invoke(new()
    {
        Bindings = new[]
        {
            new Gcp.Organizations.Inputs.GetIAMPolicyBindingInputArgs
            {
                Role = "roles/secretmanager.secretAccessor",
                Members = new[]
                {
                    "serviceAccount:service-123456789@gcp-sa-cloudbuild.iam.gserviceaccount.com",
                },
            },
        },
    });

    var policy = new Gcp.SecretManager.SecretIamPolicy("policy", new()
    {
        SecretId = github_token_secret.SecretId,
        PolicyData = p4sa_secretAccessor.Apply(p4sa_secretAccessor => p4sa_secretAccessor.Apply(getIAMPolicyResult => getIAMPolicyResult.PolicyData)),
    });

    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 = github_token_secret_version.Id,
            },
        },
    });

});
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.SecretReplicationAutoArgs;
import com.pulumi.gcp.secretmanager.SecretVersion;
import com.pulumi.gcp.secretmanager.SecretVersionArgs;
import com.pulumi.std.StdFunctions;
import com.pulumi.std.inputs.FileArgs;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetIAMPolicyArgs;
import com.pulumi.gcp.secretmanager.SecretIamPolicy;
import com.pulumi.gcp.secretmanager.SecretIamPolicyArgs;
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 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 github_token_secret = new Secret("github-token-secret", SecretArgs.builder()
            .secretId("github-token-secret")
            .replication(SecretReplicationArgs.builder()
                .auto(SecretReplicationAutoArgs.builder()
                    .build())
                .build())
            .build());

        var github_token_secret_version = new SecretVersion("github-token-secret-version", SecretVersionArgs.builder()
            .secret(github_token_secret.id())
            .secretData(StdFunctions.file(FileArgs.builder()
                .input("my-github-token.txt")
                .build()).result())
            .build());

        final var p4sa-secretAccessor = OrganizationsFunctions.getIAMPolicy(GetIAMPolicyArgs.builder()
            .bindings(GetIAMPolicyBindingArgs.builder()
                .role("roles/secretmanager.secretAccessor")
                .members("serviceAccount:service-123456789@gcp-sa-cloudbuild.iam.gserviceaccount.com")
                .build())
            .build());

        var policy = new SecretIamPolicy("policy", SecretIamPolicyArgs.builder()
            .secretId(github_token_secret.secretId())
            .policyData(p4sa_secretAccessor.policyData())
            .build());

        var my_connection = new Connection("my-connection", ConnectionArgs.builder()
            .location("us-central1")
            .name("my-connection")
            .githubConfig(ConnectionGithubConfigArgs.builder()
                .appInstallationId(123123)
                .authorizerCredential(ConnectionGithubConfigAuthorizerCredentialArgs.builder()
                    .oauthTokenSecretVersion(github_token_secret_version.id())
                    .build())
                .build())
            .build());

    }
}
resources:
  github-token-secret:
    type: gcp:secretmanager:Secret
    properties:
      secretId: github-token-secret
      replication:
        auto: {}
  github-token-secret-version:
    type: gcp:secretmanager:SecretVersion
    properties:
      secret: ${["github-token-secret"].id}
      secretData:
        fn::invoke:
          function: std:file
          arguments:
            input: my-github-token.txt
          return: result
  policy:
    type: gcp:secretmanager:SecretIamPolicy
    properties:
      secretId: ${["github-token-secret"].secretId}
      policyData: ${["p4sa-secretAccessor"].policyData}
  my-connection:
    type: gcp:cloudbuildv2:Connection
    properties:
      location: us-central1
      name: my-connection
      githubConfig:
        appInstallationId: 123123
        authorizerCredential:
          oauthTokenSecretVersion: ${["github-token-secret-version"].id}
variables:
  p4sa-secretAccessor:
    fn::invoke:
      function: gcp:organizations:getIAMPolicy
      arguments:
        bindings:
          - role: roles/secretmanager.secretAccessor
            members:
              - serviceAccount:service-123456789@gcp-sa-cloudbuild.iam.gserviceaccount.com

This configuration also uses githubConfig but loads the OAuth token from a local file (my-github-token.txt) before storing it in Secret Manager. The IAM policy grants the Cloud Build service account access to read the secret. This approach works when you prefer file-based credential management over direct secret creation.

Beyond these examples

These snippets focus on specific connection features: GitHub and GitHub Enterprise authentication, Secret Manager integration for credentials, and IAM policy configuration for service accounts. They’re intentionally minimal rather than full CI/CD pipelines.

The examples rely on pre-existing infrastructure such as GitHub App installations with installation IDs, Secret Manager secrets containing OAuth tokens, private keys, or webhook secrets, and the Cloud Build service account (service-PROJECT_NUMBER@gcp-sa-cloudbuild.iam.gserviceaccount.com). They focus on configuring the connection rather than provisioning the surrounding infrastructure.

To keep things focused, common connection patterns are omitted, including:

  • Bitbucket Cloud and Data Center connections (bitbucketCloudConfig, bitbucketDataCenterConfig)
  • GitLab connections (gitlabConfig)
  • Connection disabling (disabled property)
  • Custom annotations for metadata (annotations)

These omissions are intentional: the goal is to illustrate how each connection type is wired, not provide drop-in CI/CD modules. See the Cloud Build Connection resource reference for all available configuration options.

Let's configure GCP Cloud Build Connections

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Configuration & Setup
What properties can't I change after creating a connection?
The location, name, and project properties are immutable and cannot be modified after creation. Plan these values carefully, as changes require recreating the resource.
What source control systems can I connect to?
You can connect to GitHub, GitHub Enterprise, Bitbucket Cloud, Bitbucket Data Center, and GitLab using the corresponding config properties: githubConfig, githubEnterpriseConfig, bitbucketCloudConfig, bitbucketDataCenterConfig, or gitlabConfig.
What happens when I set disabled to true?
Setting disabled to true disables all functionality for the connection, including repository-based API methods and webhook processing for repositories in this connection.
Secrets & Authentication
How do I store credentials for my connection?
Use Google Secret Manager to store credentials like OAuth tokens, private keys, or webhook secrets. Create a gcp.secretmanager.Secret and gcp.secretmanager.SecretVersion, then reference the version ID in your connection configuration.
What IAM permissions does Cloud Build need to access my secrets?
Grant the Cloud Build service account (service-{project-number}@gcp-sa-cloudbuild.iam.gserviceaccount.com) the roles/secretmanager.secretAccessor role on your secrets using gcp.secretmanager.SecretIamPolicy.
Why does the GitHub Enterprise example use dependsOn?
The connection requires IAM policies to be created first so Cloud Build can access the secrets. Using dependsOn ensures the secret access policies are set up before creating the connection.
Advanced Configuration
Why should I use effectiveAnnotations instead of annotations?
The annotations field is non-authoritative and only manages annotations present in your configuration. Use effectiveAnnotations to see all annotations actually present on the resource.
Can I import an existing connection?
Yes, you can import using formats like projects/{{project}}/locations/{{location}}/connections/{{name}}, {{project}}/{{location}}/{{name}}, {{location}}/{{name}}, or just {{name}}.

Using a different cloud?

Explore integration guides for other cloud providers: