Configure GCP Internal Ranges for VPC Networks

The gcp:networkconnectivity/internalRange:InternalRange resource, part of the Pulumi GCP provider, defines reserved IP address ranges within VPC networks for IPAM operations, tracking both internal VPC allocations and external address space. This guide focuses on four capabilities: fixed CIDR reservation, automatic range allocation, external range tracking, and overlap handling and migration coordination.

Internal ranges belong to VPC networks and may reference existing subnets for overlap or migration scenarios. The examples are intentionally small. Combine them with your own VPC infrastructure and subnet planning.

Reserve a fixed CIDR range for VPC use

Most deployments start by reserving a specific CIDR block for VPC resources, establishing address space ownership and preventing conflicts.

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

const defaultNetwork = new gcp.compute.Network("default", {
    name: "internal-ranges",
    autoCreateSubnetworks: false,
});
const _default = new gcp.networkconnectivity.InternalRange("default", {
    name: "basic",
    description: "Test internal range",
    network: defaultNetwork.selfLink,
    usage: "FOR_VPC",
    peering: "FOR_SELF",
    ipCidrRange: "10.0.0.0/24",
    labels: {
        "label-a": "b",
    },
});
import pulumi
import pulumi_gcp as gcp

default_network = gcp.compute.Network("default",
    name="internal-ranges",
    auto_create_subnetworks=False)
default = gcp.networkconnectivity.InternalRange("default",
    name="basic",
    description="Test internal range",
    network=default_network.self_link,
    usage="FOR_VPC",
    peering="FOR_SELF",
    ip_cidr_range="10.0.0.0/24",
    labels={
        "label-a": "b",
    })
package main

import (
	"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 {
		defaultNetwork, err := compute.NewNetwork(ctx, "default", &compute.NetworkArgs{
			Name:                  pulumi.String("internal-ranges"),
			AutoCreateSubnetworks: pulumi.Bool(false),
		})
		if err != nil {
			return err
		}
		_, err = networkconnectivity.NewInternalRange(ctx, "default", &networkconnectivity.InternalRangeArgs{
			Name:        pulumi.String("basic"),
			Description: pulumi.String("Test internal range"),
			Network:     defaultNetwork.SelfLink,
			Usage:       pulumi.String("FOR_VPC"),
			Peering:     pulumi.String("FOR_SELF"),
			IpCidrRange: pulumi.String("10.0.0.0/24"),
			Labels: pulumi.StringMap{
				"label-a": pulumi.String("b"),
			},
		})
		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 defaultNetwork = new Gcp.Compute.Network("default", new()
    {
        Name = "internal-ranges",
        AutoCreateSubnetworks = false,
    });

    var @default = new Gcp.NetworkConnectivity.InternalRange("default", new()
    {
        Name = "basic",
        Description = "Test internal range",
        Network = defaultNetwork.SelfLink,
        Usage = "FOR_VPC",
        Peering = "FOR_SELF",
        IpCidrRange = "10.0.0.0/24",
        Labels = 
        {
            { "label-a", "b" },
        },
    });

});
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 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 defaultNetwork = new Network("defaultNetwork", NetworkArgs.builder()
            .name("internal-ranges")
            .autoCreateSubnetworks(false)
            .build());

        var default_ = new InternalRange("default", InternalRangeArgs.builder()
            .name("basic")
            .description("Test internal range")
            .network(defaultNetwork.selfLink())
            .usage("FOR_VPC")
            .peering("FOR_SELF")
            .ipCidrRange("10.0.0.0/24")
            .labels(Map.of("label-a", "b"))
            .build());

    }
}
resources:
  default:
    type: gcp:networkconnectivity:InternalRange
    properties:
      name: basic
      description: Test internal range
      network: ${defaultNetwork.selfLink}
      usage: FOR_VPC
      peering: FOR_SELF
      ipCidrRange: 10.0.0.0/24
      labels:
        label-a: b
  defaultNetwork:
    type: gcp:compute:Network
    name: default
    properties:
      name: internal-ranges
      autoCreateSubnetworks: false

The ipCidrRange property specifies the exact address block to reserve. The usage property set to FOR_VPC indicates this range is for VPC resource allocation. The peering property controls whether this range is shared with peered networks; FOR_SELF means it’s only for this VPC. The network property links the range to a specific VPC.

Automatically allocate a range from target space

When you need a range of a specific size but don’t care about the exact CIDR, automatic allocation finds available space within your target address ranges.

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

const defaultNetwork = new gcp.compute.Network("default", {
    name: "internal-ranges",
    autoCreateSubnetworks: false,
});
const _default = new gcp.networkconnectivity.InternalRange("default", {
    name: "automatic-reservation",
    network: defaultNetwork.id,
    usage: "FOR_VPC",
    peering: "FOR_SELF",
    prefixLength: 24,
    targetCidrRanges: ["192.16.0.0/16"],
});
import pulumi
import pulumi_gcp as gcp

default_network = gcp.compute.Network("default",
    name="internal-ranges",
    auto_create_subnetworks=False)
default = gcp.networkconnectivity.InternalRange("default",
    name="automatic-reservation",
    network=default_network.id,
    usage="FOR_VPC",
    peering="FOR_SELF",
    prefix_length=24,
    target_cidr_ranges=["192.16.0.0/16"])
package main

import (
	"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 {
		defaultNetwork, err := compute.NewNetwork(ctx, "default", &compute.NetworkArgs{
			Name:                  pulumi.String("internal-ranges"),
			AutoCreateSubnetworks: pulumi.Bool(false),
		})
		if err != nil {
			return err
		}
		_, err = networkconnectivity.NewInternalRange(ctx, "default", &networkconnectivity.InternalRangeArgs{
			Name:         pulumi.String("automatic-reservation"),
			Network:      defaultNetwork.ID(),
			Usage:        pulumi.String("FOR_VPC"),
			Peering:      pulumi.String("FOR_SELF"),
			PrefixLength: pulumi.Int(24),
			TargetCidrRanges: pulumi.StringArray{
				pulumi.String("192.16.0.0/16"),
			},
		})
		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 defaultNetwork = new Gcp.Compute.Network("default", new()
    {
        Name = "internal-ranges",
        AutoCreateSubnetworks = false,
    });

    var @default = new Gcp.NetworkConnectivity.InternalRange("default", new()
    {
        Name = "automatic-reservation",
        Network = defaultNetwork.Id,
        Usage = "FOR_VPC",
        Peering = "FOR_SELF",
        PrefixLength = 24,
        TargetCidrRanges = new[]
        {
            "192.16.0.0/16",
        },
    });

});
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 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 defaultNetwork = new Network("defaultNetwork", NetworkArgs.builder()
            .name("internal-ranges")
            .autoCreateSubnetworks(false)
            .build());

        var default_ = new InternalRange("default", InternalRangeArgs.builder()
            .name("automatic-reservation")
            .network(defaultNetwork.id())
            .usage("FOR_VPC")
            .peering("FOR_SELF")
            .prefixLength(24)
            .targetCidrRanges("192.16.0.0/16")
            .build());

    }
}
resources:
  default:
    type: gcp:networkconnectivity:InternalRange
    properties:
      name: automatic-reservation
      network: ${defaultNetwork.id}
      usage: FOR_VPC
      peering: FOR_SELF
      prefixLength: 24
      targetCidrRanges:
        - 192.16.0.0/16
  defaultNetwork:
    type: gcp:compute:Network
    name: default
    properties:
      name: internal-ranges
      autoCreateSubnetworks: false

Instead of ipCidrRange, the prefixLength property specifies the desired size (24 means a /24 network). The targetCidrRanges property defines the address space to search within; GCP automatically selects an available block. This approach simplifies address planning when exact CIDRs don’t matter.

Track external address space outside the VPC

Organizations with on-premises networks or external systems need to document address ranges that exist outside GCP but must be tracked for routing and conflict prevention.

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

const defaultNetwork = new gcp.compute.Network("default", {
    name: "internal-ranges",
    autoCreateSubnetworks: false,
});
const _default = new gcp.networkconnectivity.InternalRange("default", {
    name: "external-ranges",
    network: defaultNetwork.id,
    usage: "EXTERNAL_TO_VPC",
    peering: "FOR_SELF",
    ipCidrRange: "172.16.0.0/24",
    labels: {
        "external-reserved-range": "on-premises",
    },
});
import pulumi
import pulumi_gcp as gcp

default_network = gcp.compute.Network("default",
    name="internal-ranges",
    auto_create_subnetworks=False)
default = gcp.networkconnectivity.InternalRange("default",
    name="external-ranges",
    network=default_network.id,
    usage="EXTERNAL_TO_VPC",
    peering="FOR_SELF",
    ip_cidr_range="172.16.0.0/24",
    labels={
        "external-reserved-range": "on-premises",
    })
package main

import (
	"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 {
		defaultNetwork, err := compute.NewNetwork(ctx, "default", &compute.NetworkArgs{
			Name:                  pulumi.String("internal-ranges"),
			AutoCreateSubnetworks: pulumi.Bool(false),
		})
		if err != nil {
			return err
		}
		_, err = networkconnectivity.NewInternalRange(ctx, "default", &networkconnectivity.InternalRangeArgs{
			Name:        pulumi.String("external-ranges"),
			Network:     defaultNetwork.ID(),
			Usage:       pulumi.String("EXTERNAL_TO_VPC"),
			Peering:     pulumi.String("FOR_SELF"),
			IpCidrRange: pulumi.String("172.16.0.0/24"),
			Labels: pulumi.StringMap{
				"external-reserved-range": pulumi.String("on-premises"),
			},
		})
		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 defaultNetwork = new Gcp.Compute.Network("default", new()
    {
        Name = "internal-ranges",
        AutoCreateSubnetworks = false,
    });

    var @default = new Gcp.NetworkConnectivity.InternalRange("default", new()
    {
        Name = "external-ranges",
        Network = defaultNetwork.Id,
        Usage = "EXTERNAL_TO_VPC",
        Peering = "FOR_SELF",
        IpCidrRange = "172.16.0.0/24",
        Labels = 
        {
            { "external-reserved-range", "on-premises" },
        },
    });

});
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 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 defaultNetwork = new Network("defaultNetwork", NetworkArgs.builder()
            .name("internal-ranges")
            .autoCreateSubnetworks(false)
            .build());

        var default_ = new InternalRange("default", InternalRangeArgs.builder()
            .name("external-ranges")
            .network(defaultNetwork.id())
            .usage("EXTERNAL_TO_VPC")
            .peering("FOR_SELF")
            .ipCidrRange("172.16.0.0/24")
            .labels(Map.of("external-reserved-range", "on-premises"))
            .build());

    }
}
resources:
  default:
    type: gcp:networkconnectivity:InternalRange
    properties:
      name: external-ranges
      network: ${defaultNetwork.id}
      usage: EXTERNAL_TO_VPC
      peering: FOR_SELF
      ipCidrRange: 172.16.0.0/24
      labels:
        external-reserved-range: on-premises
  defaultNetwork:
    type: gcp:compute:Network
    name: default
    properties:
      name: internal-ranges
      autoCreateSubnetworks: false

The usage property set to EXTERNAL_TO_VPC marks this range as existing outside the VPC. This doesn’t allocate GCP resources; it documents external address space for coordination. The labels help identify the external system (here, “on-premises”).

Reserve a range that overlaps existing subnets

Some network designs require internal ranges that overlap with existing subnet allocations, such as when coordinating with peered networks or planning migrations.

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

const defaultNetwork = new gcp.compute.Network("default", {
    name: "internal-ranges",
    autoCreateSubnetworks: false,
});
const defaultSubnetwork = new gcp.compute.Subnetwork("default", {
    name: "overlapping-subnet",
    ipCidrRange: "10.0.0.0/24",
    region: "us-central1",
    network: defaultNetwork.id,
});
const _default = new gcp.networkconnectivity.InternalRange("default", {
    name: "overlap-range",
    description: "Test internal range",
    network: defaultNetwork.id,
    usage: "FOR_VPC",
    peering: "FOR_SELF",
    ipCidrRange: "10.0.0.0/30",
    overlaps: ["OVERLAP_EXISTING_SUBNET_RANGE"],
}, {
    dependsOn: [defaultSubnetwork],
});
import pulumi
import pulumi_gcp as gcp

default_network = gcp.compute.Network("default",
    name="internal-ranges",
    auto_create_subnetworks=False)
default_subnetwork = gcp.compute.Subnetwork("default",
    name="overlapping-subnet",
    ip_cidr_range="10.0.0.0/24",
    region="us-central1",
    network=default_network.id)
default = gcp.networkconnectivity.InternalRange("default",
    name="overlap-range",
    description="Test internal range",
    network=default_network.id,
    usage="FOR_VPC",
    peering="FOR_SELF",
    ip_cidr_range="10.0.0.0/30",
    overlaps=["OVERLAP_EXISTING_SUBNET_RANGE"],
    opts = pulumi.ResourceOptions(depends_on=[default_subnetwork]))
package main

import (
	"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 {
		defaultNetwork, err := compute.NewNetwork(ctx, "default", &compute.NetworkArgs{
			Name:                  pulumi.String("internal-ranges"),
			AutoCreateSubnetworks: pulumi.Bool(false),
		})
		if err != nil {
			return err
		}
		defaultSubnetwork, err := compute.NewSubnetwork(ctx, "default", &compute.SubnetworkArgs{
			Name:        pulumi.String("overlapping-subnet"),
			IpCidrRange: pulumi.String("10.0.0.0/24"),
			Region:      pulumi.String("us-central1"),
			Network:     defaultNetwork.ID(),
		})
		if err != nil {
			return err
		}
		_, err = networkconnectivity.NewInternalRange(ctx, "default", &networkconnectivity.InternalRangeArgs{
			Name:        pulumi.String("overlap-range"),
			Description: pulumi.String("Test internal range"),
			Network:     defaultNetwork.ID(),
			Usage:       pulumi.String("FOR_VPC"),
			Peering:     pulumi.String("FOR_SELF"),
			IpCidrRange: pulumi.String("10.0.0.0/30"),
			Overlaps: pulumi.StringArray{
				pulumi.String("OVERLAP_EXISTING_SUBNET_RANGE"),
			},
		}, pulumi.DependsOn([]pulumi.Resource{
			defaultSubnetwork,
		}))
		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 defaultNetwork = new Gcp.Compute.Network("default", new()
    {
        Name = "internal-ranges",
        AutoCreateSubnetworks = false,
    });

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

    var @default = new Gcp.NetworkConnectivity.InternalRange("default", new()
    {
        Name = "overlap-range",
        Description = "Test internal range",
        Network = defaultNetwork.Id,
        Usage = "FOR_VPC",
        Peering = "FOR_SELF",
        IpCidrRange = "10.0.0.0/30",
        Overlaps = new[]
        {
            "OVERLAP_EXISTING_SUBNET_RANGE",
        },
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            defaultSubnetwork,
        },
    });

});
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.networkconnectivity.InternalRange;
import com.pulumi.gcp.networkconnectivity.InternalRangeArgs;
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 defaultNetwork = new Network("defaultNetwork", NetworkArgs.builder()
            .name("internal-ranges")
            .autoCreateSubnetworks(false)
            .build());

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

        var default_ = new InternalRange("default", InternalRangeArgs.builder()
            .name("overlap-range")
            .description("Test internal range")
            .network(defaultNetwork.id())
            .usage("FOR_VPC")
            .peering("FOR_SELF")
            .ipCidrRange("10.0.0.0/30")
            .overlaps("OVERLAP_EXISTING_SUBNET_RANGE")
            .build(), CustomResourceOptions.builder()
                .dependsOn(defaultSubnetwork)
                .build());

    }
}
resources:
  default:
    type: gcp:networkconnectivity:InternalRange
    properties:
      name: overlap-range
      description: Test internal range
      network: ${defaultNetwork.id}
      usage: FOR_VPC
      peering: FOR_SELF
      ipCidrRange: 10.0.0.0/30
      overlaps:
        - OVERLAP_EXISTING_SUBNET_RANGE
    options:
      dependsOn:
        - ${defaultSubnetwork}
  defaultNetwork:
    type: gcp:compute:Network
    name: default
    properties:
      name: internal-ranges
      autoCreateSubnetworks: false
  defaultSubnetwork:
    type: gcp:compute:Subnetwork
    name: default
    properties:
      name: overlapping-subnet
      ipCidrRange: 10.0.0.0/24
      region: us-central1
      network: ${defaultNetwork.id}

The overlaps property allows intentional overlap with existing resources. OVERLAP_EXISTING_SUBNET_RANGE permits this range to overlap with the subnet created earlier. The dependsOn ensures the subnet exists before the internal range is created. Without the overlaps property, GCP would reject the conflicting CIDR.

Define a range for subnet migration

Subnet migrations between projects or regions require an internal range that tracks both the source subnet and the target destination.

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

const defaultNetwork = new gcp.compute.Network("default", {
    name: "internal-ranges",
    autoCreateSubnetworks: false,
});
const source = new gcp.compute.Subnetwork("source", {
    name: "source-subnet",
    ipCidrRange: "10.1.0.0/16",
    region: "us-central1",
    network: defaultNetwork.name,
});
const targetProject = gcp.organizations.getProject({});
const _default = new gcp.networkconnectivity.InternalRange("default", {
    name: "migration",
    description: "Test internal range",
    network: defaultNetwork.selfLink,
    usage: "FOR_MIGRATION",
    peering: "FOR_SELF",
    ipCidrRange: "10.1.0.0/16",
    migration: {
        source: source.selfLink,
        target: targetProject.then(targetProject => `projects/${targetProject.projectId}/regions/us-central1/subnetworks/target-subnet`),
    },
});
import pulumi
import pulumi_gcp as gcp

default_network = gcp.compute.Network("default",
    name="internal-ranges",
    auto_create_subnetworks=False)
source = gcp.compute.Subnetwork("source",
    name="source-subnet",
    ip_cidr_range="10.1.0.0/16",
    region="us-central1",
    network=default_network.name)
target_project = gcp.organizations.get_project()
default = gcp.networkconnectivity.InternalRange("default",
    name="migration",
    description="Test internal range",
    network=default_network.self_link,
    usage="FOR_MIGRATION",
    peering="FOR_SELF",
    ip_cidr_range="10.1.0.0/16",
    migration={
        "source": source.self_link,
        "target": f"projects/{target_project.project_id}/regions/us-central1/subnetworks/target-subnet",
    })
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-gcp/sdk/v9/go/gcp/organizations"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		defaultNetwork, err := compute.NewNetwork(ctx, "default", &compute.NetworkArgs{
			Name:                  pulumi.String("internal-ranges"),
			AutoCreateSubnetworks: pulumi.Bool(false),
		})
		if err != nil {
			return err
		}
		source, err := compute.NewSubnetwork(ctx, "source", &compute.SubnetworkArgs{
			Name:        pulumi.String("source-subnet"),
			IpCidrRange: pulumi.String("10.1.0.0/16"),
			Region:      pulumi.String("us-central1"),
			Network:     defaultNetwork.Name,
		})
		if err != nil {
			return err
		}
		targetProject, err := organizations.LookupProject(ctx, &organizations.LookupProjectArgs{}, nil)
		if err != nil {
			return err
		}
		_, err = networkconnectivity.NewInternalRange(ctx, "default", &networkconnectivity.InternalRangeArgs{
			Name:        pulumi.String("migration"),
			Description: pulumi.String("Test internal range"),
			Network:     defaultNetwork.SelfLink,
			Usage:       pulumi.String("FOR_MIGRATION"),
			Peering:     pulumi.String("FOR_SELF"),
			IpCidrRange: pulumi.String("10.1.0.0/16"),
			Migration: &networkconnectivity.InternalRangeMigrationArgs{
				Source: source.SelfLink,
				Target: pulumi.Sprintf("projects/%v/regions/us-central1/subnetworks/target-subnet", targetProject.ProjectId),
			},
		})
		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 defaultNetwork = new Gcp.Compute.Network("default", new()
    {
        Name = "internal-ranges",
        AutoCreateSubnetworks = false,
    });

    var source = new Gcp.Compute.Subnetwork("source", new()
    {
        Name = "source-subnet",
        IpCidrRange = "10.1.0.0/16",
        Region = "us-central1",
        Network = defaultNetwork.Name,
    });

    var targetProject = Gcp.Organizations.GetProject.Invoke();

    var @default = new Gcp.NetworkConnectivity.InternalRange("default", new()
    {
        Name = "migration",
        Description = "Test internal range",
        Network = defaultNetwork.SelfLink,
        Usage = "FOR_MIGRATION",
        Peering = "FOR_SELF",
        IpCidrRange = "10.1.0.0/16",
        Migration = new Gcp.NetworkConnectivity.Inputs.InternalRangeMigrationArgs
        {
            Source = source.SelfLink,
            Target = $"projects/{targetProject.Apply(getProjectResult => getProjectResult.ProjectId)}/regions/us-central1/subnetworks/target-subnet",
        },
    });

});
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.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetProjectArgs;
import com.pulumi.gcp.networkconnectivity.InternalRange;
import com.pulumi.gcp.networkconnectivity.InternalRangeArgs;
import com.pulumi.gcp.networkconnectivity.inputs.InternalRangeMigrationArgs;
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 defaultNetwork = new Network("defaultNetwork", NetworkArgs.builder()
            .name("internal-ranges")
            .autoCreateSubnetworks(false)
            .build());

        var source = new Subnetwork("source", SubnetworkArgs.builder()
            .name("source-subnet")
            .ipCidrRange("10.1.0.0/16")
            .region("us-central1")
            .network(defaultNetwork.name())
            .build());

        final var targetProject = OrganizationsFunctions.getProject(GetProjectArgs.builder()
            .build());

        var default_ = new InternalRange("default", InternalRangeArgs.builder()
            .name("migration")
            .description("Test internal range")
            .network(defaultNetwork.selfLink())
            .usage("FOR_MIGRATION")
            .peering("FOR_SELF")
            .ipCidrRange("10.1.0.0/16")
            .migration(InternalRangeMigrationArgs.builder()
                .source(source.selfLink())
                .target(String.format("projects/%s/regions/us-central1/subnetworks/target-subnet", targetProject.projectId()))
                .build())
            .build());

    }
}
resources:
  default:
    type: gcp:networkconnectivity:InternalRange
    properties:
      name: migration
      description: Test internal range
      network: ${defaultNetwork.selfLink}
      usage: FOR_MIGRATION
      peering: FOR_SELF
      ipCidrRange: 10.1.0.0/16
      migration:
        source: ${source.selfLink}
        target: projects/${targetProject.projectId}/regions/us-central1/subnetworks/target-subnet
  defaultNetwork:
    type: gcp:compute:Network
    name: default
    properties:
      name: internal-ranges
      autoCreateSubnetworks: false
  source:
    type: gcp:compute:Subnetwork
    properties:
      name: source-subnet
      ipCidrRange: 10.1.0.0/16
      region: us-central1
      network: ${defaultNetwork.name}
variables:
  targetProject:
    fn::invoke:
      function: gcp:organizations:getProject
      arguments: {}

The usage property set to FOR_MIGRATION indicates this range coordinates a subnet move. The migration block specifies the source subnet’s self-link and the target subnet’s path. This range acts as a coordination point during the migration process, ensuring address space continuity.

Control allocation strategy for automatic ranges

When using automatic allocation, you can specify how GCP selects from available address space, optimizing for compactness or other allocation patterns.

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

const defaultNetwork = new gcp.compute.Network("default", {
    name: "internal-ranges",
    autoCreateSubnetworks: false,
});
const _default = new gcp.networkconnectivity.InternalRange("default", {
    name: "allocation-algorithms",
    network: defaultNetwork.id,
    usage: "FOR_VPC",
    peering: "FOR_SELF",
    prefixLength: 24,
    targetCidrRanges: ["192.16.0.0/16"],
    allocationOptions: {
        allocationStrategy: "FIRST_SMALLEST_FITTING",
    },
});
import pulumi
import pulumi_gcp as gcp

default_network = gcp.compute.Network("default",
    name="internal-ranges",
    auto_create_subnetworks=False)
default = gcp.networkconnectivity.InternalRange("default",
    name="allocation-algorithms",
    network=default_network.id,
    usage="FOR_VPC",
    peering="FOR_SELF",
    prefix_length=24,
    target_cidr_ranges=["192.16.0.0/16"],
    allocation_options={
        "allocation_strategy": "FIRST_SMALLEST_FITTING",
    })
package main

import (
	"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 {
		defaultNetwork, err := compute.NewNetwork(ctx, "default", &compute.NetworkArgs{
			Name:                  pulumi.String("internal-ranges"),
			AutoCreateSubnetworks: pulumi.Bool(false),
		})
		if err != nil {
			return err
		}
		_, err = networkconnectivity.NewInternalRange(ctx, "default", &networkconnectivity.InternalRangeArgs{
			Name:         pulumi.String("allocation-algorithms"),
			Network:      defaultNetwork.ID(),
			Usage:        pulumi.String("FOR_VPC"),
			Peering:      pulumi.String("FOR_SELF"),
			PrefixLength: pulumi.Int(24),
			TargetCidrRanges: pulumi.StringArray{
				pulumi.String("192.16.0.0/16"),
			},
			AllocationOptions: &networkconnectivity.InternalRangeAllocationOptionsArgs{
				AllocationStrategy: pulumi.String("FIRST_SMALLEST_FITTING"),
			},
		})
		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 defaultNetwork = new Gcp.Compute.Network("default", new()
    {
        Name = "internal-ranges",
        AutoCreateSubnetworks = false,
    });

    var @default = new Gcp.NetworkConnectivity.InternalRange("default", new()
    {
        Name = "allocation-algorithms",
        Network = defaultNetwork.Id,
        Usage = "FOR_VPC",
        Peering = "FOR_SELF",
        PrefixLength = 24,
        TargetCidrRanges = new[]
        {
            "192.16.0.0/16",
        },
        AllocationOptions = new Gcp.NetworkConnectivity.Inputs.InternalRangeAllocationOptionsArgs
        {
            AllocationStrategy = "FIRST_SMALLEST_FITTING",
        },
    });

});
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.networkconnectivity.inputs.InternalRangeAllocationOptionsArgs;
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 defaultNetwork = new Network("defaultNetwork", NetworkArgs.builder()
            .name("internal-ranges")
            .autoCreateSubnetworks(false)
            .build());

        var default_ = new InternalRange("default", InternalRangeArgs.builder()
            .name("allocation-algorithms")
            .network(defaultNetwork.id())
            .usage("FOR_VPC")
            .peering("FOR_SELF")
            .prefixLength(24)
            .targetCidrRanges("192.16.0.0/16")
            .allocationOptions(InternalRangeAllocationOptionsArgs.builder()
                .allocationStrategy("FIRST_SMALLEST_FITTING")
                .build())
            .build());

    }
}
resources:
  default:
    type: gcp:networkconnectivity:InternalRange
    properties:
      name: allocation-algorithms
      network: ${defaultNetwork.id}
      usage: FOR_VPC
      peering: FOR_SELF
      prefixLength: 24
      targetCidrRanges:
        - 192.16.0.0/16
      allocationOptions:
        allocationStrategy: FIRST_SMALLEST_FITTING
  defaultNetwork:
    type: gcp:compute:Network
    name: default
    properties:
      name: internal-ranges
      autoCreateSubnetworks: false

The allocationOptions block controls how GCP chooses from available space. The allocationStrategy property set to FIRST_SMALLEST_FITTING selects the smallest available range that fits the requested size, promoting efficient address packing. This extends the automatic allocation pattern from earlier examples with explicit strategy control.

Beyond these examples

These snippets focus on specific internal range features: fixed and automatic CIDR allocation, external range tracking, and overlap handling and subnet migration. They’re intentionally minimal rather than full network designs.

The examples reference pre-existing infrastructure such as VPC networks, and existing subnets for overlap and migration scenarios. They focus on configuring the internal range rather than provisioning complete network topologies.

To keep things focused, common internal range patterns are omitted, including:

  • Exclusion ranges (excludeCidrRanges)
  • Immutability controls (immutable property)
  • IPv6 range configuration
  • Random allocation with firstAvailableRangesLookupSize

These omissions are intentional: the goal is to illustrate how each internal range feature is wired, not provide drop-in IPAM modules. See the InternalRange resource reference for all available configuration options.

Let's configure GCP Internal Ranges for VPC Networks

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

IP Range Configuration
Should I use ipCidrRange or prefixLength to specify my IP range?
Use ipCidrRange to manually specify an exact range (e.g., “10.0.0.0/24”), or use prefixLength to let GCP automatically find a free range of that size. If you set both, their sizes must match or you’ll get an error. For IPv6, you must always set ipCidrRange.
What are the limitations for IPv6 ranges?
IPv6 ranges are limited to usage=EXTERNAL_TO_VPC and peering=FOR_SELF. You must explicitly specify ipCidrRange for IPv6. If you also set prefixLength, it must match the ipCidrRange size (it acts as a redundant parameter).
Can I change the IP range size after creation?
Yes, you can use prefixLength during updates to change the range size. This allows resizing without recreating the resource.
Automatic Allocation
How do I let GCP automatically pick an IP range for me?
Set prefixLength to your desired size (e.g., 24 for a /24 network) and optionally configure targetCidrRanges to narrow the search space. GCP will automatically find a free range.
What's the default search space for automatic IP allocation?
If you don’t set targetCidrRanges, GCP defaults to searching within “10.0.0.0/8”. You can specify other RFC-1918 ranges like “172.16.0.0/12” and “192.168.0.0/16”, or non-RFC-1918 ranges used in your VPC.
What allocation strategies can I use for automatic ranges?

You have two options in allocationOptions:

  1. FIRST_SMALLEST_FITTING - Finds the smallest available range that fits
  2. RANDOM_FIRST_N_AVAILABLE - Randomly selects from the first N available ranges (set N with firstAvailableRangesLookupSize)
Usage Types & Peering
What are the different usage types for internal ranges?

Three usage types are available:

  • FOR_VPC - For internal VPC resources
  • EXTERNAL_TO_VPC - For on-premises or external networks
  • FOR_MIGRATION - For migrating subnets between projects
How do I reserve a range for my on-premises network?
Set usage to EXTERNAL_TO_VPC and specify your on-premises IP range in ipCidrRange. This reserves the range without creating VPC resources.
What do the peering options mean?
The peering field controls range sharing: FOR_SELF (only your VPC), FOR_PEER (shared with peered VPCs), or NOT_SHARED (not shared at all).
Overlaps & Conflicts
Can I create a range that overlaps with existing subnets?
Yes, set the overlaps field to ["OVERLAP_EXISTING_SUBNET_RANGE"]. Use dependsOn to ensure the overlapping subnet exists before creating the internal range.
Why do I only see some of my labels on the resource?
The labels field is non-authoritative and only manages labels in your Pulumi configuration. To see all labels (including those set by other clients), use the effectiveLabels output property.
Migration & Immutability
How do I migrate a subnet to another project using internal ranges?
Set usage to FOR_MIGRATION and configure the migration block with source (your existing subnet’s self-link) and target (the destination subnet path in the target project).
What properties can't I change after creation?
These properties are immutable: name, project, allocationOptions, and migration. If you set immutable to true, all fields except labels and description become immutable.

Using a different cloud?

Explore networking guides for other cloud providers: