Configure GCP Backend Authentication for Load Balancers

The gcp:networksecurity/backendAuthenticationConfig:BackendAuthenticationConfig resource, part of the Pulumi GCP provider, defines how load balancers authenticate to backends and validate server certificates. This guide focuses on three capabilities: public root CA validation, mutual TLS with custom certificates, and backend service integration.

Authentication configs reference Certificate Manager resources and are consumed by backend services through their TLS settings. The examples are intentionally small. Combine them with your own certificate infrastructure and backend service definitions.

Validate backends using Google’s public root CAs

Load balancers connecting to backends over TLS need to validate server certificates. Using Google’s well-known public roots provides baseline trust without managing custom certificate chains.

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

const _default = new gcp.networksecurity.BackendAuthenticationConfig("default", {
    name: "my-backend-authentication-config",
    labels: {
        foo: "bar",
    },
    description: "my description",
    wellKnownRoots: "PUBLIC_ROOTS",
});
import pulumi
import pulumi_gcp as gcp

default = gcp.networksecurity.BackendAuthenticationConfig("default",
    name="my-backend-authentication-config",
    labels={
        "foo": "bar",
    },
    description="my description",
    well_known_roots="PUBLIC_ROOTS")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := networksecurity.NewBackendAuthenticationConfig(ctx, "default", &networksecurity.BackendAuthenticationConfigArgs{
			Name: pulumi.String("my-backend-authentication-config"),
			Labels: pulumi.StringMap{
				"foo": pulumi.String("bar"),
			},
			Description:    pulumi.String("my description"),
			WellKnownRoots: pulumi.String("PUBLIC_ROOTS"),
		})
		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 @default = new Gcp.NetworkSecurity.BackendAuthenticationConfig("default", new()
    {
        Name = "my-backend-authentication-config",
        Labels = 
        {
            { "foo", "bar" },
        },
        Description = "my description",
        WellKnownRoots = "PUBLIC_ROOTS",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.networksecurity.BackendAuthenticationConfig;
import com.pulumi.gcp.networksecurity.BackendAuthenticationConfigArgs;
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 BackendAuthenticationConfig("default", BackendAuthenticationConfigArgs.builder()
            .name("my-backend-authentication-config")
            .labels(Map.of("foo", "bar"))
            .description("my description")
            .wellKnownRoots("PUBLIC_ROOTS")
            .build());

    }
}
resources:
  default:
    type: gcp:networksecurity:BackendAuthenticationConfig
    properties:
      name: my-backend-authentication-config
      labels:
        foo: bar
      description: my description
      wellKnownRoots: PUBLIC_ROOTS

The wellKnownRoots property set to PUBLIC_ROOTS tells the load balancer to trust server certificates signed by Google’s managed set of public root CAs. This works for backends using certificates from public certificate authorities.

Authenticate with mutual TLS using custom certificates

Backends requiring mutual TLS need both server certificate validation and client certificate presentation. Custom trust chains and client certificates enable private certificate authorities.

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

const certificate = new gcp.certificatemanager.Certificate("certificate", {
    name: "my-certificate",
    labels: {
        foo: "bar",
    },
    location: "global",
    selfManaged: {
        pemCertificate: std.file({
            input: "test-fixtures/cert.pem",
        }).then(invoke => invoke.result),
        pemPrivateKey: std.file({
            input: "test-fixtures/key.pem",
        }).then(invoke => invoke.result),
    },
    scope: "CLIENT_AUTH",
});
const trustConfig = new gcp.certificatemanager.TrustConfig("trust_config", {
    name: "my-trust-config",
    description: "sample description for the trust config",
    location: "global",
    trustStores: [{
        trustAnchors: [{
            pemCertificate: std.file({
                input: "test-fixtures/cert.pem",
            }).then(invoke => invoke.result),
        }],
        intermediateCas: [{
            pemCertificate: std.file({
                input: "test-fixtures/cert.pem",
            }).then(invoke => invoke.result),
        }],
    }],
    labels: {
        foo: "bar",
    },
});
const _default = new gcp.networksecurity.BackendAuthenticationConfig("default", {
    name: "my-backend-authentication-config",
    labels: {
        bar: "foo",
    },
    location: "global",
    description: "my description",
    wellKnownRoots: "PUBLIC_ROOTS",
    clientCertificate: certificate.id,
    trustConfig: trustConfig.id,
});
import pulumi
import pulumi_gcp as gcp
import pulumi_std as std

certificate = gcp.certificatemanager.Certificate("certificate",
    name="my-certificate",
    labels={
        "foo": "bar",
    },
    location="global",
    self_managed={
        "pem_certificate": std.file(input="test-fixtures/cert.pem").result,
        "pem_private_key": std.file(input="test-fixtures/key.pem").result,
    },
    scope="CLIENT_AUTH")
trust_config = gcp.certificatemanager.TrustConfig("trust_config",
    name="my-trust-config",
    description="sample description for the trust config",
    location="global",
    trust_stores=[{
        "trust_anchors": [{
            "pem_certificate": std.file(input="test-fixtures/cert.pem").result,
        }],
        "intermediate_cas": [{
            "pem_certificate": std.file(input="test-fixtures/cert.pem").result,
        }],
    }],
    labels={
        "foo": "bar",
    })
default = gcp.networksecurity.BackendAuthenticationConfig("default",
    name="my-backend-authentication-config",
    labels={
        "bar": "foo",
    },
    location="global",
    description="my description",
    well_known_roots="PUBLIC_ROOTS",
    client_certificate=certificate.id,
    trust_config=trust_config.id)
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/certificatemanager"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/networksecurity"
	"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/key.pem",
		}, nil)
		if err != nil {
			return err
		}
		certificate, err := certificatemanager.NewCertificate(ctx, "certificate", &certificatemanager.CertificateArgs{
			Name: pulumi.String("my-certificate"),
			Labels: pulumi.StringMap{
				"foo": pulumi.String("bar"),
			},
			Location: pulumi.String("global"),
			SelfManaged: &certificatemanager.CertificateSelfManagedArgs{
				PemCertificate: pulumi.String(invokeFile.Result),
				PemPrivateKey:  pulumi.String(invokeFile1.Result),
			},
			Scope: pulumi.String("CLIENT_AUTH"),
		})
		if err != nil {
			return err
		}
		invokeFile2, err := std.File(ctx, &std.FileArgs{
			Input: "test-fixtures/cert.pem",
		}, nil)
		if err != nil {
			return err
		}
		invokeFile3, err := std.File(ctx, &std.FileArgs{
			Input: "test-fixtures/cert.pem",
		}, nil)
		if err != nil {
			return err
		}
		trustConfig, err := certificatemanager.NewTrustConfig(ctx, "trust_config", &certificatemanager.TrustConfigArgs{
			Name:        pulumi.String("my-trust-config"),
			Description: pulumi.String("sample description for the trust config"),
			Location:    pulumi.String("global"),
			TrustStores: certificatemanager.TrustConfigTrustStoreArray{
				&certificatemanager.TrustConfigTrustStoreArgs{
					TrustAnchors: certificatemanager.TrustConfigTrustStoreTrustAnchorArray{
						&certificatemanager.TrustConfigTrustStoreTrustAnchorArgs{
							PemCertificate: pulumi.String(invokeFile2.Result),
						},
					},
					IntermediateCas: certificatemanager.TrustConfigTrustStoreIntermediateCaArray{
						&certificatemanager.TrustConfigTrustStoreIntermediateCaArgs{
							PemCertificate: pulumi.String(invokeFile3.Result),
						},
					},
				},
			},
			Labels: pulumi.StringMap{
				"foo": pulumi.String("bar"),
			},
		})
		if err != nil {
			return err
		}
		_, err = networksecurity.NewBackendAuthenticationConfig(ctx, "default", &networksecurity.BackendAuthenticationConfigArgs{
			Name: pulumi.String("my-backend-authentication-config"),
			Labels: pulumi.StringMap{
				"bar": pulumi.String("foo"),
			},
			Location:          pulumi.String("global"),
			Description:       pulumi.String("my description"),
			WellKnownRoots:    pulumi.String("PUBLIC_ROOTS"),
			ClientCertificate: certificate.ID(),
			TrustConfig:       trustConfig.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 certificate = new Gcp.CertificateManager.Certificate("certificate", new()
    {
        Name = "my-certificate",
        Labels = 
        {
            { "foo", "bar" },
        },
        Location = "global",
        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/key.pem",
            }).Apply(invoke => invoke.Result),
        },
        Scope = "CLIENT_AUTH",
    });

    var trustConfig = new Gcp.CertificateManager.TrustConfig("trust_config", new()
    {
        Name = "my-trust-config",
        Description = "sample description for the trust config",
        Location = "global",
        TrustStores = new[]
        {
            new Gcp.CertificateManager.Inputs.TrustConfigTrustStoreArgs
            {
                TrustAnchors = new[]
                {
                    new Gcp.CertificateManager.Inputs.TrustConfigTrustStoreTrustAnchorArgs
                    {
                        PemCertificate = Std.File.Invoke(new()
                        {
                            Input = "test-fixtures/cert.pem",
                        }).Apply(invoke => invoke.Result),
                    },
                },
                IntermediateCas = new[]
                {
                    new Gcp.CertificateManager.Inputs.TrustConfigTrustStoreIntermediateCaArgs
                    {
                        PemCertificate = Std.File.Invoke(new()
                        {
                            Input = "test-fixtures/cert.pem",
                        }).Apply(invoke => invoke.Result),
                    },
                },
            },
        },
        Labels = 
        {
            { "foo", "bar" },
        },
    });

    var @default = new Gcp.NetworkSecurity.BackendAuthenticationConfig("default", new()
    {
        Name = "my-backend-authentication-config",
        Labels = 
        {
            { "bar", "foo" },
        },
        Location = "global",
        Description = "my description",
        WellKnownRoots = "PUBLIC_ROOTS",
        ClientCertificate = certificate.Id,
        TrustConfig = trustConfig.Id,
    });

});
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 com.pulumi.gcp.certificatemanager.TrustConfig;
import com.pulumi.gcp.certificatemanager.TrustConfigArgs;
import com.pulumi.gcp.certificatemanager.inputs.TrustConfigTrustStoreArgs;
import com.pulumi.gcp.networksecurity.BackendAuthenticationConfig;
import com.pulumi.gcp.networksecurity.BackendAuthenticationConfigArgs;
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 certificate = new Certificate("certificate", CertificateArgs.builder()
            .name("my-certificate")
            .labels(Map.of("foo", "bar"))
            .location("global")
            .selfManaged(CertificateSelfManagedArgs.builder()
                .pemCertificate(StdFunctions.file(FileArgs.builder()
                    .input("test-fixtures/cert.pem")
                    .build()).result())
                .pemPrivateKey(StdFunctions.file(FileArgs.builder()
                    .input("test-fixtures/key.pem")
                    .build()).result())
                .build())
            .scope("CLIENT_AUTH")
            .build());

        var trustConfig = new TrustConfig("trustConfig", TrustConfigArgs.builder()
            .name("my-trust-config")
            .description("sample description for the trust config")
            .location("global")
            .trustStores(TrustConfigTrustStoreArgs.builder()
                .trustAnchors(TrustConfigTrustStoreTrustAnchorArgs.builder()
                    .pemCertificate(StdFunctions.file(FileArgs.builder()
                        .input("test-fixtures/cert.pem")
                        .build()).result())
                    .build())
                .intermediateCas(TrustConfigTrustStoreIntermediateCaArgs.builder()
                    .pemCertificate(StdFunctions.file(FileArgs.builder()
                        .input("test-fixtures/cert.pem")
                        .build()).result())
                    .build())
                .build())
            .labels(Map.of("foo", "bar"))
            .build());

        var default_ = new BackendAuthenticationConfig("default", BackendAuthenticationConfigArgs.builder()
            .name("my-backend-authentication-config")
            .labels(Map.of("bar", "foo"))
            .location("global")
            .description("my description")
            .wellKnownRoots("PUBLIC_ROOTS")
            .clientCertificate(certificate.id())
            .trustConfig(trustConfig.id())
            .build());

    }
}
resources:
  certificate:
    type: gcp:certificatemanager:Certificate
    properties:
      name: my-certificate
      labels:
        foo: bar
      location: global
      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/key.pem
            return: result
      scope: CLIENT_AUTH
  trustConfig:
    type: gcp:certificatemanager:TrustConfig
    name: trust_config
    properties:
      name: my-trust-config
      description: sample description for the trust config
      location: global
      trustStores:
        - trustAnchors:
            - pemCertificate:
                fn::invoke:
                  function: std:file
                  arguments:
                    input: test-fixtures/cert.pem
                  return: result
          intermediateCas:
            - pemCertificate:
                fn::invoke:
                  function: std:file
                  arguments:
                    input: test-fixtures/cert.pem
                  return: result
      labels:
        foo: bar
  default:
    type: gcp:networksecurity:BackendAuthenticationConfig
    properties:
      name: my-backend-authentication-config
      labels:
        bar: foo
      location: global
      description: my description
      wellKnownRoots: PUBLIC_ROOTS
      clientCertificate: ${certificate.id}
      trustConfig: ${trustConfig.id}

The trustConfig property references a TrustConfig resource that defines the chain of trust for validating backend server certificates. The clientCertificate property points to a Certificate resource that the load balancer presents to backends during the TLS handshake. When both are set alongside wellKnownRoots: PUBLIC_ROOTS, the load balancer validates backends against both custom and public roots.

Connect backend services to authentication configs

Backend services reference authentication configs to control how they validate and authenticate to backends. The tlsSettings block ties SNI, subject alternative names, and authentication together.

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

const defaultHealthCheck = new gcp.compute.HealthCheck("default", {
    name: "health-check",
    httpHealthCheck: {
        port: 80,
    },
});
const defaultBackendAuthenticationConfig = new gcp.networksecurity.BackendAuthenticationConfig("default", {
    name: "authentication",
    wellKnownRoots: "PUBLIC_ROOTS",
});
const _default = new gcp.compute.BackendService("default", {
    name: "backend-service",
    healthChecks: defaultHealthCheck.id,
    loadBalancingScheme: "EXTERNAL_MANAGED",
    protocol: "HTTPS",
    tlsSettings: {
        sni: "example.com",
        subjectAltNames: [
            {
                dnsName: "example.com",
            },
            {
                uniformResourceIdentifier: "https://example.com",
            },
        ],
        authenticationConfig: pulumi.interpolate`//networksecurity.googleapis.com/${defaultBackendAuthenticationConfig.id}`,
    },
});
import pulumi
import pulumi_gcp as gcp

default_health_check = gcp.compute.HealthCheck("default",
    name="health-check",
    http_health_check={
        "port": 80,
    })
default_backend_authentication_config = gcp.networksecurity.BackendAuthenticationConfig("default",
    name="authentication",
    well_known_roots="PUBLIC_ROOTS")
default = gcp.compute.BackendService("default",
    name="backend-service",
    health_checks=default_health_check.id,
    load_balancing_scheme="EXTERNAL_MANAGED",
    protocol="HTTPS",
    tls_settings={
        "sni": "example.com",
        "subject_alt_names": [
            {
                "dns_name": "example.com",
            },
            {
                "uniform_resource_identifier": "https://example.com",
            },
        ],
        "authentication_config": default_backend_authentication_config.id.apply(lambda id: f"//networksecurity.googleapis.com/{id}"),
    })
package main

import (
	"fmt"

	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/networksecurity"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		defaultHealthCheck, err := compute.NewHealthCheck(ctx, "default", &compute.HealthCheckArgs{
			Name: pulumi.String("health-check"),
			HttpHealthCheck: &compute.HealthCheckHttpHealthCheckArgs{
				Port: pulumi.Int(80),
			},
		})
		if err != nil {
			return err
		}
		defaultBackendAuthenticationConfig, err := networksecurity.NewBackendAuthenticationConfig(ctx, "default", &networksecurity.BackendAuthenticationConfigArgs{
			Name:           pulumi.String("authentication"),
			WellKnownRoots: pulumi.String("PUBLIC_ROOTS"),
		})
		if err != nil {
			return err
		}
		_, err = compute.NewBackendService(ctx, "default", &compute.BackendServiceArgs{
			Name:                pulumi.String("backend-service"),
			HealthChecks:        defaultHealthCheck.ID(),
			LoadBalancingScheme: pulumi.String("EXTERNAL_MANAGED"),
			Protocol:            pulumi.String("HTTPS"),
			TlsSettings: &compute.BackendServiceTlsSettingsArgs{
				Sni: pulumi.String("example.com"),
				SubjectAltNames: compute.BackendServiceTlsSettingsSubjectAltNameArray{
					&compute.BackendServiceTlsSettingsSubjectAltNameArgs{
						DnsName: pulumi.String("example.com"),
					},
					&compute.BackendServiceTlsSettingsSubjectAltNameArgs{
						UniformResourceIdentifier: pulumi.String("https://example.com"),
					},
				},
				AuthenticationConfig: defaultBackendAuthenticationConfig.ID().ApplyT(func(id string) (string, error) {
					return fmt.Sprintf("//networksecurity.googleapis.com/%v", id), nil
				}).(pulumi.StringOutput),
			},
		})
		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 defaultHealthCheck = new Gcp.Compute.HealthCheck("default", new()
    {
        Name = "health-check",
        HttpHealthCheck = new Gcp.Compute.Inputs.HealthCheckHttpHealthCheckArgs
        {
            Port = 80,
        },
    });

    var defaultBackendAuthenticationConfig = new Gcp.NetworkSecurity.BackendAuthenticationConfig("default", new()
    {
        Name = "authentication",
        WellKnownRoots = "PUBLIC_ROOTS",
    });

    var @default = new Gcp.Compute.BackendService("default", new()
    {
        Name = "backend-service",
        HealthChecks = defaultHealthCheck.Id,
        LoadBalancingScheme = "EXTERNAL_MANAGED",
        Protocol = "HTTPS",
        TlsSettings = new Gcp.Compute.Inputs.BackendServiceTlsSettingsArgs
        {
            Sni = "example.com",
            SubjectAltNames = new[]
            {
                new Gcp.Compute.Inputs.BackendServiceTlsSettingsSubjectAltNameArgs
                {
                    DnsName = "example.com",
                },
                new Gcp.Compute.Inputs.BackendServiceTlsSettingsSubjectAltNameArgs
                {
                    UniformResourceIdentifier = "https://example.com",
                },
            },
            AuthenticationConfig = defaultBackendAuthenticationConfig.Id.Apply(id => $"//networksecurity.googleapis.com/{id}"),
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.HealthCheck;
import com.pulumi.gcp.compute.HealthCheckArgs;
import com.pulumi.gcp.compute.inputs.HealthCheckHttpHealthCheckArgs;
import com.pulumi.gcp.networksecurity.BackendAuthenticationConfig;
import com.pulumi.gcp.networksecurity.BackendAuthenticationConfigArgs;
import com.pulumi.gcp.compute.BackendService;
import com.pulumi.gcp.compute.BackendServiceArgs;
import com.pulumi.gcp.compute.inputs.BackendServiceTlsSettingsArgs;
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 defaultHealthCheck = new HealthCheck("defaultHealthCheck", HealthCheckArgs.builder()
            .name("health-check")
            .httpHealthCheck(HealthCheckHttpHealthCheckArgs.builder()
                .port(80)
                .build())
            .build());

        var defaultBackendAuthenticationConfig = new BackendAuthenticationConfig("defaultBackendAuthenticationConfig", BackendAuthenticationConfigArgs.builder()
            .name("authentication")
            .wellKnownRoots("PUBLIC_ROOTS")
            .build());

        var default_ = new BackendService("default", BackendServiceArgs.builder()
            .name("backend-service")
            .healthChecks(defaultHealthCheck.id())
            .loadBalancingScheme("EXTERNAL_MANAGED")
            .protocol("HTTPS")
            .tlsSettings(BackendServiceTlsSettingsArgs.builder()
                .sni("example.com")
                .subjectAltNames(                
                    BackendServiceTlsSettingsSubjectAltNameArgs.builder()
                        .dnsName("example.com")
                        .build(),
                    BackendServiceTlsSettingsSubjectAltNameArgs.builder()
                        .uniformResourceIdentifier("https://example.com")
                        .build())
                .authenticationConfig(defaultBackendAuthenticationConfig.id().applyValue(_id -> String.format("//networksecurity.googleapis.com/%s", _id)))
                .build())
            .build());

    }
}
resources:
  default:
    type: gcp:compute:BackendService
    properties:
      name: backend-service
      healthChecks: ${defaultHealthCheck.id}
      loadBalancingScheme: EXTERNAL_MANAGED
      protocol: HTTPS
      tlsSettings:
        sni: example.com
        subjectAltNames:
          - dnsName: example.com
          - uniformResourceIdentifier: https://example.com
        authenticationConfig: //networksecurity.googleapis.com/${defaultBackendAuthenticationConfig.id}
  defaultHealthCheck:
    type: gcp:compute:HealthCheck
    name: default
    properties:
      name: health-check
      httpHealthCheck:
        port: 80
  defaultBackendAuthenticationConfig:
    type: gcp:networksecurity:BackendAuthenticationConfig
    name: default
    properties:
      name: authentication
      wellKnownRoots: PUBLIC_ROOTS

The BackendService resource’s tlsSettings.authenticationConfig property references the authentication config by its full resource path. The sni property specifies the server name for TLS negotiation, and subjectAltNames lists acceptable identities in the backend’s certificate. The authentication config controls which roots are trusted and whether a client certificate is presented.

Beyond these examples

These snippets focus on specific authentication config features: public root CA validation, mutual TLS with custom certificates, and backend service integration. They’re intentionally minimal rather than full load balancing deployments.

The examples may reference pre-existing infrastructure such as Certificate Manager resources (TrustConfig, Certificate), health checks for backend services, and PEM certificate and key files. They focus on configuring authentication rather than provisioning the surrounding infrastructure.

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

  • Custom trust chains without public roots (wellKnownRoots: NONE)
  • Location-specific configs (defaults to global)
  • Label management and organization

These omissions are intentional: the goal is to illustrate how each authentication feature is wired, not provide drop-in load balancing modules. See the BackendAuthenticationConfig resource reference for all available configuration options.

Let's configure GCP Backend Authentication for Load Balancers

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Certificate & Trust Configuration
What's the difference between trustConfig and wellKnownRoots?
You need either trustConfig or wellKnownRoots set to PUBLIC_ROOTS. If you set wellKnownRoots to PUBLIC_ROOTS, the BackendService uses Google-managed root CAs plus any roots in trustConfig. If you set it to NONE, only the trustConfig roots are used, making trustConfig required.
What certificate scope is required for client certificates?
Certificates used for clientCertificate must have CLIENT_AUTH scope, or mTLS negotiation will fail.
When does PUBLIC_ROOTS validation actually work?
Validation with PUBLIC_ROOTS only happens when the TlsSettings.sni field is set in your BackendService configuration.
Integration & Usage
How do I reference this resource in a BackendService?
Use pulumi.interpolate with the full resource path: pulumi.interpolate//networksecurity.googleapis.com/${config.id}`` in the tlsSettings.authenticationConfig field.
Can I use this for backend mTLS?
Yes, configure clientCertificate with a Certificate resource that has CLIENT_AUTH scope. The BackendService will use this certificate when the backend requests client authentication.
What's the default location for this resource?
The default location is global if not specified.
Resource Management
What properties can't be changed after creation?
The name, project, clientCertificate, and trustConfig properties are immutable and require resource replacement if changed.
Why aren't all my labels showing up in the labels field?
The labels field is non-authoritative and only manages labels in your Pulumi configuration. Use effectiveLabels to see all labels present on the resource, including those set by other clients.

Using a different cloud?

Explore security guides for other cloud providers: