Configure GCP Developer Connect Connections

The gcp:developerconnect/connection:Connection resource, part of the Pulumi GCP provider, defines Developer Connect connections that link GCP projects to source code repositories on GitHub, GitLab, and Bitbucket. This guide focuses on three capabilities: GitHub and GitHub Enterprise integration, GitLab connectivity, and Bitbucket Cloud setup.

Connections depend on Secret Manager secrets for OAuth tokens and webhook secrets, plus IAM policies granting the Developer Connect service identity access to those secrets. The examples are intentionally small. Combine them with your own secret management and repository configuration.

Connect to GitHub with Firebase app integration

Teams starting with Developer Connect often link GitHub repositories through the Firebase GitHub app, which handles OAuth flows automatically.

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

// Setup permissions. Only needed once per project
const devconnect_p4sa = new gcp.projects.ServiceIdentity("devconnect-p4sa", {service: "developerconnect.googleapis.com"});
const devconnect_secret = new gcp.projects.IAMMember("devconnect-secret", {
    project: "my-project-name",
    role: "roles/secretmanager.admin",
    member: devconnect_p4sa.member,
});
const my_connection = new gcp.developerconnect.Connection("my-connection", {
    location: "us-central1",
    connectionId: "tf-test-connection-new",
    githubConfig: {
        githubApp: "FIREBASE",
    },
}, {
    dependsOn: [devconnect_secret],
});
export const nextSteps = my_connection.installationStates;
import pulumi
import pulumi_gcp as gcp

# Setup permissions. Only needed once per project
devconnect_p4sa = gcp.projects.ServiceIdentity("devconnect-p4sa", service="developerconnect.googleapis.com")
devconnect_secret = gcp.projects.IAMMember("devconnect-secret",
    project="my-project-name",
    role="roles/secretmanager.admin",
    member=devconnect_p4sa.member)
my_connection = gcp.developerconnect.Connection("my-connection",
    location="us-central1",
    connection_id="tf-test-connection-new",
    github_config={
        "github_app": "FIREBASE",
    },
    opts = pulumi.ResourceOptions(depends_on=[devconnect_secret]))
pulumi.export("nextSteps", my_connection.installation_states)
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/developerconnect"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/projects"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		// Setup permissions. Only needed once per project
		devconnect_p4sa, err := projects.NewServiceIdentity(ctx, "devconnect-p4sa", &projects.ServiceIdentityArgs{
			Service: pulumi.String("developerconnect.googleapis.com"),
		})
		if err != nil {
			return err
		}
		devconnect_secret, err := projects.NewIAMMember(ctx, "devconnect-secret", &projects.IAMMemberArgs{
			Project: pulumi.String("my-project-name"),
			Role:    pulumi.String("roles/secretmanager.admin"),
			Member:  devconnect_p4sa.Member,
		})
		if err != nil {
			return err
		}
		my_connection, err := developerconnect.NewConnection(ctx, "my-connection", &developerconnect.ConnectionArgs{
			Location:     pulumi.String("us-central1"),
			ConnectionId: pulumi.String("tf-test-connection-new"),
			GithubConfig: &developerconnect.ConnectionGithubConfigArgs{
				GithubApp: pulumi.String("FIREBASE"),
			},
		}, pulumi.DependsOn([]pulumi.Resource{
			devconnect_secret,
		}))
		if err != nil {
			return err
		}
		ctx.Export("nextSteps", my_connection.InstallationStates)
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    // Setup permissions. Only needed once per project
    var devconnect_p4sa = new Gcp.Projects.ServiceIdentity("devconnect-p4sa", new()
    {
        Service = "developerconnect.googleapis.com",
    });

    var devconnect_secret = new Gcp.Projects.IAMMember("devconnect-secret", new()
    {
        Project = "my-project-name",
        Role = "roles/secretmanager.admin",
        Member = devconnect_p4sa.Member,
    });

    var my_connection = new Gcp.DeveloperConnect.Connection("my-connection", new()
    {
        Location = "us-central1",
        ConnectionId = "tf-test-connection-new",
        GithubConfig = new Gcp.DeveloperConnect.Inputs.ConnectionGithubConfigArgs
        {
            GithubApp = "FIREBASE",
        },
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            devconnect_secret,
        },
    });

    return new Dictionary<string, object?>
    {
        ["nextSteps"] = my_connection.InstallationStates,
    };
});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.projects.ServiceIdentity;
import com.pulumi.gcp.projects.ServiceIdentityArgs;
import com.pulumi.gcp.projects.IAMMember;
import com.pulumi.gcp.projects.IAMMemberArgs;
import com.pulumi.gcp.developerconnect.Connection;
import com.pulumi.gcp.developerconnect.ConnectionArgs;
import com.pulumi.gcp.developerconnect.inputs.ConnectionGithubConfigArgs;
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) {
        // Setup permissions. Only needed once per project
        var devconnect_p4sa = new ServiceIdentity("devconnect-p4sa", ServiceIdentityArgs.builder()
            .service("developerconnect.googleapis.com")
            .build());

        var devconnect_secret = new IAMMember("devconnect-secret", IAMMemberArgs.builder()
            .project("my-project-name")
            .role("roles/secretmanager.admin")
            .member(devconnect_p4sa.member())
            .build());

        var my_connection = new Connection("my-connection", ConnectionArgs.builder()
            .location("us-central1")
            .connectionId("tf-test-connection-new")
            .githubConfig(ConnectionGithubConfigArgs.builder()
                .githubApp("FIREBASE")
                .build())
            .build(), CustomResourceOptions.builder()
                .dependsOn(devconnect_secret)
                .build());

        ctx.export("nextSteps", my_connection.installationStates());
    }
}
resources:
  my-connection:
    type: gcp:developerconnect:Connection
    properties:
      location: us-central1
      connectionId: tf-test-connection-new
      githubConfig:
        githubApp: FIREBASE
    options:
      dependsOn:
        - ${["devconnect-secret"]}
  # Setup permissions. Only needed once per project
  devconnect-p4sa:
    type: gcp:projects:ServiceIdentity
    properties:
      service: developerconnect.googleapis.com
  devconnect-secret:
    type: gcp:projects:IAMMember
    properties:
      project: my-project-name
      role: roles/secretmanager.admin
      member: ${["devconnect-p4sa"].member}
outputs:
  nextSteps: ${["my-connection"].installationStates}

The githubApp property set to “FIREBASE” uses Google’s managed GitHub app. The ServiceIdentity resource creates the Developer Connect service account, and the IAMMember grants it access to Secret Manager for storing OAuth tokens. The installationStates output provides the URL to complete GitHub app installation in your organization.

Connect using pre-existing OAuth tokens

When you’ve already obtained GitHub OAuth tokens through your own flow, you can reference them directly rather than going through the Firebase app setup.

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

const my_connection = new gcp.developerconnect.Connection("my-connection", {
    location: "us-central1",
    connectionId: "tf-test-connection-cred",
    githubConfig: {
        githubApp: "DEVELOPER_CONNECT",
        authorizerCredential: {
            oauthTokenSecretVersion: "projects/your-project/secrets/your-secret-id/versions/latest",
        },
    },
});
export const nextSteps = my_connection.installationStates;
import pulumi
import pulumi_gcp as gcp

my_connection = gcp.developerconnect.Connection("my-connection",
    location="us-central1",
    connection_id="tf-test-connection-cred",
    github_config={
        "github_app": "DEVELOPER_CONNECT",
        "authorizer_credential": {
            "oauth_token_secret_version": "projects/your-project/secrets/your-secret-id/versions/latest",
        },
    })
pulumi.export("nextSteps", my_connection.installation_states)
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/developerconnect"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		my_connection, err := developerconnect.NewConnection(ctx, "my-connection", &developerconnect.ConnectionArgs{
			Location:     pulumi.String("us-central1"),
			ConnectionId: pulumi.String("tf-test-connection-cred"),
			GithubConfig: &developerconnect.ConnectionGithubConfigArgs{
				GithubApp: pulumi.String("DEVELOPER_CONNECT"),
				AuthorizerCredential: &developerconnect.ConnectionGithubConfigAuthorizerCredentialArgs{
					OauthTokenSecretVersion: pulumi.String("projects/your-project/secrets/your-secret-id/versions/latest"),
				},
			},
		})
		if err != nil {
			return err
		}
		ctx.Export("nextSteps", my_connection.InstallationStates)
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var my_connection = new Gcp.DeveloperConnect.Connection("my-connection", new()
    {
        Location = "us-central1",
        ConnectionId = "tf-test-connection-cred",
        GithubConfig = new Gcp.DeveloperConnect.Inputs.ConnectionGithubConfigArgs
        {
            GithubApp = "DEVELOPER_CONNECT",
            AuthorizerCredential = new Gcp.DeveloperConnect.Inputs.ConnectionGithubConfigAuthorizerCredentialArgs
            {
                OauthTokenSecretVersion = "projects/your-project/secrets/your-secret-id/versions/latest",
            },
        },
    });

    return new Dictionary<string, object?>
    {
        ["nextSteps"] = my_connection.InstallationStates,
    };
});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.developerconnect.Connection;
import com.pulumi.gcp.developerconnect.ConnectionArgs;
import com.pulumi.gcp.developerconnect.inputs.ConnectionGithubConfigArgs;
import com.pulumi.gcp.developerconnect.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")
            .connectionId("tf-test-connection-cred")
            .githubConfig(ConnectionGithubConfigArgs.builder()
                .githubApp("DEVELOPER_CONNECT")
                .authorizerCredential(ConnectionGithubConfigAuthorizerCredentialArgs.builder()
                    .oauthTokenSecretVersion("projects/your-project/secrets/your-secret-id/versions/latest")
                    .build())
                .build())
            .build());

        ctx.export("nextSteps", my_connection.installationStates());
    }
}
resources:
  my-connection:
    type: gcp:developerconnect:Connection
    properties:
      location: us-central1
      connectionId: tf-test-connection-cred
      githubConfig:
        githubApp: DEVELOPER_CONNECT
        authorizerCredential:
          oauthTokenSecretVersion: projects/your-project/secrets/your-secret-id/versions/latest
outputs:
  nextSteps: ${["my-connection"].installationStates}

The authorizerCredential block points to an existing Secret Manager secret containing your OAuth token. Setting githubApp to “DEVELOPER_CONNECT” indicates you’re managing the GitHub app yourself. The installationStates output still provides next steps for completing the connection.

Connect to a specific GitHub app installation

Organizations with existing GitHub app installations can connect directly by providing the installation ID and OAuth token.

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 devconnect_p4sa = new gcp.projects.ServiceIdentity("devconnect-p4sa", {service: "developerconnect.googleapis.com"});
const p4sa_secretAccessor = gcp.organizations.getIAMPolicyOutput({
    bindings: [{
        role: "roles/secretmanager.secretAccessor",
        members: [devconnect_p4sa.member],
    }],
});
const policy = new gcp.secretmanager.SecretIamPolicy("policy", {
    secretId: github_token_secret.secretId,
    policyData: p4sa_secretAccessor.apply(p4sa_secretAccessor => p4sa_secretAccessor.policyData),
});
const my_connection = new gcp.developerconnect.Connection("my-connection", {
    location: "us-central1",
    connectionId: "my-connection",
    githubConfig: {
        githubApp: "DEVELOPER_CONNECT",
        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)
devconnect_p4sa = gcp.projects.ServiceIdentity("devconnect-p4sa", service="developerconnect.googleapis.com")
p4sa_secret_accessor = gcp.organizations.get_iam_policy_output(bindings=[{
    "role": "roles/secretmanager.secretAccessor",
    "members": [devconnect_p4sa.member],
}])
policy = gcp.secretmanager.SecretIamPolicy("policy",
    secret_id=github_token_secret.secret_id,
    policy_data=p4sa_secret_accessor.policy_data)
my_connection = gcp.developerconnect.Connection("my-connection",
    location="us-central1",
    connection_id="my-connection",
    github_config={
        "github_app": "DEVELOPER_CONNECT",
        "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/developerconnect"
	"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/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
		}
		devconnect_p4sa, err := projects.NewServiceIdentity(ctx, "devconnect-p4sa", &projects.ServiceIdentityArgs{
			Service: pulumi.String("developerconnect.googleapis.com"),
		})
		if err != nil {
			return err
		}
		p4sa_secretAccessor := organizations.LookupIAMPolicyOutput(ctx, organizations.GetIAMPolicyOutputArgs{
			Bindings: organizations.GetIAMPolicyBindingArray{
				&organizations.GetIAMPolicyBindingArgs{
					Role: pulumi.String("roles/secretmanager.secretAccessor"),
					Members: pulumi.StringArray{
						devconnect_p4sa.Member,
					},
				},
			},
		}, nil)
		_, err = secretmanager.NewSecretIamPolicy(ctx, "policy", &secretmanager.SecretIamPolicyArgs{
			SecretId: github_token_secret.SecretId,
			PolicyData: pulumi.String(p4sa_secretAccessor.ApplyT(func(p4sa_secretAccessor organizations.GetIAMPolicyResult) (*string, error) {
				return &p4sa_secretAccessor.PolicyData, nil
			}).(pulumi.StringPtrOutput)),
		})
		if err != nil {
			return err
		}
		_, err = developerconnect.NewConnection(ctx, "my-connection", &developerconnect.ConnectionArgs{
			Location:     pulumi.String("us-central1"),
			ConnectionId: pulumi.String("my-connection"),
			GithubConfig: &developerconnect.ConnectionGithubConfigArgs{
				GithubApp:         pulumi.String("DEVELOPER_CONNECT"),
				AppInstallationId: pulumi.String("123123"),
				AuthorizerCredential: &developerconnect.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 devconnect_p4sa = new Gcp.Projects.ServiceIdentity("devconnect-p4sa", new()
    {
        Service = "developerconnect.googleapis.com",
    });

    var p4sa_secretAccessor = Gcp.Organizations.GetIAMPolicy.Invoke(new()
    {
        Bindings = new[]
        {
            new Gcp.Organizations.Inputs.GetIAMPolicyBindingInputArgs
            {
                Role = "roles/secretmanager.secretAccessor",
                Members = new[]
                {
                    devconnect_p4sa.Member,
                },
            },
        },
    });

    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.DeveloperConnect.Connection("my-connection", new()
    {
        Location = "us-central1",
        ConnectionId = "my-connection",
        GithubConfig = new Gcp.DeveloperConnect.Inputs.ConnectionGithubConfigArgs
        {
            GithubApp = "DEVELOPER_CONNECT",
            AppInstallationId = "123123",
            AuthorizerCredential = new Gcp.DeveloperConnect.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.projects.ServiceIdentity;
import com.pulumi.gcp.projects.ServiceIdentityArgs;
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.developerconnect.Connection;
import com.pulumi.gcp.developerconnect.ConnectionArgs;
import com.pulumi.gcp.developerconnect.inputs.ConnectionGithubConfigArgs;
import com.pulumi.gcp.developerconnect.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());

        var devconnect_p4sa = new ServiceIdentity("devconnect-p4sa", ServiceIdentityArgs.builder()
            .service("developerconnect.googleapis.com")
            .build());

        final var p4sa-secretAccessor = OrganizationsFunctions.getIAMPolicy(GetIAMPolicyArgs.builder()
            .bindings(GetIAMPolicyBindingArgs.builder()
                .role("roles/secretmanager.secretAccessor")
                .members(devconnect_p4sa.member())
                .build())
            .build());

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

        var my_connection = new Connection("my-connection", ConnectionArgs.builder()
            .location("us-central1")
            .connectionId("my-connection")
            .githubConfig(ConnectionGithubConfigArgs.builder()
                .githubApp("DEVELOPER_CONNECT")
                .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
  devconnect-p4sa:
    type: gcp:projects:ServiceIdentity
    properties:
      service: developerconnect.googleapis.com
  policy:
    type: gcp:secretmanager:SecretIamPolicy
    properties:
      secretId: ${["github-token-secret"].secretId}
      policyData: ${["p4sa-secretAccessor"].policyData}
  my-connection:
    type: gcp:developerconnect:Connection
    properties:
      location: us-central1
      connectionId: my-connection
      githubConfig:
        githubApp: DEVELOPER_CONNECT
        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:
              - ${["devconnect-p4sa"].member}

The appInstallationId property links to a specific GitHub app installation in your organization. The SecretIamPolicy grants the Developer Connect service identity permission to read the OAuth token from Secret Manager. You must obtain the installation ID from your GitHub app settings.

Connect to GitHub Enterprise Server

Teams running GitHub Enterprise Server on-premises need to provide the host URI and GitHub app credentials for their self-hosted instance.

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

const my_connection = new gcp.developerconnect.Connection("my-connection", {
    location: "us-central1",
    connectionId: "tf-test-connection",
    githubEnterpriseConfig: {
        hostUri: "https://ghe.proctor-staging-test.com",
        appId: "864434",
        privateKeySecretVersion: "projects/devconnect-terraform-creds/secrets/tf-test-ghe-do-not-change-ghe-private-key-f522d2/versions/latest",
        webhookSecretSecretVersion: "projects/devconnect-terraform-creds/secrets/tf-test-ghe-do-not-change-ghe-webhook-secret-3c806f/versions/latest",
        appInstallationId: "837537",
    },
});
import pulumi
import pulumi_gcp as gcp

my_connection = gcp.developerconnect.Connection("my-connection",
    location="us-central1",
    connection_id="tf-test-connection",
    github_enterprise_config={
        "host_uri": "https://ghe.proctor-staging-test.com",
        "app_id": "864434",
        "private_key_secret_version": "projects/devconnect-terraform-creds/secrets/tf-test-ghe-do-not-change-ghe-private-key-f522d2/versions/latest",
        "webhook_secret_secret_version": "projects/devconnect-terraform-creds/secrets/tf-test-ghe-do-not-change-ghe-webhook-secret-3c806f/versions/latest",
        "app_installation_id": "837537",
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/developerconnect"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := developerconnect.NewConnection(ctx, "my-connection", &developerconnect.ConnectionArgs{
			Location:     pulumi.String("us-central1"),
			ConnectionId: pulumi.String("tf-test-connection"),
			GithubEnterpriseConfig: &developerconnect.ConnectionGithubEnterpriseConfigArgs{
				HostUri:                    pulumi.String("https://ghe.proctor-staging-test.com"),
				AppId:                      pulumi.String("864434"),
				PrivateKeySecretVersion:    pulumi.String("projects/devconnect-terraform-creds/secrets/tf-test-ghe-do-not-change-ghe-private-key-f522d2/versions/latest"),
				WebhookSecretSecretVersion: pulumi.String("projects/devconnect-terraform-creds/secrets/tf-test-ghe-do-not-change-ghe-webhook-secret-3c806f/versions/latest"),
				AppInstallationId:          pulumi.String("837537"),
			},
		})
		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.DeveloperConnect.Connection("my-connection", new()
    {
        Location = "us-central1",
        ConnectionId = "tf-test-connection",
        GithubEnterpriseConfig = new Gcp.DeveloperConnect.Inputs.ConnectionGithubEnterpriseConfigArgs
        {
            HostUri = "https://ghe.proctor-staging-test.com",
            AppId = "864434",
            PrivateKeySecretVersion = "projects/devconnect-terraform-creds/secrets/tf-test-ghe-do-not-change-ghe-private-key-f522d2/versions/latest",
            WebhookSecretSecretVersion = "projects/devconnect-terraform-creds/secrets/tf-test-ghe-do-not-change-ghe-webhook-secret-3c806f/versions/latest",
            AppInstallationId = "837537",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.developerconnect.Connection;
import com.pulumi.gcp.developerconnect.ConnectionArgs;
import com.pulumi.gcp.developerconnect.inputs.ConnectionGithubEnterpriseConfigArgs;
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")
            .connectionId("tf-test-connection")
            .githubEnterpriseConfig(ConnectionGithubEnterpriseConfigArgs.builder()
                .hostUri("https://ghe.proctor-staging-test.com")
                .appId("864434")
                .privateKeySecretVersion("projects/devconnect-terraform-creds/secrets/tf-test-ghe-do-not-change-ghe-private-key-f522d2/versions/latest")
                .webhookSecretSecretVersion("projects/devconnect-terraform-creds/secrets/tf-test-ghe-do-not-change-ghe-webhook-secret-3c806f/versions/latest")
                .appInstallationId("837537")
                .build())
            .build());

    }
}
resources:
  my-connection:
    type: gcp:developerconnect:Connection
    properties:
      location: us-central1
      connectionId: tf-test-connection
      githubEnterpriseConfig:
        hostUri: https://ghe.proctor-staging-test.com
        appId: 864434
        privateKeySecretVersion: projects/devconnect-terraform-creds/secrets/tf-test-ghe-do-not-change-ghe-private-key-f522d2/versions/latest
        webhookSecretSecretVersion: projects/devconnect-terraform-creds/secrets/tf-test-ghe-do-not-change-ghe-webhook-secret-3c806f/versions/latest
        appInstallationId: 837537

The githubEnterpriseConfig block replaces githubConfig for self-hosted instances. The hostUri points to your GitHub Enterprise Server, and appId identifies your GitHub app. Both privateKeySecretVersion and webhookSecretSecretVersion reference secrets you create when setting up the GitHub app in your GHE instance.

Connect to GitLab.com

GitLab connections require both read and write tokens, plus a webhook secret for receiving repository events.

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

const my_connection = new gcp.developerconnect.Connection("my-connection", {
    location: "us-central1",
    connectionId: "tf-test-connection",
    gitlabConfig: {
        webhookSecretSecretVersion: "projects/devconnect-terraform-creds/secrets/gitlab-webhook/versions/latest",
        readAuthorizerCredential: {
            userTokenSecretVersion: "projects/devconnect-terraform-creds/secrets/gitlab-read-cred/versions/latest",
        },
        authorizerCredential: {
            userTokenSecretVersion: "projects/devconnect-terraform-creds/secrets/gitlab-auth-cred/versions/latest",
        },
    },
});
import pulumi
import pulumi_gcp as gcp

my_connection = gcp.developerconnect.Connection("my-connection",
    location="us-central1",
    connection_id="tf-test-connection",
    gitlab_config={
        "webhook_secret_secret_version": "projects/devconnect-terraform-creds/secrets/gitlab-webhook/versions/latest",
        "read_authorizer_credential": {
            "user_token_secret_version": "projects/devconnect-terraform-creds/secrets/gitlab-read-cred/versions/latest",
        },
        "authorizer_credential": {
            "user_token_secret_version": "projects/devconnect-terraform-creds/secrets/gitlab-auth-cred/versions/latest",
        },
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/developerconnect"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := developerconnect.NewConnection(ctx, "my-connection", &developerconnect.ConnectionArgs{
			Location:     pulumi.String("us-central1"),
			ConnectionId: pulumi.String("tf-test-connection"),
			GitlabConfig: &developerconnect.ConnectionGitlabConfigArgs{
				WebhookSecretSecretVersion: pulumi.String("projects/devconnect-terraform-creds/secrets/gitlab-webhook/versions/latest"),
				ReadAuthorizerCredential: &developerconnect.ConnectionGitlabConfigReadAuthorizerCredentialArgs{
					UserTokenSecretVersion: pulumi.String("projects/devconnect-terraform-creds/secrets/gitlab-read-cred/versions/latest"),
				},
				AuthorizerCredential: &developerconnect.ConnectionGitlabConfigAuthorizerCredentialArgs{
					UserTokenSecretVersion: pulumi.String("projects/devconnect-terraform-creds/secrets/gitlab-auth-cred/versions/latest"),
				},
			},
		})
		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.DeveloperConnect.Connection("my-connection", new()
    {
        Location = "us-central1",
        ConnectionId = "tf-test-connection",
        GitlabConfig = new Gcp.DeveloperConnect.Inputs.ConnectionGitlabConfigArgs
        {
            WebhookSecretSecretVersion = "projects/devconnect-terraform-creds/secrets/gitlab-webhook/versions/latest",
            ReadAuthorizerCredential = new Gcp.DeveloperConnect.Inputs.ConnectionGitlabConfigReadAuthorizerCredentialArgs
            {
                UserTokenSecretVersion = "projects/devconnect-terraform-creds/secrets/gitlab-read-cred/versions/latest",
            },
            AuthorizerCredential = new Gcp.DeveloperConnect.Inputs.ConnectionGitlabConfigAuthorizerCredentialArgs
            {
                UserTokenSecretVersion = "projects/devconnect-terraform-creds/secrets/gitlab-auth-cred/versions/latest",
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.developerconnect.Connection;
import com.pulumi.gcp.developerconnect.ConnectionArgs;
import com.pulumi.gcp.developerconnect.inputs.ConnectionGitlabConfigArgs;
import com.pulumi.gcp.developerconnect.inputs.ConnectionGitlabConfigReadAuthorizerCredentialArgs;
import com.pulumi.gcp.developerconnect.inputs.ConnectionGitlabConfigAuthorizerCredentialArgs;
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")
            .connectionId("tf-test-connection")
            .gitlabConfig(ConnectionGitlabConfigArgs.builder()
                .webhookSecretSecretVersion("projects/devconnect-terraform-creds/secrets/gitlab-webhook/versions/latest")
                .readAuthorizerCredential(ConnectionGitlabConfigReadAuthorizerCredentialArgs.builder()
                    .userTokenSecretVersion("projects/devconnect-terraform-creds/secrets/gitlab-read-cred/versions/latest")
                    .build())
                .authorizerCredential(ConnectionGitlabConfigAuthorizerCredentialArgs.builder()
                    .userTokenSecretVersion("projects/devconnect-terraform-creds/secrets/gitlab-auth-cred/versions/latest")
                    .build())
                .build())
            .build());

    }
}
resources:
  my-connection:
    type: gcp:developerconnect:Connection
    properties:
      location: us-central1
      connectionId: tf-test-connection
      gitlabConfig:
        webhookSecretSecretVersion: projects/devconnect-terraform-creds/secrets/gitlab-webhook/versions/latest
        readAuthorizerCredential:
          userTokenSecretVersion: projects/devconnect-terraform-creds/secrets/gitlab-read-cred/versions/latest
        authorizerCredential:
          userTokenSecretVersion: projects/devconnect-terraform-creds/secrets/gitlab-auth-cred/versions/latest

The gitlabConfig block requires three secrets: webhookSecretSecretVersion for webhook verification, readAuthorizerCredential for read-only operations, and authorizerCredential for write operations. Each credential references a userTokenSecretVersion pointing to GitLab personal access tokens stored in Secret Manager.

Connect to Bitbucket Cloud

Bitbucket Cloud connections require workspace identification and separate tokens for read and write operations.

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

const my_connection = new gcp.developerconnect.Connection("my-connection", {
    location: "us-central1",
    connectionId: "tf-test-connection",
    bitbucketCloudConfig: {
        workspace: "proctor-test",
        webhookSecretSecretVersion: "projects/devconnect-terraform-creds/secrets/bbc-webhook/versions/latest",
        readAuthorizerCredential: {
            userTokenSecretVersion: "projects/devconnect-terraform-creds/secrets/bbc-read-token/versions/latest",
        },
        authorizerCredential: {
            userTokenSecretVersion: "projects/devconnect-terraform-creds/secrets/bbc-auth-token/versions/latest",
        },
    },
});
import pulumi
import pulumi_gcp as gcp

my_connection = gcp.developerconnect.Connection("my-connection",
    location="us-central1",
    connection_id="tf-test-connection",
    bitbucket_cloud_config={
        "workspace": "proctor-test",
        "webhook_secret_secret_version": "projects/devconnect-terraform-creds/secrets/bbc-webhook/versions/latest",
        "read_authorizer_credential": {
            "user_token_secret_version": "projects/devconnect-terraform-creds/secrets/bbc-read-token/versions/latest",
        },
        "authorizer_credential": {
            "user_token_secret_version": "projects/devconnect-terraform-creds/secrets/bbc-auth-token/versions/latest",
        },
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/developerconnect"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := developerconnect.NewConnection(ctx, "my-connection", &developerconnect.ConnectionArgs{
			Location:     pulumi.String("us-central1"),
			ConnectionId: pulumi.String("tf-test-connection"),
			BitbucketCloudConfig: &developerconnect.ConnectionBitbucketCloudConfigArgs{
				Workspace:                  pulumi.String("proctor-test"),
				WebhookSecretSecretVersion: pulumi.String("projects/devconnect-terraform-creds/secrets/bbc-webhook/versions/latest"),
				ReadAuthorizerCredential: &developerconnect.ConnectionBitbucketCloudConfigReadAuthorizerCredentialArgs{
					UserTokenSecretVersion: pulumi.String("projects/devconnect-terraform-creds/secrets/bbc-read-token/versions/latest"),
				},
				AuthorizerCredential: &developerconnect.ConnectionBitbucketCloudConfigAuthorizerCredentialArgs{
					UserTokenSecretVersion: pulumi.String("projects/devconnect-terraform-creds/secrets/bbc-auth-token/versions/latest"),
				},
			},
		})
		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.DeveloperConnect.Connection("my-connection", new()
    {
        Location = "us-central1",
        ConnectionId = "tf-test-connection",
        BitbucketCloudConfig = new Gcp.DeveloperConnect.Inputs.ConnectionBitbucketCloudConfigArgs
        {
            Workspace = "proctor-test",
            WebhookSecretSecretVersion = "projects/devconnect-terraform-creds/secrets/bbc-webhook/versions/latest",
            ReadAuthorizerCredential = new Gcp.DeveloperConnect.Inputs.ConnectionBitbucketCloudConfigReadAuthorizerCredentialArgs
            {
                UserTokenSecretVersion = "projects/devconnect-terraform-creds/secrets/bbc-read-token/versions/latest",
            },
            AuthorizerCredential = new Gcp.DeveloperConnect.Inputs.ConnectionBitbucketCloudConfigAuthorizerCredentialArgs
            {
                UserTokenSecretVersion = "projects/devconnect-terraform-creds/secrets/bbc-auth-token/versions/latest",
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.developerconnect.Connection;
import com.pulumi.gcp.developerconnect.ConnectionArgs;
import com.pulumi.gcp.developerconnect.inputs.ConnectionBitbucketCloudConfigArgs;
import com.pulumi.gcp.developerconnect.inputs.ConnectionBitbucketCloudConfigReadAuthorizerCredentialArgs;
import com.pulumi.gcp.developerconnect.inputs.ConnectionBitbucketCloudConfigAuthorizerCredentialArgs;
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")
            .connectionId("tf-test-connection")
            .bitbucketCloudConfig(ConnectionBitbucketCloudConfigArgs.builder()
                .workspace("proctor-test")
                .webhookSecretSecretVersion("projects/devconnect-terraform-creds/secrets/bbc-webhook/versions/latest")
                .readAuthorizerCredential(ConnectionBitbucketCloudConfigReadAuthorizerCredentialArgs.builder()
                    .userTokenSecretVersion("projects/devconnect-terraform-creds/secrets/bbc-read-token/versions/latest")
                    .build())
                .authorizerCredential(ConnectionBitbucketCloudConfigAuthorizerCredentialArgs.builder()
                    .userTokenSecretVersion("projects/devconnect-terraform-creds/secrets/bbc-auth-token/versions/latest")
                    .build())
                .build())
            .build());

    }
}
resources:
  my-connection:
    type: gcp:developerconnect:Connection
    properties:
      location: us-central1
      connectionId: tf-test-connection
      bitbucketCloudConfig:
        workspace: proctor-test
        webhookSecretSecretVersion: projects/devconnect-terraform-creds/secrets/bbc-webhook/versions/latest
        readAuthorizerCredential:
          userTokenSecretVersion: projects/devconnect-terraform-creds/secrets/bbc-read-token/versions/latest
        authorizerCredential:
          userTokenSecretVersion: projects/devconnect-terraform-creds/secrets/bbc-auth-token/versions/latest

The bitbucketCloudConfig block identifies your Bitbucket workspace and provides separate credentials for read and write access. Like GitLab, it requires a webhook secret and two user tokens. Bitbucket app passwords serve as the tokens referenced in readAuthorizerCredential and authorizerCredential.

Beyond these examples

These snippets focus on specific connection-level features: GitHub and GitHub Enterprise integration, GitLab and Bitbucket connectivity, and OAuth token and webhook management. They’re intentionally minimal rather than full CI/CD integrations.

The examples rely on pre-existing infrastructure such as Secret Manager secrets for tokens and credentials, IAM service identities and policies, and GitHub/GitLab/Bitbucket apps and installations. They focus on configuring the connection rather than provisioning the surrounding secret management infrastructure.

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

  • Customer-managed encryption keys (cryptoKeyConfig)
  • Connection disabling and lifecycle management (disabled property)
  • Annotations and labels for metadata
  • GitLab Enterprise and Bitbucket Data Center configurations

These omissions are intentional: the goal is to illustrate how each connection type is wired, not provide drop-in repository integration modules. See the Developer Connect Connection resource reference for all available configuration options.

Let's configure GCP Developer Connect Connections

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Setup & Prerequisites
What IAM permissions do I need before creating a connection?
The Developer Connect service identity needs roles/secretmanager.admin or roles/secretmanager.secretAccessor to access secret credentials. Create the service identity using gcp.projects.ServiceIdentity and grant permissions with gcp.projects.IAMMember or gcp.secretmanager.SecretIamPolicy, then use dependsOn to ensure proper ordering.
Why do I need to use dependsOn when setting up IAM permissions?
The connection creation depends on the service identity having secret access permissions. Using dependsOn ensures IAM permissions are granted before the connection resource is created.
Configuration & Platform Support
What source control platforms can I connect to?
You can connect to GitHub, GitHub Enterprise, GitLab, GitLab Enterprise, Bitbucket Cloud, and Bitbucket Data Center. Each platform has its own configuration property (githubConfig, githubEnterpriseConfig, gitlabConfig, etc.).
What's the difference between FIREBASE and DEVELOPER_CONNECT GitHub apps?
FIREBASE is used for new connections without existing credentials, while DEVELOPER_CONNECT is used when you have existing OAuth tokens or app installations.
How do I use existing GitHub credentials instead of creating new ones?
Set githubConfig.githubApp to DEVELOPER_CONNECT and provide authorizerCredential.oauthTokenSecretVersion pointing to your existing OAuth token in Secret Manager.
Can I disable a connection without deleting it?
Yes, set disabled to true. This disables repository API methods and webhook processing for all repositories in the connection.
Resource Management
Which connection properties can't be changed after creation?
The connectionId, location, and project fields are immutable and cannot be changed after the connection is created.
What's the difference between annotations and labels?
Both annotations and labels are non-authoritative fields that only manage values present in your configuration. Use effectiveAnnotations or effectiveLabels to see all values on the resource, including those set by other clients.
What is the etag field used for?
The etag is a checksum computed by the server that can be sent on update and delete requests to ensure you have an up-to-date value before proceeding (optimistic concurrency control).
Installation & Next Steps
How do I complete the GitHub installation after creating a connection?
Check the installationStates output, which describes the installation stage and necessary actions you need to complete for GitHub and GitHub Enterprise connections.

Using a different cloud?

Explore integration guides for other cloud providers: