Create GCP Certificate Manager Certificates

The gcp:certificatemanager/certificate:Certificate resource, part of the Pulumi GCP provider, defines TLS certificates for Google Cloud load balancers and services: their provisioning method, domain coverage, and geographic scope. This guide focuses on four capabilities: Google-managed certificates with DNS authorization, self-managed certificate uploads, private CA integration, and regional and client authentication scopes.

Certificates require DNS control for domain validation, may reference Certificate Authority Service pools for private CA issuance, or require PEM-encoded certificate files for self-managed uploads. The examples are intentionally small. Combine them with your own load balancers, DNS configuration, and certificate management workflows.

Provision Google-managed certificates with DNS authorization

Most teams start with Google-managed certificates that handle provisioning and renewal automatically. DNS authorization proves domain ownership by creating validation records that Google checks before issuing certificates.

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

const instance = new gcp.certificatemanager.DnsAuthorization("instance", {
    name: "dns-auth",
    description: "The default dnss",
    domain: "subdomain.hashicorptest.com",
});
const instance2 = new gcp.certificatemanager.DnsAuthorization("instance2", {
    name: "dns-auth2",
    description: "The default dnss",
    domain: "subdomain2.hashicorptest.com",
});
const _default = new gcp.certificatemanager.Certificate("default", {
    name: "dns-cert",
    description: "The default cert",
    scope: "EDGE_CACHE",
    labels: {
        env: "test",
    },
    managed: {
        domains: [
            instance.domain,
            instance2.domain,
        ],
        dnsAuthorizations: [
            instance.id,
            instance2.id,
        ],
    },
});
import pulumi
import pulumi_gcp as gcp

instance = gcp.certificatemanager.DnsAuthorization("instance",
    name="dns-auth",
    description="The default dnss",
    domain="subdomain.hashicorptest.com")
instance2 = gcp.certificatemanager.DnsAuthorization("instance2",
    name="dns-auth2",
    description="The default dnss",
    domain="subdomain2.hashicorptest.com")
default = gcp.certificatemanager.Certificate("default",
    name="dns-cert",
    description="The default cert",
    scope="EDGE_CACHE",
    labels={
        "env": "test",
    },
    managed={
        "domains": [
            instance.domain,
            instance2.domain,
        ],
        "dns_authorizations": [
            instance.id,
            instance2.id,
        ],
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		instance, err := certificatemanager.NewDnsAuthorization(ctx, "instance", &certificatemanager.DnsAuthorizationArgs{
			Name:        pulumi.String("dns-auth"),
			Description: pulumi.String("The default dnss"),
			Domain:      pulumi.String("subdomain.hashicorptest.com"),
		})
		if err != nil {
			return err
		}
		instance2, err := certificatemanager.NewDnsAuthorization(ctx, "instance2", &certificatemanager.DnsAuthorizationArgs{
			Name:        pulumi.String("dns-auth2"),
			Description: pulumi.String("The default dnss"),
			Domain:      pulumi.String("subdomain2.hashicorptest.com"),
		})
		if err != nil {
			return err
		}
		_, err = certificatemanager.NewCertificate(ctx, "default", &certificatemanager.CertificateArgs{
			Name:        pulumi.String("dns-cert"),
			Description: pulumi.String("The default cert"),
			Scope:       pulumi.String("EDGE_CACHE"),
			Labels: pulumi.StringMap{
				"env": pulumi.String("test"),
			},
			Managed: &certificatemanager.CertificateManagedArgs{
				Domains: pulumi.StringArray{
					instance.Domain,
					instance2.Domain,
				},
				DnsAuthorizations: pulumi.StringArray{
					instance.ID(),
					instance2.ID(),
				},
			},
		})
		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 instance = new Gcp.CertificateManager.DnsAuthorization("instance", new()
    {
        Name = "dns-auth",
        Description = "The default dnss",
        Domain = "subdomain.hashicorptest.com",
    });

    var instance2 = new Gcp.CertificateManager.DnsAuthorization("instance2", new()
    {
        Name = "dns-auth2",
        Description = "The default dnss",
        Domain = "subdomain2.hashicorptest.com",
    });

    var @default = new Gcp.CertificateManager.Certificate("default", new()
    {
        Name = "dns-cert",
        Description = "The default cert",
        Scope = "EDGE_CACHE",
        Labels = 
        {
            { "env", "test" },
        },
        Managed = new Gcp.CertificateManager.Inputs.CertificateManagedArgs
        {
            Domains = new[]
            {
                instance.Domain,
                instance2.Domain,
            },
            DnsAuthorizations = new[]
            {
                instance.Id,
                instance2.Id,
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.certificatemanager.DnsAuthorization;
import com.pulumi.gcp.certificatemanager.DnsAuthorizationArgs;
import com.pulumi.gcp.certificatemanager.Certificate;
import com.pulumi.gcp.certificatemanager.CertificateArgs;
import com.pulumi.gcp.certificatemanager.inputs.CertificateManagedArgs;
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 instance = new DnsAuthorization("instance", DnsAuthorizationArgs.builder()
            .name("dns-auth")
            .description("The default dnss")
            .domain("subdomain.hashicorptest.com")
            .build());

        var instance2 = new DnsAuthorization("instance2", DnsAuthorizationArgs.builder()
            .name("dns-auth2")
            .description("The default dnss")
            .domain("subdomain2.hashicorptest.com")
            .build());

        var default_ = new Certificate("default", CertificateArgs.builder()
            .name("dns-cert")
            .description("The default cert")
            .scope("EDGE_CACHE")
            .labels(Map.of("env", "test"))
            .managed(CertificateManagedArgs.builder()
                .domains(                
                    instance.domain(),
                    instance2.domain())
                .dnsAuthorizations(                
                    instance.id(),
                    instance2.id())
                .build())
            .build());

    }
}
resources:
  default:
    type: gcp:certificatemanager:Certificate
    properties:
      name: dns-cert
      description: The default cert
      scope: EDGE_CACHE
      labels:
        env: test
      managed:
        domains:
          - ${instance.domain}
          - ${instance2.domain}
        dnsAuthorizations:
          - ${instance.id}
          - ${instance2.id}
  instance:
    type: gcp:certificatemanager:DnsAuthorization
    properties:
      name: dns-auth
      description: The default dnss
      domain: subdomain.hashicorptest.com
  instance2:
    type: gcp:certificatemanager:DnsAuthorization
    properties:
      name: dns-auth2
      description: The default dnss
      domain: subdomain2.hashicorptest.com

The managed block configures automatic certificate provisioning. The domains property lists the domains to cover, and dnsAuthorizations references DnsAuthorization resources that define the DNS validation records. The scope property controls where the certificate is served; EDGE_CACHE serves from edge locations for lower latency. Google handles renewal automatically as long as DNS authorization remains valid.

Upload self-managed certificates for regional use

Applications that already have certificates from external CAs or need specific certificate properties can upload PEM-encoded files directly.

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

const _default = new gcp.certificatemanager.Certificate("default", {
    name: "self-managed-cert",
    description: "Regional cert",
    location: "us-central1",
    selfManaged: {
        pemCertificate: std.file({
            input: "test-fixtures/cert.pem",
        }).then(invoke => invoke.result),
        pemPrivateKey: std.file({
            input: "test-fixtures/private-key.pem",
        }).then(invoke => invoke.result),
    },
});
import pulumi
import pulumi_gcp as gcp
import pulumi_std as std

default = gcp.certificatemanager.Certificate("default",
    name="self-managed-cert",
    description="Regional cert",
    location="us-central1",
    self_managed={
        "pem_certificate": std.file(input="test-fixtures/cert.pem").result,
        "pem_private_key": std.file(input="test-fixtures/private-key.pem").result,
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/certificatemanager"
	"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 {
		invokeFile, err := std.File(ctx, &std.FileArgs{
			Input: "test-fixtures/cert.pem",
		}, nil)
		if err != nil {
			return err
		}
		invokeFile1, err := std.File(ctx, &std.FileArgs{
			Input: "test-fixtures/private-key.pem",
		}, nil)
		if err != nil {
			return err
		}
		_, err = certificatemanager.NewCertificate(ctx, "default", &certificatemanager.CertificateArgs{
			Name:        pulumi.String("self-managed-cert"),
			Description: pulumi.String("Regional cert"),
			Location:    pulumi.String("us-central1"),
			SelfManaged: &certificatemanager.CertificateSelfManagedArgs{
				PemCertificate: pulumi.String(invokeFile.Result),
				PemPrivateKey:  pulumi.String(invokeFile1.Result),
			},
		})
		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 @default = new Gcp.CertificateManager.Certificate("default", new()
    {
        Name = "self-managed-cert",
        Description = "Regional cert",
        Location = "us-central1",
        SelfManaged = new Gcp.CertificateManager.Inputs.CertificateSelfManagedArgs
        {
            PemCertificate = Std.File.Invoke(new()
            {
                Input = "test-fixtures/cert.pem",
            }).Apply(invoke => invoke.Result),
            PemPrivateKey = Std.File.Invoke(new()
            {
                Input = "test-fixtures/private-key.pem",
            }).Apply(invoke => invoke.Result),
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.certificatemanager.Certificate;
import com.pulumi.gcp.certificatemanager.CertificateArgs;
import com.pulumi.gcp.certificatemanager.inputs.CertificateSelfManagedArgs;
import com.pulumi.std.StdFunctions;
import com.pulumi.std.inputs.FileArgs;
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 default_ = new Certificate("default", CertificateArgs.builder()
            .name("self-managed-cert")
            .description("Regional cert")
            .location("us-central1")
            .selfManaged(CertificateSelfManagedArgs.builder()
                .pemCertificate(StdFunctions.file(FileArgs.builder()
                    .input("test-fixtures/cert.pem")
                    .build()).result())
                .pemPrivateKey(StdFunctions.file(FileArgs.builder()
                    .input("test-fixtures/private-key.pem")
                    .build()).result())
                .build())
            .build());

    }
}
resources:
  default:
    type: gcp:certificatemanager:Certificate
    properties:
      name: self-managed-cert
      description: Regional cert
      location: us-central1
      selfManaged:
        pemCertificate:
          fn::invoke:
            function: std:file
            arguments:
              input: test-fixtures/cert.pem
            return: result
        pemPrivateKey:
          fn::invoke:
            function: std:file
            arguments:
              input: test-fixtures/private-key.pem
            return: result

The selfManaged block accepts PEM-encoded certificate and private key data. The location property restricts the certificate to a specific region rather than serving globally. Unlike managed certificates, you’re responsible for renewing self-managed certificates before they expire.

Issue certificates from private Certificate Authority

Organizations with private Certificate Authority Service (CAS) pools can configure Certificate Manager to issue certificates from their internal CA infrastructure.

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

const pool = new gcp.certificateauthority.CaPool("pool", {
    name: "ca-pool",
    location: "us-central1",
    tier: "ENTERPRISE",
});
const caAuthority = new gcp.certificateauthority.Authority("ca_authority", {
    location: "us-central1",
    pool: pool.name,
    certificateAuthorityId: "ca-authority",
    config: {
        subjectConfig: {
            subject: {
                organization: "HashiCorp",
                commonName: "my-certificate-authority",
            },
            subjectAltName: {
                dnsNames: ["hashicorp.com"],
            },
        },
        x509Config: {
            caOptions: {
                isCa: true,
            },
            keyUsage: {
                baseKeyUsage: {
                    certSign: true,
                    crlSign: true,
                },
                extendedKeyUsage: {
                    serverAuth: true,
                },
            },
        },
    },
    keySpec: {
        algorithm: "RSA_PKCS1_4096_SHA256",
    },
    deletionProtection: false,
    skipGracePeriod: true,
    ignoreActiveCertificatesOnDeletion: true,
});
// creating certificate_issuance_config to use it in the managed certificate
const issuanceconfig = new gcp.certificatemanager.CertificateIssuanceConfig("issuanceconfig", {
    name: "issuance-config",
    description: "sample description for the certificate issuanceConfigs",
    certificateAuthorityConfig: {
        certificateAuthorityServiceConfig: {
            caPool: pool.id,
        },
    },
    lifetime: "1814400s",
    rotationWindowPercentage: 34,
    keyAlgorithm: "ECDSA_P256",
}, {
    dependsOn: [caAuthority],
});
const _default = new gcp.certificatemanager.Certificate("default", {
    name: "issuance-config-cert",
    description: "The default cert",
    scope: "EDGE_CACHE",
    managed: {
        domains: ["terraform.subdomain1.com"],
        issuanceConfig: issuanceconfig.id,
    },
});
import pulumi
import pulumi_gcp as gcp

pool = gcp.certificateauthority.CaPool("pool",
    name="ca-pool",
    location="us-central1",
    tier="ENTERPRISE")
ca_authority = gcp.certificateauthority.Authority("ca_authority",
    location="us-central1",
    pool=pool.name,
    certificate_authority_id="ca-authority",
    config={
        "subject_config": {
            "subject": {
                "organization": "HashiCorp",
                "common_name": "my-certificate-authority",
            },
            "subject_alt_name": {
                "dns_names": ["hashicorp.com"],
            },
        },
        "x509_config": {
            "ca_options": {
                "is_ca": True,
            },
            "key_usage": {
                "base_key_usage": {
                    "cert_sign": True,
                    "crl_sign": True,
                },
                "extended_key_usage": {
                    "server_auth": True,
                },
            },
        },
    },
    key_spec={
        "algorithm": "RSA_PKCS1_4096_SHA256",
    },
    deletion_protection=False,
    skip_grace_period=True,
    ignore_active_certificates_on_deletion=True)
# creating certificate_issuance_config to use it in the managed certificate
issuanceconfig = gcp.certificatemanager.CertificateIssuanceConfig("issuanceconfig",
    name="issuance-config",
    description="sample description for the certificate issuanceConfigs",
    certificate_authority_config={
        "certificate_authority_service_config": {
            "ca_pool": pool.id,
        },
    },
    lifetime="1814400s",
    rotation_window_percentage=34,
    key_algorithm="ECDSA_P256",
    opts = pulumi.ResourceOptions(depends_on=[ca_authority]))
default = gcp.certificatemanager.Certificate("default",
    name="issuance-config-cert",
    description="The default cert",
    scope="EDGE_CACHE",
    managed={
        "domains": ["terraform.subdomain1.com"],
        "issuance_config": issuanceconfig.id,
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		pool, err := certificateauthority.NewCaPool(ctx, "pool", &certificateauthority.CaPoolArgs{
			Name:     pulumi.String("ca-pool"),
			Location: pulumi.String("us-central1"),
			Tier:     pulumi.String("ENTERPRISE"),
		})
		if err != nil {
			return err
		}
		caAuthority, err := certificateauthority.NewAuthority(ctx, "ca_authority", &certificateauthority.AuthorityArgs{
			Location:               pulumi.String("us-central1"),
			Pool:                   pool.Name,
			CertificateAuthorityId: pulumi.String("ca-authority"),
			Config: &certificateauthority.AuthorityConfigArgs{
				SubjectConfig: &certificateauthority.AuthorityConfigSubjectConfigArgs{
					Subject: &certificateauthority.AuthorityConfigSubjectConfigSubjectArgs{
						Organization: pulumi.String("HashiCorp"),
						CommonName:   pulumi.String("my-certificate-authority"),
					},
					SubjectAltName: &certificateauthority.AuthorityConfigSubjectConfigSubjectAltNameArgs{
						DnsNames: pulumi.StringArray{
							pulumi.String("hashicorp.com"),
						},
					},
				},
				X509Config: &certificateauthority.AuthorityConfigX509ConfigArgs{
					CaOptions: &certificateauthority.AuthorityConfigX509ConfigCaOptionsArgs{
						IsCa: pulumi.Bool(true),
					},
					KeyUsage: &certificateauthority.AuthorityConfigX509ConfigKeyUsageArgs{
						BaseKeyUsage: &certificateauthority.AuthorityConfigX509ConfigKeyUsageBaseKeyUsageArgs{
							CertSign: pulumi.Bool(true),
							CrlSign:  pulumi.Bool(true),
						},
						ExtendedKeyUsage: &certificateauthority.AuthorityConfigX509ConfigKeyUsageExtendedKeyUsageArgs{
							ServerAuth: pulumi.Bool(true),
						},
					},
				},
			},
			KeySpec: &certificateauthority.AuthorityKeySpecArgs{
				Algorithm: pulumi.String("RSA_PKCS1_4096_SHA256"),
			},
			DeletionProtection:                 pulumi.Bool(false),
			SkipGracePeriod:                    pulumi.Bool(true),
			IgnoreActiveCertificatesOnDeletion: pulumi.Bool(true),
		})
		if err != nil {
			return err
		}
		// creating certificate_issuance_config to use it in the managed certificate
		issuanceconfig, err := certificatemanager.NewCertificateIssuanceConfig(ctx, "issuanceconfig", &certificatemanager.CertificateIssuanceConfigArgs{
			Name:        pulumi.String("issuance-config"),
			Description: pulumi.String("sample description for the certificate issuanceConfigs"),
			CertificateAuthorityConfig: &certificatemanager.CertificateIssuanceConfigCertificateAuthorityConfigArgs{
				CertificateAuthorityServiceConfig: &certificatemanager.CertificateIssuanceConfigCertificateAuthorityConfigCertificateAuthorityServiceConfigArgs{
					CaPool: pool.ID(),
				},
			},
			Lifetime:                 pulumi.String("1814400s"),
			RotationWindowPercentage: pulumi.Int(34),
			KeyAlgorithm:             pulumi.String("ECDSA_P256"),
		}, pulumi.DependsOn([]pulumi.Resource{
			caAuthority,
		}))
		if err != nil {
			return err
		}
		_, err = certificatemanager.NewCertificate(ctx, "default", &certificatemanager.CertificateArgs{
			Name:        pulumi.String("issuance-config-cert"),
			Description: pulumi.String("The default cert"),
			Scope:       pulumi.String("EDGE_CACHE"),
			Managed: &certificatemanager.CertificateManagedArgs{
				Domains: pulumi.StringArray{
					pulumi.String("terraform.subdomain1.com"),
				},
				IssuanceConfig: issuanceconfig.ID(),
			},
		})
		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 pool = new Gcp.CertificateAuthority.CaPool("pool", new()
    {
        Name = "ca-pool",
        Location = "us-central1",
        Tier = "ENTERPRISE",
    });

    var caAuthority = new Gcp.CertificateAuthority.Authority("ca_authority", new()
    {
        Location = "us-central1",
        Pool = pool.Name,
        CertificateAuthorityId = "ca-authority",
        Config = new Gcp.CertificateAuthority.Inputs.AuthorityConfigArgs
        {
            SubjectConfig = new Gcp.CertificateAuthority.Inputs.AuthorityConfigSubjectConfigArgs
            {
                Subject = new Gcp.CertificateAuthority.Inputs.AuthorityConfigSubjectConfigSubjectArgs
                {
                    Organization = "HashiCorp",
                    CommonName = "my-certificate-authority",
                },
                SubjectAltName = new Gcp.CertificateAuthority.Inputs.AuthorityConfigSubjectConfigSubjectAltNameArgs
                {
                    DnsNames = new[]
                    {
                        "hashicorp.com",
                    },
                },
            },
            X509Config = new Gcp.CertificateAuthority.Inputs.AuthorityConfigX509ConfigArgs
            {
                CaOptions = new Gcp.CertificateAuthority.Inputs.AuthorityConfigX509ConfigCaOptionsArgs
                {
                    IsCa = true,
                },
                KeyUsage = new Gcp.CertificateAuthority.Inputs.AuthorityConfigX509ConfigKeyUsageArgs
                {
                    BaseKeyUsage = new Gcp.CertificateAuthority.Inputs.AuthorityConfigX509ConfigKeyUsageBaseKeyUsageArgs
                    {
                        CertSign = true,
                        CrlSign = true,
                    },
                    ExtendedKeyUsage = new Gcp.CertificateAuthority.Inputs.AuthorityConfigX509ConfigKeyUsageExtendedKeyUsageArgs
                    {
                        ServerAuth = true,
                    },
                },
            },
        },
        KeySpec = new Gcp.CertificateAuthority.Inputs.AuthorityKeySpecArgs
        {
            Algorithm = "RSA_PKCS1_4096_SHA256",
        },
        DeletionProtection = false,
        SkipGracePeriod = true,
        IgnoreActiveCertificatesOnDeletion = true,
    });

    // creating certificate_issuance_config to use it in the managed certificate
    var issuanceconfig = new Gcp.CertificateManager.CertificateIssuanceConfig("issuanceconfig", new()
    {
        Name = "issuance-config",
        Description = "sample description for the certificate issuanceConfigs",
        CertificateAuthorityConfig = new Gcp.CertificateManager.Inputs.CertificateIssuanceConfigCertificateAuthorityConfigArgs
        {
            CertificateAuthorityServiceConfig = new Gcp.CertificateManager.Inputs.CertificateIssuanceConfigCertificateAuthorityConfigCertificateAuthorityServiceConfigArgs
            {
                CaPool = pool.Id,
            },
        },
        Lifetime = "1814400s",
        RotationWindowPercentage = 34,
        KeyAlgorithm = "ECDSA_P256",
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            caAuthority,
        },
    });

    var @default = new Gcp.CertificateManager.Certificate("default", new()
    {
        Name = "issuance-config-cert",
        Description = "The default cert",
        Scope = "EDGE_CACHE",
        Managed = new Gcp.CertificateManager.Inputs.CertificateManagedArgs
        {
            Domains = new[]
            {
                "terraform.subdomain1.com",
            },
            IssuanceConfig = issuanceconfig.Id,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.certificateauthority.CaPool;
import com.pulumi.gcp.certificateauthority.CaPoolArgs;
import com.pulumi.gcp.certificateauthority.Authority;
import com.pulumi.gcp.certificateauthority.AuthorityArgs;
import com.pulumi.gcp.certificateauthority.inputs.AuthorityConfigArgs;
import com.pulumi.gcp.certificateauthority.inputs.AuthorityConfigSubjectConfigArgs;
import com.pulumi.gcp.certificateauthority.inputs.AuthorityConfigSubjectConfigSubjectArgs;
import com.pulumi.gcp.certificateauthority.inputs.AuthorityConfigSubjectConfigSubjectAltNameArgs;
import com.pulumi.gcp.certificateauthority.inputs.AuthorityConfigX509ConfigArgs;
import com.pulumi.gcp.certificateauthority.inputs.AuthorityConfigX509ConfigCaOptionsArgs;
import com.pulumi.gcp.certificateauthority.inputs.AuthorityConfigX509ConfigKeyUsageArgs;
import com.pulumi.gcp.certificateauthority.inputs.AuthorityConfigX509ConfigKeyUsageBaseKeyUsageArgs;
import com.pulumi.gcp.certificateauthority.inputs.AuthorityConfigX509ConfigKeyUsageExtendedKeyUsageArgs;
import com.pulumi.gcp.certificateauthority.inputs.AuthorityKeySpecArgs;
import com.pulumi.gcp.certificatemanager.CertificateIssuanceConfig;
import com.pulumi.gcp.certificatemanager.CertificateIssuanceConfigArgs;
import com.pulumi.gcp.certificatemanager.inputs.CertificateIssuanceConfigCertificateAuthorityConfigArgs;
import com.pulumi.gcp.certificatemanager.inputs.CertificateIssuanceConfigCertificateAuthorityConfigCertificateAuthorityServiceConfigArgs;
import com.pulumi.gcp.certificatemanager.Certificate;
import com.pulumi.gcp.certificatemanager.CertificateArgs;
import com.pulumi.gcp.certificatemanager.inputs.CertificateManagedArgs;
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 pool = new CaPool("pool", CaPoolArgs.builder()
            .name("ca-pool")
            .location("us-central1")
            .tier("ENTERPRISE")
            .build());

        var caAuthority = new Authority("caAuthority", AuthorityArgs.builder()
            .location("us-central1")
            .pool(pool.name())
            .certificateAuthorityId("ca-authority")
            .config(AuthorityConfigArgs.builder()
                .subjectConfig(AuthorityConfigSubjectConfigArgs.builder()
                    .subject(AuthorityConfigSubjectConfigSubjectArgs.builder()
                        .organization("HashiCorp")
                        .commonName("my-certificate-authority")
                        .build())
                    .subjectAltName(AuthorityConfigSubjectConfigSubjectAltNameArgs.builder()
                        .dnsNames("hashicorp.com")
                        .build())
                    .build())
                .x509Config(AuthorityConfigX509ConfigArgs.builder()
                    .caOptions(AuthorityConfigX509ConfigCaOptionsArgs.builder()
                        .isCa(true)
                        .build())
                    .keyUsage(AuthorityConfigX509ConfigKeyUsageArgs.builder()
                        .baseKeyUsage(AuthorityConfigX509ConfigKeyUsageBaseKeyUsageArgs.builder()
                            .certSign(true)
                            .crlSign(true)
                            .build())
                        .extendedKeyUsage(AuthorityConfigX509ConfigKeyUsageExtendedKeyUsageArgs.builder()
                            .serverAuth(true)
                            .build())
                        .build())
                    .build())
                .build())
            .keySpec(AuthorityKeySpecArgs.builder()
                .algorithm("RSA_PKCS1_4096_SHA256")
                .build())
            .deletionProtection(false)
            .skipGracePeriod(true)
            .ignoreActiveCertificatesOnDeletion(true)
            .build());

        // creating certificate_issuance_config to use it in the managed certificate
        var issuanceconfig = new CertificateIssuanceConfig("issuanceconfig", CertificateIssuanceConfigArgs.builder()
            .name("issuance-config")
            .description("sample description for the certificate issuanceConfigs")
            .certificateAuthorityConfig(CertificateIssuanceConfigCertificateAuthorityConfigArgs.builder()
                .certificateAuthorityServiceConfig(CertificateIssuanceConfigCertificateAuthorityConfigCertificateAuthorityServiceConfigArgs.builder()
                    .caPool(pool.id())
                    .build())
                .build())
            .lifetime("1814400s")
            .rotationWindowPercentage(34)
            .keyAlgorithm("ECDSA_P256")
            .build(), CustomResourceOptions.builder()
                .dependsOn(caAuthority)
                .build());

        var default_ = new Certificate("default", CertificateArgs.builder()
            .name("issuance-config-cert")
            .description("The default cert")
            .scope("EDGE_CACHE")
            .managed(CertificateManagedArgs.builder()
                .domains("terraform.subdomain1.com")
                .issuanceConfig(issuanceconfig.id())
                .build())
            .build());

    }
}
resources:
  default:
    type: gcp:certificatemanager:Certificate
    properties:
      name: issuance-config-cert
      description: The default cert
      scope: EDGE_CACHE
      managed:
        domains:
          - terraform.subdomain1.com
        issuanceConfig: ${issuanceconfig.id}
  # creating certificate_issuance_config to use it in the managed certificate
  issuanceconfig:
    type: gcp:certificatemanager:CertificateIssuanceConfig
    properties:
      name: issuance-config
      description: sample description for the certificate issuanceConfigs
      certificateAuthorityConfig:
        certificateAuthorityServiceConfig:
          caPool: ${pool.id}
      lifetime: 1814400s
      rotationWindowPercentage: 34
      keyAlgorithm: ECDSA_P256
    options:
      dependsOn:
        - ${caAuthority}
  pool:
    type: gcp:certificateauthority:CaPool
    properties:
      name: ca-pool
      location: us-central1
      tier: ENTERPRISE
  caAuthority:
    type: gcp:certificateauthority:Authority
    name: ca_authority
    properties:
      location: us-central1
      pool: ${pool.name}
      certificateAuthorityId: ca-authority
      config:
        subjectConfig:
          subject:
            organization: HashiCorp
            commonName: my-certificate-authority
          subjectAltName:
            dnsNames:
              - hashicorp.com
        x509Config:
          caOptions:
            isCa: true
          keyUsage:
            baseKeyUsage:
              certSign: true
              crlSign: true
            extendedKeyUsage:
              serverAuth: true
      keySpec:
        algorithm: RSA_PKCS1_4096_SHA256
      deletionProtection: false
      skipGracePeriod: true
      ignoreActiveCertificatesOnDeletion: true

The issuanceConfig property references a CertificateIssuanceConfig resource that connects to your CA pool. Certificate Manager requests certificates from your private CA instead of a public CA. The lifetime and rotationWindowPercentage properties control certificate validity and automatic rotation timing.

Deploy regional certificates with DNS authorization

Regional certificates serve traffic from specific GCP regions rather than globally, useful for applications with regional load balancers or compliance requirements.

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

const instance = new gcp.certificatemanager.DnsAuthorization("instance", {
    name: "dns-auth",
    location: "us-central1",
    description: "The default dnss",
    domain: "subdomain.hashicorptest.com",
});
const _default = new gcp.certificatemanager.Certificate("default", {
    name: "dns-cert",
    description: "regional managed certs",
    location: "us-central1",
    managed: {
        domains: [instance.domain],
        dnsAuthorizations: [instance.id],
    },
});
import pulumi
import pulumi_gcp as gcp

instance = gcp.certificatemanager.DnsAuthorization("instance",
    name="dns-auth",
    location="us-central1",
    description="The default dnss",
    domain="subdomain.hashicorptest.com")
default = gcp.certificatemanager.Certificate("default",
    name="dns-cert",
    description="regional managed certs",
    location="us-central1",
    managed={
        "domains": [instance.domain],
        "dns_authorizations": [instance.id],
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		instance, err := certificatemanager.NewDnsAuthorization(ctx, "instance", &certificatemanager.DnsAuthorizationArgs{
			Name:        pulumi.String("dns-auth"),
			Location:    pulumi.String("us-central1"),
			Description: pulumi.String("The default dnss"),
			Domain:      pulumi.String("subdomain.hashicorptest.com"),
		})
		if err != nil {
			return err
		}
		_, err = certificatemanager.NewCertificate(ctx, "default", &certificatemanager.CertificateArgs{
			Name:        pulumi.String("dns-cert"),
			Description: pulumi.String("regional managed certs"),
			Location:    pulumi.String("us-central1"),
			Managed: &certificatemanager.CertificateManagedArgs{
				Domains: pulumi.StringArray{
					instance.Domain,
				},
				DnsAuthorizations: pulumi.StringArray{
					instance.ID(),
				},
			},
		})
		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 instance = new Gcp.CertificateManager.DnsAuthorization("instance", new()
    {
        Name = "dns-auth",
        Location = "us-central1",
        Description = "The default dnss",
        Domain = "subdomain.hashicorptest.com",
    });

    var @default = new Gcp.CertificateManager.Certificate("default", new()
    {
        Name = "dns-cert",
        Description = "regional managed certs",
        Location = "us-central1",
        Managed = new Gcp.CertificateManager.Inputs.CertificateManagedArgs
        {
            Domains = new[]
            {
                instance.Domain,
            },
            DnsAuthorizations = new[]
            {
                instance.Id,
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.certificatemanager.DnsAuthorization;
import com.pulumi.gcp.certificatemanager.DnsAuthorizationArgs;
import com.pulumi.gcp.certificatemanager.Certificate;
import com.pulumi.gcp.certificatemanager.CertificateArgs;
import com.pulumi.gcp.certificatemanager.inputs.CertificateManagedArgs;
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 instance = new DnsAuthorization("instance", DnsAuthorizationArgs.builder()
            .name("dns-auth")
            .location("us-central1")
            .description("The default dnss")
            .domain("subdomain.hashicorptest.com")
            .build());

        var default_ = new Certificate("default", CertificateArgs.builder()
            .name("dns-cert")
            .description("regional managed certs")
            .location("us-central1")
            .managed(CertificateManagedArgs.builder()
                .domains(instance.domain())
                .dnsAuthorizations(instance.id())
                .build())
            .build());

    }
}
resources:
  default:
    type: gcp:certificatemanager:Certificate
    properties:
      name: dns-cert
      description: regional managed certs
      location: us-central1
      managed:
        domains:
          - ${instance.domain}
        dnsAuthorizations:
          - ${instance.id}
  instance:
    type: gcp:certificatemanager:DnsAuthorization
    properties:
      name: dns-auth
      location: us-central1
      description: The default dnss
      domain: subdomain.hashicorptest.com

The location property restricts both the certificate and its DNS authorization to a specific region. Regional certificates work with regional load balancers and can help meet data residency requirements. The DnsAuthorization must be in the same region as the certificate.

Configure client authentication certificates for mTLS

Backend mutual TLS (mTLS) requires load balancers to present client certificates when connecting to backend services that verify client identity.

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

const _default = new gcp.certificatemanager.Certificate("default", {
    name: "client-auth-cert",
    description: "Global cert",
    scope: "CLIENT_AUTH",
    selfManaged: {
        pemCertificate: std.file({
            input: "test-fixtures/cert.pem",
        }).then(invoke => invoke.result),
        pemPrivateKey: std.file({
            input: "test-fixtures/private-key.pem",
        }).then(invoke => invoke.result),
    },
});
import pulumi
import pulumi_gcp as gcp
import pulumi_std as std

default = gcp.certificatemanager.Certificate("default",
    name="client-auth-cert",
    description="Global cert",
    scope="CLIENT_AUTH",
    self_managed={
        "pem_certificate": std.file(input="test-fixtures/cert.pem").result,
        "pem_private_key": std.file(input="test-fixtures/private-key.pem").result,
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/certificatemanager"
	"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 {
		invokeFile, err := std.File(ctx, &std.FileArgs{
			Input: "test-fixtures/cert.pem",
		}, nil)
		if err != nil {
			return err
		}
		invokeFile1, err := std.File(ctx, &std.FileArgs{
			Input: "test-fixtures/private-key.pem",
		}, nil)
		if err != nil {
			return err
		}
		_, err = certificatemanager.NewCertificate(ctx, "default", &certificatemanager.CertificateArgs{
			Name:        pulumi.String("client-auth-cert"),
			Description: pulumi.String("Global cert"),
			Scope:       pulumi.String("CLIENT_AUTH"),
			SelfManaged: &certificatemanager.CertificateSelfManagedArgs{
				PemCertificate: pulumi.String(invokeFile.Result),
				PemPrivateKey:  pulumi.String(invokeFile1.Result),
			},
		})
		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 @default = new Gcp.CertificateManager.Certificate("default", new()
    {
        Name = "client-auth-cert",
        Description = "Global cert",
        Scope = "CLIENT_AUTH",
        SelfManaged = new Gcp.CertificateManager.Inputs.CertificateSelfManagedArgs
        {
            PemCertificate = Std.File.Invoke(new()
            {
                Input = "test-fixtures/cert.pem",
            }).Apply(invoke => invoke.Result),
            PemPrivateKey = Std.File.Invoke(new()
            {
                Input = "test-fixtures/private-key.pem",
            }).Apply(invoke => invoke.Result),
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.certificatemanager.Certificate;
import com.pulumi.gcp.certificatemanager.CertificateArgs;
import com.pulumi.gcp.certificatemanager.inputs.CertificateSelfManagedArgs;
import com.pulumi.std.StdFunctions;
import com.pulumi.std.inputs.FileArgs;
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 default_ = new Certificate("default", CertificateArgs.builder()
            .name("client-auth-cert")
            .description("Global cert")
            .scope("CLIENT_AUTH")
            .selfManaged(CertificateSelfManagedArgs.builder()
                .pemCertificate(StdFunctions.file(FileArgs.builder()
                    .input("test-fixtures/cert.pem")
                    .build()).result())
                .pemPrivateKey(StdFunctions.file(FileArgs.builder()
                    .input("test-fixtures/private-key.pem")
                    .build()).result())
                .build())
            .build());

    }
}
resources:
  default:
    type: gcp:certificatemanager:Certificate
    properties:
      name: client-auth-cert
      description: Global cert
      scope: CLIENT_AUTH
      selfManaged:
        pemCertificate:
          fn::invoke:
            function: std:file
            arguments:
              input: test-fixtures/cert.pem
            return: result
        pemPrivateKey:
          fn::invoke:
            function: std:file
            arguments:
              input: test-fixtures/private-key.pem
            return: result

The scope property set to CLIENT_AUTH designates this certificate for load balancer-to-backend authentication rather than client-to-load-balancer. The load balancer presents this certificate when connecting to backends configured for mTLS. Backend services must be configured to require and validate client certificates.

Beyond these examples

These snippets focus on specific certificate-level features: Google-managed and self-managed certificate types, DNS authorization and private CA issuance, and global, regional, and client authentication scopes. They’re intentionally minimal rather than full TLS configurations.

The examples may reference pre-existing infrastructure such as DNS control for domain validation, Certificate Authority Service pools for private CA examples, and PEM certificate and private key files for self-managed examples. They focus on configuring the certificate rather than provisioning the surrounding infrastructure.

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

  • Certificate rotation and renewal monitoring
  • Load balancer attachment and SSL policy configuration
  • Certificate map creation for multi-domain routing
  • Wildcard certificate configuration

These omissions are intentional: the goal is to illustrate how each certificate feature is wired, not provide drop-in TLS modules. See the Certificate Manager Certificate resource reference for all available configuration options.

Let's create GCP Certificate Manager Certificates

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Certificate Types & Management
What's the difference between managed and self-managed certificates?
Managed certificates are automatically provisioned and renewed by Certificate Manager. Configure them using managed with domains and either dnsAuthorizations or issuanceConfig. Self-managed certificates are uploaded by you using selfManaged with pemCertificate and pemPrivateKey, and you’re responsible for renewing them before expiration.
How do I create a Google-managed certificate?
Set the managed property with domains and either dnsAuthorizations (for DNS validation) or issuanceConfig (for Certificate Authority integration). Certificate Manager will automatically provision and renew the certificate.
How do I upload my own certificate?
Use the selfManaged property with pemCertificate and pemPrivateKey containing your certificate and private key data. You’re responsible for updating the certificate before it expires.
What are the certificate scope options and when should I use each?

You have four scope options:

  1. DEFAULT - Served from core Google datacenters (use if unsure)
  2. EDGE_CACHE - Served from Edge Points of Presence for lower latency
  3. ALL_REGIONS - Served from all GCP regions (requires global certificates)
  4. CLIENT_AUTH - Used for backend mTLS when load balancer presents cert to backend
Configuration & Constraints
What properties can't I change after creating a certificate?
The following properties are immutable: name, location, scope, managed, and selfManaged. Changing any of these requires recreating the certificate.
What are the naming requirements for certificates?
Certificate names must be 1-64 characters long and match the pattern [a-zA-Z][a-zA-Z0-9_-]*. The first character must be a letter, and subsequent characters can be letters, digits, dashes, or underscores.
Why doesn't my labels configuration match the actual labels on the resource?
The labels field is non-authoritative and only manages labels present in your configuration. Other clients and services may add additional labels. Use the effectiveLabels output to see all labels actually present on the resource.
Regional & Global Deployment
What's the difference between regional and global certificates?
Regional certificates are deployed to a specific region by setting location (e.g., us-central1). Global certificates are deployed across Google’s network by omitting location or setting it to global.
Can I use ALL_REGIONS scope with a regional certificate?
No, ALL_REGIONS scope can only be used with global certificates. Don’t specify location or set it to global when using ALL_REGIONS scope.

Using a different cloud?

Explore security guides for other cloud providers: