Deploy GCP Windows Desktop (HyOps Blueprint)¶
- Purpose: Provision a Windows Server VM in GCP with a public IP and an RDP firewall rule scoped to your operator IP.
- Owner: Platform engineering
- Trigger: Need for a GCP Windows host: GUI access, Windows-only tooling, or operator lab
- Impact: Creates a GCP Compute instance and a named firewall rule; incurs GCP compute cost while running
- Severity: P2
- Pre-reqs:
hyops init gcpcomplete for the target env,gcloudSDK installed, Windows Server quota available in target zone. - Rollback strategy:
hyops blueprint destroytears down both steps in reverse order. See §Teardown.
Context¶
Blueprint ref: gcp/windows-desktop@v1
Location: hybridops-core/blueprints/gcp/windows-desktop@v1/blueprint.yml
Step flow:
platform/gcp/platform-vm#gcp_windows_vm: Windows Server 2022 VM, public IP,allow-rdptagplatform/gcp/vm-firewall-rules#gcp_windows_firewall: ingress rule TCP 3389, scoped tosource_ranges
Both steps must succeed. The firewall rule is only useful once the VM is running, so step 2 requires step 1 (requires: [gcp_windows_vm]).
The VM does not use SSH keys (ssh_keys_from_init: false). RDP credentials are generated by GCP and retrieved via gcloud compute reset-windows-password after provisioning.
Cost¶
An e2-standard-2 Windows Server 2022 VM in europe-west2 costs roughly £0.09–0.11/hr depending on region. Stop the instance when not in use to pause compute billing. The firewall rule has no cost.
Stop:
gcloud compute instances stop win-rdp-01 \
--project <PROJECT_ID> --zone <ZONE>
Start:
gcloud compute instances start win-rdp-01 \
--project <PROJECT_ID> --zone <ZONE>
Preconditions and safety checks¶
-
GCP init is ready:
hyops state show --env dev --init gcp -
Confirm your current public IP (used to scope the RDP firewall rule):
curl -s https://checkip.amazonaws.com -
Confirm Windows Server quota in the target zone:
gcloud compute regions describe <REGION> \ --project <PROJECT_ID> \ --format="table(quotas[].metric,quotas[].usage,quotas[].limit)" \ | grep CPUS -
Review the two
CHANGE_MEvalues you must set before deploy: CHANGE_ME_GCP_ZONE: e.g.,europe-west2-bCHANGE_ME_YOUR_IP/32: your operator public IP in CIDR notation
Steps¶
- Initialise the blueprint config
hyops blueprint init --env dev \ --ref gcp/windows-desktop@v1 \ --dest-name windows-desktop.yml
This writes the blueprint to:
~/.hybridops/envs/dev/config/blueprints/windows-desktop.yml
- Set real values
Open ~/.hybridops/envs/dev/config/blueprints/windows-desktop.yml and replace:
CHANGE_ME_GCP_ZONE→ e.g.,europe-west2-bCHANGE_ME_YOUR_IP/32→ your public IP, e.g.,203.0.113.42/32
Optional overrides (defaults are usable as-is):
machine_type: defaulte2-standard-2; increase if the workload needs more CPU/RAMboot_disk_size_gb: default80-
source_image_family: defaultwindows-2022; change towindows-2019if needed -
Validate
hyops blueprint validate \ --file "$HOME/.hybridops/envs/dev/config/blueprints/windows-desktop.yml" -
Preflight
hyops blueprint preflight --env dev \ --file "$HOME/.hybridops/envs/dev/config/blueprints/windows-desktop.yml" -
Deploy
hyops blueprint deploy --env dev \ --file "$HOME/.hybridops/envs/dev/config/blueprints/windows-desktop.yml" \ --execute
Both steps run in sequence. Typical duration: 3–6 minutes (Windows boot is slower than Linux).
- Verify state
cat ~/.hybridops/envs/dev/state/modules/platform__gcp__platform-vm/instances/gcp_windows_vm.json \ | python3 -m json.tool | grep -E '"status"|"state_instance"' cat ~/.hybridops/envs/dev/state/modules/platform__gcp__vm-firewall-rules/instances/gcp_windows_firewall.json \ | python3 -m json.tool | grep '"status"'
Both should show "status": "ok".
- Get the public IP
cat ~/.hybridops/envs/dev/state/modules/platform__gcp__platform-vm/instances/gcp_windows_vm.json \ | python3 -m json.tool
Look for outputs.ipv4_addresses.win-rdp-01. Or directly:
gcloud compute instances describe win-rdp-01 \
--project <PROJECT_ID> --zone <ZONE> \
--format="get(networkInterfaces[0].accessConfigs[0].natIP)"
- Get RDP credentials
Windows VMs do not use SSH keys. Retrieve a GCP-generated username and password:
gcloud compute reset-windows-password win-rdp-01 \
--project <PROJECT_ID> \
--zone <ZONE>
This outputs a username and password. Store them securely: GCP does not retain the plaintext password after this call.
- RDP in
Connect with any RDP client to <PUBLIC_IP>:3389 using the credentials from step 8.
- Windows: built-in Remote Desktop Connection (
mstsc) - macOS: Microsoft Remote Desktop (App Store)
- Linux:
rdesktoporxfreerdp
Example with xfreerdp:
xfreerdp /v:<PUBLIC_IP> /u:<USERNAME> /p:<PASSWORD> /cert-ignore
Verification¶
Primary state:
~/.hybridops/envs/<env>/state/modules/platform__gcp__platform-vm/instances/gcp_windows_vm.json
~/.hybridops/envs/<env>/state/modules/platform__gcp__vm-firewall-rules/instances/gcp_windows_firewall.json
Cross-check in GCP:
# VM running
gcloud compute instances describe win-rdp-01 \
--project <PROJECT_ID> --zone <ZONE> \
--format="get(status)"
# Firewall rule present
gcloud compute firewall-rules list \
--project <PROJECT_ID> \
--filter="targetTags~allow-rdp" \
--format="table(name,sourceRanges,allowed)"
Teardown¶
Destroy all blueprint resources in reverse order using the blueprint file:
hyops blueprint destroy --env dev \
--file "$HOME/.hybridops/envs/dev/config/blueprints/windows-desktop.yml" \
--execute
This destroys steps in the order: firewall → VM. A confirmation prompt lists both steps and their current state before proceeding. Use --yes to skip the prompt in non-interactive contexts.
Manual teardown (individual steps)
If only specific steps need retargeting, destroy each module individually. The --inputs flag is required because zone and other required inputs are not stored in the state record directly.
# Destroy the firewall rules
hyops destroy --env dev \
--module platform/gcp/vm-firewall-rules \
--state-instance gcp_windows_firewall \
--inputs "$HOME/.hybridops/envs/dev/config/modules/platform__gcp__vm-firewall-rules/instances/gcp_windows_firewall.inputs.yml"
# Destroy the VM
hyops destroy --env dev \
--module platform/gcp/platform-vm \
--state-instance gcp_windows_vm \
--inputs "$HOME/.hybridops/envs/dev/config/modules/platform__gcp__platform-vm/instances/gcp_windows_vm.inputs.yml"
Troubleshooting¶
RDP connection refused¶
Check (in order):
- Public IP is correct: retrieve it again via
gcloud compute instances describe - Firewall rule
allow-rdpexists andsource_rangesincludes your current IP (your IP may have changed) -
VM status is
RUNNING:gcloud compute instances describe win-rdp-01 \ --project <PROJECT_ID> --zone <ZONE> \ --format="get(status)" -
Windows RDP service is ready: Windows takes 3–5 minutes after the VM reports
RUNNINGbefore RDP is accepting connections
gcloud compute reset-windows-password fails with permission error¶
Cause: operator lacks compute.instances.setMetadata permission.
Fix: ensure operator has roles/compute.instanceAdmin.v1 or a custom role with that permission on the project.
VM state stuck at STAGING¶
Cause: zone capacity or quota issue.
Fix:
- Try a different zone in the same region (e.g.,
europe-west2-ainstead ofeurope-west2-b) - Check quota:
gcloud compute regions describe <REGION> --format="table(quotas[].metric,quotas[].usage,quotas[].limit)" | grep CPU - Retry
hyops applywith an updated zone in the blueprint config
Error: resource already exists¶
Cause: a firewall rule with the same computed name already exists outside state.
Fix: rename context_id in the blueprint config (e.g., desktop2) to generate a different rule name, or delete the orphaned GCP rule manually before applying.