Configure GCP Networking Forwarding Rules

The gcp:compute/forwardingRule:ForwardingRule resource, part of the Pulumi GCP provider, defines forwarding rules that route traffic to load balancer backends, target pools, or Private Service Connect endpoints based on IP address, protocol, and port configuration. This guide focuses on three capabilities: external and internal load balancing, Private Service Connect connectivity, and source IP-based traffic steering.

Forwarding rules reference backend services, target pools, or service attachments, and typically require VPC networks, subnets, and reserved IP addresses. The examples are intentionally small. Combine them with your own backend infrastructure and network configuration.

Route external traffic to a backend service

Most external load balancers route internet traffic to a regional backend service that distributes requests across instance groups.

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

const hc = new gcp.compute.RegionHealthCheck("hc", {
    name: "check-website-backend",
    checkIntervalSec: 1,
    timeoutSec: 1,
    region: "us-central1",
    tcpHealthCheck: {
        port: 80,
    },
});
const backend = new gcp.compute.RegionBackendService("backend", {
    name: "website-backend",
    region: "us-central1",
    loadBalancingScheme: "EXTERNAL",
    healthChecks: hc.id,
});
// Forwarding rule for External Network Load Balancing using Backend Services
const _default = new gcp.compute.ForwardingRule("default", {
    name: "website-forwarding-rule",
    region: "us-central1",
    portRange: "80",
    backendService: backend.id,
});
import pulumi
import pulumi_gcp as gcp

hc = gcp.compute.RegionHealthCheck("hc",
    name="check-website-backend",
    check_interval_sec=1,
    timeout_sec=1,
    region="us-central1",
    tcp_health_check={
        "port": 80,
    })
backend = gcp.compute.RegionBackendService("backend",
    name="website-backend",
    region="us-central1",
    load_balancing_scheme="EXTERNAL",
    health_checks=hc.id)
# Forwarding rule for External Network Load Balancing using Backend Services
default = gcp.compute.ForwardingRule("default",
    name="website-forwarding-rule",
    region="us-central1",
    port_range="80",
    backend_service=backend.id)
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		hc, err := compute.NewRegionHealthCheck(ctx, "hc", &compute.RegionHealthCheckArgs{
			Name:             pulumi.String("check-website-backend"),
			CheckIntervalSec: pulumi.Int(1),
			TimeoutSec:       pulumi.Int(1),
			Region:           pulumi.String("us-central1"),
			TcpHealthCheck: &compute.RegionHealthCheckTcpHealthCheckArgs{
				Port: pulumi.Int(80),
			},
		})
		if err != nil {
			return err
		}
		backend, err := compute.NewRegionBackendService(ctx, "backend", &compute.RegionBackendServiceArgs{
			Name:                pulumi.String("website-backend"),
			Region:              pulumi.String("us-central1"),
			LoadBalancingScheme: pulumi.String("EXTERNAL"),
			HealthChecks:        hc.ID(),
		})
		if err != nil {
			return err
		}
		// Forwarding rule for External Network Load Balancing using Backend Services
		_, err = compute.NewForwardingRule(ctx, "default", &compute.ForwardingRuleArgs{
			Name:           pulumi.String("website-forwarding-rule"),
			Region:         pulumi.String("us-central1"),
			PortRange:      pulumi.String("80"),
			BackendService: backend.ID(),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var hc = new Gcp.Compute.RegionHealthCheck("hc", new()
    {
        Name = "check-website-backend",
        CheckIntervalSec = 1,
        TimeoutSec = 1,
        Region = "us-central1",
        TcpHealthCheck = new Gcp.Compute.Inputs.RegionHealthCheckTcpHealthCheckArgs
        {
            Port = 80,
        },
    });

    var backend = new Gcp.Compute.RegionBackendService("backend", new()
    {
        Name = "website-backend",
        Region = "us-central1",
        LoadBalancingScheme = "EXTERNAL",
        HealthChecks = hc.Id,
    });

    // Forwarding rule for External Network Load Balancing using Backend Services
    var @default = new Gcp.Compute.ForwardingRule("default", new()
    {
        Name = "website-forwarding-rule",
        Region = "us-central1",
        PortRange = "80",
        BackendService = backend.Id,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.RegionHealthCheck;
import com.pulumi.gcp.compute.RegionHealthCheckArgs;
import com.pulumi.gcp.compute.inputs.RegionHealthCheckTcpHealthCheckArgs;
import com.pulumi.gcp.compute.RegionBackendService;
import com.pulumi.gcp.compute.RegionBackendServiceArgs;
import com.pulumi.gcp.compute.ForwardingRule;
import com.pulumi.gcp.compute.ForwardingRuleArgs;
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 hc = new RegionHealthCheck("hc", RegionHealthCheckArgs.builder()
            .name("check-website-backend")
            .checkIntervalSec(1)
            .timeoutSec(1)
            .region("us-central1")
            .tcpHealthCheck(RegionHealthCheckTcpHealthCheckArgs.builder()
                .port(80)
                .build())
            .build());

        var backend = new RegionBackendService("backend", RegionBackendServiceArgs.builder()
            .name("website-backend")
            .region("us-central1")
            .loadBalancingScheme("EXTERNAL")
            .healthChecks(hc.id())
            .build());

        // Forwarding rule for External Network Load Balancing using Backend Services
        var default_ = new ForwardingRule("default", ForwardingRuleArgs.builder()
            .name("website-forwarding-rule")
            .region("us-central1")
            .portRange("80")
            .backendService(backend.id())
            .build());

    }
}
resources:
  # Forwarding rule for External Network Load Balancing using Backend Services
  default:
    type: gcp:compute:ForwardingRule
    properties:
      name: website-forwarding-rule
      region: us-central1
      portRange: 80
      backendService: ${backend.id}
  backend:
    type: gcp:compute:RegionBackendService
    properties:
      name: website-backend
      region: us-central1
      loadBalancingScheme: EXTERNAL
      healthChecks: ${hc.id}
  hc:
    type: gcp:compute:RegionHealthCheck
    properties:
      name: check-website-backend
      checkIntervalSec: 1
      timeoutSec: 1
      region: us-central1
      tcpHealthCheck:
        port: '80'

When traffic arrives at the forwarding rule’s IP address on the specified port, GCP routes it to the backend service. The loadBalancingScheme defaults to EXTERNAL for internet-facing load balancers. The portRange property specifies which ports accept traffic; here, only port 80 is open.

Route internal VPC traffic to backend services

Applications within a VPC need private load balancing to distribute traffic across internal services without internet exposure.

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

const hc = new gcp.compute.HealthCheck("hc", {
    name: "check-website-backend",
    checkIntervalSec: 1,
    timeoutSec: 1,
    tcpHealthCheck: {
        port: 80,
    },
});
const backend = new gcp.compute.RegionBackendService("backend", {
    name: "website-backend",
    region: "us-central1",
    healthChecks: hc.id,
});
const defaultNetwork = new gcp.compute.Network("default", {
    name: "website-net",
    autoCreateSubnetworks: false,
});
const defaultSubnetwork = new gcp.compute.Subnetwork("default", {
    name: "website-net",
    ipCidrRange: "10.0.0.0/16",
    region: "us-central1",
    network: defaultNetwork.id,
});
// Forwarding rule for Internal Load Balancing
const _default = new gcp.compute.ForwardingRule("default", {
    name: "website-forwarding-rule",
    region: "us-central1",
    loadBalancingScheme: "INTERNAL",
    backendService: backend.id,
    allPorts: true,
    network: defaultNetwork.name,
    subnetwork: defaultSubnetwork.name,
    ipVersion: "IPV4",
});
import pulumi
import pulumi_gcp as gcp

hc = gcp.compute.HealthCheck("hc",
    name="check-website-backend",
    check_interval_sec=1,
    timeout_sec=1,
    tcp_health_check={
        "port": 80,
    })
backend = gcp.compute.RegionBackendService("backend",
    name="website-backend",
    region="us-central1",
    health_checks=hc.id)
default_network = gcp.compute.Network("default",
    name="website-net",
    auto_create_subnetworks=False)
default_subnetwork = gcp.compute.Subnetwork("default",
    name="website-net",
    ip_cidr_range="10.0.0.0/16",
    region="us-central1",
    network=default_network.id)
# Forwarding rule for Internal Load Balancing
default = gcp.compute.ForwardingRule("default",
    name="website-forwarding-rule",
    region="us-central1",
    load_balancing_scheme="INTERNAL",
    backend_service=backend.id,
    all_ports=True,
    network=default_network.name,
    subnetwork=default_subnetwork.name,
    ip_version="IPV4")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		hc, err := compute.NewHealthCheck(ctx, "hc", &compute.HealthCheckArgs{
			Name:             pulumi.String("check-website-backend"),
			CheckIntervalSec: pulumi.Int(1),
			TimeoutSec:       pulumi.Int(1),
			TcpHealthCheck: &compute.HealthCheckTcpHealthCheckArgs{
				Port: pulumi.Int(80),
			},
		})
		if err != nil {
			return err
		}
		backend, err := compute.NewRegionBackendService(ctx, "backend", &compute.RegionBackendServiceArgs{
			Name:         pulumi.String("website-backend"),
			Region:       pulumi.String("us-central1"),
			HealthChecks: hc.ID(),
		})
		if err != nil {
			return err
		}
		defaultNetwork, err := compute.NewNetwork(ctx, "default", &compute.NetworkArgs{
			Name:                  pulumi.String("website-net"),
			AutoCreateSubnetworks: pulumi.Bool(false),
		})
		if err != nil {
			return err
		}
		defaultSubnetwork, err := compute.NewSubnetwork(ctx, "default", &compute.SubnetworkArgs{
			Name:        pulumi.String("website-net"),
			IpCidrRange: pulumi.String("10.0.0.0/16"),
			Region:      pulumi.String("us-central1"),
			Network:     defaultNetwork.ID(),
		})
		if err != nil {
			return err
		}
		// Forwarding rule for Internal Load Balancing
		_, err = compute.NewForwardingRule(ctx, "default", &compute.ForwardingRuleArgs{
			Name:                pulumi.String("website-forwarding-rule"),
			Region:              pulumi.String("us-central1"),
			LoadBalancingScheme: pulumi.String("INTERNAL"),
			BackendService:      backend.ID(),
			AllPorts:            pulumi.Bool(true),
			Network:             defaultNetwork.Name,
			Subnetwork:          defaultSubnetwork.Name,
			IpVersion:           pulumi.String("IPV4"),
		})
		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 hc = new Gcp.Compute.HealthCheck("hc", new()
    {
        Name = "check-website-backend",
        CheckIntervalSec = 1,
        TimeoutSec = 1,
        TcpHealthCheck = new Gcp.Compute.Inputs.HealthCheckTcpHealthCheckArgs
        {
            Port = 80,
        },
    });

    var backend = new Gcp.Compute.RegionBackendService("backend", new()
    {
        Name = "website-backend",
        Region = "us-central1",
        HealthChecks = hc.Id,
    });

    var defaultNetwork = new Gcp.Compute.Network("default", new()
    {
        Name = "website-net",
        AutoCreateSubnetworks = false,
    });

    var defaultSubnetwork = new Gcp.Compute.Subnetwork("default", new()
    {
        Name = "website-net",
        IpCidrRange = "10.0.0.0/16",
        Region = "us-central1",
        Network = defaultNetwork.Id,
    });

    // Forwarding rule for Internal Load Balancing
    var @default = new Gcp.Compute.ForwardingRule("default", new()
    {
        Name = "website-forwarding-rule",
        Region = "us-central1",
        LoadBalancingScheme = "INTERNAL",
        BackendService = backend.Id,
        AllPorts = true,
        Network = defaultNetwork.Name,
        Subnetwork = defaultSubnetwork.Name,
        IpVersion = "IPV4",
    });

});
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.HealthCheckTcpHealthCheckArgs;
import com.pulumi.gcp.compute.RegionBackendService;
import com.pulumi.gcp.compute.RegionBackendServiceArgs;
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.compute.ForwardingRule;
import com.pulumi.gcp.compute.ForwardingRuleArgs;
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 hc = new HealthCheck("hc", HealthCheckArgs.builder()
            .name("check-website-backend")
            .checkIntervalSec(1)
            .timeoutSec(1)
            .tcpHealthCheck(HealthCheckTcpHealthCheckArgs.builder()
                .port(80)
                .build())
            .build());

        var backend = new RegionBackendService("backend", RegionBackendServiceArgs.builder()
            .name("website-backend")
            .region("us-central1")
            .healthChecks(hc.id())
            .build());

        var defaultNetwork = new Network("defaultNetwork", NetworkArgs.builder()
            .name("website-net")
            .autoCreateSubnetworks(false)
            .build());

        var defaultSubnetwork = new Subnetwork("defaultSubnetwork", SubnetworkArgs.builder()
            .name("website-net")
            .ipCidrRange("10.0.0.0/16")
            .region("us-central1")
            .network(defaultNetwork.id())
            .build());

        // Forwarding rule for Internal Load Balancing
        var default_ = new ForwardingRule("default", ForwardingRuleArgs.builder()
            .name("website-forwarding-rule")
            .region("us-central1")
            .loadBalancingScheme("INTERNAL")
            .backendService(backend.id())
            .allPorts(true)
            .network(defaultNetwork.name())
            .subnetwork(defaultSubnetwork.name())
            .ipVersion("IPV4")
            .build());

    }
}
resources:
  # Forwarding rule for Internal Load Balancing
  default:
    type: gcp:compute:ForwardingRule
    properties:
      name: website-forwarding-rule
      region: us-central1
      loadBalancingScheme: INTERNAL
      backendService: ${backend.id}
      allPorts: true
      network: ${defaultNetwork.name}
      subnetwork: ${defaultSubnetwork.name}
      ipVersion: IPV4
  backend:
    type: gcp:compute:RegionBackendService
    properties:
      name: website-backend
      region: us-central1
      healthChecks: ${hc.id}
  hc:
    type: gcp:compute:HealthCheck
    properties:
      name: check-website-backend
      checkIntervalSec: 1
      timeoutSec: 1
      tcpHealthCheck:
        port: '80'
  defaultNetwork:
    type: gcp:compute:Network
    name: default
    properties:
      name: website-net
      autoCreateSubnetworks: false
  defaultSubnetwork:
    type: gcp:compute:Subnetwork
    name: default
    properties:
      name: website-net
      ipCidrRange: 10.0.0.0/16
      region: us-central1
      network: ${defaultNetwork.id}

Setting loadBalancingScheme to INTERNAL creates a private load balancer accessible only within the VPC. The network and subnetwork properties place the forwarding rule in your VPC. The allPorts property accepts traffic on any port, simplifying configuration when backends listen on multiple ports.

Forward traffic to a target pool

Legacy network load balancing uses target pools to group instances that receive traffic directly.

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

const defaultTargetPool = new gcp.compute.TargetPool("default", {name: "website-target-pool"});
const _default = new gcp.compute.ForwardingRule("default", {
    name: "website-forwarding-rule",
    target: defaultTargetPool.id,
    portRange: "80",
});
import pulumi
import pulumi_gcp as gcp

default_target_pool = gcp.compute.TargetPool("default", name="website-target-pool")
default = gcp.compute.ForwardingRule("default",
    name="website-forwarding-rule",
    target=default_target_pool.id,
    port_range="80")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		defaultTargetPool, err := compute.NewTargetPool(ctx, "default", &compute.TargetPoolArgs{
			Name: pulumi.String("website-target-pool"),
		})
		if err != nil {
			return err
		}
		_, err = compute.NewForwardingRule(ctx, "default", &compute.ForwardingRuleArgs{
			Name:      pulumi.String("website-forwarding-rule"),
			Target:    defaultTargetPool.ID(),
			PortRange: pulumi.String("80"),
		})
		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 defaultTargetPool = new Gcp.Compute.TargetPool("default", new()
    {
        Name = "website-target-pool",
    });

    var @default = new Gcp.Compute.ForwardingRule("default", new()
    {
        Name = "website-forwarding-rule",
        Target = defaultTargetPool.Id,
        PortRange = "80",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.TargetPool;
import com.pulumi.gcp.compute.TargetPoolArgs;
import com.pulumi.gcp.compute.ForwardingRule;
import com.pulumi.gcp.compute.ForwardingRuleArgs;
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 defaultTargetPool = new TargetPool("defaultTargetPool", TargetPoolArgs.builder()
            .name("website-target-pool")
            .build());

        var default_ = new ForwardingRule("default", ForwardingRuleArgs.builder()
            .name("website-forwarding-rule")
            .target(defaultTargetPool.id())
            .portRange("80")
            .build());

    }
}
resources:
  default:
    type: gcp:compute:ForwardingRule
    properties:
      name: website-forwarding-rule
      target: ${defaultTargetPool.id}
      portRange: '80'
  defaultTargetPool:
    type: gcp:compute:TargetPool
    name: default
    properties:
      name: website-target-pool

The target property points to a TargetPool resource instead of a backend service. This simpler configuration works for basic load balancing scenarios where you don’t need the advanced features of backend services like health checks or session affinity.

Connect to services via Private Service Connect

Private Service Connect enables private connectivity to services across VPCs or projects without internet exposure or VPC peering.

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

// Consumer service endpoint
const consumerNet = new gcp.compute.Network("consumer_net", {
    name: "consumer-net",
    autoCreateSubnetworks: false,
});
const consumerSubnet = new gcp.compute.Subnetwork("consumer_subnet", {
    name: "consumer-net",
    ipCidrRange: "10.0.0.0/16",
    region: "us-central1",
    network: consumerNet.id,
});
const consumerAddress = new gcp.compute.Address("consumer_address", {
    name: "website-ip-1",
    region: "us-central1",
    subnetwork: consumerSubnet.id,
    addressType: "INTERNAL",
});
// Producer service attachment
const producerNet = new gcp.compute.Network("producer_net", {
    name: "producer-net",
    autoCreateSubnetworks: false,
});
const pscProducerSubnet = new gcp.compute.Subnetwork("psc_producer_subnet", {
    name: "producer-psc-net",
    ipCidrRange: "10.1.0.0/16",
    region: "us-central1",
    purpose: "PRIVATE_SERVICE_CONNECT",
    network: producerNet.id,
});
const producerSubnet = new gcp.compute.Subnetwork("producer_subnet", {
    name: "producer-net",
    ipCidrRange: "10.0.0.0/16",
    region: "us-central1",
    network: producerNet.id,
});
const producerServiceHealthCheck = new gcp.compute.HealthCheck("producer_service_health_check", {
    name: "producer-service-health-check",
    checkIntervalSec: 1,
    timeoutSec: 1,
    tcpHealthCheck: {
        port: 80,
    },
});
const producerServiceBackend = new gcp.compute.RegionBackendService("producer_service_backend", {
    name: "producer-service-backend",
    region: "us-central1",
    healthChecks: producerServiceHealthCheck.id,
});
const producerTargetService = new gcp.compute.ForwardingRule("producer_target_service", {
    name: "producer-forwarding-rule",
    region: "us-central1",
    loadBalancingScheme: "INTERNAL",
    backendService: producerServiceBackend.id,
    allPorts: true,
    network: producerNet.name,
    subnetwork: producerSubnet.name,
});
const producerServiceAttachment = new gcp.compute.ServiceAttachment("producer_service_attachment", {
    name: "producer-service",
    region: "us-central1",
    description: "A service attachment configured with Terraform",
    enableProxyProtocol: true,
    connectionPreference: "ACCEPT_AUTOMATIC",
    natSubnets: [pscProducerSubnet.name],
    targetService: producerTargetService.id,
});
// Forwarding rule for VPC private service connect
const _default = new gcp.compute.ForwardingRule("default", {
    name: "psc-endpoint",
    region: "us-central1",
    loadBalancingScheme: "",
    target: producerServiceAttachment.id,
    network: consumerNet.name,
    ipAddress: consumerAddress.id,
    allowPscGlobalAccess: true,
});
import pulumi
import pulumi_gcp as gcp

# Consumer service endpoint
consumer_net = gcp.compute.Network("consumer_net",
    name="consumer-net",
    auto_create_subnetworks=False)
consumer_subnet = gcp.compute.Subnetwork("consumer_subnet",
    name="consumer-net",
    ip_cidr_range="10.0.0.0/16",
    region="us-central1",
    network=consumer_net.id)
consumer_address = gcp.compute.Address("consumer_address",
    name="website-ip-1",
    region="us-central1",
    subnetwork=consumer_subnet.id,
    address_type="INTERNAL")
# Producer service attachment
producer_net = gcp.compute.Network("producer_net",
    name="producer-net",
    auto_create_subnetworks=False)
psc_producer_subnet = gcp.compute.Subnetwork("psc_producer_subnet",
    name="producer-psc-net",
    ip_cidr_range="10.1.0.0/16",
    region="us-central1",
    purpose="PRIVATE_SERVICE_CONNECT",
    network=producer_net.id)
producer_subnet = gcp.compute.Subnetwork("producer_subnet",
    name="producer-net",
    ip_cidr_range="10.0.0.0/16",
    region="us-central1",
    network=producer_net.id)
producer_service_health_check = gcp.compute.HealthCheck("producer_service_health_check",
    name="producer-service-health-check",
    check_interval_sec=1,
    timeout_sec=1,
    tcp_health_check={
        "port": 80,
    })
producer_service_backend = gcp.compute.RegionBackendService("producer_service_backend",
    name="producer-service-backend",
    region="us-central1",
    health_checks=producer_service_health_check.id)
producer_target_service = gcp.compute.ForwardingRule("producer_target_service",
    name="producer-forwarding-rule",
    region="us-central1",
    load_balancing_scheme="INTERNAL",
    backend_service=producer_service_backend.id,
    all_ports=True,
    network=producer_net.name,
    subnetwork=producer_subnet.name)
producer_service_attachment = gcp.compute.ServiceAttachment("producer_service_attachment",
    name="producer-service",
    region="us-central1",
    description="A service attachment configured with Terraform",
    enable_proxy_protocol=True,
    connection_preference="ACCEPT_AUTOMATIC",
    nat_subnets=[psc_producer_subnet.name],
    target_service=producer_target_service.id)
# Forwarding rule for VPC private service connect
default = gcp.compute.ForwardingRule("default",
    name="psc-endpoint",
    region="us-central1",
    load_balancing_scheme="",
    target=producer_service_attachment.id,
    network=consumer_net.name,
    ip_address=consumer_address.id,
    allow_psc_global_access=True)
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		// Consumer service endpoint
		consumerNet, err := compute.NewNetwork(ctx, "consumer_net", &compute.NetworkArgs{
			Name:                  pulumi.String("consumer-net"),
			AutoCreateSubnetworks: pulumi.Bool(false),
		})
		if err != nil {
			return err
		}
		consumerSubnet, err := compute.NewSubnetwork(ctx, "consumer_subnet", &compute.SubnetworkArgs{
			Name:        pulumi.String("consumer-net"),
			IpCidrRange: pulumi.String("10.0.0.0/16"),
			Region:      pulumi.String("us-central1"),
			Network:     consumerNet.ID(),
		})
		if err != nil {
			return err
		}
		consumerAddress, err := compute.NewAddress(ctx, "consumer_address", &compute.AddressArgs{
			Name:        pulumi.String("website-ip-1"),
			Region:      pulumi.String("us-central1"),
			Subnetwork:  consumerSubnet.ID(),
			AddressType: pulumi.String("INTERNAL"),
		})
		if err != nil {
			return err
		}
		// Producer service attachment
		producerNet, err := compute.NewNetwork(ctx, "producer_net", &compute.NetworkArgs{
			Name:                  pulumi.String("producer-net"),
			AutoCreateSubnetworks: pulumi.Bool(false),
		})
		if err != nil {
			return err
		}
		pscProducerSubnet, err := compute.NewSubnetwork(ctx, "psc_producer_subnet", &compute.SubnetworkArgs{
			Name:        pulumi.String("producer-psc-net"),
			IpCidrRange: pulumi.String("10.1.0.0/16"),
			Region:      pulumi.String("us-central1"),
			Purpose:     pulumi.String("PRIVATE_SERVICE_CONNECT"),
			Network:     producerNet.ID(),
		})
		if err != nil {
			return err
		}
		producerSubnet, err := compute.NewSubnetwork(ctx, "producer_subnet", &compute.SubnetworkArgs{
			Name:        pulumi.String("producer-net"),
			IpCidrRange: pulumi.String("10.0.0.0/16"),
			Region:      pulumi.String("us-central1"),
			Network:     producerNet.ID(),
		})
		if err != nil {
			return err
		}
		producerServiceHealthCheck, err := compute.NewHealthCheck(ctx, "producer_service_health_check", &compute.HealthCheckArgs{
			Name:             pulumi.String("producer-service-health-check"),
			CheckIntervalSec: pulumi.Int(1),
			TimeoutSec:       pulumi.Int(1),
			TcpHealthCheck: &compute.HealthCheckTcpHealthCheckArgs{
				Port: pulumi.Int(80),
			},
		})
		if err != nil {
			return err
		}
		producerServiceBackend, err := compute.NewRegionBackendService(ctx, "producer_service_backend", &compute.RegionBackendServiceArgs{
			Name:         pulumi.String("producer-service-backend"),
			Region:       pulumi.String("us-central1"),
			HealthChecks: producerServiceHealthCheck.ID(),
		})
		if err != nil {
			return err
		}
		producerTargetService, err := compute.NewForwardingRule(ctx, "producer_target_service", &compute.ForwardingRuleArgs{
			Name:                pulumi.String("producer-forwarding-rule"),
			Region:              pulumi.String("us-central1"),
			LoadBalancingScheme: pulumi.String("INTERNAL"),
			BackendService:      producerServiceBackend.ID(),
			AllPorts:            pulumi.Bool(true),
			Network:             producerNet.Name,
			Subnetwork:          producerSubnet.Name,
		})
		if err != nil {
			return err
		}
		producerServiceAttachment, err := compute.NewServiceAttachment(ctx, "producer_service_attachment", &compute.ServiceAttachmentArgs{
			Name:                 pulumi.String("producer-service"),
			Region:               pulumi.String("us-central1"),
			Description:          pulumi.String("A service attachment configured with Terraform"),
			EnableProxyProtocol:  pulumi.Bool(true),
			ConnectionPreference: pulumi.String("ACCEPT_AUTOMATIC"),
			NatSubnets: pulumi.StringArray{
				pscProducerSubnet.Name,
			},
			TargetService: producerTargetService.ID(),
		})
		if err != nil {
			return err
		}
		// Forwarding rule for VPC private service connect
		_, err = compute.NewForwardingRule(ctx, "default", &compute.ForwardingRuleArgs{
			Name:                 pulumi.String("psc-endpoint"),
			Region:               pulumi.String("us-central1"),
			LoadBalancingScheme:  pulumi.String(""),
			Target:               producerServiceAttachment.ID(),
			Network:              consumerNet.Name,
			IpAddress:            consumerAddress.ID(),
			AllowPscGlobalAccess: pulumi.Bool(true),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    // Consumer service endpoint
    var consumerNet = new Gcp.Compute.Network("consumer_net", new()
    {
        Name = "consumer-net",
        AutoCreateSubnetworks = false,
    });

    var consumerSubnet = new Gcp.Compute.Subnetwork("consumer_subnet", new()
    {
        Name = "consumer-net",
        IpCidrRange = "10.0.0.0/16",
        Region = "us-central1",
        Network = consumerNet.Id,
    });

    var consumerAddress = new Gcp.Compute.Address("consumer_address", new()
    {
        Name = "website-ip-1",
        Region = "us-central1",
        Subnetwork = consumerSubnet.Id,
        AddressType = "INTERNAL",
    });

    // Producer service attachment
    var producerNet = new Gcp.Compute.Network("producer_net", new()
    {
        Name = "producer-net",
        AutoCreateSubnetworks = false,
    });

    var pscProducerSubnet = new Gcp.Compute.Subnetwork("psc_producer_subnet", new()
    {
        Name = "producer-psc-net",
        IpCidrRange = "10.1.0.0/16",
        Region = "us-central1",
        Purpose = "PRIVATE_SERVICE_CONNECT",
        Network = producerNet.Id,
    });

    var producerSubnet = new Gcp.Compute.Subnetwork("producer_subnet", new()
    {
        Name = "producer-net",
        IpCidrRange = "10.0.0.0/16",
        Region = "us-central1",
        Network = producerNet.Id,
    });

    var producerServiceHealthCheck = new Gcp.Compute.HealthCheck("producer_service_health_check", new()
    {
        Name = "producer-service-health-check",
        CheckIntervalSec = 1,
        TimeoutSec = 1,
        TcpHealthCheck = new Gcp.Compute.Inputs.HealthCheckTcpHealthCheckArgs
        {
            Port = 80,
        },
    });

    var producerServiceBackend = new Gcp.Compute.RegionBackendService("producer_service_backend", new()
    {
        Name = "producer-service-backend",
        Region = "us-central1",
        HealthChecks = producerServiceHealthCheck.Id,
    });

    var producerTargetService = new Gcp.Compute.ForwardingRule("producer_target_service", new()
    {
        Name = "producer-forwarding-rule",
        Region = "us-central1",
        LoadBalancingScheme = "INTERNAL",
        BackendService = producerServiceBackend.Id,
        AllPorts = true,
        Network = producerNet.Name,
        Subnetwork = producerSubnet.Name,
    });

    var producerServiceAttachment = new Gcp.Compute.ServiceAttachment("producer_service_attachment", new()
    {
        Name = "producer-service",
        Region = "us-central1",
        Description = "A service attachment configured with Terraform",
        EnableProxyProtocol = true,
        ConnectionPreference = "ACCEPT_AUTOMATIC",
        NatSubnets = new[]
        {
            pscProducerSubnet.Name,
        },
        TargetService = producerTargetService.Id,
    });

    // Forwarding rule for VPC private service connect
    var @default = new Gcp.Compute.ForwardingRule("default", new()
    {
        Name = "psc-endpoint",
        Region = "us-central1",
        LoadBalancingScheme = "",
        Target = producerServiceAttachment.Id,
        Network = consumerNet.Name,
        IpAddress = consumerAddress.Id,
        AllowPscGlobalAccess = true,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
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.compute.Address;
import com.pulumi.gcp.compute.AddressArgs;
import com.pulumi.gcp.compute.HealthCheck;
import com.pulumi.gcp.compute.HealthCheckArgs;
import com.pulumi.gcp.compute.inputs.HealthCheckTcpHealthCheckArgs;
import com.pulumi.gcp.compute.RegionBackendService;
import com.pulumi.gcp.compute.RegionBackendServiceArgs;
import com.pulumi.gcp.compute.ForwardingRule;
import com.pulumi.gcp.compute.ForwardingRuleArgs;
import com.pulumi.gcp.compute.ServiceAttachment;
import com.pulumi.gcp.compute.ServiceAttachmentArgs;
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) {
        // Consumer service endpoint
        var consumerNet = new Network("consumerNet", NetworkArgs.builder()
            .name("consumer-net")
            .autoCreateSubnetworks(false)
            .build());

        var consumerSubnet = new Subnetwork("consumerSubnet", SubnetworkArgs.builder()
            .name("consumer-net")
            .ipCidrRange("10.0.0.0/16")
            .region("us-central1")
            .network(consumerNet.id())
            .build());

        var consumerAddress = new Address("consumerAddress", AddressArgs.builder()
            .name("website-ip-1")
            .region("us-central1")
            .subnetwork(consumerSubnet.id())
            .addressType("INTERNAL")
            .build());

        // Producer service attachment
        var producerNet = new Network("producerNet", NetworkArgs.builder()
            .name("producer-net")
            .autoCreateSubnetworks(false)
            .build());

        var pscProducerSubnet = new Subnetwork("pscProducerSubnet", SubnetworkArgs.builder()
            .name("producer-psc-net")
            .ipCidrRange("10.1.0.0/16")
            .region("us-central1")
            .purpose("PRIVATE_SERVICE_CONNECT")
            .network(producerNet.id())
            .build());

        var producerSubnet = new Subnetwork("producerSubnet", SubnetworkArgs.builder()
            .name("producer-net")
            .ipCidrRange("10.0.0.0/16")
            .region("us-central1")
            .network(producerNet.id())
            .build());

        var producerServiceHealthCheck = new HealthCheck("producerServiceHealthCheck", HealthCheckArgs.builder()
            .name("producer-service-health-check")
            .checkIntervalSec(1)
            .timeoutSec(1)
            .tcpHealthCheck(HealthCheckTcpHealthCheckArgs.builder()
                .port(80)
                .build())
            .build());

        var producerServiceBackend = new RegionBackendService("producerServiceBackend", RegionBackendServiceArgs.builder()
            .name("producer-service-backend")
            .region("us-central1")
            .healthChecks(producerServiceHealthCheck.id())
            .build());

        var producerTargetService = new ForwardingRule("producerTargetService", ForwardingRuleArgs.builder()
            .name("producer-forwarding-rule")
            .region("us-central1")
            .loadBalancingScheme("INTERNAL")
            .backendService(producerServiceBackend.id())
            .allPorts(true)
            .network(producerNet.name())
            .subnetwork(producerSubnet.name())
            .build());

        var producerServiceAttachment = new ServiceAttachment("producerServiceAttachment", ServiceAttachmentArgs.builder()
            .name("producer-service")
            .region("us-central1")
            .description("A service attachment configured with Terraform")
            .enableProxyProtocol(true)
            .connectionPreference("ACCEPT_AUTOMATIC")
            .natSubnets(pscProducerSubnet.name())
            .targetService(producerTargetService.id())
            .build());

        // Forwarding rule for VPC private service connect
        var default_ = new ForwardingRule("default", ForwardingRuleArgs.builder()
            .name("psc-endpoint")
            .region("us-central1")
            .loadBalancingScheme("")
            .target(producerServiceAttachment.id())
            .network(consumerNet.name())
            .ipAddress(consumerAddress.id())
            .allowPscGlobalAccess(true)
            .build());

    }
}
resources:
  # Forwarding rule for VPC private service connect
  default:
    type: gcp:compute:ForwardingRule
    properties:
      name: psc-endpoint
      region: us-central1
      loadBalancingScheme: ""
      target: ${producerServiceAttachment.id}
      network: ${consumerNet.name}
      ipAddress: ${consumerAddress.id}
      allowPscGlobalAccess: true
  # Consumer service endpoint
  consumerNet:
    type: gcp:compute:Network
    name: consumer_net
    properties:
      name: consumer-net
      autoCreateSubnetworks: false
  consumerSubnet:
    type: gcp:compute:Subnetwork
    name: consumer_subnet
    properties:
      name: consumer-net
      ipCidrRange: 10.0.0.0/16
      region: us-central1
      network: ${consumerNet.id}
  consumerAddress:
    type: gcp:compute:Address
    name: consumer_address
    properties:
      name: website-ip-1
      region: us-central1
      subnetwork: ${consumerSubnet.id}
      addressType: INTERNAL
  # Producer service attachment
  producerNet:
    type: gcp:compute:Network
    name: producer_net
    properties:
      name: producer-net
      autoCreateSubnetworks: false
  producerSubnet:
    type: gcp:compute:Subnetwork
    name: producer_subnet
    properties:
      name: producer-net
      ipCidrRange: 10.0.0.0/16
      region: us-central1
      network: ${producerNet.id}
  pscProducerSubnet:
    type: gcp:compute:Subnetwork
    name: psc_producer_subnet
    properties:
      name: producer-psc-net
      ipCidrRange: 10.1.0.0/16
      region: us-central1
      purpose: PRIVATE_SERVICE_CONNECT
      network: ${producerNet.id}
  producerServiceAttachment:
    type: gcp:compute:ServiceAttachment
    name: producer_service_attachment
    properties:
      name: producer-service
      region: us-central1
      description: A service attachment configured with Terraform
      enableProxyProtocol: true
      connectionPreference: ACCEPT_AUTOMATIC
      natSubnets:
        - ${pscProducerSubnet.name}
      targetService: ${producerTargetService.id}
  producerTargetService:
    type: gcp:compute:ForwardingRule
    name: producer_target_service
    properties:
      name: producer-forwarding-rule
      region: us-central1
      loadBalancingScheme: INTERNAL
      backendService: ${producerServiceBackend.id}
      allPorts: true
      network: ${producerNet.name}
      subnetwork: ${producerSubnet.name}
  producerServiceBackend:
    type: gcp:compute:RegionBackendService
    name: producer_service_backend
    properties:
      name: producer-service-backend
      region: us-central1
      healthChecks: ${producerServiceHealthCheck.id}
  producerServiceHealthCheck:
    type: gcp:compute:HealthCheck
    name: producer_service_health_check
    properties:
      name: producer-service-health-check
      checkIntervalSec: 1
      timeoutSec: 1
      tcpHealthCheck:
        port: '80'

The target property references a ServiceAttachment from the producer service. Setting loadBalancingScheme to an empty string ("") indicates this is a Private Service Connect endpoint, not a traditional load balancer. The allowPscGlobalAccess property enables cross-region connectivity to the producer service.

Route traffic based on source IP ranges

Traffic steering routes requests from specific source IP ranges to different backends, enabling geographic routing or client-specific load balancing.

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

const basic = new gcp.compute.Address("basic", {
    name: "website-ip",
    region: "us-central1",
});
const external = new gcp.compute.RegionBackendService("external", {
    name: "service-backend",
    region: "us-central1",
    loadBalancingScheme: "EXTERNAL",
});
const externalForwardingRule = new gcp.compute.ForwardingRule("external", {
    name: "external-forwarding-rule",
    region: "us-central1",
    ipAddress: basic.address,
    backendService: external.selfLink,
    loadBalancingScheme: "EXTERNAL",
});
const steering = new gcp.compute.ForwardingRule("steering", {
    name: "steering-rule",
    region: "us-central1",
    ipAddress: basic.address,
    backendService: external.selfLink,
    loadBalancingScheme: "EXTERNAL",
    sourceIpRanges: [
        "34.121.88.0/24",
        "35.187.239.137",
    ],
}, {
    dependsOn: [externalForwardingRule],
});
import pulumi
import pulumi_gcp as gcp

basic = gcp.compute.Address("basic",
    name="website-ip",
    region="us-central1")
external = gcp.compute.RegionBackendService("external",
    name="service-backend",
    region="us-central1",
    load_balancing_scheme="EXTERNAL")
external_forwarding_rule = gcp.compute.ForwardingRule("external",
    name="external-forwarding-rule",
    region="us-central1",
    ip_address=basic.address,
    backend_service=external.self_link,
    load_balancing_scheme="EXTERNAL")
steering = gcp.compute.ForwardingRule("steering",
    name="steering-rule",
    region="us-central1",
    ip_address=basic.address,
    backend_service=external.self_link,
    load_balancing_scheme="EXTERNAL",
    source_ip_ranges=[
        "34.121.88.0/24",
        "35.187.239.137",
    ],
    opts = pulumi.ResourceOptions(depends_on=[external_forwarding_rule]))
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		basic, err := compute.NewAddress(ctx, "basic", &compute.AddressArgs{
			Name:   pulumi.String("website-ip"),
			Region: pulumi.String("us-central1"),
		})
		if err != nil {
			return err
		}
		external, err := compute.NewRegionBackendService(ctx, "external", &compute.RegionBackendServiceArgs{
			Name:                pulumi.String("service-backend"),
			Region:              pulumi.String("us-central1"),
			LoadBalancingScheme: pulumi.String("EXTERNAL"),
		})
		if err != nil {
			return err
		}
		externalForwardingRule, err := compute.NewForwardingRule(ctx, "external", &compute.ForwardingRuleArgs{
			Name:                pulumi.String("external-forwarding-rule"),
			Region:              pulumi.String("us-central1"),
			IpAddress:           basic.Address,
			BackendService:      external.SelfLink,
			LoadBalancingScheme: pulumi.String("EXTERNAL"),
		})
		if err != nil {
			return err
		}
		_, err = compute.NewForwardingRule(ctx, "steering", &compute.ForwardingRuleArgs{
			Name:                pulumi.String("steering-rule"),
			Region:              pulumi.String("us-central1"),
			IpAddress:           basic.Address,
			BackendService:      external.SelfLink,
			LoadBalancingScheme: pulumi.String("EXTERNAL"),
			SourceIpRanges: pulumi.StringArray{
				pulumi.String("34.121.88.0/24"),
				pulumi.String("35.187.239.137"),
			},
		}, pulumi.DependsOn([]pulumi.Resource{
			externalForwardingRule,
		}))
		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 basic = new Gcp.Compute.Address("basic", new()
    {
        Name = "website-ip",
        Region = "us-central1",
    });

    var external = new Gcp.Compute.RegionBackendService("external", new()
    {
        Name = "service-backend",
        Region = "us-central1",
        LoadBalancingScheme = "EXTERNAL",
    });

    var externalForwardingRule = new Gcp.Compute.ForwardingRule("external", new()
    {
        Name = "external-forwarding-rule",
        Region = "us-central1",
        IpAddress = basic.IPAddress,
        BackendService = external.SelfLink,
        LoadBalancingScheme = "EXTERNAL",
    });

    var steering = new Gcp.Compute.ForwardingRule("steering", new()
    {
        Name = "steering-rule",
        Region = "us-central1",
        IpAddress = basic.IPAddress,
        BackendService = external.SelfLink,
        LoadBalancingScheme = "EXTERNAL",
        SourceIpRanges = new[]
        {
            "34.121.88.0/24",
            "35.187.239.137",
        },
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            externalForwardingRule,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.Address;
import com.pulumi.gcp.compute.AddressArgs;
import com.pulumi.gcp.compute.RegionBackendService;
import com.pulumi.gcp.compute.RegionBackendServiceArgs;
import com.pulumi.gcp.compute.ForwardingRule;
import com.pulumi.gcp.compute.ForwardingRuleArgs;
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 basic = new Address("basic", AddressArgs.builder()
            .name("website-ip")
            .region("us-central1")
            .build());

        var external = new RegionBackendService("external", RegionBackendServiceArgs.builder()
            .name("service-backend")
            .region("us-central1")
            .loadBalancingScheme("EXTERNAL")
            .build());

        var externalForwardingRule = new ForwardingRule("externalForwardingRule", ForwardingRuleArgs.builder()
            .name("external-forwarding-rule")
            .region("us-central1")
            .ipAddress(basic.address())
            .backendService(external.selfLink())
            .loadBalancingScheme("EXTERNAL")
            .build());

        var steering = new ForwardingRule("steering", ForwardingRuleArgs.builder()
            .name("steering-rule")
            .region("us-central1")
            .ipAddress(basic.address())
            .backendService(external.selfLink())
            .loadBalancingScheme("EXTERNAL")
            .sourceIpRanges(            
                "34.121.88.0/24",
                "35.187.239.137")
            .build(), CustomResourceOptions.builder()
                .dependsOn(externalForwardingRule)
                .build());

    }
}
resources:
  steering:
    type: gcp:compute:ForwardingRule
    properties:
      name: steering-rule
      region: us-central1
      ipAddress: ${basic.address}
      backendService: ${external.selfLink}
      loadBalancingScheme: EXTERNAL
      sourceIpRanges:
        - 34.121.88.0/24
        - 35.187.239.137
    options:
      dependsOn:
        - ${externalForwardingRule}
  basic:
    type: gcp:compute:Address
    properties:
      name: website-ip
      region: us-central1
  external:
    type: gcp:compute:RegionBackendService
    properties:
      name: service-backend
      region: us-central1
      loadBalancingScheme: EXTERNAL
  externalForwardingRule:
    type: gcp:compute:ForwardingRule
    name: external
    properties:
      name: external-forwarding-rule
      region: us-central1
      ipAddress: ${basic.address}
      backendService: ${external.selfLink}
      loadBalancingScheme: EXTERNAL

The sourceIpRanges property restricts which clients can use this forwarding rule. Traffic from the specified IP addresses or CIDR ranges routes to this rule’s backend, while other traffic uses the base forwarding rule. This requires a base rule with the same IP address and protocol but without source IP restrictions.

Beyond these examples

These snippets focus on specific forwarding rule features: external and internal load balancing schemes, Private Service Connect endpoints, and source IP-based traffic steering. They’re intentionally minimal rather than full load balancing deployments.

The examples may reference pre-existing infrastructure such as backend services, target pools, or service attachments, VPC networks and subnets, health checks and instance groups, and reserved IP addresses. They focus on configuring the forwarding rule rather than provisioning the complete load balancing stack.

To keep things focused, common forwarding rule patterns are omitted, including:

  • IPv6 configuration (ipVersion)
  • Service Directory registration
  • Network tier selection (networkTier)
  • Protocol-specific settings (ipProtocol, allPorts vs portRange)
  • Global access controls (allowGlobalAccess)
  • Labels and metadata

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

Let's configure GCP Networking Forwarding Rules

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Port Configuration & Conflicts
What's the difference between ports, portRange, and allPorts?
These three fields are mutually exclusive. Use portRange to specify a range like “80-443”, ports to list up to 5 specific ports, or allPorts set to true to forward all ports. The allPorts option requires ipProtocol to be TCP, UDP, SCTP, or L3_DEFAULT.
Why am I getting a port conflict error with my forwarding rules?
Forwarding rules cannot share the same [ipAddress, ipProtocol] pair with overlapping port ranges. This applies to external forwarding rules globally and internal forwarding rules within the same VPC network.
Load Balancing Schemes & Configuration
What load balancing schemes are available?
You can choose from EXTERNAL (default), EXTERNAL_MANAGED, INTERNAL, INTERNAL_MANAGED, or an empty string for Private Service Connect use cases. The scheme determines whether the load balancer is internet-facing or internal, and whether it’s managed or passthrough.
How do I set up a Private Service Connect forwarding rule?
Set loadBalancingScheme to an empty string and configure target to point to a service attachment. You can optionally enable cross-region access with allowPscGlobalAccess.
What are the naming requirements for PSC forwarding rules to Google APIs?
The name must be 1-20 characters with lowercase letters and numbers, starting with a letter. Regular forwarding rules can be 1-63 characters following RFC1035.
Immutability & Resource Constraints
What properties can't be changed after creating a forwarding rule?
Most properties are immutable, including ipAddress, ipProtocol, ipVersion, name, network, networkTier, portRange, region, subnetwork, backendService, target, and loadBalancingScheme. Changing these requires recreating the resource.
What are the limits on source IP ranges for traffic steering?
You can specify up to 64 source IP addresses or CIDR ranges using sourceIpRanges. This feature only works with regional forwarding rules whose scheme is EXTERNAL.
Cross-Region Access & Networking
How do I enable cross-region access for an internal load balancer?
Set allowGlobalAccess to true. This allows clients from all regions to access your internal load balancer, not just clients in the same region.
What's the difference between allowGlobalAccess and allowPscGlobalAccess?
Use allowGlobalAccess for internal load balancers with backendService or target fields. Use allowPscGlobalAccess specifically for Private Service Connect consumer forwarding rules to enable cross-region PSC endpoint access.
How do I implement traffic steering based on source IP addresses?
Configure sourceIpRanges with up to 64 IP addresses or CIDR ranges. The forwarding rule will only forward traffic when the source IP matches one of these ranges. This only works with regional EXTERNAL forwarding rules.
IP Protocols & Advanced Features
When should I use the L3_DEFAULT protocol?
Use L3_DEFAULT for protocol forwarding when you need to forward packets regardless of protocol. You must set allPorts to true and configure your backend service with UNSPECIFIED protocol. Note that L3_DEFAULT cannot attach to backend services with TCP or UDP protocols.
How do I specify an IP address for my forwarding rule?
You can specify an IP address as a number (100.1.2.3), IPv6 range (2600:1234::/96), full resource URL, partial URL (projects/PROJECT/regions/REGION/addresses/NAME), or just the address name. Some configurations like PSC Google APIs bundles require an explicit IP address.
Can I use packet mirroring with any load balancer?
No, isMirroringCollector can only be set to true for load balancers with loadBalancingScheme set to INTERNAL. This prevents mirroring loops.

Using a different cloud?

Explore networking guides for other cloud providers: