Configure GCP Backend Authentication for Load Balancers

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

Authentication configs reference Certificate Manager resources and are consumed by backend services through their tlsSettings blocks. The examples are intentionally small. Combine them with your own certificates, trust configurations, and backend infrastructure.

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 a managed set of trusted CAs.

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 backends signed by Google’s managed set of public certificate authorities. This avoids maintaining custom trust configurations when backends use publicly trusted certificates.

Authenticate with mutual TLS using custom trust anchors

Applications requiring mutual TLS need both a client certificate for the load balancer to present and a trust configuration to validate backend certificates.

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 clientCertificate property references a Certificate Manager certificate with CLIENT_AUTH scope that the load balancer presents to backends. The trustConfig property points to a TrustConfig resource containing custom trust anchors and intermediate CAs. Together with wellKnownRoots set to PUBLIC_ROOTS, the load balancer validates backends against both custom and public CAs.

Connect a backend service to authentication config

Backend services reference authentication configs through their tlsSettings block to control TLS validation and authentication.

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 tlsSettings.authenticationConfig property links the backend service to the authentication config using its full resource path. The sni property specifies the server name for TLS negotiation, and subjectAltNames defines acceptable certificate identities. The backend service uses the authentication config’s validation rules when connecting to backends.

Beyond these examples

These snippets focus on specific authentication config features: public root CA validation, mutual TLS with custom trust anchors, 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 API enabled, PEM certificate and key files for mTLS, and health checks for backend services. They focus on configuring authentication rather than provisioning the complete load balancing stack.

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

  • Custom trust configuration without public roots (wellKnownRoots: NONE)
  • Location-specific deployments (defaults to global)
  • Label management and resource tagging

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
When do I need to provide a trustConfig?
You must provide trustConfig unless wellKnownRoots is set to PUBLIC_ROOTS. The trustConfig references a TrustConfig resource that defines the chain of trust for validating backend server certificates.
What's the difference between NONE and PUBLIC_ROOTS for wellKnownRoots?
When set to NONE, the BackendService only validates server certificates against roots specified in trustConfig. When set to PUBLIC_ROOTS, it uses Google-managed public roots in addition to any roots in trustConfig.
When does PUBLIC_ROOTS validation actually work?
Validation with PUBLIC_ROOTS only occurs when the TlsSettings.sni field is set in the BackendService. Without SNI configured, PUBLIC_ROOTS validation won’t be applied.
Can Google change the PUBLIC_ROOTS certificate authorities?
Yes, CAs in the PUBLIC_ROOTS set are managed by Google and can be added or removed without notice.
What scope does my clientCertificate need?
The clientCertificate must have a CLIENT_AUTH scope. It’s used by the BackendService to negotiate mTLS when the backend requests a client certificate.
Integration & Usage
How do I reference this config in a BackendService?
Use pulumi.interpolate to construct the full resource path: pulumi.interpolate//networksecurity.googleapis.com/${config.id}`` and assign it to tlsSettings.authenticationConfig in your BackendService.
Resource Management
What properties can't I change after creation?
The following properties are immutable: name, project, clientCertificate, and trustConfig. You’ll need to recreate the resource to change these values.
Why aren't all my labels showing up in the labels field?
The labels field is non-authoritative and only manages labels present in your configuration. Use effectiveLabels to see all labels on the resource, including those set by other clients or services.
What's the default location for this resource?
The default location is global if not specified.

Using a different cloud?

Explore security guides for other cloud providers: