Configure GCP TLS Routes

The gcp:networkservices/tlsRoute:TlsRoute resource, part of the Pulumi GCP provider, defines TLS routing rules that match on SNI hostname and ALPN protocol, directing traffic to backend services. This guide focuses on three capabilities: SNI-based traffic routing, service mesh attachment, and gateway attachment.

TLS routes reference backend services and attach to either meshes or gateways that must exist separately. The examples are intentionally small. Combine them with your own backend services, health checks, and mesh or gateway infrastructure.

Route TLS traffic by SNI hostname

Service meshes and load balancers often route encrypted traffic based on the Server Name Indication (SNI) hostname without terminating TLS, preserving end-to-end encryption while making routing decisions.

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

const defaultHttpHealthCheck = new gcp.compute.HttpHealthCheck("default", {
    name: "backend-service-health-check",
    requestPath: "/",
    checkIntervalSec: 1,
    timeoutSec: 1,
});
const _default = new gcp.compute.BackendService("default", {
    name: "my-backend-service",
    healthChecks: defaultHttpHealthCheck.id,
});
const defaultTlsRoute = new gcp.networkservices.TlsRoute("default", {
    name: "my-tls-route",
    description: "my description",
    rules: [{
        matches: [{
            sniHosts: ["example.com"],
            alpns: ["http/1.1"],
        }],
        action: {
            destinations: [{
                serviceName: _default.id,
                weight: 1,
            }],
        },
    }],
});
import pulumi
import pulumi_gcp as gcp

default_http_health_check = gcp.compute.HttpHealthCheck("default",
    name="backend-service-health-check",
    request_path="/",
    check_interval_sec=1,
    timeout_sec=1)
default = gcp.compute.BackendService("default",
    name="my-backend-service",
    health_checks=default_http_health_check.id)
default_tls_route = gcp.networkservices.TlsRoute("default",
    name="my-tls-route",
    description="my description",
    rules=[{
        "matches": [{
            "sni_hosts": ["example.com"],
            "alpns": ["http/1.1"],
        }],
        "action": {
            "destinations": [{
                "service_name": default.id,
                "weight": 1,
            }],
        },
    }])
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		defaultHttpHealthCheck, err := compute.NewHttpHealthCheck(ctx, "default", &compute.HttpHealthCheckArgs{
			Name:             pulumi.String("backend-service-health-check"),
			RequestPath:      pulumi.String("/"),
			CheckIntervalSec: pulumi.Int(1),
			TimeoutSec:       pulumi.Int(1),
		})
		if err != nil {
			return err
		}
		_default, err := compute.NewBackendService(ctx, "default", &compute.BackendServiceArgs{
			Name:         pulumi.String("my-backend-service"),
			HealthChecks: defaultHttpHealthCheck.ID(),
		})
		if err != nil {
			return err
		}
		_, err = networkservices.NewTlsRoute(ctx, "default", &networkservices.TlsRouteArgs{
			Name:        pulumi.String("my-tls-route"),
			Description: pulumi.String("my description"),
			Rules: networkservices.TlsRouteRuleArray{
				&networkservices.TlsRouteRuleArgs{
					Matches: networkservices.TlsRouteRuleMatchArray{
						&networkservices.TlsRouteRuleMatchArgs{
							SniHosts: pulumi.StringArray{
								pulumi.String("example.com"),
							},
							Alpns: pulumi.StringArray{
								pulumi.String("http/1.1"),
							},
						},
					},
					Action: &networkservices.TlsRouteRuleActionArgs{
						Destinations: networkservices.TlsRouteRuleActionDestinationArray{
							&networkservices.TlsRouteRuleActionDestinationArgs{
								ServiceName: _default.ID(),
								Weight:      pulumi.Int(1),
							},
						},
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var defaultHttpHealthCheck = new Gcp.Compute.HttpHealthCheck("default", new()
    {
        Name = "backend-service-health-check",
        RequestPath = "/",
        CheckIntervalSec = 1,
        TimeoutSec = 1,
    });

    var @default = new Gcp.Compute.BackendService("default", new()
    {
        Name = "my-backend-service",
        HealthChecks = defaultHttpHealthCheck.Id,
    });

    var defaultTlsRoute = new Gcp.NetworkServices.TlsRoute("default", new()
    {
        Name = "my-tls-route",
        Description = "my description",
        Rules = new[]
        {
            new Gcp.NetworkServices.Inputs.TlsRouteRuleArgs
            {
                Matches = new[]
                {
                    new Gcp.NetworkServices.Inputs.TlsRouteRuleMatchArgs
                    {
                        SniHosts = new[]
                        {
                            "example.com",
                        },
                        Alpns = new[]
                        {
                            "http/1.1",
                        },
                    },
                },
                Action = new Gcp.NetworkServices.Inputs.TlsRouteRuleActionArgs
                {
                    Destinations = new[]
                    {
                        new Gcp.NetworkServices.Inputs.TlsRouteRuleActionDestinationArgs
                        {
                            ServiceName = @default.Id,
                            Weight = 1,
                        },
                    },
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.HttpHealthCheck;
import com.pulumi.gcp.compute.HttpHealthCheckArgs;
import com.pulumi.gcp.compute.BackendService;
import com.pulumi.gcp.compute.BackendServiceArgs;
import com.pulumi.gcp.networkservices.TlsRoute;
import com.pulumi.gcp.networkservices.TlsRouteArgs;
import com.pulumi.gcp.networkservices.inputs.TlsRouteRuleArgs;
import com.pulumi.gcp.networkservices.inputs.TlsRouteRuleActionArgs;
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 defaultHttpHealthCheck = new HttpHealthCheck("defaultHttpHealthCheck", HttpHealthCheckArgs.builder()
            .name("backend-service-health-check")
            .requestPath("/")
            .checkIntervalSec(1)
            .timeoutSec(1)
            .build());

        var default_ = new BackendService("default", BackendServiceArgs.builder()
            .name("my-backend-service")
            .healthChecks(defaultHttpHealthCheck.id())
            .build());

        var defaultTlsRoute = new TlsRoute("defaultTlsRoute", TlsRouteArgs.builder()
            .name("my-tls-route")
            .description("my description")
            .rules(TlsRouteRuleArgs.builder()
                .matches(TlsRouteRuleMatchArgs.builder()
                    .sniHosts("example.com")
                    .alpns("http/1.1")
                    .build())
                .action(TlsRouteRuleActionArgs.builder()
                    .destinations(TlsRouteRuleActionDestinationArgs.builder()
                        .serviceName(default_.id())
                        .weight(1)
                        .build())
                    .build())
                .build())
            .build());

    }
}
resources:
  default:
    type: gcp:compute:BackendService
    properties:
      name: my-backend-service
      healthChecks: ${defaultHttpHealthCheck.id}
  defaultHttpHealthCheck:
    type: gcp:compute:HttpHealthCheck
    name: default
    properties:
      name: backend-service-health-check
      requestPath: /
      checkIntervalSec: 1
      timeoutSec: 1
  defaultTlsRoute:
    type: gcp:networkservices:TlsRoute
    name: default
    properties:
      name: my-tls-route
      description: my description
      rules:
        - matches:
            - sniHosts:
                - example.com
              alpns:
                - http/1.1
          action:
            destinations:
              - serviceName: ${default.id}
                weight: 1

When a TLS connection arrives, the route inspects the SNI hostname in the ClientHello message. The matches array defines which sniHosts and alpns trigger this rule. The action.destinations array specifies which backend services receive matching traffic, with optional weight for traffic splitting.

Attach routes to a service mesh

Service mesh deployments use TLS routes to control how sidecar proxies route encrypted traffic between services.

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

const defaultHttpHealthCheck = new gcp.compute.HttpHealthCheck("default", {
    name: "backend-service-health-check",
    requestPath: "/",
    checkIntervalSec: 1,
    timeoutSec: 1,
});
const _default = new gcp.compute.BackendService("default", {
    name: "my-backend-service",
    healthChecks: defaultHttpHealthCheck.id,
});
const defaultMesh = new gcp.networkservices.Mesh("default", {
    name: "my-tls-route",
    labels: {
        foo: "bar",
    },
    description: "my description",
});
const defaultTlsRoute = new gcp.networkservices.TlsRoute("default", {
    name: "my-tls-route",
    description: "my description",
    meshes: [defaultMesh.id],
    rules: [{
        matches: [{
            sniHosts: ["example.com"],
            alpns: ["http/1.1"],
        }],
        action: {
            destinations: [{
                serviceName: _default.id,
                weight: 1,
            }],
        },
    }],
});
import pulumi
import pulumi_gcp as gcp

default_http_health_check = gcp.compute.HttpHealthCheck("default",
    name="backend-service-health-check",
    request_path="/",
    check_interval_sec=1,
    timeout_sec=1)
default = gcp.compute.BackendService("default",
    name="my-backend-service",
    health_checks=default_http_health_check.id)
default_mesh = gcp.networkservices.Mesh("default",
    name="my-tls-route",
    labels={
        "foo": "bar",
    },
    description="my description")
default_tls_route = gcp.networkservices.TlsRoute("default",
    name="my-tls-route",
    description="my description",
    meshes=[default_mesh.id],
    rules=[{
        "matches": [{
            "sni_hosts": ["example.com"],
            "alpns": ["http/1.1"],
        }],
        "action": {
            "destinations": [{
                "service_name": default.id,
                "weight": 1,
            }],
        },
    }])
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		defaultHttpHealthCheck, err := compute.NewHttpHealthCheck(ctx, "default", &compute.HttpHealthCheckArgs{
			Name:             pulumi.String("backend-service-health-check"),
			RequestPath:      pulumi.String("/"),
			CheckIntervalSec: pulumi.Int(1),
			TimeoutSec:       pulumi.Int(1),
		})
		if err != nil {
			return err
		}
		_default, err := compute.NewBackendService(ctx, "default", &compute.BackendServiceArgs{
			Name:         pulumi.String("my-backend-service"),
			HealthChecks: defaultHttpHealthCheck.ID(),
		})
		if err != nil {
			return err
		}
		defaultMesh, err := networkservices.NewMesh(ctx, "default", &networkservices.MeshArgs{
			Name: pulumi.String("my-tls-route"),
			Labels: pulumi.StringMap{
				"foo": pulumi.String("bar"),
			},
			Description: pulumi.String("my description"),
		})
		if err != nil {
			return err
		}
		_, err = networkservices.NewTlsRoute(ctx, "default", &networkservices.TlsRouteArgs{
			Name:        pulumi.String("my-tls-route"),
			Description: pulumi.String("my description"),
			Meshes: pulumi.StringArray{
				defaultMesh.ID(),
			},
			Rules: networkservices.TlsRouteRuleArray{
				&networkservices.TlsRouteRuleArgs{
					Matches: networkservices.TlsRouteRuleMatchArray{
						&networkservices.TlsRouteRuleMatchArgs{
							SniHosts: pulumi.StringArray{
								pulumi.String("example.com"),
							},
							Alpns: pulumi.StringArray{
								pulumi.String("http/1.1"),
							},
						},
					},
					Action: &networkservices.TlsRouteRuleActionArgs{
						Destinations: networkservices.TlsRouteRuleActionDestinationArray{
							&networkservices.TlsRouteRuleActionDestinationArgs{
								ServiceName: _default.ID(),
								Weight:      pulumi.Int(1),
							},
						},
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var defaultHttpHealthCheck = new Gcp.Compute.HttpHealthCheck("default", new()
    {
        Name = "backend-service-health-check",
        RequestPath = "/",
        CheckIntervalSec = 1,
        TimeoutSec = 1,
    });

    var @default = new Gcp.Compute.BackendService("default", new()
    {
        Name = "my-backend-service",
        HealthChecks = defaultHttpHealthCheck.Id,
    });

    var defaultMesh = new Gcp.NetworkServices.Mesh("default", new()
    {
        Name = "my-tls-route",
        Labels = 
        {
            { "foo", "bar" },
        },
        Description = "my description",
    });

    var defaultTlsRoute = new Gcp.NetworkServices.TlsRoute("default", new()
    {
        Name = "my-tls-route",
        Description = "my description",
        Meshes = new[]
        {
            defaultMesh.Id,
        },
        Rules = new[]
        {
            new Gcp.NetworkServices.Inputs.TlsRouteRuleArgs
            {
                Matches = new[]
                {
                    new Gcp.NetworkServices.Inputs.TlsRouteRuleMatchArgs
                    {
                        SniHosts = new[]
                        {
                            "example.com",
                        },
                        Alpns = new[]
                        {
                            "http/1.1",
                        },
                    },
                },
                Action = new Gcp.NetworkServices.Inputs.TlsRouteRuleActionArgs
                {
                    Destinations = new[]
                    {
                        new Gcp.NetworkServices.Inputs.TlsRouteRuleActionDestinationArgs
                        {
                            ServiceName = @default.Id,
                            Weight = 1,
                        },
                    },
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.HttpHealthCheck;
import com.pulumi.gcp.compute.HttpHealthCheckArgs;
import com.pulumi.gcp.compute.BackendService;
import com.pulumi.gcp.compute.BackendServiceArgs;
import com.pulumi.gcp.networkservices.Mesh;
import com.pulumi.gcp.networkservices.MeshArgs;
import com.pulumi.gcp.networkservices.TlsRoute;
import com.pulumi.gcp.networkservices.TlsRouteArgs;
import com.pulumi.gcp.networkservices.inputs.TlsRouteRuleArgs;
import com.pulumi.gcp.networkservices.inputs.TlsRouteRuleActionArgs;
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 defaultHttpHealthCheck = new HttpHealthCheck("defaultHttpHealthCheck", HttpHealthCheckArgs.builder()
            .name("backend-service-health-check")
            .requestPath("/")
            .checkIntervalSec(1)
            .timeoutSec(1)
            .build());

        var default_ = new BackendService("default", BackendServiceArgs.builder()
            .name("my-backend-service")
            .healthChecks(defaultHttpHealthCheck.id())
            .build());

        var defaultMesh = new Mesh("defaultMesh", MeshArgs.builder()
            .name("my-tls-route")
            .labels(Map.of("foo", "bar"))
            .description("my description")
            .build());

        var defaultTlsRoute = new TlsRoute("defaultTlsRoute", TlsRouteArgs.builder()
            .name("my-tls-route")
            .description("my description")
            .meshes(defaultMesh.id())
            .rules(TlsRouteRuleArgs.builder()
                .matches(TlsRouteRuleMatchArgs.builder()
                    .sniHosts("example.com")
                    .alpns("http/1.1")
                    .build())
                .action(TlsRouteRuleActionArgs.builder()
                    .destinations(TlsRouteRuleActionDestinationArgs.builder()
                        .serviceName(default_.id())
                        .weight(1)
                        .build())
                    .build())
                .build())
            .build());

    }
}
resources:
  default:
    type: gcp:compute:BackendService
    properties:
      name: my-backend-service
      healthChecks: ${defaultHttpHealthCheck.id}
  defaultHttpHealthCheck:
    type: gcp:compute:HttpHealthCheck
    name: default
    properties:
      name: backend-service-health-check
      requestPath: /
      checkIntervalSec: 1
      timeoutSec: 1
  defaultMesh:
    type: gcp:networkservices:Mesh
    name: default
    properties:
      name: my-tls-route
      labels:
        foo: bar
      description: my description
  defaultTlsRoute:
    type: gcp:networkservices:TlsRoute
    name: default
    properties:
      name: my-tls-route
      description: my description
      meshes:
        - ${defaultMesh.id}
      rules:
        - matches:
            - sniHosts:
                - example.com
              alpns:
                - http/1.1
          action:
            destinations:
              - serviceName: ${default.id}
                weight: 1

The meshes property attaches this route to a service mesh, making the routing rules available to all sidecar proxies in that mesh. The mesh must be of type SIDECAR. This extends the basic SNI routing pattern by distributing rules mesh-wide.

Attach routes to an ingress gateway

Gateway deployments use TLS routes to control how ingress traffic enters the mesh at the boundary.

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

const defaultHttpHealthCheck = new gcp.compute.HttpHealthCheck("default", {
    name: "backend-service-health-check",
    requestPath: "/",
    checkIntervalSec: 1,
    timeoutSec: 1,
});
const _default = new gcp.compute.BackendService("default", {
    name: "my-backend-service",
    healthChecks: defaultHttpHealthCheck.id,
});
const defaultGateway = new gcp.networkservices.Gateway("default", {
    name: "my-tls-route",
    labels: {
        foo: "bar",
    },
    description: "my description",
    scope: "my-scope",
    type: "OPEN_MESH",
    ports: [443],
});
const defaultTlsRoute = new gcp.networkservices.TlsRoute("default", {
    name: "my-tls-route",
    description: "my description",
    gateways: [defaultGateway.id],
    rules: [{
        matches: [{
            sniHosts: ["example.com"],
            alpns: ["http/1.1"],
        }],
        action: {
            destinations: [{
                serviceName: _default.id,
                weight: 1,
            }],
        },
    }],
});
import pulumi
import pulumi_gcp as gcp

default_http_health_check = gcp.compute.HttpHealthCheck("default",
    name="backend-service-health-check",
    request_path="/",
    check_interval_sec=1,
    timeout_sec=1)
default = gcp.compute.BackendService("default",
    name="my-backend-service",
    health_checks=default_http_health_check.id)
default_gateway = gcp.networkservices.Gateway("default",
    name="my-tls-route",
    labels={
        "foo": "bar",
    },
    description="my description",
    scope="my-scope",
    type="OPEN_MESH",
    ports=[443])
default_tls_route = gcp.networkservices.TlsRoute("default",
    name="my-tls-route",
    description="my description",
    gateways=[default_gateway.id],
    rules=[{
        "matches": [{
            "sni_hosts": ["example.com"],
            "alpns": ["http/1.1"],
        }],
        "action": {
            "destinations": [{
                "service_name": default.id,
                "weight": 1,
            }],
        },
    }])
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		defaultHttpHealthCheck, err := compute.NewHttpHealthCheck(ctx, "default", &compute.HttpHealthCheckArgs{
			Name:             pulumi.String("backend-service-health-check"),
			RequestPath:      pulumi.String("/"),
			CheckIntervalSec: pulumi.Int(1),
			TimeoutSec:       pulumi.Int(1),
		})
		if err != nil {
			return err
		}
		_default, err := compute.NewBackendService(ctx, "default", &compute.BackendServiceArgs{
			Name:         pulumi.String("my-backend-service"),
			HealthChecks: defaultHttpHealthCheck.ID(),
		})
		if err != nil {
			return err
		}
		defaultGateway, err := networkservices.NewGateway(ctx, "default", &networkservices.GatewayArgs{
			Name: pulumi.String("my-tls-route"),
			Labels: pulumi.StringMap{
				"foo": pulumi.String("bar"),
			},
			Description: pulumi.String("my description"),
			Scope:       pulumi.String("my-scope"),
			Type:        pulumi.String("OPEN_MESH"),
			Ports: pulumi.IntArray{
				pulumi.Int(443),
			},
		})
		if err != nil {
			return err
		}
		_, err = networkservices.NewTlsRoute(ctx, "default", &networkservices.TlsRouteArgs{
			Name:        pulumi.String("my-tls-route"),
			Description: pulumi.String("my description"),
			Gateways: pulumi.StringArray{
				defaultGateway.ID(),
			},
			Rules: networkservices.TlsRouteRuleArray{
				&networkservices.TlsRouteRuleArgs{
					Matches: networkservices.TlsRouteRuleMatchArray{
						&networkservices.TlsRouteRuleMatchArgs{
							SniHosts: pulumi.StringArray{
								pulumi.String("example.com"),
							},
							Alpns: pulumi.StringArray{
								pulumi.String("http/1.1"),
							},
						},
					},
					Action: &networkservices.TlsRouteRuleActionArgs{
						Destinations: networkservices.TlsRouteRuleActionDestinationArray{
							&networkservices.TlsRouteRuleActionDestinationArgs{
								ServiceName: _default.ID(),
								Weight:      pulumi.Int(1),
							},
						},
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var defaultHttpHealthCheck = new Gcp.Compute.HttpHealthCheck("default", new()
    {
        Name = "backend-service-health-check",
        RequestPath = "/",
        CheckIntervalSec = 1,
        TimeoutSec = 1,
    });

    var @default = new Gcp.Compute.BackendService("default", new()
    {
        Name = "my-backend-service",
        HealthChecks = defaultHttpHealthCheck.Id,
    });

    var defaultGateway = new Gcp.NetworkServices.Gateway("default", new()
    {
        Name = "my-tls-route",
        Labels = 
        {
            { "foo", "bar" },
        },
        Description = "my description",
        Scope = "my-scope",
        Type = "OPEN_MESH",
        Ports = new[]
        {
            443,
        },
    });

    var defaultTlsRoute = new Gcp.NetworkServices.TlsRoute("default", new()
    {
        Name = "my-tls-route",
        Description = "my description",
        Gateways = new[]
        {
            defaultGateway.Id,
        },
        Rules = new[]
        {
            new Gcp.NetworkServices.Inputs.TlsRouteRuleArgs
            {
                Matches = new[]
                {
                    new Gcp.NetworkServices.Inputs.TlsRouteRuleMatchArgs
                    {
                        SniHosts = new[]
                        {
                            "example.com",
                        },
                        Alpns = new[]
                        {
                            "http/1.1",
                        },
                    },
                },
                Action = new Gcp.NetworkServices.Inputs.TlsRouteRuleActionArgs
                {
                    Destinations = new[]
                    {
                        new Gcp.NetworkServices.Inputs.TlsRouteRuleActionDestinationArgs
                        {
                            ServiceName = @default.Id,
                            Weight = 1,
                        },
                    },
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.HttpHealthCheck;
import com.pulumi.gcp.compute.HttpHealthCheckArgs;
import com.pulumi.gcp.compute.BackendService;
import com.pulumi.gcp.compute.BackendServiceArgs;
import com.pulumi.gcp.networkservices.Gateway;
import com.pulumi.gcp.networkservices.GatewayArgs;
import com.pulumi.gcp.networkservices.TlsRoute;
import com.pulumi.gcp.networkservices.TlsRouteArgs;
import com.pulumi.gcp.networkservices.inputs.TlsRouteRuleArgs;
import com.pulumi.gcp.networkservices.inputs.TlsRouteRuleActionArgs;
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 defaultHttpHealthCheck = new HttpHealthCheck("defaultHttpHealthCheck", HttpHealthCheckArgs.builder()
            .name("backend-service-health-check")
            .requestPath("/")
            .checkIntervalSec(1)
            .timeoutSec(1)
            .build());

        var default_ = new BackendService("default", BackendServiceArgs.builder()
            .name("my-backend-service")
            .healthChecks(defaultHttpHealthCheck.id())
            .build());

        var defaultGateway = new Gateway("defaultGateway", GatewayArgs.builder()
            .name("my-tls-route")
            .labels(Map.of("foo", "bar"))
            .description("my description")
            .scope("my-scope")
            .type("OPEN_MESH")
            .ports(443)
            .build());

        var defaultTlsRoute = new TlsRoute("defaultTlsRoute", TlsRouteArgs.builder()
            .name("my-tls-route")
            .description("my description")
            .gateways(defaultGateway.id())
            .rules(TlsRouteRuleArgs.builder()
                .matches(TlsRouteRuleMatchArgs.builder()
                    .sniHosts("example.com")
                    .alpns("http/1.1")
                    .build())
                .action(TlsRouteRuleActionArgs.builder()
                    .destinations(TlsRouteRuleActionDestinationArgs.builder()
                        .serviceName(default_.id())
                        .weight(1)
                        .build())
                    .build())
                .build())
            .build());

    }
}
resources:
  default:
    type: gcp:compute:BackendService
    properties:
      name: my-backend-service
      healthChecks: ${defaultHttpHealthCheck.id}
  defaultHttpHealthCheck:
    type: gcp:compute:HttpHealthCheck
    name: default
    properties:
      name: backend-service-health-check
      requestPath: /
      checkIntervalSec: 1
      timeoutSec: 1
  defaultGateway:
    type: gcp:networkservices:Gateway
    name: default
    properties:
      name: my-tls-route
      labels:
        foo: bar
      description: my description
      scope: my-scope
      type: OPEN_MESH
      ports:
        - 443
  defaultTlsRoute:
    type: gcp:networkservices:TlsRoute
    name: default
    properties:
      name: my-tls-route
      description: my description
      gateways:
        - ${defaultGateway.id}
      rules:
        - matches:
            - sniHosts:
                - example.com
              alpns:
                - http/1.1
          action:
            destinations:
              - serviceName: ${default.id}
                weight: 1

The gateways property attaches this route to an ingress gateway instead of a mesh. The gateway must be configured with appropriate ports (typically 443 for TLS) and type. This provides an alternative to mesh attachment for ingress scenarios.

Beyond these examples

These snippets focus on specific TLS route features: SNI-based routing with ALPN protocol selection, and mesh and gateway attachment. They’re intentionally minimal rather than full traffic management configurations.

The examples reference pre-existing infrastructure such as Compute Engine backend services with health checks, and Network Services meshes or gateways for attachment examples. They focus on configuring the route rather than provisioning the complete infrastructure.

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

  • Multiple routing rules with different match criteria
  • Weighted traffic splitting across destinations
  • Route labels and metadata

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

Let's configure GCP TLS Routes

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Configuration & Attachment
What's the difference between attaching to gateways vs meshes?
Gateways and meshes are two different attachment options for TlsRoute. Gateways use the pattern projects/*/locations/global/gateways/<gateway_name>, while meshes use projects/*/locations/global/meshes/<mesh_name>. The examples show you can attach to either, or use the route standalone without attachment.
What type of mesh can I attach to a TlsRoute?
Only meshes of type SIDECAR can be attached to TlsRoute.
Can I attach a TlsRoute to both a gateway and a mesh?
The schema allows both gateways and meshes properties, but the examples show them used separately. Each example demonstrates one attachment pattern: standalone, mesh-only, or gateway-only.
Routing & Traffic Matching
How do I route traffic based on SNI hostname?
Configure rules with matches containing sniHosts (e.g., ["example.com"]) and specify destinations with a serviceName and weight in the action.
What is the ALPN property used for?
The alpns property in matches allows you to match traffic based on Application-Layer Protocol Negotiation values (e.g., ["http/1.1"]).
How do I distribute traffic across multiple backend services?
Use the weight property in destinations to control traffic distribution. Each destination specifies a serviceName and weight.
Resource Management
What properties can't I change after creating a TlsRoute?
The name and project properties are immutable and will force resource replacement if changed.
What backend services can I use as destinations?
The examples show gcp.compute.BackendService used as destinations via the serviceName property.

Using a different cloud?

Explore networking guides for other cloud providers: