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 internet access, VPC network peering, and Private Service Connect configuration.

Index endpoints can operate in three mutually exclusive modes: public internet access, VPC peering (requires VPC network and Service Networking setup), or Private Service Connect (requires project allowlists). The examples are intentionally small. Combine them with your own index deployments and access policies.

Expose an endpoint through a public domain

Some deployments need index endpoints accessible from the internet without complex networking setup.

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 the publicEndpointDomainName output property) that clients can use to query deployed indexes. This mode requires no VPC configuration but exposes the endpoint to the internet.

Peer an endpoint with a VPC network

Most production deployments connect index endpoints to a VPC network to enable private access from compute resources.

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 specifies the VPC to peer with, using the format projects/{project_number}/global/networks/{network_name}. Before creating the endpoint, you must configure VPC peering through the Service Networking API: reserve an IP range with compute.GlobalAddress (purpose set to VPC_PEERING) and establish the connection with servicenetworking.Connection. The dependsOn ensures peering completes before endpoint creation.

Configure Private Service Connect access

Private Service Connect provides an alternative to VPC peering with more granular project-level access control.

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 mode is mutually exclusive with the network property; you configure either VPC peering or Private Service Connect, not both.

Beyond these examples

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

The examples may reference pre-existing infrastructure such as VPC networks and IP address ranges for VPC peering, and Service Networking API enablement. They focus on endpoint connectivity rather than index deployment or query operations.

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 Index Endpoint 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

Connectivity & Networking
What are my options for connecting to an index endpoint?

You have three connectivity options:

  1. VPC peering - Use network property to peer with a VPC
  2. Private Service Connect - Use privateServiceConnectConfig for PSC
  3. Public endpoint - Set publicEndpointEnabled: true for internet access
Can I use both VPC peering and Private Service Connect?
No, network and privateServiceConnectConfig are mutually exclusive. Choose one connectivity method.
Why isn't my VPC network peering working?
Check two common issues: (1) The network property requires project NUMBER, not project ID, in format projects/{number}/global/networks/{name}. (2) Private services access must already be configured via servicenetworking.Connection before creating the endpoint.
Why do I need dependsOn for the VPC connection?
The servicenetworking.Connection must complete before creating the index endpoint to ensure private services access is properly configured for the network.
Configuration & Immutability
What can't I change after creating an index endpoint?
These properties are immutable and require resource replacement if changed: network, privateServiceConnectConfig, project, encryptionSpec, publicEndpointEnabled, and region.
Why aren't my label changes showing up in the resource?
The labels field is non-authoritative and only manages labels in your configuration. Use effectiveLabels output to see all labels present on the resource, including those set by other clients.
How long can the display name be?
The displayName can be up to 128 characters long and support any UTF-8 characters.
Setup & Configuration
How do I enable customer-managed encryption?
Set the encryptionSpec property with your encryption key. This secures the index endpoint and all its sub-resources. Note that encryptionSpec is immutable.
What happens when I enable publicEndpointEnabled?
When publicEndpointEnabled is true, the publicEndpointDomainName output field will be populated with the domain name for accessing the endpoint.

Using a different cloud?

Explore analytics guides for other cloud providers: