Configure GCP Network Services Gateways

The gcp:networkservices/gateway:Gateway resource, part of the Pulumi GCP provider, defines a Network Services Gateway that routes traffic through a proxy or load balancer, capturing IP addresses, ports, and policy configurations. This guide focuses on three capabilities: open mesh gateways for service routing, secure web gateways with TLS and policy enforcement, and VPC network integration.

Gateway types determine infrastructure requirements. Open mesh gateways operate independently, while secure web gateways require VPC networks, subnets, certificates, and security policies. The examples are intentionally small. Combine them with your own network infrastructure and security policies.

Create an open mesh gateway for service routing

Service mesh deployments route traffic across multiple services without VPC-specific configuration. Open mesh gateways listen on all interfaces and merge configurations across instances sharing the same scope.

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

const _default = new gcp.networkservices.Gateway("default", {
    name: "my-gateway",
    scope: "default-scope-basic",
    type: "OPEN_MESH",
    ports: [443],
});
import pulumi
import pulumi_gcp as gcp

default = gcp.networkservices.Gateway("default",
    name="my-gateway",
    scope="default-scope-basic",
    type="OPEN_MESH",
    ports=[443])
package main

import (
	"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 {
		_, err := networkservices.NewGateway(ctx, "default", &networkservices.GatewayArgs{
			Name:  pulumi.String("my-gateway"),
			Scope: pulumi.String("default-scope-basic"),
			Type:  pulumi.String("OPEN_MESH"),
			Ports: pulumi.IntArray{
				pulumi.Int(443),
			},
		})
		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.NetworkServices.Gateway("default", new()
    {
        Name = "my-gateway",
        Scope = "default-scope-basic",
        Type = "OPEN_MESH",
        Ports = new[]
        {
            443,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.networkservices.Gateway;
import com.pulumi.gcp.networkservices.GatewayArgs;
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 Gateway("default", GatewayArgs.builder()
            .name("my-gateway")
            .scope("default-scope-basic")
            .type("OPEN_MESH")
            .ports(443)
            .build());

    }
}
resources:
  default:
    type: gcp:networkservices:Gateway
    properties:
      name: my-gateway
      scope: default-scope-basic
      type: OPEN_MESH
      ports:
        - 443

The type property set to OPEN_MESH creates a gateway that listens on 0.0.0.0 for IPv4 and :: for IPv6. The scope property determines how configurations merge: gateways with the same scope present as a single configuration to the proxy. The ports array specifies which ports receive traffic.

Add labels and descriptions for organization

Production gateways benefit from metadata for tracking ownership, environment, and cost allocation.

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

const _default = new gcp.networkservices.Gateway("default", {
    name: "my-gateway",
    labels: {
        foo: "bar",
    },
    description: "my description",
    type: "OPEN_MESH",
    ports: [443],
    scope: "default-scope-advance",
});
import pulumi
import pulumi_gcp as gcp

default = gcp.networkservices.Gateway("default",
    name="my-gateway",
    labels={
        "foo": "bar",
    },
    description="my description",
    type="OPEN_MESH",
    ports=[443],
    scope="default-scope-advance")
package main

import (
	"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 {
		_, err := networkservices.NewGateway(ctx, "default", &networkservices.GatewayArgs{
			Name: pulumi.String("my-gateway"),
			Labels: pulumi.StringMap{
				"foo": pulumi.String("bar"),
			},
			Description: pulumi.String("my description"),
			Type:        pulumi.String("OPEN_MESH"),
			Ports: pulumi.IntArray{
				pulumi.Int(443),
			},
			Scope: pulumi.String("default-scope-advance"),
		})
		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.NetworkServices.Gateway("default", new()
    {
        Name = "my-gateway",
        Labels = 
        {
            { "foo", "bar" },
        },
        Description = "my description",
        Type = "OPEN_MESH",
        Ports = new[]
        {
            443,
        },
        Scope = "default-scope-advance",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.networkservices.Gateway;
import com.pulumi.gcp.networkservices.GatewayArgs;
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 Gateway("default", GatewayArgs.builder()
            .name("my-gateway")
            .labels(Map.of("foo", "bar"))
            .description("my description")
            .type("OPEN_MESH")
            .ports(443)
            .scope("default-scope-advance")
            .build());

    }
}
resources:
  default:
    type: gcp:networkservices:Gateway
    properties:
      name: my-gateway
      labels:
        foo: bar
      description: my description
      type: OPEN_MESH
      ports:
        - 443
      scope: default-scope-advance

The labels property adds key-value pairs for organization and cost tracking. The description property provides human-readable context. These fields don’t affect runtime behavior but help teams manage multiple gateways.

Deploy a secure web gateway with TLS and policy enforcement

Organizations that inspect and control outbound traffic deploy secure web gateways that terminate TLS, apply security policies, and route through specific VPC networks.

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: "my-certificate",
    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),
    },
});
const defaultNetwork = new gcp.compute.Network("default", {
    name: "my-network",
    routingMode: "REGIONAL",
    autoCreateSubnetworks: false,
});
const defaultSubnetwork = new gcp.compute.Subnetwork("default", {
    name: "my-subnetwork-name",
    purpose: "PRIVATE",
    ipCidrRange: "10.128.0.0/20",
    region: "us-central1",
    network: defaultNetwork.id,
    role: "ACTIVE",
});
const proxyonlysubnet = new gcp.compute.Subnetwork("proxyonlysubnet", {
    name: "my-proxy-only-subnetwork",
    purpose: "REGIONAL_MANAGED_PROXY",
    ipCidrRange: "192.168.0.0/23",
    region: "us-central1",
    network: defaultNetwork.id,
    role: "ACTIVE",
});
const defaultGatewaySecurityPolicy = new gcp.networksecurity.GatewaySecurityPolicy("default", {
    name: "my-policy-name",
    location: "us-central1",
});
const defaultGatewaySecurityPolicyRule = new gcp.networksecurity.GatewaySecurityPolicyRule("default", {
    name: "my-policyrule-name",
    location: "us-central1",
    gatewaySecurityPolicy: defaultGatewaySecurityPolicy.name,
    enabled: true,
    priority: 1,
    sessionMatcher: "host() == 'example.com'",
    basicProfile: "ALLOW",
});
const defaultGateway = new gcp.networkservices.Gateway("default", {
    name: "my-gateway1",
    location: "us-central1",
    addresses: ["10.128.0.99"],
    type: "SECURE_WEB_GATEWAY",
    ports: [443],
    scope: "my-default-scope1",
    certificateUrls: [_default.id],
    gatewaySecurityPolicy: defaultGatewaySecurityPolicy.id,
    network: defaultNetwork.id,
    subnetwork: defaultSubnetwork.id,
    deleteSwgAutogenRouterOnDestroy: true,
}, {
    dependsOn: [proxyonlysubnet],
});
import pulumi
import pulumi_gcp as gcp
import pulumi_std as std

default = gcp.certificatemanager.Certificate("default",
    name="my-certificate",
    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,
    })
default_network = gcp.compute.Network("default",
    name="my-network",
    routing_mode="REGIONAL",
    auto_create_subnetworks=False)
default_subnetwork = gcp.compute.Subnetwork("default",
    name="my-subnetwork-name",
    purpose="PRIVATE",
    ip_cidr_range="10.128.0.0/20",
    region="us-central1",
    network=default_network.id,
    role="ACTIVE")
proxyonlysubnet = gcp.compute.Subnetwork("proxyonlysubnet",
    name="my-proxy-only-subnetwork",
    purpose="REGIONAL_MANAGED_PROXY",
    ip_cidr_range="192.168.0.0/23",
    region="us-central1",
    network=default_network.id,
    role="ACTIVE")
default_gateway_security_policy = gcp.networksecurity.GatewaySecurityPolicy("default",
    name="my-policy-name",
    location="us-central1")
default_gateway_security_policy_rule = gcp.networksecurity.GatewaySecurityPolicyRule("default",
    name="my-policyrule-name",
    location="us-central1",
    gateway_security_policy=default_gateway_security_policy.name,
    enabled=True,
    priority=1,
    session_matcher="host() == 'example.com'",
    basic_profile="ALLOW")
default_gateway = gcp.networkservices.Gateway("default",
    name="my-gateway1",
    location="us-central1",
    addresses=["10.128.0.99"],
    type="SECURE_WEB_GATEWAY",
    ports=[443],
    scope="my-default-scope1",
    certificate_urls=[default.id],
    gateway_security_policy=default_gateway_security_policy.id,
    network=default_network.id,
    subnetwork=default_subnetwork.id,
    delete_swg_autogen_router_on_destroy=True,
    opts = pulumi.ResourceOptions(depends_on=[proxyonlysubnet]))
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/certificatemanager"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/networksecurity"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/networkservices"
	"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
		}
		_default, err := certificatemanager.NewCertificate(ctx, "default", &certificatemanager.CertificateArgs{
			Name:     pulumi.String("my-certificate"),
			Location: pulumi.String("us-central1"),
			SelfManaged: &certificatemanager.CertificateSelfManagedArgs{
				PemCertificate: pulumi.String(invokeFile.Result),
				PemPrivateKey:  pulumi.String(invokeFile1.Result),
			},
		})
		if err != nil {
			return err
		}
		defaultNetwork, err := compute.NewNetwork(ctx, "default", &compute.NetworkArgs{
			Name:                  pulumi.String("my-network"),
			RoutingMode:           pulumi.String("REGIONAL"),
			AutoCreateSubnetworks: pulumi.Bool(false),
		})
		if err != nil {
			return err
		}
		defaultSubnetwork, err := compute.NewSubnetwork(ctx, "default", &compute.SubnetworkArgs{
			Name:        pulumi.String("my-subnetwork-name"),
			Purpose:     pulumi.String("PRIVATE"),
			IpCidrRange: pulumi.String("10.128.0.0/20"),
			Region:      pulumi.String("us-central1"),
			Network:     defaultNetwork.ID(),
			Role:        pulumi.String("ACTIVE"),
		})
		if err != nil {
			return err
		}
		proxyonlysubnet, err := compute.NewSubnetwork(ctx, "proxyonlysubnet", &compute.SubnetworkArgs{
			Name:        pulumi.String("my-proxy-only-subnetwork"),
			Purpose:     pulumi.String("REGIONAL_MANAGED_PROXY"),
			IpCidrRange: pulumi.String("192.168.0.0/23"),
			Region:      pulumi.String("us-central1"),
			Network:     defaultNetwork.ID(),
			Role:        pulumi.String("ACTIVE"),
		})
		if err != nil {
			return err
		}
		defaultGatewaySecurityPolicy, err := networksecurity.NewGatewaySecurityPolicy(ctx, "default", &networksecurity.GatewaySecurityPolicyArgs{
			Name:     pulumi.String("my-policy-name"),
			Location: pulumi.String("us-central1"),
		})
		if err != nil {
			return err
		}
		_, err = networksecurity.NewGatewaySecurityPolicyRule(ctx, "default", &networksecurity.GatewaySecurityPolicyRuleArgs{
			Name:                  pulumi.String("my-policyrule-name"),
			Location:              pulumi.String("us-central1"),
			GatewaySecurityPolicy: defaultGatewaySecurityPolicy.Name,
			Enabled:               pulumi.Bool(true),
			Priority:              pulumi.Int(1),
			SessionMatcher:        pulumi.String("host() == 'example.com'"),
			BasicProfile:          pulumi.String("ALLOW"),
		})
		if err != nil {
			return err
		}
		_, err = networkservices.NewGateway(ctx, "default", &networkservices.GatewayArgs{
			Name:     pulumi.String("my-gateway1"),
			Location: pulumi.String("us-central1"),
			Addresses: pulumi.StringArray{
				pulumi.String("10.128.0.99"),
			},
			Type: pulumi.String("SECURE_WEB_GATEWAY"),
			Ports: pulumi.IntArray{
				pulumi.Int(443),
			},
			Scope: pulumi.String("my-default-scope1"),
			CertificateUrls: pulumi.StringArray{
				_default.ID(),
			},
			GatewaySecurityPolicy:           defaultGatewaySecurityPolicy.ID(),
			Network:                         defaultNetwork.ID(),
			Subnetwork:                      defaultSubnetwork.ID(),
			DeleteSwgAutogenRouterOnDestroy: pulumi.Bool(true),
		}, pulumi.DependsOn([]pulumi.Resource{
			proxyonlysubnet,
		}))
		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 = "my-certificate",
        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),
        },
    });

    var defaultNetwork = new Gcp.Compute.Network("default", new()
    {
        Name = "my-network",
        RoutingMode = "REGIONAL",
        AutoCreateSubnetworks = false,
    });

    var defaultSubnetwork = new Gcp.Compute.Subnetwork("default", new()
    {
        Name = "my-subnetwork-name",
        Purpose = "PRIVATE",
        IpCidrRange = "10.128.0.0/20",
        Region = "us-central1",
        Network = defaultNetwork.Id,
        Role = "ACTIVE",
    });

    var proxyonlysubnet = new Gcp.Compute.Subnetwork("proxyonlysubnet", new()
    {
        Name = "my-proxy-only-subnetwork",
        Purpose = "REGIONAL_MANAGED_PROXY",
        IpCidrRange = "192.168.0.0/23",
        Region = "us-central1",
        Network = defaultNetwork.Id,
        Role = "ACTIVE",
    });

    var defaultGatewaySecurityPolicy = new Gcp.NetworkSecurity.GatewaySecurityPolicy("default", new()
    {
        Name = "my-policy-name",
        Location = "us-central1",
    });

    var defaultGatewaySecurityPolicyRule = new Gcp.NetworkSecurity.GatewaySecurityPolicyRule("default", new()
    {
        Name = "my-policyrule-name",
        Location = "us-central1",
        GatewaySecurityPolicy = defaultGatewaySecurityPolicy.Name,
        Enabled = true,
        Priority = 1,
        SessionMatcher = "host() == 'example.com'",
        BasicProfile = "ALLOW",
    });

    var defaultGateway = new Gcp.NetworkServices.Gateway("default", new()
    {
        Name = "my-gateway1",
        Location = "us-central1",
        Addresses = new[]
        {
            "10.128.0.99",
        },
        Type = "SECURE_WEB_GATEWAY",
        Ports = new[]
        {
            443,
        },
        Scope = "my-default-scope1",
        CertificateUrls = new[]
        {
            @default.Id,
        },
        GatewaySecurityPolicy = defaultGatewaySecurityPolicy.Id,
        Network = defaultNetwork.Id,
        Subnetwork = defaultSubnetwork.Id,
        DeleteSwgAutogenRouterOnDestroy = true,
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            proxyonlysubnet,
        },
    });

});
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.compute.Network;
import com.pulumi.gcp.compute.NetworkArgs;
import com.pulumi.gcp.compute.Subnetwork;
import com.pulumi.gcp.compute.SubnetworkArgs;
import com.pulumi.gcp.networksecurity.GatewaySecurityPolicy;
import com.pulumi.gcp.networksecurity.GatewaySecurityPolicyArgs;
import com.pulumi.gcp.networksecurity.GatewaySecurityPolicyRule;
import com.pulumi.gcp.networksecurity.GatewaySecurityPolicyRuleArgs;
import com.pulumi.gcp.networkservices.Gateway;
import com.pulumi.gcp.networkservices.GatewayArgs;
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 default_ = new Certificate("default", CertificateArgs.builder()
            .name("my-certificate")
            .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());

        var defaultNetwork = new Network("defaultNetwork", NetworkArgs.builder()
            .name("my-network")
            .routingMode("REGIONAL")
            .autoCreateSubnetworks(false)
            .build());

        var defaultSubnetwork = new Subnetwork("defaultSubnetwork", SubnetworkArgs.builder()
            .name("my-subnetwork-name")
            .purpose("PRIVATE")
            .ipCidrRange("10.128.0.0/20")
            .region("us-central1")
            .network(defaultNetwork.id())
            .role("ACTIVE")
            .build());

        var proxyonlysubnet = new Subnetwork("proxyonlysubnet", SubnetworkArgs.builder()
            .name("my-proxy-only-subnetwork")
            .purpose("REGIONAL_MANAGED_PROXY")
            .ipCidrRange("192.168.0.0/23")
            .region("us-central1")
            .network(defaultNetwork.id())
            .role("ACTIVE")
            .build());

        var defaultGatewaySecurityPolicy = new GatewaySecurityPolicy("defaultGatewaySecurityPolicy", GatewaySecurityPolicyArgs.builder()
            .name("my-policy-name")
            .location("us-central1")
            .build());

        var defaultGatewaySecurityPolicyRule = new GatewaySecurityPolicyRule("defaultGatewaySecurityPolicyRule", GatewaySecurityPolicyRuleArgs.builder()
            .name("my-policyrule-name")
            .location("us-central1")
            .gatewaySecurityPolicy(defaultGatewaySecurityPolicy.name())
            .enabled(true)
            .priority(1)
            .sessionMatcher("host() == 'example.com'")
            .basicProfile("ALLOW")
            .build());

        var defaultGateway = new Gateway("defaultGateway", GatewayArgs.builder()
            .name("my-gateway1")
            .location("us-central1")
            .addresses("10.128.0.99")
            .type("SECURE_WEB_GATEWAY")
            .ports(443)
            .scope("my-default-scope1")
            .certificateUrls(default_.id())
            .gatewaySecurityPolicy(defaultGatewaySecurityPolicy.id())
            .network(defaultNetwork.id())
            .subnetwork(defaultSubnetwork.id())
            .deleteSwgAutogenRouterOnDestroy(true)
            .build(), CustomResourceOptions.builder()
                .dependsOn(proxyonlysubnet)
                .build());

    }
}
resources:
  default:
    type: gcp:certificatemanager:Certificate
    properties:
      name: my-certificate
      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
  defaultNetwork:
    type: gcp:compute:Network
    name: default
    properties:
      name: my-network
      routingMode: REGIONAL
      autoCreateSubnetworks: false
  defaultSubnetwork:
    type: gcp:compute:Subnetwork
    name: default
    properties:
      name: my-subnetwork-name
      purpose: PRIVATE
      ipCidrRange: 10.128.0.0/20
      region: us-central1
      network: ${defaultNetwork.id}
      role: ACTIVE
  proxyonlysubnet:
    type: gcp:compute:Subnetwork
    properties:
      name: my-proxy-only-subnetwork
      purpose: REGIONAL_MANAGED_PROXY
      ipCidrRange: 192.168.0.0/23
      region: us-central1
      network: ${defaultNetwork.id}
      role: ACTIVE
  defaultGatewaySecurityPolicy:
    type: gcp:networksecurity:GatewaySecurityPolicy
    name: default
    properties:
      name: my-policy-name
      location: us-central1
  defaultGatewaySecurityPolicyRule:
    type: gcp:networksecurity:GatewaySecurityPolicyRule
    name: default
    properties:
      name: my-policyrule-name
      location: us-central1
      gatewaySecurityPolicy: ${defaultGatewaySecurityPolicy.name}
      enabled: true
      priority: 1
      sessionMatcher: host() == 'example.com'
      basicProfile: ALLOW
  defaultGateway:
    type: gcp:networkservices:Gateway
    name: default
    properties:
      name: my-gateway1
      location: us-central1
      addresses:
        - 10.128.0.99
      type: SECURE_WEB_GATEWAY
      ports:
        - 443
      scope: my-default-scope1
      certificateUrls:
        - ${default.id}
      gatewaySecurityPolicy: ${defaultGatewaySecurityPolicy.id}
      network: ${defaultNetwork.id}
      subnetwork: ${defaultSubnetwork.id}
      deleteSwgAutogenRouterOnDestroy: true
    options:
      dependsOn:
        - ${proxyonlysubnet}

Setting type to SECURE_WEB_GATEWAY changes the gateway’s behavior: it requires a specific IP address from the addresses array, operates within a VPC network and subnetwork, and enforces security policies. The certificateUrls property references Certificate Manager certificates for TLS termination. The gatewaySecurityPolicy property applies rules that filter traffic based on session matchers. The deleteSwgAutogenRouterOnDestroy property controls cleanup: when true, deleting the gateway also removes the auto-generated router if no other secure web gateways remain in that region and network.

Beyond these examples

These snippets focus on specific gateway-level features: open mesh and secure web gateway types, TLS termination and security policy enforcement, and VPC network integration. They’re intentionally minimal rather than full traffic management solutions.

The examples may reference pre-existing infrastructure such as VPC networks, subnets, and proxy-only subnets, Certificate Manager certificates, and Gateway Security Policies and rules. They focus on configuring the gateway rather than provisioning everything around it.

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

  • Server TLS policies (serverTlsPolicy)
  • Routing mode configuration (routingMode)
  • Envoy debug headers (envoyHeaders)
  • IP version selection (ipVersion)

These omissions are intentional: the goal is to illustrate how each gateway feature is wired, not provide drop-in proxy modules. See the Network Services Gateway resource reference for all available configuration options.

Let's configure GCP Network Services Gateways

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Gateway Types & Configuration
What's the difference between OPEN_MESH and SECURE_WEB_GATEWAY gateway types?
OPEN_MESH gateways listen on 0.0.0.0 for IPv4 and :: for IPv6, supporting multiple ports. SECURE_WEB_GATEWAY gateways require VPC network and subnetwork configuration, are limited to 1 port, and support additional features like certificate URLs, gateway security policies, and TLS termination.
How many ports can I configure on a gateway?
Port numbers range from 1 to 65535. SECURE_WEB_GATEWAY gateways are limited to 1 port, while OPEN_MESH gateways support multiple ports.
What does the scope parameter do?
Scope determines how configuration across multiple Gateway instances are merged. Gateways with the same scope have their configurations merged and presented as a single configuration to the proxy/load balancer. Scope is immutable, has a max length of 64 characters, must start with a letter, and can only contain letters, numbers, and hyphens.
Networking & Subnets
Why do I need a proxy-only subnet for SECURE_WEB_GATEWAY?
SECURE_WEB_GATEWAY gateways require a proxy-only subnet (with purpose REGIONAL_MANAGED_PROXY) to exist before creation. Use dependsOn to ensure the proxy-only subnet is created first.
Can I deploy multiple SECURE_WEB_GATEWAY gateways on the same network?
Yes, multiple SECURE_WEB_GATEWAY gateways can share the same network and subnetwork by using different IP addresses in the addresses field.
What happens if I don't specify an address for my gateway?
For SECURE_WEB_GATEWAY gateways, an IP address is automatically allocated from the subnetwork if you don’t provide one. OPEN_MESH gateways always listen on 0.0.0.0 for IPv4 and :: for IPv6 regardless of the addresses field.
Immutability & Lifecycle
Which gateway properties can't be changed after creation?
The following properties are immutable: type, addresses, project, network, subnetwork, and scope. Changing any of these requires replacing the gateway resource.
What does deleteSwgAutogenRouterOnDestroy do?
When deleting a SECURE_WEB_GATEWAY gateway with this option enabled, Pulumi will also delete the auto-generated router created during gateway setup. The router is only deleted if no other SECURE_WEB_GATEWAY gateways remain in that region and network.
Labels & Metadata
What's the difference between labels and effectiveLabels?
The labels field is non-authoritative and only manages labels present in your Pulumi configuration. Use effectiveLabels to see all labels present on the resource in GCP, including those configured through other clients and services.

Using a different cloud?

Explore networking guides for other cloud providers: