The gcp:compute/forwardingRule:ForwardingRule resource, part of the Pulumi GCP provider, defines forwarding rules that route traffic to backend services, target pools, or Private Service Connect endpoints based on IP address, protocol, and port configuration. This guide focuses on four capabilities: external and internal load balancing, Private Service Connect endpoints, protocol-based routing (including L3_DEFAULT), and source IP-based traffic steering.
Forwarding rules reference backend services, target pools, or service attachments, and typically require VPC networks, subnetworks, and static 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 balancing deployments create a forwarding rule that directs incoming traffic from the internet to a regional backend service, providing a stable IP address while distributing load across instances.
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'
The backendService property points to your backend service, which must already exist with health checks configured. The portRange specifies which port receives traffic. When loadBalancingScheme is EXTERNAL (the default), the rule accepts traffic from the internet and routes it to your backend.
Route internal traffic within a VPC
Applications that load balance traffic between services within a VPC use internal forwarding rules, which provide private IP addresses accessible only from within the specified network.
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 restricts access to the specified network and subnetwork. The allPorts property accepts traffic on any port, simplifying configuration when backends listen on multiple ports. The ipVersion property controls whether the rule uses IPv4 or IPv6 addressing.
Forward traffic to a target pool
Legacy load balancing configurations use target pools to group instances, with forwarding rules directing traffic based on IP protocol and port range.
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 references a TargetPool instead of a backend service. The portRange limits which port receives forwarded traffic. This configuration uses the default network when network and subnetwork are omitted.
Connect to services via Private Service Connect
Private Service Connect enables private connectivity to services across VPC networks without exposing traffic to the internet. Consumer forwarding rules establish endpoints that connect to producer service attachments.
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 side. Setting loadBalancingScheme to an empty string ("") indicates Private Service Connect usage. The ipAddress property specifies the consumer-side endpoint address, and allowPscGlobalAccess controls whether the endpoint can be accessed from other regions.
Forward all protocols with L3_DEFAULT
Some workloads need to handle multiple protocols or non-standard protocols. The L3_DEFAULT protocol forwards all IP traffic regardless of protocol type.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const healthCheck = new gcp.compute.RegionHealthCheck("health_check", {
name: "health-check",
region: "us-central1",
tcpHealthCheck: {
port: 80,
},
});
const service = new gcp.compute.RegionBackendService("service", {
region: "us-central1",
name: "service",
healthChecks: healthCheck.id,
protocol: "UNSPECIFIED",
loadBalancingScheme: "EXTERNAL",
});
const fwdRule = new gcp.compute.ForwardingRule("fwd_rule", {
name: "l3-forwarding-rule",
backendService: service.id,
ipProtocol: "L3_DEFAULT",
allPorts: true,
});
import pulumi
import pulumi_gcp as gcp
health_check = gcp.compute.RegionHealthCheck("health_check",
name="health-check",
region="us-central1",
tcp_health_check={
"port": 80,
})
service = gcp.compute.RegionBackendService("service",
region="us-central1",
name="service",
health_checks=health_check.id,
protocol="UNSPECIFIED",
load_balancing_scheme="EXTERNAL")
fwd_rule = gcp.compute.ForwardingRule("fwd_rule",
name="l3-forwarding-rule",
backend_service=service.id,
ip_protocol="L3_DEFAULT",
all_ports=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 {
healthCheck, err := compute.NewRegionHealthCheck(ctx, "health_check", &compute.RegionHealthCheckArgs{
Name: pulumi.String("health-check"),
Region: pulumi.String("us-central1"),
TcpHealthCheck: &compute.RegionHealthCheckTcpHealthCheckArgs{
Port: pulumi.Int(80),
},
})
if err != nil {
return err
}
service, err := compute.NewRegionBackendService(ctx, "service", &compute.RegionBackendServiceArgs{
Region: pulumi.String("us-central1"),
Name: pulumi.String("service"),
HealthChecks: healthCheck.ID(),
Protocol: pulumi.String("UNSPECIFIED"),
LoadBalancingScheme: pulumi.String("EXTERNAL"),
})
if err != nil {
return err
}
_, err = compute.NewForwardingRule(ctx, "fwd_rule", &compute.ForwardingRuleArgs{
Name: pulumi.String("l3-forwarding-rule"),
BackendService: service.ID(),
IpProtocol: pulumi.String("L3_DEFAULT"),
AllPorts: 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(() =>
{
var healthCheck = new Gcp.Compute.RegionHealthCheck("health_check", new()
{
Name = "health-check",
Region = "us-central1",
TcpHealthCheck = new Gcp.Compute.Inputs.RegionHealthCheckTcpHealthCheckArgs
{
Port = 80,
},
});
var service = new Gcp.Compute.RegionBackendService("service", new()
{
Region = "us-central1",
Name = "service",
HealthChecks = healthCheck.Id,
Protocol = "UNSPECIFIED",
LoadBalancingScheme = "EXTERNAL",
});
var fwdRule = new Gcp.Compute.ForwardingRule("fwd_rule", new()
{
Name = "l3-forwarding-rule",
BackendService = service.Id,
IpProtocol = "L3_DEFAULT",
AllPorts = true,
});
});
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 healthCheck = new RegionHealthCheck("healthCheck", RegionHealthCheckArgs.builder()
.name("health-check")
.region("us-central1")
.tcpHealthCheck(RegionHealthCheckTcpHealthCheckArgs.builder()
.port(80)
.build())
.build());
var service = new RegionBackendService("service", RegionBackendServiceArgs.builder()
.region("us-central1")
.name("service")
.healthChecks(healthCheck.id())
.protocol("UNSPECIFIED")
.loadBalancingScheme("EXTERNAL")
.build());
var fwdRule = new ForwardingRule("fwdRule", ForwardingRuleArgs.builder()
.name("l3-forwarding-rule")
.backendService(service.id())
.ipProtocol("L3_DEFAULT")
.allPorts(true)
.build());
}
}
resources:
fwdRule:
type: gcp:compute:ForwardingRule
name: fwd_rule
properties:
name: l3-forwarding-rule
backendService: ${service.id}
ipProtocol: L3_DEFAULT
allPorts: true
service:
type: gcp:compute:RegionBackendService
properties:
region: us-central1
name: service
healthChecks: ${healthCheck.id}
protocol: UNSPECIFIED
loadBalancingScheme: EXTERNAL
healthCheck:
type: gcp:compute:RegionHealthCheck
name: health_check
properties:
name: health-check
region: us-central1
tcpHealthCheck:
port: 80
Setting ipProtocol to L3_DEFAULT requires allPorts to be true, allowing the rule to forward packets regardless of destination port. The backend service must use UNSPECIFIED protocol to accept this traffic. This configuration works with target instances or backend services that handle multiple protocols.
Route traffic based on source IP ranges
Traffic steering allows different forwarding rules to share the same IP address but route traffic differently based on source IP ranges, enabling region-specific or client-specific routing policies.
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 lists IP addresses or CIDR ranges that trigger this forwarding rule. Multiple rules can share the same ipAddress as long as they have different sourceIpRanges. This configuration requires a base forwarding rule (without sourceIpRanges) to exist first, which handles traffic from all other sources.
Beyond these examples
These snippets focus on specific forwarding rule features: external and internal load balancing schemes, Private Service Connect endpoints, protocol and port configuration, and source-based traffic steering. They’re intentionally minimal rather than full load balancing deployments.
The examples reference pre-existing infrastructure such as backend services, target pools, or service attachments, VPC networks and subnetworks, and health checks and static 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 for dual-stack)
- Service Directory registration (serviceDirectoryRegistrations)
- Packet mirroring (isMirroringCollector)
- Labels and metadata (labels property)
- Network tier selection (networkTier for PREMIUM vs STANDARD)
- Global access controls (allowGlobalAccess for cross-region)
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 FREEFrequently Asked Questions
Configuration & Immutability
ipAddress, ipProtocol, ipVersion, name, network, networkTier, portRange, ports, allPorts, loadBalancingScheme, backendService, subnetwork, and region. Changing these requires recreating the resource.ports, portRange, and allPorts fields are mutually exclusive. Choose one port configuration method based on your use case.ipAddress, the networkTier value must match the networkTier of the Address resource. Ensure both use the same tier (PREMIUM or STANDARD).Port Configuration & Conflicts
[IPAddress, IPProtocol] pair with overlapping portRanges. For external rules, this applies globally. For internal rules, this applies within the same VPC network.ports field requires ipProtocol be TCP, UDP, or SCTP.Protocol & IP Configuration
ipProtocol: L3_DEFAULT, you must set allPorts: true. L3_DEFAULT can attach to target instances or backend services with UNSPECIFIED protocol, but cannot attach to backend services with TCP or UDP.ipVersion: IPV6 and ensure your subnetwork has stackType: IPV4_IPV6 and ipv6AccessType: INTERNAL. The subnetwork must support dual-stack configuration.Private Service Connect (PSC)
loadBalancingScheme to an empty string (""), specify target as a ServiceAttachment, and configure network and ipAddress. Use allowPscGlobalAccess: true for cross-region access and noAutomateDnsZone: true to disable automatic DNS zone creation.Load Balancing Schemes
purpose: REGIONAL_MANAGED_PROXY. Create this subnetwork and add it as a dependency before creating the forwarding rule.backendService for Internal TCP/UDP Load Balancing and Network Load Balancing (required). Use target for other load balancer types like TargetPool, TargetHttpProxy, or ServiceAttachment.allowGlobalAccess: true to allow clients from all regions to access your internal load balancer. Otherwise, only clients in the same region can access it.Advanced Features & Limitations
sourceIpRanges. This field only works with regional forwarding rules whose loadBalancingScheme is EXTERNAL.isMirroringCollector can only be set to true for load balancers with loadBalancingScheme set to INTERNAL. External load balancers cannot be used as packet mirroring collectors.Using a different cloud?
Explore networking guides for other cloud providers: