Create GCP Cloud Workstations Clusters

The gcp:workstations/workstationCluster:WorkstationCluster resource, part of the Pulumi GCP provider, defines a regional grouping for workstation configurations and instances, establishing the VPC network placement and access controls. This guide focuses on three capabilities: VPC network and subnet placement, private endpoint configuration, and custom domain mapping.

Workstation clusters require VPC networks and subnets to define where workstation instances run. Custom domains require DNS ownership and separate configuration. The examples are intentionally small. Combine them with your own IAM policies, workstation configurations, and DNS records.

Create a cluster in a custom VPC network

Most deployments start by creating a cluster in a dedicated VPC with a custom subnet, providing isolation and control over IP addressing.

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

const defaultNetwork = new gcp.compute.Network("default", {
    name: "workstation-cluster",
    autoCreateSubnetworks: false,
});
const defaultSubnetwork = new gcp.compute.Subnetwork("default", {
    name: "workstation-cluster",
    ipCidrRange: "10.0.0.0/24",
    region: "us-central1",
    network: defaultNetwork.name,
});
const _default = new gcp.workstations.WorkstationCluster("default", {
    workstationClusterId: "workstation-cluster",
    network: defaultNetwork.id,
    subnetwork: defaultSubnetwork.id,
    location: "us-central1",
    labels: {
        label: "key",
    },
    annotations: {
        "label-one": "value-one",
    },
});
const project = gcp.organizations.getProject({});
import pulumi
import pulumi_gcp as gcp

default_network = gcp.compute.Network("default",
    name="workstation-cluster",
    auto_create_subnetworks=False)
default_subnetwork = gcp.compute.Subnetwork("default",
    name="workstation-cluster",
    ip_cidr_range="10.0.0.0/24",
    region="us-central1",
    network=default_network.name)
default = gcp.workstations.WorkstationCluster("default",
    workstation_cluster_id="workstation-cluster",
    network=default_network.id,
    subnetwork=default_subnetwork.id,
    location="us-central1",
    labels={
        "label": "key",
    },
    annotations={
        "label-one": "value-one",
    })
project = gcp.organizations.get_project()
package main

import (
	"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/workstations"
	"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("workstation-cluster"),
			AutoCreateSubnetworks: pulumi.Bool(false),
		})
		if err != nil {
			return err
		}
		defaultSubnetwork, err := compute.NewSubnetwork(ctx, "default", &compute.SubnetworkArgs{
			Name:        pulumi.String("workstation-cluster"),
			IpCidrRange: pulumi.String("10.0.0.0/24"),
			Region:      pulumi.String("us-central1"),
			Network:     defaultNetwork.Name,
		})
		if err != nil {
			return err
		}
		_, err = workstations.NewWorkstationCluster(ctx, "default", &workstations.WorkstationClusterArgs{
			WorkstationClusterId: pulumi.String("workstation-cluster"),
			Network:              defaultNetwork.ID(),
			Subnetwork:           defaultSubnetwork.ID(),
			Location:             pulumi.String("us-central1"),
			Labels: pulumi.StringMap{
				"label": pulumi.String("key"),
			},
			Annotations: pulumi.StringMap{
				"label-one": pulumi.String("value-one"),
			},
		})
		if err != nil {
			return err
		}
		_, err = organizations.LookupProject(ctx, &organizations.LookupProjectArgs{}, nil)
		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 = "workstation-cluster",
        AutoCreateSubnetworks = false,
    });

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

    var @default = new Gcp.Workstations.WorkstationCluster("default", new()
    {
        WorkstationClusterId = "workstation-cluster",
        Network = defaultNetwork.Id,
        Subnetwork = defaultSubnetwork.Id,
        Location = "us-central1",
        Labels = 
        {
            { "label", "key" },
        },
        Annotations = 
        {
            { "label-one", "value-one" },
        },
    });

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

});
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.workstations.WorkstationCluster;
import com.pulumi.gcp.workstations.WorkstationClusterArgs;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetProjectArgs;
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("workstation-cluster")
            .autoCreateSubnetworks(false)
            .build());

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

        var default_ = new WorkstationCluster("default", WorkstationClusterArgs.builder()
            .workstationClusterId("workstation-cluster")
            .network(defaultNetwork.id())
            .subnetwork(defaultSubnetwork.id())
            .location("us-central1")
            .labels(Map.of("label", "key"))
            .annotations(Map.of("label-one", "value-one"))
            .build());

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

    }
}
resources:
  default:
    type: gcp:workstations:WorkstationCluster
    properties:
      workstationClusterId: workstation-cluster
      network: ${defaultNetwork.id}
      subnetwork: ${defaultSubnetwork.id}
      location: us-central1
      labels:
        label: key
      annotations:
        label-one: value-one
  defaultNetwork:
    type: gcp:compute:Network
    name: default
    properties:
      name: workstation-cluster
      autoCreateSubnetworks: false
  defaultSubnetwork:
    type: gcp:compute:Subnetwork
    name: default
    properties:
      name: workstation-cluster
      ipCidrRange: 10.0.0.0/24
      region: us-central1
      network: ${defaultNetwork.name}
variables:
  project:
    fn::invoke:
      function: gcp:organizations:getProject
      arguments: {}

The workstationClusterId provides a unique identifier within the region. The network and subnetwork properties place the cluster in your VPC infrastructure, determining where workstation instances will run. The location property sets the region. Labels and annotations add metadata for organization and tooling integration.

Restrict access with private endpoint configuration

Teams that need to prevent public internet access can enable private endpoint mode, keeping all traffic within the VPC.

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

const defaultNetwork = new gcp.compute.Network("default", {
    name: "workstation-cluster-private",
    autoCreateSubnetworks: false,
});
const defaultSubnetwork = new gcp.compute.Subnetwork("default", {
    name: "workstation-cluster-private",
    ipCidrRange: "10.0.0.0/24",
    region: "us-central1",
    network: defaultNetwork.name,
});
const _default = new gcp.workstations.WorkstationCluster("default", {
    workstationClusterId: "workstation-cluster-private",
    network: defaultNetwork.id,
    subnetwork: defaultSubnetwork.id,
    location: "us-central1",
    privateClusterConfig: {
        enablePrivateEndpoint: true,
    },
    labels: {
        label: "key",
    },
    annotations: {
        "label-one": "value-one",
    },
});
const project = gcp.organizations.getProject({});
import pulumi
import pulumi_gcp as gcp

default_network = gcp.compute.Network("default",
    name="workstation-cluster-private",
    auto_create_subnetworks=False)
default_subnetwork = gcp.compute.Subnetwork("default",
    name="workstation-cluster-private",
    ip_cidr_range="10.0.0.0/24",
    region="us-central1",
    network=default_network.name)
default = gcp.workstations.WorkstationCluster("default",
    workstation_cluster_id="workstation-cluster-private",
    network=default_network.id,
    subnetwork=default_subnetwork.id,
    location="us-central1",
    private_cluster_config={
        "enable_private_endpoint": True,
    },
    labels={
        "label": "key",
    },
    annotations={
        "label-one": "value-one",
    })
project = gcp.organizations.get_project()
package main

import (
	"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/workstations"
	"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("workstation-cluster-private"),
			AutoCreateSubnetworks: pulumi.Bool(false),
		})
		if err != nil {
			return err
		}
		defaultSubnetwork, err := compute.NewSubnetwork(ctx, "default", &compute.SubnetworkArgs{
			Name:        pulumi.String("workstation-cluster-private"),
			IpCidrRange: pulumi.String("10.0.0.0/24"),
			Region:      pulumi.String("us-central1"),
			Network:     defaultNetwork.Name,
		})
		if err != nil {
			return err
		}
		_, err = workstations.NewWorkstationCluster(ctx, "default", &workstations.WorkstationClusterArgs{
			WorkstationClusterId: pulumi.String("workstation-cluster-private"),
			Network:              defaultNetwork.ID(),
			Subnetwork:           defaultSubnetwork.ID(),
			Location:             pulumi.String("us-central1"),
			PrivateClusterConfig: &workstations.WorkstationClusterPrivateClusterConfigArgs{
				EnablePrivateEndpoint: pulumi.Bool(true),
			},
			Labels: pulumi.StringMap{
				"label": pulumi.String("key"),
			},
			Annotations: pulumi.StringMap{
				"label-one": pulumi.String("value-one"),
			},
		})
		if err != nil {
			return err
		}
		_, err = organizations.LookupProject(ctx, &organizations.LookupProjectArgs{}, nil)
		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 = "workstation-cluster-private",
        AutoCreateSubnetworks = false,
    });

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

    var @default = new Gcp.Workstations.WorkstationCluster("default", new()
    {
        WorkstationClusterId = "workstation-cluster-private",
        Network = defaultNetwork.Id,
        Subnetwork = defaultSubnetwork.Id,
        Location = "us-central1",
        PrivateClusterConfig = new Gcp.Workstations.Inputs.WorkstationClusterPrivateClusterConfigArgs
        {
            EnablePrivateEndpoint = true,
        },
        Labels = 
        {
            { "label", "key" },
        },
        Annotations = 
        {
            { "label-one", "value-one" },
        },
    });

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

});
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.workstations.WorkstationCluster;
import com.pulumi.gcp.workstations.WorkstationClusterArgs;
import com.pulumi.gcp.workstations.inputs.WorkstationClusterPrivateClusterConfigArgs;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetProjectArgs;
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("workstation-cluster-private")
            .autoCreateSubnetworks(false)
            .build());

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

        var default_ = new WorkstationCluster("default", WorkstationClusterArgs.builder()
            .workstationClusterId("workstation-cluster-private")
            .network(defaultNetwork.id())
            .subnetwork(defaultSubnetwork.id())
            .location("us-central1")
            .privateClusterConfig(WorkstationClusterPrivateClusterConfigArgs.builder()
                .enablePrivateEndpoint(true)
                .build())
            .labels(Map.of("label", "key"))
            .annotations(Map.of("label-one", "value-one"))
            .build());

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

    }
}
resources:
  default:
    type: gcp:workstations:WorkstationCluster
    properties:
      workstationClusterId: workstation-cluster-private
      network: ${defaultNetwork.id}
      subnetwork: ${defaultSubnetwork.id}
      location: us-central1
      privateClusterConfig:
        enablePrivateEndpoint: true
      labels:
        label: key
      annotations:
        label-one: value-one
  defaultNetwork:
    type: gcp:compute:Network
    name: default
    properties:
      name: workstation-cluster-private
      autoCreateSubnetworks: false
  defaultSubnetwork:
    type: gcp:compute:Subnetwork
    name: default
    properties:
      name: workstation-cluster-private
      ipCidrRange: 10.0.0.0/24
      region: us-central1
      network: ${defaultNetwork.name}
variables:
  project:
    fn::invoke:
      function: gcp:organizations:getProject
      arguments: {}

The privateClusterConfig block controls network access. Setting enablePrivateEndpoint to true prevents workstations from being accessible via public internet, requiring VPC connectivity for all access. This configuration extends the basic cluster setup with private networking.

Configure a custom domain for workstation access

Organizations with existing DNS infrastructure can map workstations to a custom domain rather than using default Google-provided URLs.

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

const defaultNetwork = new gcp.compute.Network("default", {
    name: "workstation-cluster-custom-domain",
    autoCreateSubnetworks: false,
});
const defaultSubnetwork = new gcp.compute.Subnetwork("default", {
    name: "workstation-cluster-custom-domain",
    ipCidrRange: "10.0.0.0/24",
    region: "us-central1",
    network: defaultNetwork.name,
});
const _default = new gcp.workstations.WorkstationCluster("default", {
    workstationClusterId: "workstation-cluster-custom-domain",
    network: defaultNetwork.id,
    subnetwork: defaultSubnetwork.id,
    location: "us-central1",
    privateClusterConfig: {
        enablePrivateEndpoint: true,
    },
    domainConfig: {
        domain: "workstations.example.com",
    },
    labels: {
        label: "key",
    },
    annotations: {
        "label-one": "value-one",
    },
});
const project = gcp.organizations.getProject({});
import pulumi
import pulumi_gcp as gcp

default_network = gcp.compute.Network("default",
    name="workstation-cluster-custom-domain",
    auto_create_subnetworks=False)
default_subnetwork = gcp.compute.Subnetwork("default",
    name="workstation-cluster-custom-domain",
    ip_cidr_range="10.0.0.0/24",
    region="us-central1",
    network=default_network.name)
default = gcp.workstations.WorkstationCluster("default",
    workstation_cluster_id="workstation-cluster-custom-domain",
    network=default_network.id,
    subnetwork=default_subnetwork.id,
    location="us-central1",
    private_cluster_config={
        "enable_private_endpoint": True,
    },
    domain_config={
        "domain": "workstations.example.com",
    },
    labels={
        "label": "key",
    },
    annotations={
        "label-one": "value-one",
    })
project = gcp.organizations.get_project()
package main

import (
	"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/workstations"
	"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("workstation-cluster-custom-domain"),
			AutoCreateSubnetworks: pulumi.Bool(false),
		})
		if err != nil {
			return err
		}
		defaultSubnetwork, err := compute.NewSubnetwork(ctx, "default", &compute.SubnetworkArgs{
			Name:        pulumi.String("workstation-cluster-custom-domain"),
			IpCidrRange: pulumi.String("10.0.0.0/24"),
			Region:      pulumi.String("us-central1"),
			Network:     defaultNetwork.Name,
		})
		if err != nil {
			return err
		}
		_, err = workstations.NewWorkstationCluster(ctx, "default", &workstations.WorkstationClusterArgs{
			WorkstationClusterId: pulumi.String("workstation-cluster-custom-domain"),
			Network:              defaultNetwork.ID(),
			Subnetwork:           defaultSubnetwork.ID(),
			Location:             pulumi.String("us-central1"),
			PrivateClusterConfig: &workstations.WorkstationClusterPrivateClusterConfigArgs{
				EnablePrivateEndpoint: pulumi.Bool(true),
			},
			DomainConfig: &workstations.WorkstationClusterDomainConfigArgs{
				Domain: pulumi.String("workstations.example.com"),
			},
			Labels: pulumi.StringMap{
				"label": pulumi.String("key"),
			},
			Annotations: pulumi.StringMap{
				"label-one": pulumi.String("value-one"),
			},
		})
		if err != nil {
			return err
		}
		_, err = organizations.LookupProject(ctx, &organizations.LookupProjectArgs{}, nil)
		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 = "workstation-cluster-custom-domain",
        AutoCreateSubnetworks = false,
    });

    var defaultSubnetwork = new Gcp.Compute.Subnetwork("default", new()
    {
        Name = "workstation-cluster-custom-domain",
        IpCidrRange = "10.0.0.0/24",
        Region = "us-central1",
        Network = defaultNetwork.Name,
    });

    var @default = new Gcp.Workstations.WorkstationCluster("default", new()
    {
        WorkstationClusterId = "workstation-cluster-custom-domain",
        Network = defaultNetwork.Id,
        Subnetwork = defaultSubnetwork.Id,
        Location = "us-central1",
        PrivateClusterConfig = new Gcp.Workstations.Inputs.WorkstationClusterPrivateClusterConfigArgs
        {
            EnablePrivateEndpoint = true,
        },
        DomainConfig = new Gcp.Workstations.Inputs.WorkstationClusterDomainConfigArgs
        {
            Domain = "workstations.example.com",
        },
        Labels = 
        {
            { "label", "key" },
        },
        Annotations = 
        {
            { "label-one", "value-one" },
        },
    });

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

});
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.workstations.WorkstationCluster;
import com.pulumi.gcp.workstations.WorkstationClusterArgs;
import com.pulumi.gcp.workstations.inputs.WorkstationClusterPrivateClusterConfigArgs;
import com.pulumi.gcp.workstations.inputs.WorkstationClusterDomainConfigArgs;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetProjectArgs;
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("workstation-cluster-custom-domain")
            .autoCreateSubnetworks(false)
            .build());

        var defaultSubnetwork = new Subnetwork("defaultSubnetwork", SubnetworkArgs.builder()
            .name("workstation-cluster-custom-domain")
            .ipCidrRange("10.0.0.0/24")
            .region("us-central1")
            .network(defaultNetwork.name())
            .build());

        var default_ = new WorkstationCluster("default", WorkstationClusterArgs.builder()
            .workstationClusterId("workstation-cluster-custom-domain")
            .network(defaultNetwork.id())
            .subnetwork(defaultSubnetwork.id())
            .location("us-central1")
            .privateClusterConfig(WorkstationClusterPrivateClusterConfigArgs.builder()
                .enablePrivateEndpoint(true)
                .build())
            .domainConfig(WorkstationClusterDomainConfigArgs.builder()
                .domain("workstations.example.com")
                .build())
            .labels(Map.of("label", "key"))
            .annotations(Map.of("label-one", "value-one"))
            .build());

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

    }
}
resources:
  default:
    type: gcp:workstations:WorkstationCluster
    properties:
      workstationClusterId: workstation-cluster-custom-domain
      network: ${defaultNetwork.id}
      subnetwork: ${defaultSubnetwork.id}
      location: us-central1
      privateClusterConfig:
        enablePrivateEndpoint: true
      domainConfig:
        domain: workstations.example.com
      labels:
        label: key
      annotations:
        label-one: value-one
  defaultNetwork:
    type: gcp:compute:Network
    name: default
    properties:
      name: workstation-cluster-custom-domain
      autoCreateSubnetworks: false
  defaultSubnetwork:
    type: gcp:compute:Subnetwork
    name: default
    properties:
      name: workstation-cluster-custom-domain
      ipCidrRange: 10.0.0.0/24
      region: us-central1
      network: ${defaultNetwork.name}
variables:
  project:
    fn::invoke:
      function: gcp:organizations:getProject
      arguments: {}

The domainConfig block specifies your organization-owned domain. This configuration combines private endpoint access with custom domain mapping. You must own the specified domain and configure DNS records separately to complete the setup.

Beyond these examples

These snippets focus on specific cluster-level features: VPC network placement and subnet configuration, private endpoint access control, and custom domain mapping. They’re intentionally minimal rather than full workstation deployments.

The examples create VPC networks and subnets inline but may reference DNS infrastructure for custom domains. They focus on cluster configuration rather than provisioning workstation configurations or individual instances.

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

  • Resource manager tags for cost allocation and governance
  • Display names for human-readable identification
  • Workstation configurations and individual workstation instances
  • IAM permissions and service account configuration

These omissions are intentional: the goal is to illustrate how each cluster feature is wired, not provide drop-in workstation modules. See the Workstation Cluster resource reference for all available configuration options.

Let's create GCP Cloud Workstations Clusters

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Configuration & Immutability
What properties can't I change after creating the cluster?
Six properties are immutable: network, project, subnetwork, workstationClusterId, location, and tags. Changes to these require recreating the cluster.
Why don't annotations and labels show all values on my cluster?
The annotations and labels fields are non-authoritative and only manage values in your configuration. Use effectiveAnnotations and effectiveLabels output properties to see all values present on the resource.
Networking & Connectivity
What network configuration is required for a workstation cluster?
You must specify a network (VPC network reference) and subnetwork (Compute Engine subnetwork name). The subnetwork must be part of the specified network. Both are immutable after creation.
What firewall rules do I need for workstation connectivity?
Workstation VMs require egress access to the cluster’s control plane IP address (controlPlaneIp output property). Configure firewall rules allowing this egress traffic for workstations to function properly.
How do I create a private workstation cluster?
Configure privateClusterConfig with enablePrivateEndpoint set to true. This restricts cluster access to private endpoints only.
Customization & Features
How do I configure a custom domain for my cluster?
Use the domainConfig property with the domain field set to your custom domain (e.g., workstations.example.com).
How do I add resource manager tags to my cluster?
Use the tags property with tag key/value pairs in the format "123/environment": "production". Tags are immutable after cluster creation.
What's the difference between labels, annotations, and tags?
labels are propagated to underlying Compute Engine resources, annotations are client-specified metadata distinct from labels, and tags are resource manager tags. All three are optional, but labels and annotations are non-authoritative while tags are immutable.

Using a different cloud?

Explore compute guides for other cloud providers: