Deploy GCP Vertex AI Index Endpoints

The gcp:vertex/aiIndexEndpoint:AiIndexEndpoint resource, part of the Pulumi GCP provider, defines a Vertex AI index endpoint that serves as the deployment target for vector similarity search indexes. This guide focuses on three connectivity capabilities: public endpoint exposure, Private Service Connect configuration, and VPC network peering.

Index endpoints are containers where you deploy indexes for serving. The examples show connectivity options but don’t deploy actual indexes. Combine them with your own index deployment configuration.

Expose an endpoint through a public domain

Some deployments need index endpoints accessible from the internet without VPC peering or Private Service Connect.

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

const indexEndpoint = new gcp.vertex.AiIndexEndpoint("index_endpoint", {
    displayName: "sample-endpoint",
    description: "A sample vertex endpoint with an public endpoint",
    region: "us-central1",
    labels: {
        "label-one": "value-one",
    },
    publicEndpointEnabled: true,
});
import pulumi
import pulumi_gcp as gcp

index_endpoint = gcp.vertex.AiIndexEndpoint("index_endpoint",
    display_name="sample-endpoint",
    description="A sample vertex endpoint with an public endpoint",
    region="us-central1",
    labels={
        "label-one": "value-one",
    },
    public_endpoint_enabled=True)
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := vertex.NewAiIndexEndpoint(ctx, "index_endpoint", &vertex.AiIndexEndpointArgs{
			DisplayName: pulumi.String("sample-endpoint"),
			Description: pulumi.String("A sample vertex endpoint with an public endpoint"),
			Region:      pulumi.String("us-central1"),
			Labels: pulumi.StringMap{
				"label-one": pulumi.String("value-one"),
			},
			PublicEndpointEnabled: pulumi.Bool(true),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var indexEndpoint = new Gcp.Vertex.AiIndexEndpoint("index_endpoint", new()
    {
        DisplayName = "sample-endpoint",
        Description = "A sample vertex endpoint with an public endpoint",
        Region = "us-central1",
        Labels = 
        {
            { "label-one", "value-one" },
        },
        PublicEndpointEnabled = true,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.vertex.AiIndexEndpoint;
import com.pulumi.gcp.vertex.AiIndexEndpointArgs;
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 indexEndpoint = new AiIndexEndpoint("indexEndpoint", AiIndexEndpointArgs.builder()
            .displayName("sample-endpoint")
            .description("A sample vertex endpoint with an public endpoint")
            .region("us-central1")
            .labels(Map.of("label-one", "value-one"))
            .publicEndpointEnabled(true)
            .build());

    }
}
resources:
  indexEndpoint:
    type: gcp:vertex:AiIndexEndpoint
    name: index_endpoint
    properties:
      displayName: sample-endpoint
      description: A sample vertex endpoint with an public endpoint
      region: us-central1
      labels:
        label-one: value-one
      publicEndpointEnabled: true

When publicEndpointEnabled is true, Vertex AI provisions a public domain name (available in publicEndpointDomainName) that clients can use to query deployed indexes. This is the simplest connectivity option but exposes the endpoint to the internet.

Connect privately using Private Service Connect

Teams that need private connectivity without VPC peering can use Private Service Connect, which allows controlled access from specific projects.

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

const project = gcp.organizations.getProject({});
const indexEndpoint = new gcp.vertex.AiIndexEndpoint("index_endpoint", {
    displayName: "sample-endpoint",
    description: "A sample vertex endpoint",
    region: "us-central1",
    labels: {
        "label-one": "value-one",
    },
    privateServiceConnectConfig: {
        enablePrivateServiceConnect: true,
        projectAllowlists: [project.then(project => project.name)],
    },
});
import pulumi
import pulumi_gcp as gcp

project = gcp.organizations.get_project()
index_endpoint = gcp.vertex.AiIndexEndpoint("index_endpoint",
    display_name="sample-endpoint",
    description="A sample vertex endpoint",
    region="us-central1",
    labels={
        "label-one": "value-one",
    },
    private_service_connect_config={
        "enable_private_service_connect": True,
        "project_allowlists": [project.name],
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		project, err := organizations.LookupProject(ctx, &organizations.LookupProjectArgs{}, nil)
		if err != nil {
			return err
		}
		_, err = vertex.NewAiIndexEndpoint(ctx, "index_endpoint", &vertex.AiIndexEndpointArgs{
			DisplayName: pulumi.String("sample-endpoint"),
			Description: pulumi.String("A sample vertex endpoint"),
			Region:      pulumi.String("us-central1"),
			Labels: pulumi.StringMap{
				"label-one": pulumi.String("value-one"),
			},
			PrivateServiceConnectConfig: &vertex.AiIndexEndpointPrivateServiceConnectConfigArgs{
				EnablePrivateServiceConnect: pulumi.Bool(true),
				ProjectAllowlists: pulumi.StringArray{
					pulumi.String(project.Name),
				},
			},
		})
		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 project = Gcp.Organizations.GetProject.Invoke();

    var indexEndpoint = new Gcp.Vertex.AiIndexEndpoint("index_endpoint", new()
    {
        DisplayName = "sample-endpoint",
        Description = "A sample vertex endpoint",
        Region = "us-central1",
        Labels = 
        {
            { "label-one", "value-one" },
        },
        PrivateServiceConnectConfig = new Gcp.Vertex.Inputs.AiIndexEndpointPrivateServiceConnectConfigArgs
        {
            EnablePrivateServiceConnect = true,
            ProjectAllowlists = new[]
            {
                project.Apply(getProjectResult => getProjectResult.Name),
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetProjectArgs;
import com.pulumi.gcp.vertex.AiIndexEndpoint;
import com.pulumi.gcp.vertex.AiIndexEndpointArgs;
import com.pulumi.gcp.vertex.inputs.AiIndexEndpointPrivateServiceConnectConfigArgs;
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) {
        final var project = OrganizationsFunctions.getProject(GetProjectArgs.builder()
            .build());

        var indexEndpoint = new AiIndexEndpoint("indexEndpoint", AiIndexEndpointArgs.builder()
            .displayName("sample-endpoint")
            .description("A sample vertex endpoint")
            .region("us-central1")
            .labels(Map.of("label-one", "value-one"))
            .privateServiceConnectConfig(AiIndexEndpointPrivateServiceConnectConfigArgs.builder()
                .enablePrivateServiceConnect(true)
                .projectAllowlists(project.name())
                .build())
            .build());

    }
}
resources:
  indexEndpoint:
    type: gcp:vertex:AiIndexEndpoint
    name: index_endpoint
    properties:
      displayName: sample-endpoint
      description: A sample vertex endpoint
      region: us-central1
      labels:
        label-one: value-one
      privateServiceConnectConfig:
        enablePrivateServiceConnect: true
        projectAllowlists:
          - ${project.name}
variables:
  project:
    fn::invoke:
      function: gcp:organizations:getProject
      arguments: {}

The privateServiceConnectConfig block enables Private Service Connect and defines which projects can access the endpoint through projectAllowlists. This provides private connectivity without the complexity of VPC peering. The network and privateServiceConnectConfig properties are mutually exclusive.

Peer with a VPC network for private access

Production deployments often require private network connectivity to index endpoints through VPC peering.

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

const vertexNetwork = new gcp.compute.Network("vertex_network", {name: "network-name"});
const vertexRange = new gcp.compute.GlobalAddress("vertex_range", {
    name: "address-name",
    purpose: "VPC_PEERING",
    addressType: "INTERNAL",
    prefixLength: 24,
    network: vertexNetwork.id,
});
const vertexVpcConnection = new gcp.servicenetworking.Connection("vertex_vpc_connection", {
    network: vertexNetwork.id,
    service: "servicenetworking.googleapis.com",
    reservedPeeringRanges: [vertexRange.name],
});
const project = gcp.organizations.getProject({});
const indexEndpoint = new gcp.vertex.AiIndexEndpoint("index_endpoint", {
    displayName: "sample-endpoint",
    description: "A sample vertex endpoint",
    region: "us-central1",
    labels: {
        "label-one": "value-one",
    },
    network: pulumi.all([project, vertexNetwork.name]).apply(([project, name]) => `projects/${project.number}/global/networks/${name}`),
}, {
    dependsOn: [vertexVpcConnection],
});
import pulumi
import pulumi_gcp as gcp

vertex_network = gcp.compute.Network("vertex_network", name="network-name")
vertex_range = gcp.compute.GlobalAddress("vertex_range",
    name="address-name",
    purpose="VPC_PEERING",
    address_type="INTERNAL",
    prefix_length=24,
    network=vertex_network.id)
vertex_vpc_connection = gcp.servicenetworking.Connection("vertex_vpc_connection",
    network=vertex_network.id,
    service="servicenetworking.googleapis.com",
    reserved_peering_ranges=[vertex_range.name])
project = gcp.organizations.get_project()
index_endpoint = gcp.vertex.AiIndexEndpoint("index_endpoint",
    display_name="sample-endpoint",
    description="A sample vertex endpoint",
    region="us-central1",
    labels={
        "label-one": "value-one",
    },
    network=vertex_network.name.apply(lambda name: f"projects/{project.number}/global/networks/{name}"),
    opts = pulumi.ResourceOptions(depends_on=[vertex_vpc_connection]))
package main

import (
	"fmt"

	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/servicenetworking"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/vertex"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		vertexNetwork, err := compute.NewNetwork(ctx, "vertex_network", &compute.NetworkArgs{
			Name: pulumi.String("network-name"),
		})
		if err != nil {
			return err
		}
		vertexRange, err := compute.NewGlobalAddress(ctx, "vertex_range", &compute.GlobalAddressArgs{
			Name:         pulumi.String("address-name"),
			Purpose:      pulumi.String("VPC_PEERING"),
			AddressType:  pulumi.String("INTERNAL"),
			PrefixLength: pulumi.Int(24),
			Network:      vertexNetwork.ID(),
		})
		if err != nil {
			return err
		}
		vertexVpcConnection, err := servicenetworking.NewConnection(ctx, "vertex_vpc_connection", &servicenetworking.ConnectionArgs{
			Network: vertexNetwork.ID(),
			Service: pulumi.String("servicenetworking.googleapis.com"),
			ReservedPeeringRanges: pulumi.StringArray{
				vertexRange.Name,
			},
		})
		if err != nil {
			return err
		}
		project, err := organizations.LookupProject(ctx, &organizations.LookupProjectArgs{}, nil)
		if err != nil {
			return err
		}
		_, err = vertex.NewAiIndexEndpoint(ctx, "index_endpoint", &vertex.AiIndexEndpointArgs{
			DisplayName: pulumi.String("sample-endpoint"),
			Description: pulumi.String("A sample vertex endpoint"),
			Region:      pulumi.String("us-central1"),
			Labels: pulumi.StringMap{
				"label-one": pulumi.String("value-one"),
			},
			Network: vertexNetwork.Name.ApplyT(func(name string) (string, error) {
				return fmt.Sprintf("projects/%v/global/networks/%v", project.Number, name), nil
			}).(pulumi.StringOutput),
		}, pulumi.DependsOn([]pulumi.Resource{
			vertexVpcConnection,
		}))
		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 vertexNetwork = new Gcp.Compute.Network("vertex_network", new()
    {
        Name = "network-name",
    });

    var vertexRange = new Gcp.Compute.GlobalAddress("vertex_range", new()
    {
        Name = "address-name",
        Purpose = "VPC_PEERING",
        AddressType = "INTERNAL",
        PrefixLength = 24,
        Network = vertexNetwork.Id,
    });

    var vertexVpcConnection = new Gcp.ServiceNetworking.Connection("vertex_vpc_connection", new()
    {
        Network = vertexNetwork.Id,
        Service = "servicenetworking.googleapis.com",
        ReservedPeeringRanges = new[]
        {
            vertexRange.Name,
        },
    });

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

    var indexEndpoint = new Gcp.Vertex.AiIndexEndpoint("index_endpoint", new()
    {
        DisplayName = "sample-endpoint",
        Description = "A sample vertex endpoint",
        Region = "us-central1",
        Labels = 
        {
            { "label-one", "value-one" },
        },
        Network = Output.Tuple(project, vertexNetwork.Name).Apply(values =>
        {
            var project = values.Item1;
            var name = values.Item2;
            return $"projects/{project.Apply(getProjectResult => getProjectResult.Number)}/global/networks/{name}";
        }),
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            vertexVpcConnection,
        },
    });

});
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.GlobalAddress;
import com.pulumi.gcp.compute.GlobalAddressArgs;
import com.pulumi.gcp.servicenetworking.Connection;
import com.pulumi.gcp.servicenetworking.ConnectionArgs;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetProjectArgs;
import com.pulumi.gcp.vertex.AiIndexEndpoint;
import com.pulumi.gcp.vertex.AiIndexEndpointArgs;
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 vertexNetwork = new Network("vertexNetwork", NetworkArgs.builder()
            .name("network-name")
            .build());

        var vertexRange = new GlobalAddress("vertexRange", GlobalAddressArgs.builder()
            .name("address-name")
            .purpose("VPC_PEERING")
            .addressType("INTERNAL")
            .prefixLength(24)
            .network(vertexNetwork.id())
            .build());

        var vertexVpcConnection = new Connection("vertexVpcConnection", ConnectionArgs.builder()
            .network(vertexNetwork.id())
            .service("servicenetworking.googleapis.com")
            .reservedPeeringRanges(vertexRange.name())
            .build());

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

        var indexEndpoint = new AiIndexEndpoint("indexEndpoint", AiIndexEndpointArgs.builder()
            .displayName("sample-endpoint")
            .description("A sample vertex endpoint")
            .region("us-central1")
            .labels(Map.of("label-one", "value-one"))
            .network(vertexNetwork.name().applyValue(_name -> String.format("projects/%s/global/networks/%s", project.number(),_name)))
            .build(), CustomResourceOptions.builder()
                .dependsOn(vertexVpcConnection)
                .build());

    }
}
resources:
  indexEndpoint:
    type: gcp:vertex:AiIndexEndpoint
    name: index_endpoint
    properties:
      displayName: sample-endpoint
      description: A sample vertex endpoint
      region: us-central1
      labels:
        label-one: value-one
      network: projects/${project.number}/global/networks/${vertexNetwork.name}
    options:
      dependsOn:
        - ${vertexVpcConnection}
  vertexVpcConnection:
    type: gcp:servicenetworking:Connection
    name: vertex_vpc_connection
    properties:
      network: ${vertexNetwork.id}
      service: servicenetworking.googleapis.com
      reservedPeeringRanges:
        - ${vertexRange.name}
  vertexRange:
    type: gcp:compute:GlobalAddress
    name: vertex_range
    properties:
      name: address-name
      purpose: VPC_PEERING
      addressType: INTERNAL
      prefixLength: 24
      network: ${vertexNetwork.id}
  vertexNetwork:
    type: gcp:compute:Network
    name: vertex_network
    properties:
      name: network-name
variables:
  project:
    fn::invoke:
      function: gcp:organizations:getProject
      arguments: {}

The network property connects the endpoint to your VPC using the format projects/{project_number}/global/networks/{network_name}. This requires VPC peering to be configured with servicenetworking.googleapis.com first (shown via servicenetworking.Connection). The dependsOn ensures peering completes before creating the endpoint. The network and privateServiceConnectConfig properties are mutually exclusive.

Beyond these examples

These snippets focus on specific endpoint connectivity features: public endpoint exposure, Private Service Connect configuration, and VPC network peering. They’re intentionally minimal rather than full vector search deployments.

The examples may reference pre-existing infrastructure such as GCP projects for Private Service Connect allowlists. They focus on endpoint connectivity rather than index deployment or querying.

To keep things focused, common endpoint patterns are omitted, including:

  • Customer-managed encryption keys (encryptionSpec)
  • Index deployment to endpoints
  • Endpoint monitoring and observability
  • Multi-region endpoint configuration

These omissions are intentional: the goal is to illustrate how each connectivity mode is wired, not provide drop-in vector search modules. See the Vertex AI IndexEndpoint resource reference for all available configuration options.

Let's deploy GCP Vertex AI Index Endpoints

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Networking & Connectivity
Can I use both VPC peering and Private Service Connect for my index endpoint?
No, network and privateServiceConnectConfig are mutually exclusive. Choose one connectivity option: either VPC peering (using network) or Private Service Connect (using privateServiceConnectConfig), but not both.
Why am I getting an error with my network configuration?
The network property requires a specific format: projects/{project}/global/networks/{network}, where {project} must be a project number (like 12345), not a project name. The example shows using project.number to get the correct value.
What properties can't I change after creating an index endpoint?
The following properties are immutable and require recreating the resource to change: network, privateServiceConnectConfig, publicEndpointEnabled, region, encryptionSpec, and project.
Do I need to configure anything before setting up VPC peering?
Yes, private services access must already be configured for the network. Create a servicenetworking.Connection resource first and use dependsOn to ensure it exists before creating the index endpoint.
Deployment Options
How do I set up VPC peering for my index endpoint?
Configure the network property with the full VPC path (projects/{project_number}/global/networks/{network_name}), create a servicenetworking.Connection resource, and use dependsOn to ensure the connection is established first.
How do I use Private Service Connect instead of VPC peering?
Set privateServiceConnectConfig with enablePrivateServiceConnect: true and specify projectAllowlists. Don’t configure the network property, as they’re mutually exclusive.
How do I create a publicly accessible index endpoint?
Set publicEndpointEnabled: true. After creation, the publicEndpointDomainName output property will contain the domain name for accessing the endpoint.
Labels & Metadata
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 added by other clients or services), use the effectiveLabels output property.

Using a different cloud?

Explore analytics guides for other cloud providers: