Configure GCP Internal Ranges for VPC Networks

The gcp:networkconnectivity/internalRange:InternalRange resource, part of the Pulumi GCP provider, reserves and tracks IP address ranges within VPC networks for IPAM operations, defining both the address space and its behavioral characteristics. This guide focuses on three capabilities: fixed and automatic CIDR allocation, external range tracking, and overlap handling and subnet migration.

Internal ranges belong to VPC networks and may reference existing subnets or target projects for 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 resources, while peering set to FOR_SELF means the range is not shared with peered networks. 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 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 specifying ipCidrRange, the prefixLength property requests a /24 range. The targetCidrRanges property constrains where GCP searches for available space. GCP selects an available CIDR that doesn’t conflict with existing ranges.

Track external address space outside VPC

Organizations often document on-premises or external address ranges to prevent VPC conflicts with external infrastructure.

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

Setting usage to EXTERNAL_TO_VPC marks this range as external documentation rather than VPC allocation. This prevents the VPC from using address space that exists elsewhere in your network. The labels property helps categorize external ranges by source or purpose.

Allow overlap with existing subnet ranges

Some scenarios require reserving address space that overlaps with existing subnets, such as when coordinating with legacy infrastructure.

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 with OVERLAP_EXISTING_SUBNET_RANGE allows the internal range to coexist with an existing subnet. The dependsOn ensures the subnet exists before creating the overlapping range. This is useful for documenting address space relationships without modifying existing resources.

Configure subnet migration between projects

Subnet migrations between projects or regions require tracking both source and target resources to coordinate the address space transition.

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 (
	"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: {}

Setting usage to FOR_MIGRATION activates migration tracking. The migration block specifies the source subnet’s self-link and the target subnet’s path in another project. This coordinates address space during cross-project subnet moves.

Control allocation strategy for automatic ranges

When automatically allocating ranges, you can specify the algorithm GCP uses to select from available address space.

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 selects from available space. Setting allocationStrategy to FIRST_SMALLEST_FITTING chooses the smallest available range that fits the requested size. This extends the automatic allocation concept 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 coordination. They’re intentionally minimal rather than full IPAM solutions.

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

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

  • Immutable range configuration (immutable property)
  • CIDR exclusion lists (excludeCidrRanges)
  • Peering configurations beyond FOR_SELF (FOR_PEER, NOT_SHARED)
  • IPv6 range handling (limited to EXTERNAL_TO_VPC usage)

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
What's the difference between ipCidrRange and prefixLength?
You can specify a range in two ways: ipCidrRange defines an explicit CIDR block (e.g., “10.0.0.0/24”), while prefixLength automatically finds a free range of the given size. If you set both, they must specify the same size or you’ll get an error. For automatic allocation, use prefixLength with targetCidrRanges to narrow the search space.
How do I automatically allocate an IP range without specifying the exact CIDR?
Use prefixLength to specify the desired size (e.g., 24 for a /24 network) and optionally set targetCidrRanges to narrow the search space. By default, allocation searches within “10.0.0.0/8”, but you can specify other RFC-1918 ranges like “172.16.0.0/12” or “192.168.0.0/16”.
What allocation strategies are available for automatic range selection?
Two strategies are available: FIRST_SMALLEST_FITTING (finds the smallest range that fits) and RANDOM_FIRST_N_AVAILABLE (randomly selects from the first N available ranges). For the random strategy, use firstAvailableRangesLookupSize to control how many ranges to consider.
How do I prevent my range from overlapping with specific CIDR blocks?
Use excludeCidrRanges to list IP CIDR ranges that should not overlap with your internal range. Only IPv4 CIDR ranges are supported for exclusion.
IPv6 Limitations
Can I use IPv6 ranges, and what are the limitations?
Yes, but IPv6 ranges have three key restrictions: (1) they’re limited to usage=EXTERNAL_TO_VPC and peering=FOR_SELF, (2) you must explicitly specify ipCidrRange (it’s compulsory for IPv6), and (3) if you use prefixLength with IPv6, you must also set ipCidrRange to a matching value (making prefixLength redundant).
Overlaps & Conflicts
How do I create a range that overlaps with existing resources?
Use the overlaps array to specify allowed overlap types: OVERLAP_ROUTE_RANGE for route ranges or OVERLAP_EXISTING_SUBNET_RANGE for existing subnets. When overlapping with a subnet, use dependsOn to ensure the subnet exists before creating the internal range.
Immutability & Updates
What properties can't I change after creating an internal range?
The following properties are immutable: name, project, allocationOptions, and migration. If you set immutable to true, all fields except labels and description become immutable.
Why aren't all my labels showing up in the labels field?
The labels field is non-authoritative and only manages labels present in your Pulumi configuration. To see all labels on the resource (including those set by other clients or services), use effectiveLabels.
Usage Types & Peering
What are the different usage types for internal ranges?
Three usage types are available: FOR_VPC (for VPC resources like subnets), EXTERNAL_TO_VPC (for representing on-premises or external address space), and FOR_MIGRATION (for migrating existing subnets to internal ranges).
What do the peering options mean?
Peering controls how the range is shared: FOR_SELF (used only by this VPC), FOR_PEER (shared with peered VPCs), or NOT_SHARED (not shared with any peers).
Advanced Features
How do I migrate an existing subnet to use an internal range?
Set usage to FOR_MIGRATION and configure the migration block with source (the existing subnet’s self-link) and target (the target subnet path). The ipCidrRange must match the source subnet’s range.
How can I see which resources are using my internal range?
Check the users output property, which lists all resources that reference this internal range. Resources mark themselves as users by creating a reference, which prevents deletion of the range.

Using a different cloud?

Explore networking guides for other cloud providers: