The gcp:compute/subnetwork:Subnetwork resource, part of the Pulumi GCP provider, defines regional IP address ranges within a VPC network, controlling where VM instances and other resources draw their internal addresses. This guide focuses on four capabilities: primary and secondary IP range allocation, VPC flow logging, IPv6 configurations, and proxy-only subnets for load balancers.
Subnets belong to VPC networks that must have custom subnet mode enabled (autoCreateSubnetworks set to false). Some configurations reference InternalRange resources for coordinated IP allocation. The examples are intentionally small. Combine them with your own VPC networks and routing configuration.
Create a subnet with secondary IP ranges
Most VPC deployments subdivide the network into regional subnets with primary CIDR blocks for VM instances. Secondary ranges provide additional IP space for container pods or alias IPs.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const custom_test = new gcp.compute.Network("custom-test", {
name: "test-network",
autoCreateSubnetworks: false,
});
const network_with_private_secondary_ip_ranges = new gcp.compute.Subnetwork("network-with-private-secondary-ip-ranges", {
name: "test-subnetwork",
ipCidrRange: "10.2.0.0/16",
region: "us-central1",
network: custom_test.id,
secondaryIpRanges: [{
rangeName: "tf-test-secondary-range-update1",
ipCidrRange: "192.168.10.0/24",
}],
});
import pulumi
import pulumi_gcp as gcp
custom_test = gcp.compute.Network("custom-test",
name="test-network",
auto_create_subnetworks=False)
network_with_private_secondary_ip_ranges = gcp.compute.Subnetwork("network-with-private-secondary-ip-ranges",
name="test-subnetwork",
ip_cidr_range="10.2.0.0/16",
region="us-central1",
network=custom_test.id,
secondary_ip_ranges=[{
"range_name": "tf-test-secondary-range-update1",
"ip_cidr_range": "192.168.10.0/24",
}])
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 {
custom_test, err := compute.NewNetwork(ctx, "custom-test", &compute.NetworkArgs{
Name: pulumi.String("test-network"),
AutoCreateSubnetworks: pulumi.Bool(false),
})
if err != nil {
return err
}
_, err = compute.NewSubnetwork(ctx, "network-with-private-secondary-ip-ranges", &compute.SubnetworkArgs{
Name: pulumi.String("test-subnetwork"),
IpCidrRange: pulumi.String("10.2.0.0/16"),
Region: pulumi.String("us-central1"),
Network: custom_test.ID(),
SecondaryIpRanges: compute.SubnetworkSecondaryIpRangeArray{
&compute.SubnetworkSecondaryIpRangeArgs{
RangeName: pulumi.String("tf-test-secondary-range-update1"),
IpCidrRange: pulumi.String("192.168.10.0/24"),
},
},
})
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 custom_test = new Gcp.Compute.Network("custom-test", new()
{
Name = "test-network",
AutoCreateSubnetworks = false,
});
var network_with_private_secondary_ip_ranges = new Gcp.Compute.Subnetwork("network-with-private-secondary-ip-ranges", new()
{
Name = "test-subnetwork",
IpCidrRange = "10.2.0.0/16",
Region = "us-central1",
Network = custom_test.Id,
SecondaryIpRanges = new[]
{
new Gcp.Compute.Inputs.SubnetworkSecondaryIpRangeArgs
{
RangeName = "tf-test-secondary-range-update1",
IpCidrRange = "192.168.10.0/24",
},
},
});
});
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.inputs.SubnetworkSecondaryIpRangeArgs;
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 custom_test = new Network("custom-test", NetworkArgs.builder()
.name("test-network")
.autoCreateSubnetworks(false)
.build());
var network_with_private_secondary_ip_ranges = new Subnetwork("network-with-private-secondary-ip-ranges", SubnetworkArgs.builder()
.name("test-subnetwork")
.ipCidrRange("10.2.0.0/16")
.region("us-central1")
.network(custom_test.id())
.secondaryIpRanges(SubnetworkSecondaryIpRangeArgs.builder()
.rangeName("tf-test-secondary-range-update1")
.ipCidrRange("192.168.10.0/24")
.build())
.build());
}
}
resources:
network-with-private-secondary-ip-ranges:
type: gcp:compute:Subnetwork
properties:
name: test-subnetwork
ipCidrRange: 10.2.0.0/16
region: us-central1
network: ${["custom-test"].id}
secondaryIpRanges:
- rangeName: tf-test-secondary-range-update1
ipCidrRange: 192.168.10.0/24
custom-test:
type: gcp:compute:Network
properties:
name: test-network
autoCreateSubnetworks: false
The ipCidrRange defines the primary address space for VM instances. The secondaryIpRanges array adds named ranges for containers or alias IPs. Each secondary range needs a rangeName and its own ipCidrRange that doesn’t overlap with the primary range.
Enable VPC flow logs for traffic analysis
Network troubleshooting and security analysis require visibility into traffic patterns. VPC flow logs capture metadata about IP traffic flowing through the subnet.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const custom_test = new gcp.compute.Network("custom-test", {
name: "log-test-network",
autoCreateSubnetworks: false,
});
const subnet_with_logging = new gcp.compute.Subnetwork("subnet-with-logging", {
name: "log-test-subnetwork",
ipCidrRange: "10.2.0.0/16",
region: "us-central1",
network: custom_test.id,
logConfig: {
aggregationInterval: "INTERVAL_10_MIN",
flowSampling: 0.5,
metadata: "INCLUDE_ALL_METADATA",
},
});
import pulumi
import pulumi_gcp as gcp
custom_test = gcp.compute.Network("custom-test",
name="log-test-network",
auto_create_subnetworks=False)
subnet_with_logging = gcp.compute.Subnetwork("subnet-with-logging",
name="log-test-subnetwork",
ip_cidr_range="10.2.0.0/16",
region="us-central1",
network=custom_test.id,
log_config={
"aggregation_interval": "INTERVAL_10_MIN",
"flow_sampling": 0.5,
"metadata": "INCLUDE_ALL_METADATA",
})
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 {
custom_test, err := compute.NewNetwork(ctx, "custom-test", &compute.NetworkArgs{
Name: pulumi.String("log-test-network"),
AutoCreateSubnetworks: pulumi.Bool(false),
})
if err != nil {
return err
}
_, err = compute.NewSubnetwork(ctx, "subnet-with-logging", &compute.SubnetworkArgs{
Name: pulumi.String("log-test-subnetwork"),
IpCidrRange: pulumi.String("10.2.0.0/16"),
Region: pulumi.String("us-central1"),
Network: custom_test.ID(),
LogConfig: &compute.SubnetworkLogConfigArgs{
AggregationInterval: pulumi.String("INTERVAL_10_MIN"),
FlowSampling: pulumi.Float64(0.5),
Metadata: pulumi.String("INCLUDE_ALL_METADATA"),
},
})
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 custom_test = new Gcp.Compute.Network("custom-test", new()
{
Name = "log-test-network",
AutoCreateSubnetworks = false,
});
var subnet_with_logging = new Gcp.Compute.Subnetwork("subnet-with-logging", new()
{
Name = "log-test-subnetwork",
IpCidrRange = "10.2.0.0/16",
Region = "us-central1",
Network = custom_test.Id,
LogConfig = new Gcp.Compute.Inputs.SubnetworkLogConfigArgs
{
AggregationInterval = "INTERVAL_10_MIN",
FlowSampling = 0.5,
Metadata = "INCLUDE_ALL_METADATA",
},
});
});
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.inputs.SubnetworkLogConfigArgs;
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 custom_test = new Network("custom-test", NetworkArgs.builder()
.name("log-test-network")
.autoCreateSubnetworks(false)
.build());
var subnet_with_logging = new Subnetwork("subnet-with-logging", SubnetworkArgs.builder()
.name("log-test-subnetwork")
.ipCidrRange("10.2.0.0/16")
.region("us-central1")
.network(custom_test.id())
.logConfig(SubnetworkLogConfigArgs.builder()
.aggregationInterval("INTERVAL_10_MIN")
.flowSampling(0.5)
.metadata("INCLUDE_ALL_METADATA")
.build())
.build());
}
}
resources:
subnet-with-logging:
type: gcp:compute:Subnetwork
properties:
name: log-test-subnetwork
ipCidrRange: 10.2.0.0/16
region: us-central1
network: ${["custom-test"].id}
logConfig:
aggregationInterval: INTERVAL_10_MIN
flowSampling: 0.5
metadata: INCLUDE_ALL_METADATA
custom-test:
type: gcp:compute:Network
properties:
name: log-test-network
autoCreateSubnetworks: false
The logConfig block enables flow logging with three key settings: aggregationInterval controls how often logs are written (here, every 10 minutes), flowSampling determines what fraction of traffic to capture (0.5 means 50%), and metadata specifies whether to include all available fields. Logs export to Cloud Logging automatically.
Reserve a proxy-only subnet for load balancers
Regional internal Application Load Balancers require dedicated proxy-only subnets that don’t host VM instances. These subnets provide IP addresses for the load balancer’s Envoy proxies.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const custom_test = new gcp.compute.Network("custom-test", {
name: "l7lb-test-network",
autoCreateSubnetworks: false,
});
const network_for_l7lb = new gcp.compute.Subnetwork("network-for-l7lb", {
name: "l7lb-test-subnetwork",
ipCidrRange: "10.0.0.0/22",
region: "us-central1",
purpose: "REGIONAL_MANAGED_PROXY",
role: "ACTIVE",
network: custom_test.id,
});
import pulumi
import pulumi_gcp as gcp
custom_test = gcp.compute.Network("custom-test",
name="l7lb-test-network",
auto_create_subnetworks=False)
network_for_l7lb = gcp.compute.Subnetwork("network-for-l7lb",
name="l7lb-test-subnetwork",
ip_cidr_range="10.0.0.0/22",
region="us-central1",
purpose="REGIONAL_MANAGED_PROXY",
role="ACTIVE",
network=custom_test.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 {
custom_test, err := compute.NewNetwork(ctx, "custom-test", &compute.NetworkArgs{
Name: pulumi.String("l7lb-test-network"),
AutoCreateSubnetworks: pulumi.Bool(false),
})
if err != nil {
return err
}
_, err = compute.NewSubnetwork(ctx, "network-for-l7lb", &compute.SubnetworkArgs{
Name: pulumi.String("l7lb-test-subnetwork"),
IpCidrRange: pulumi.String("10.0.0.0/22"),
Region: pulumi.String("us-central1"),
Purpose: pulumi.String("REGIONAL_MANAGED_PROXY"),
Role: pulumi.String("ACTIVE"),
Network: custom_test.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 custom_test = new Gcp.Compute.Network("custom-test", new()
{
Name = "l7lb-test-network",
AutoCreateSubnetworks = false,
});
var network_for_l7lb = new Gcp.Compute.Subnetwork("network-for-l7lb", new()
{
Name = "l7lb-test-subnetwork",
IpCidrRange = "10.0.0.0/22",
Region = "us-central1",
Purpose = "REGIONAL_MANAGED_PROXY",
Role = "ACTIVE",
Network = custom_test.Id,
});
});
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 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 custom_test = new Network("custom-test", NetworkArgs.builder()
.name("l7lb-test-network")
.autoCreateSubnetworks(false)
.build());
var network_for_l7lb = new Subnetwork("network-for-l7lb", SubnetworkArgs.builder()
.name("l7lb-test-subnetwork")
.ipCidrRange("10.0.0.0/22")
.region("us-central1")
.purpose("REGIONAL_MANAGED_PROXY")
.role("ACTIVE")
.network(custom_test.id())
.build());
}
}
resources:
network-for-l7lb:
type: gcp:compute:Subnetwork
properties:
name: l7lb-test-subnetwork
ipCidrRange: 10.0.0.0/22
region: us-central1
purpose: REGIONAL_MANAGED_PROXY
role: ACTIVE
network: ${["custom-test"].id}
custom-test:
type: gcp:compute:Network
properties:
name: l7lb-test-network
autoCreateSubnetworks: false
Setting purpose to REGIONAL_MANAGED_PROXY marks this subnet as reserved for load balancer infrastructure. The role property (ACTIVE or BACKUP) controls whether the subnet is actively serving traffic or ready for failover. These subnets cannot host VM instances.
Enable external IPv6 connectivity
Applications that need to communicate with IPv6 clients on the internet require dual-stack subnets with globally routable addresses.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const custom_test = new gcp.compute.Network("custom-test", {
name: "ipv6-test-network",
autoCreateSubnetworks: false,
});
const subnetwork_ipv6 = new gcp.compute.Subnetwork("subnetwork-ipv6", {
name: "ipv6-test-subnetwork",
ipCidrRange: "10.0.0.0/22",
region: "us-west2",
stackType: "IPV4_IPV6",
ipv6AccessType: "EXTERNAL",
network: custom_test.id,
});
import pulumi
import pulumi_gcp as gcp
custom_test = gcp.compute.Network("custom-test",
name="ipv6-test-network",
auto_create_subnetworks=False)
subnetwork_ipv6 = gcp.compute.Subnetwork("subnetwork-ipv6",
name="ipv6-test-subnetwork",
ip_cidr_range="10.0.0.0/22",
region="us-west2",
stack_type="IPV4_IPV6",
ipv6_access_type="EXTERNAL",
network=custom_test.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 {
custom_test, err := compute.NewNetwork(ctx, "custom-test", &compute.NetworkArgs{
Name: pulumi.String("ipv6-test-network"),
AutoCreateSubnetworks: pulumi.Bool(false),
})
if err != nil {
return err
}
_, err = compute.NewSubnetwork(ctx, "subnetwork-ipv6", &compute.SubnetworkArgs{
Name: pulumi.String("ipv6-test-subnetwork"),
IpCidrRange: pulumi.String("10.0.0.0/22"),
Region: pulumi.String("us-west2"),
StackType: pulumi.String("IPV4_IPV6"),
Ipv6AccessType: pulumi.String("EXTERNAL"),
Network: custom_test.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 custom_test = new Gcp.Compute.Network("custom-test", new()
{
Name = "ipv6-test-network",
AutoCreateSubnetworks = false,
});
var subnetwork_ipv6 = new Gcp.Compute.Subnetwork("subnetwork-ipv6", new()
{
Name = "ipv6-test-subnetwork",
IpCidrRange = "10.0.0.0/22",
Region = "us-west2",
StackType = "IPV4_IPV6",
Ipv6AccessType = "EXTERNAL",
Network = custom_test.Id,
});
});
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 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 custom_test = new Network("custom-test", NetworkArgs.builder()
.name("ipv6-test-network")
.autoCreateSubnetworks(false)
.build());
var subnetwork_ipv6 = new Subnetwork("subnetwork-ipv6", SubnetworkArgs.builder()
.name("ipv6-test-subnetwork")
.ipCidrRange("10.0.0.0/22")
.region("us-west2")
.stackType("IPV4_IPV6")
.ipv6AccessType("EXTERNAL")
.network(custom_test.id())
.build());
}
}
resources:
subnetwork-ipv6:
type: gcp:compute:Subnetwork
properties:
name: ipv6-test-subnetwork
ipCidrRange: 10.0.0.0/22
region: us-west2
stackType: IPV4_IPV6
ipv6AccessType: EXTERNAL
network: ${["custom-test"].id}
custom-test:
type: gcp:compute:Network
properties:
name: ipv6-test-network
autoCreateSubnetworks: false
The stackType property set to IPV4_IPV6 enables dual-stack networking. The ipv6AccessType property controls whether IPv6 addresses are EXTERNAL (internet-routable) or INTERNAL (VPC-only). External IPv6 addresses allow direct communication with IPv6 clients on the internet.
Configure internal IPv6 for VPC communication
Some workloads need IPv6 for internal VPC communication without exposing addresses to the internet. Internal IPv6 uses Unique Local Addresses (ULA) that remain private.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const custom_test = new gcp.compute.Network("custom-test", {
name: "internal-ipv6-test-network",
autoCreateSubnetworks: false,
enableUlaInternalIpv6: true,
});
const subnetwork_internal_ipv6 = new gcp.compute.Subnetwork("subnetwork-internal-ipv6", {
name: "internal-ipv6-test-subnetwork",
ipCidrRange: "10.0.0.0/22",
region: "us-west2",
stackType: "IPV4_IPV6",
ipv6AccessType: "INTERNAL",
network: custom_test.id,
});
import pulumi
import pulumi_gcp as gcp
custom_test = gcp.compute.Network("custom-test",
name="internal-ipv6-test-network",
auto_create_subnetworks=False,
enable_ula_internal_ipv6=True)
subnetwork_internal_ipv6 = gcp.compute.Subnetwork("subnetwork-internal-ipv6",
name="internal-ipv6-test-subnetwork",
ip_cidr_range="10.0.0.0/22",
region="us-west2",
stack_type="IPV4_IPV6",
ipv6_access_type="INTERNAL",
network=custom_test.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 {
custom_test, err := compute.NewNetwork(ctx, "custom-test", &compute.NetworkArgs{
Name: pulumi.String("internal-ipv6-test-network"),
AutoCreateSubnetworks: pulumi.Bool(false),
EnableUlaInternalIpv6: pulumi.Bool(true),
})
if err != nil {
return err
}
_, err = compute.NewSubnetwork(ctx, "subnetwork-internal-ipv6", &compute.SubnetworkArgs{
Name: pulumi.String("internal-ipv6-test-subnetwork"),
IpCidrRange: pulumi.String("10.0.0.0/22"),
Region: pulumi.String("us-west2"),
StackType: pulumi.String("IPV4_IPV6"),
Ipv6AccessType: pulumi.String("INTERNAL"),
Network: custom_test.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 custom_test = new Gcp.Compute.Network("custom-test", new()
{
Name = "internal-ipv6-test-network",
AutoCreateSubnetworks = false,
EnableUlaInternalIpv6 = true,
});
var subnetwork_internal_ipv6 = new Gcp.Compute.Subnetwork("subnetwork-internal-ipv6", new()
{
Name = "internal-ipv6-test-subnetwork",
IpCidrRange = "10.0.0.0/22",
Region = "us-west2",
StackType = "IPV4_IPV6",
Ipv6AccessType = "INTERNAL",
Network = custom_test.Id,
});
});
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 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 custom_test = new Network("custom-test", NetworkArgs.builder()
.name("internal-ipv6-test-network")
.autoCreateSubnetworks(false)
.enableUlaInternalIpv6(true)
.build());
var subnetwork_internal_ipv6 = new Subnetwork("subnetwork-internal-ipv6", SubnetworkArgs.builder()
.name("internal-ipv6-test-subnetwork")
.ipCidrRange("10.0.0.0/22")
.region("us-west2")
.stackType("IPV4_IPV6")
.ipv6AccessType("INTERNAL")
.network(custom_test.id())
.build());
}
}
resources:
subnetwork-internal-ipv6:
type: gcp:compute:Subnetwork
properties:
name: internal-ipv6-test-subnetwork
ipCidrRange: 10.0.0.0/22
region: us-west2
stackType: IPV4_IPV6
ipv6AccessType: INTERNAL
network: ${["custom-test"].id}
custom-test:
type: gcp:compute:Network
properties:
name: internal-ipv6-test-network
autoCreateSubnetworks: false
enableUlaInternalIpv6: true
Internal IPv6 requires the parent network to have enableUlaInternalIpv6 set to true at creation time. Setting ipv6AccessType to INTERNAL allocates ULA addresses that work within the VPC but aren’t routable on the internet. This configuration is useful for IPv6-native applications that don’t need external connectivity.
Allocate IP ranges from reserved address pools
Organizations managing multiple VPCs or planning network migrations often pre-reserve IP ranges to prevent conflicts. Reserved internal ranges let you allocate subnet addresses from a centrally managed pool.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const _default = new gcp.compute.Network("default", {
name: "network-reserved-internal-range",
autoCreateSubnetworks: false,
});
const reserved = new gcp.networkconnectivity.InternalRange("reserved", {
name: "reserved",
network: _default.id,
usage: "FOR_VPC",
peering: "FOR_SELF",
prefixLength: 24,
targetCidrRanges: ["10.0.0.0/8"],
});
const subnetwork_reserved_internal_range = new gcp.compute.Subnetwork("subnetwork-reserved-internal-range", {
name: "subnetwork-reserved-internal-range",
region: "us-central1",
network: _default.id,
reservedInternalRange: pulumi.interpolate`networkconnectivity.googleapis.com/${reserved.id}`,
});
import pulumi
import pulumi_gcp as gcp
default = gcp.compute.Network("default",
name="network-reserved-internal-range",
auto_create_subnetworks=False)
reserved = gcp.networkconnectivity.InternalRange("reserved",
name="reserved",
network=default.id,
usage="FOR_VPC",
peering="FOR_SELF",
prefix_length=24,
target_cidr_ranges=["10.0.0.0/8"])
subnetwork_reserved_internal_range = gcp.compute.Subnetwork("subnetwork-reserved-internal-range",
name="subnetwork-reserved-internal-range",
region="us-central1",
network=default.id,
reserved_internal_range=reserved.id.apply(lambda id: f"networkconnectivity.googleapis.com/{id}"))
package main
import (
"fmt"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/networkconnectivity"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_default, err := compute.NewNetwork(ctx, "default", &compute.NetworkArgs{
Name: pulumi.String("network-reserved-internal-range"),
AutoCreateSubnetworks: pulumi.Bool(false),
})
if err != nil {
return err
}
reserved, err := networkconnectivity.NewInternalRange(ctx, "reserved", &networkconnectivity.InternalRangeArgs{
Name: pulumi.String("reserved"),
Network: _default.ID(),
Usage: pulumi.String("FOR_VPC"),
Peering: pulumi.String("FOR_SELF"),
PrefixLength: pulumi.Int(24),
TargetCidrRanges: pulumi.StringArray{
pulumi.String("10.0.0.0/8"),
},
})
if err != nil {
return err
}
_, err = compute.NewSubnetwork(ctx, "subnetwork-reserved-internal-range", &compute.SubnetworkArgs{
Name: pulumi.String("subnetwork-reserved-internal-range"),
Region: pulumi.String("us-central1"),
Network: _default.ID(),
ReservedInternalRange: reserved.ID().ApplyT(func(id string) (string, error) {
return fmt.Sprintf("networkconnectivity.googleapis.com/%v", id), nil
}).(pulumi.StringOutput),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
return await Deployment.RunAsync(() =>
{
var @default = new Gcp.Compute.Network("default", new()
{
Name = "network-reserved-internal-range",
AutoCreateSubnetworks = false,
});
var reserved = new Gcp.NetworkConnectivity.InternalRange("reserved", new()
{
Name = "reserved",
Network = @default.Id,
Usage = "FOR_VPC",
Peering = "FOR_SELF",
PrefixLength = 24,
TargetCidrRanges = new[]
{
"10.0.0.0/8",
},
});
var subnetwork_reserved_internal_range = new Gcp.Compute.Subnetwork("subnetwork-reserved-internal-range", new()
{
Name = "subnetwork-reserved-internal-range",
Region = "us-central1",
Network = @default.Id,
ReservedInternalRange = reserved.Id.Apply(id => $"networkconnectivity.googleapis.com/{id}"),
});
});
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.networkconnectivity.InternalRange;
import com.pulumi.gcp.networkconnectivity.InternalRangeArgs;
import com.pulumi.gcp.compute.Subnetwork;
import com.pulumi.gcp.compute.SubnetworkArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var default_ = new Network("default", NetworkArgs.builder()
.name("network-reserved-internal-range")
.autoCreateSubnetworks(false)
.build());
var reserved = new InternalRange("reserved", InternalRangeArgs.builder()
.name("reserved")
.network(default_.id())
.usage("FOR_VPC")
.peering("FOR_SELF")
.prefixLength(24)
.targetCidrRanges("10.0.0.0/8")
.build());
var subnetwork_reserved_internal_range = new Subnetwork("subnetwork-reserved-internal-range", SubnetworkArgs.builder()
.name("subnetwork-reserved-internal-range")
.region("us-central1")
.network(default_.id())
.reservedInternalRange(reserved.id().applyValue(_id -> String.format("networkconnectivity.googleapis.com/%s", _id)))
.build());
}
}
resources:
subnetwork-reserved-internal-range:
type: gcp:compute:Subnetwork
properties:
name: subnetwork-reserved-internal-range
region: us-central1
network: ${default.id}
reservedInternalRange: networkconnectivity.googleapis.com/${reserved.id}
default:
type: gcp:compute:Network
properties:
name: network-reserved-internal-range
autoCreateSubnetworks: false
reserved:
type: gcp:networkconnectivity:InternalRange
properties:
name: reserved
network: ${default.id}
usage: FOR_VPC
peering: FOR_SELF
prefixLength: 24
targetCidrRanges:
- 10.0.0.0/8
The reservedInternalRange property references an InternalRange resource using the networkconnectivity.googleapis.com API format. When specified, you can omit ipCidrRange; the subnet draws its address space from the reserved pool. This approach coordinates IP allocation across VPCs or during network migrations.
Beyond these examples
These snippets focus on specific subnet-level features: primary and secondary IP range allocation, VPC flow logging and traffic analysis, IPv4/IPv6 dual-stack and IPv6-only configurations, and proxy-only subnets for load balancers. They’re intentionally minimal rather than full network architectures.
The examples reference pre-existing infrastructure such as VPC networks (which must have autoCreateSubnetworks disabled) and InternalRange resources for reserved IP allocation. They focus on configuring the subnet rather than provisioning the entire network.
To keep things focused, common subnet patterns are omitted, including:
- Private Google Access configuration (privateIpGoogleAccess)
- CIDR overlap handling (allowSubnetCidrRoutesOverlap)
- Private NAT gateway subnets (purpose PRIVATE_NAT)
- Subnet mask resolution modes (resolveSubnetMask)
These omissions are intentional: the goal is to illustrate how each subnet feature is wired, not provide drop-in network modules. See the Subnetwork resource reference for all available configuration options.
Let's configure GCP VPC Subnetworks
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Immutability & Resource Lifecycle
name, network, region, description, externalIpv6Prefix, internalIpv6Prefix, ipCollection, params, reservedInternalRange, and resolveSubnetMask.ipv6AccessType property is immutable and can only be specified during creation or the first time the subnet is updated into IPV4_IPV6 dual stack.IPv6 Configuration
stackType to IPV4_IPV6 for dual stack or IPV6_ONLY for IPv6-only. Configure ipv6AccessType as either EXTERNAL or INTERNAL. For internal IPv6, the parent network must have enableUlaInternalIpv6 set to true.EXTERNAL provides public IPv6 connectivity but prevents enabling direct path. INTERNAL provides private IPv6 connectivity and requires the network to have ULA internal IPv6 enabled.ipv6AccessType is set to EXTERNAL. Use INTERNAL access type if direct path is required.Subnet Purpose & Load Balancers
PRIVATE (default for general use), REGIONAL_MANAGED_PROXY (preferred for regional Envoy load balancers), GLOBAL_MANAGED_PROXY (for cross-regional Envoy load balancers), PRIVATE_SERVICE_CONNECT (for published services), PEER_MIGRATION (for resource migration), and PRIVATE_NAT (for NAT gateways).purpose to REGIONAL_MANAGED_PROXY and role to ACTIVE for active use or BACKUP for standby. The role property only applies when purpose is REGIONAL_MANAGED_PROXY.purpose is set to REGIONAL_MANAGED_PROXY or GLOBAL_MANAGED_PROXY.IP Ranges & Addressing
reservedInternalRange to a networkconnectivity.InternalRange resource ID (prefixed with networkconnectivity.googleapis.com). When using reserved ranges, ipCidrRange becomes optional.secondaryIpRanges array with either explicit ipCidrRange values or reservedInternalRange references. Secondary ranges are used for VM alias IPs.secondaryIpRange. When false (default), removing secondary ranges from config won’t produce a diff. When true, removal sends an empty list to the API.allowSubnetCidrRoutesOverlap to true. This allows packets to match dynamic BGP routes even when destinations match existing subnet ranges.Network Requirements & Configuration
autoCreateSubnetworks set to false. Only distributed mode networks can have user-created subnetworks.logConfig with aggregationInterval, flowSampling, and metadata settings. Note that flow logging isn’t supported for proxy subnets (REGIONAL_MANAGED_PROXY or GLOBAL_MANAGED_PROXY purpose).Using a different cloud?
Explore networking guides for other cloud providers: