Last active
November 15, 2025 19:23
-
-
Save xSAVIKx/9c9b2a0b0e6857bc5ecc1665e095d963 to your computer and use it in GitHub Desktop.
GCP Cloud Workstations customizable setup with Pulumi, Typescript and Bun
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { dirname } from "node:path"; | |
| import { fileURLToPath } from "node:url"; | |
| import * as docker from "@pulumi/docker"; | |
| import * as gcp from "@pulumi/gcp"; | |
| import * as pulumi from "@pulumi/pulumi"; | |
| import { GoogleAuth } from "google-auth-library"; | |
| const __dirname = dirname(fileURLToPath(import.meta.url)); | |
| const region = "us-central1"; | |
| const gcpProvider = new gcp.Provider("gcpProvider", { | |
| project: gcp.config.project, | |
| region: region, | |
| defaultLabels: { team: "devops" }, | |
| }); | |
| const enableServices = (services: string[]) => { | |
| return services.map((service) => { | |
| return new gcp.projects.Service( | |
| service, | |
| { | |
| service: service, | |
| project: gcp.config.project, | |
| }, | |
| { provider: gcpProvider }, | |
| ); | |
| }); | |
| }; | |
| const requiredServices = [ | |
| "compute.googleapis.com", | |
| "workstations.googleapis.com", | |
| "artifactregistry.googleapis.com", | |
| "container.googleapis.com", | |
| ]; | |
| const services = enableServices(requiredServices); | |
| function defineNetwork() { | |
| const wsNetwork = new gcp.compute.Network( | |
| "wsNetwork", | |
| { | |
| autoCreateSubnetworks: false, | |
| }, | |
| { | |
| provider: gcpProvider, | |
| dependsOn: services, | |
| }, | |
| ); | |
| const wsSubnetwork = new gcp.compute.Subnetwork( | |
| "wsUsCentral1Subnet", | |
| { | |
| network: wsNetwork.id, | |
| region: region, | |
| ipCidrRange: "10.128.0.0/20", | |
| }, | |
| { | |
| provider: gcpProvider, | |
| parent: wsNetwork, | |
| }, | |
| ); | |
| return { wsNetwork, wsSubnetwork }; | |
| } | |
| function defineArtifactRegistry() { | |
| const artifactRegistry = new gcp.artifactregistry.Repository( | |
| "dockerRegistry", | |
| { | |
| location: region, | |
| repositoryId: "containers", | |
| description: "Private containers registry", | |
| format: "DOCKER", | |
| dockerConfig: { | |
| // usually better set to `true`, but for the lab we're setting it to false | |
| // to ease re-creation of the same containers. | |
| immutableTags: false, | |
| }, | |
| }, | |
| { provider: gcpProvider, dependsOn: services }, | |
| ); | |
| return { artifactRegistry }; | |
| } | |
| async function accessToken() { | |
| const auth = new GoogleAuth({ | |
| scopes: ["https://www.googleapis.com/auth/cloud-platform"], | |
| }); | |
| return (await auth.getAccessToken()) || undefined; | |
| } | |
| export default async function main() { | |
| const { wsNetwork, wsSubnetwork } = defineNetwork(); | |
| const { artifactRegistry } = defineArtifactRegistry(); | |
| const webstormImage = new docker.Image("webstormImage", { | |
| build: { | |
| context: `${__dirname}/base_images/webstorm`, | |
| dockerfile: `${__dirname}/base_images/webstorm/Dockerfile`, | |
| platform: "linux/amd64", | |
| }, | |
| imageName: pulumi.interpolate`${artifactRegistry.registryUri}/webstorm:latest`, | |
| registry: { | |
| server: artifactRegistry.registryUri, | |
| username: "oauth2accesstoken", | |
| password: await accessToken(), | |
| }, | |
| skipPush: false, | |
| }); | |
| const wsCluster: gcp.workstations.WorkstationCluster = | |
| new gcp.workstations.WorkstationCluster( | |
| "developmentCluster", | |
| { | |
| workstationClusterId: "customized-cluster", | |
| network: wsNetwork.id, | |
| subnetwork: wsSubnetwork.id, | |
| location: region, | |
| displayName: "Customized Cluster", | |
| annotations: { | |
| description: "Customized cluster for testing", | |
| }, | |
| labels: { | |
| purpose: "customized", | |
| }, | |
| }, | |
| { provider: gcpProvider, dependsOn: services }, | |
| ); | |
| const wsCustomizedConfig = new gcp.workstations.WorkstationConfig( | |
| "wsCustomizedConfig", | |
| { | |
| workstationConfigId: "customized-config", | |
| workstationClusterId: wsCluster.workstationClusterId, | |
| location: region, | |
| container: { | |
| image: webstormImage.repoDigest, | |
| }, | |
| idleTimeout: "3600s", | |
| runningTimeout: "43200s", | |
| host: { gceInstance: { machineType: "e2-standard-4" } }, | |
| persistentDirectories: [ | |
| { | |
| mountPath: "/home", | |
| gcePd: { | |
| diskType: "pd-standard", | |
| sizeGb: 200, | |
| reclaimPolicy: "DELETE", | |
| }, | |
| }, | |
| ], | |
| }, | |
| { provider: gcpProvider, dependsOn: services }, | |
| ); | |
| const workstation = new gcp.workstations.Workstation( | |
| "customized-workstation", | |
| { | |
| workstationId: "customized-workstation", | |
| workstationConfigId: wsCustomizedConfig.workstationConfigId, | |
| workstationClusterId: wsCluster.workstationClusterId, | |
| location: region, | |
| }, | |
| { provider: gcpProvider, dependsOn: services }, | |
| ); | |
| return { | |
| wsCluster: wsCluster.name, | |
| wsConfig: wsCustomizedConfig.name, | |
| workstation: workstation.host, | |
| artifactRegistry: artifactRegistry.registryUri, | |
| }; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment