The gcp:workstations/workstationCluster:WorkstationCluster resource, part of the Pulumi GCP provider, defines a regional container for Cloud Workstations: its network placement, access controls, and organizational metadata. This guide focuses on four capabilities: VPC network placement, private endpoint configuration, custom domain setup, and resource manager tag binding.
Workstation clusters require VPC networks and subnets. Custom domains require DNS configuration and certificate management outside the cluster resource. The examples are intentionally small. Combine them with your own network infrastructure and governance policies.
Create a cluster in a VPC subnet
Cloud Workstations deployments begin by creating a cluster that defines where workstation VMs will run. The cluster acts as a regional container for workstation configurations.
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 cluster provisions in a specific region and subnet. The workstationClusterId provides a unique identifier, while network and subnetwork specify where workstation VMs will be placed. The location property determines which GCP region hosts the cluster control plane.
Restrict access with private endpoints
Organizations with strict network policies often require that workstation control plane endpoints remain private, preventing public internet access.
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 control plane visibility. Setting enablePrivateEndpoint to true ensures the cluster’s management APIs are only accessible from within your VPC, blocking public internet access. This extends the basic cluster configuration with network isolation.
Configure custom domain for workstation access
Teams that need branded URLs or certificate management often configure custom domains rather than using Google-generated hostnames.
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 a custom domain for workstation endpoints. Users access workstations via your organization’s domain instead of Google-generated URLs. You must configure DNS records and manage SSL certificates separately; the cluster resource only binds the domain name.
Apply resource manager tags for governance
Enterprise environments use resource manager tags to enforce policies, track costs, and control access across projects.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const project = gcp.organizations.getProject({});
const tagKey = new gcp.tags.TagKey("tag_key", {
parent: project.then(project => `projects/${project.number}`),
shortName: "keyname",
});
const tagValue = new gcp.tags.TagValue("tag_value", {
parent: pulumi.interpolate`tagKeys/${tagKey.name}`,
shortName: "valuename",
});
const defaultNetwork = new gcp.compute.Network("default", {
name: "workstation-cluster-tags",
autoCreateSubnetworks: false,
});
const defaultSubnetwork = new gcp.compute.Subnetwork("default", {
name: "workstation-cluster-tags",
ipCidrRange: "10.0.0.0/24",
region: "us-central1",
network: defaultNetwork.name,
});
const _default = new gcp.workstations.WorkstationCluster("default", {
workstationClusterId: "workstation-cluster-tags",
network: defaultNetwork.id,
subnetwork: defaultSubnetwork.id,
location: "us-central1",
tags: pulumi.all([project, tagKey.shortName, tagValue.shortName]).apply(([project, tagKeyShortName, tagValueShortName]) => {
[`${project.projectId}/${tagKeyShortName}`]: tagValueShortName,
}),
});
import pulumi
import pulumi_gcp as gcp
project = gcp.organizations.get_project()
tag_key = gcp.tags.TagKey("tag_key",
parent=f"projects/{project.number}",
short_name="keyname")
tag_value = gcp.tags.TagValue("tag_value",
parent=tag_key.name.apply(lambda name: f"tagKeys/{name}"),
short_name="valuename")
default_network = gcp.compute.Network("default",
name="workstation-cluster-tags",
auto_create_subnetworks=False)
default_subnetwork = gcp.compute.Subnetwork("default",
name="workstation-cluster-tags",
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-tags",
network=default_network.id,
subnetwork=default_subnetwork.id,
location="us-central1",
tags=pulumi.Output.all(
tagKeyShort_name=tag_key.short_name,
tagValueShort_name=tag_value.short_name
).apply(lambda resolved_outputs: {
f"{project.project_id}/{resolved_outputs['tagKeyShort_name']}": resolved_outputs['tagValueShort_name'],
})
)
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/tags"
"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 {
project, err := organizations.LookupProject(ctx, &organizations.LookupProjectArgs{
}, nil);
if err != nil {
return err
}
tagKey, err := tags.NewTagKey(ctx, "tag_key", &tags.TagKeyArgs{
Parent: pulumi.Sprintf("projects/%v", project.Number),
ShortName: pulumi.String("keyname"),
})
if err != nil {
return err
}
tagValue, err := tags.NewTagValue(ctx, "tag_value", &tags.TagValueArgs{
Parent: tagKey.Name.ApplyT(func(name string) (string, error) {
return fmt.Sprintf("tagKeys/%v", name), nil
}).(pulumi.StringOutput),
ShortName: pulumi.String("valuename"),
})
if err != nil {
return err
}
defaultNetwork, err := compute.NewNetwork(ctx, "default", &compute.NetworkArgs{
Name: pulumi.String("workstation-cluster-tags"),
AutoCreateSubnetworks: pulumi.Bool(false),
})
if err != nil {
return err
}
defaultSubnetwork, err := compute.NewSubnetwork(ctx, "default", &compute.SubnetworkArgs{
Name: pulumi.String("workstation-cluster-tags"),
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-tags"),
Network: defaultNetwork.ID(),
Subnetwork: defaultSubnetwork.ID(),
Location: pulumi.String("us-central1"),
Tags: pulumi.All(tagKey.ShortName,tagValue.ShortName).ApplyT(func(_args []interface{}) (map[string]string, error) {
tagKeyShortName := _args[0].(string)
tagValueShortName := _args[1].(string)
return map[string]string{
fmt.Sprintf("%v/%v", project.ProjectId, tagKeyShortName): tagValueShortName,
}, nil
}).(pulumi.Map[string]stringOutput),
})
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 tagKey = new Gcp.Tags.TagKey("tag_key", new()
{
Parent = $"projects/{project.Apply(getProjectResult => getProjectResult.Number)}",
ShortName = "keyname",
});
var tagValue = new Gcp.Tags.TagValue("tag_value", new()
{
Parent = tagKey.Name.Apply(name => $"tagKeys/{name}"),
ShortName = "valuename",
});
var defaultNetwork = new Gcp.Compute.Network("default", new()
{
Name = "workstation-cluster-tags",
AutoCreateSubnetworks = false,
});
var defaultSubnetwork = new Gcp.Compute.Subnetwork("default", new()
{
Name = "workstation-cluster-tags",
IpCidrRange = "10.0.0.0/24",
Region = "us-central1",
Network = defaultNetwork.Name,
});
var @default = new Gcp.Workstations.WorkstationCluster("default", new()
{
WorkstationClusterId = "workstation-cluster-tags",
Network = defaultNetwork.Id,
Subnetwork = defaultSubnetwork.Id,
Location = "us-central1",
Tags = Output.Tuple(project, tagKey.ShortName, tagValue.ShortName).Apply(values =>
{
var project = values.Item1;
var tagKeyShortName = values.Item2;
var tagValueShortName = values.Item3;
return
{
{ $"{project.Apply(getProjectResult => getProjectResult.ProjectId)}/{tagKeyShortName}", tagValueShortName },
};
}),
});
});
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.tags.TagKey;
import com.pulumi.gcp.tags.TagKeyArgs;
import com.pulumi.gcp.tags.TagValue;
import com.pulumi.gcp.tags.TagValueArgs;
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 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 tagKey = new TagKey("tagKey", TagKeyArgs.builder()
.parent(String.format("projects/%s", project.number()))
.shortName("keyname")
.build());
var tagValue = new TagValue("tagValue", TagValueArgs.builder()
.parent(tagKey.name().applyValue(_name -> String.format("tagKeys/%s", _name)))
.shortName("valuename")
.build());
var defaultNetwork = new Network("defaultNetwork", NetworkArgs.builder()
.name("workstation-cluster-tags")
.autoCreateSubnetworks(false)
.build());
var defaultSubnetwork = new Subnetwork("defaultSubnetwork", SubnetworkArgs.builder()
.name("workstation-cluster-tags")
.ipCidrRange("10.0.0.0/24")
.region("us-central1")
.network(defaultNetwork.name())
.build());
var default_ = new WorkstationCluster("default", WorkstationClusterArgs.builder()
.workstationClusterId("workstation-cluster-tags")
.network(defaultNetwork.id())
.subnetwork(defaultSubnetwork.id())
.location("us-central1")
.tags(Output.tuple(tagKey.shortName(), tagValue.shortName()).applyValue(values -> {
var tagKeyShortName = values.t1;
var tagValueShortName = values.t2;
return Map.of(String.format("%s/%s", project.projectId(),tagKeyShortName), tagValueShortName);
}))
.build());
}
}
resources:
tagKey:
type: gcp:tags:TagKey
name: tag_key
properties:
parent: projects/${project.number}
shortName: keyname
tagValue:
type: gcp:tags:TagValue
name: tag_value
properties:
parent: tagKeys/${tagKey.name}
shortName: valuename
default:
type: gcp:workstations:WorkstationCluster
properties:
workstationClusterId: workstation-cluster-tags
network: ${defaultNetwork.id}
subnetwork: ${defaultSubnetwork.id}
location: us-central1
tags:
${project.projectId}/${tagKey.shortName}: ${tagValue.shortName}
defaultNetwork:
type: gcp:compute:Network
name: default
properties:
name: workstation-cluster-tags
autoCreateSubnetworks: false
defaultSubnetwork:
type: gcp:compute:Subnetwork
name: default
properties:
name: workstation-cluster-tags
ipCidrRange: 10.0.0.0/24
region: us-central1
network: ${defaultNetwork.name}
variables:
project:
fn::invoke:
function: gcp:organizations:getProject
arguments: {}
Resource manager tags differ from labels: they’re hierarchical policy bindings managed at the organization level. The example creates a TagKey and TagValue, then binds them to the cluster via the tags property. These tags enable organization-wide policy enforcement and cost allocation.
Beyond these examples
These snippets focus on specific cluster-level features: VPC network placement and subnet configuration, private endpoint access controls, and custom domain configuration and resource manager tags. They’re intentionally minimal rather than full workstation deployments.
The examples may reference pre-existing infrastructure such as VPC networks and subnets (examples create them inline but production typically references existing), DNS records for custom domains, and SSL certificates for custom domains. They focus on configuring the cluster rather than provisioning everything around it.
To keep things focused, common cluster patterns are omitted, including:
- Display names and human-readable identifiers (displayName)
- Annotations for client-specific metadata
- Labels for resource organization (shown but not explained)
- 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 WorkstationCluster 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 FREEFrequently Asked Questions
Provider & Beta Features
terraform-provider-google-beta provider.Resource Configuration & Immutability
network, project, subnetwork, workstationClusterId, location, and tags. Plan these values carefully during initial creation.network, project, subnetwork, and workstationClusterId. All four are required and immutable.Networking & Connectivity
network (in format projects/{projectNumber}/global/networks/{network_id}) and subnetwork properties. The subnetwork must be part of the specified network.privateClusterConfig with enablePrivateEndpoint set to true.controlPlaneIp address to communicate with the service. Configure your firewall rules to allow this traffic.domainConfig property with a domain field specifying your custom domain (e.g., workstations.example.com).Labels & Annotations
annotations and labels fields are non-authoritative and only manage values present in your configuration. Use effectiveAnnotations and effectiveLabels to see all annotations and labels on the resource, including those set by other clients or services.Monitoring & Troubleshooting
degraded field indicates the resource may require user action to restore full functionality. Check the conditions field for details about the current resource state.