Skip to content

Commit b345e62

Browse files
authored
feat: add Amazon DCV Windows module (#345)
1 parent 6597a2d commit b345e62

File tree

5 files changed

+305
-0
lines changed

5 files changed

+305
-0
lines changed

.icons/dcv.svg

+1
Loading

.images/amazon-dcv-windows.png

3.35 MB
Loading

amazon-dcv-windows/README.md

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
---
2+
display_name: Amazon DCV Windows
3+
description: Amazon DCV Server and Web Client for Windows
4+
icon: ../.icons/dcv.svg
5+
maintainer_github: coder
6+
verified: true
7+
tags: [windows, amazon, dcv, web, desktop]
8+
---
9+
10+
# Amazon DCV Windows
11+
12+
Amazon DCV is high performance remote display protocol that provides a secure way to deliver remote desktop and application streaming from any cloud or data center to any device, over varying network conditions.
13+
14+
![Amazon DCV on a Windows workspace](../.images/amazon-dcv-windows.png)
15+
16+
Enable DCV Server and Web Client on Windows workspaces.
17+
18+
```tf
19+
module "dcv" {
20+
count = data.coder_workspace.me.start_count
21+
source = "registry.coder.com/modules/amazon-dcv-windows/coder"
22+
version = "1.0.24"
23+
agent_id = resource.coder_agent.main.id
24+
}
25+
26+
27+
resource "coder_metadata" "dcv" {
28+
count = data.coder_workspace.me.start_count
29+
resource_id = aws_instance.dev.id # id of the instance resource
30+
31+
item {
32+
key = "DCV client instructions"
33+
value = "Run `coder port-forward ${data.coder_workspace.me.name} -p ${module.dcv[count.index].port}` and connect to **localhost:${module.dcv[count.index].port}${module.dcv[count.index].web_url_path}**"
34+
}
35+
item {
36+
key = "username"
37+
value = module.dcv[count.index].username
38+
}
39+
item {
40+
key = "password"
41+
value = module.dcv[count.index].password
42+
sensitive = true
43+
}
44+
}
45+
```
46+
47+
## License
48+
49+
Amazon DCV is free to use on AWS EC2 instances but requires a license for other cloud providers. Please see the instructions [here](https://docs.aws.amazon.com/dcv/latest/adminguide/setting-up-license.html#setting-up-license-ec2) for more information.

amazon-dcv-windows/install-dcv.ps1

+170
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
# Terraform variables
2+
$adminPassword = "${admin_password}"
3+
$port = "${port}"
4+
$webURLPath = "${web_url_path}"
5+
6+
function Set-LocalAdminUser {
7+
Write-Output "[INFO] Starting Set-LocalAdminUser function"
8+
$securePassword = ConvertTo-SecureString $adminPassword -AsPlainText -Force
9+
Write-Output "[DEBUG] Secure password created"
10+
Get-LocalUser -Name Administrator | Set-LocalUser -Password $securePassword
11+
Write-Output "[INFO] Administrator password set"
12+
Get-LocalUser -Name Administrator | Enable-LocalUser
13+
Write-Output "[INFO] User Administrator enabled successfully"
14+
Read-Host "[DEBUG] Press Enter to proceed to the next step"
15+
}
16+
17+
function Get-VirtualDisplayDriverRequired {
18+
Write-Output "[INFO] Starting Get-VirtualDisplayDriverRequired function"
19+
$token = Invoke-RestMethod -Headers @{'X-aws-ec2-metadata-token-ttl-seconds' = '21600'} -Method PUT -Uri http://169.254.169.254/latest/api/token
20+
Write-Output "[DEBUG] Token acquired: $token"
21+
$instanceType = Invoke-RestMethod -Headers @{'X-aws-ec2-metadata-token' = $token} -Method GET -Uri http://169.254.169.254/latest/meta-data/instance-type
22+
Write-Output "[DEBUG] Instance type: $instanceType"
23+
$OSVersion = ((Get-ItemProperty -Path "Microsoft.PowerShell.Core\Registry::\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ProductName).ProductName) -replace "[^0-9]", ''
24+
Write-Output "[DEBUG] OS version: $OSVersion"
25+
26+
# Force boolean result
27+
$result = (($OSVersion -ne "2019") -and ($OSVersion -ne "2022") -and ($OSVersion -ne "2025")) -and (($instanceType[0] -ne 'g') -and ($instanceType[0] -ne 'p'))
28+
Write-Output "[INFO] VirtualDisplayDriverRequired result: $result"
29+
Read-Host "[DEBUG] Press Enter to proceed to the next step"
30+
return [bool]$result
31+
}
32+
33+
function Download-DCV {
34+
param (
35+
[bool]$VirtualDisplayDriverRequired
36+
)
37+
Write-Output "[INFO] Starting Download-DCV function"
38+
39+
$downloads = @(
40+
@{
41+
Name = "DCV Display Driver"
42+
Required = $VirtualDisplayDriverRequired
43+
Path = "C:\Windows\Temp\DCVDisplayDriver.msi"
44+
Uri = "https://d1uj6qtbmh3dt5.cloudfront.net/nice-dcv-virtual-display-x64-Release.msi"
45+
},
46+
@{
47+
Name = "DCV Server"
48+
Required = $true
49+
Path = "C:\Windows\Temp\DCVServer.msi"
50+
Uri = "https://d1uj6qtbmh3dt5.cloudfront.net/nice-dcv-server-x64-Release.msi"
51+
}
52+
)
53+
54+
foreach ($download in $downloads) {
55+
if ($download.Required -and -not (Test-Path $download.Path)) {
56+
try {
57+
Write-Output "[INFO] Downloading $($download.Name)"
58+
59+
# Display progress manually (no events)
60+
$progressActivity = "Downloading $($download.Name)"
61+
$progressStatus = "Starting download..."
62+
Write-Progress -Activity $progressActivity -Status $progressStatus -PercentComplete 0
63+
64+
# Synchronously download the file
65+
$webClient = New-Object System.Net.WebClient
66+
$webClient.DownloadFile($download.Uri, $download.Path)
67+
68+
# Update progress
69+
Write-Progress -Activity $progressActivity -Status "Completed" -PercentComplete 100
70+
71+
Write-Output "[INFO] $($download.Name) downloaded successfully."
72+
} catch {
73+
Write-Output "[ERROR] Failed to download $($download.Name): $_"
74+
throw
75+
}
76+
} else {
77+
Write-Output "[INFO] $($download.Name) already exists. Skipping download."
78+
}
79+
}
80+
81+
Write-Output "[INFO] All downloads completed"
82+
Read-Host "[DEBUG] Press Enter to proceed to the next step"
83+
}
84+
85+
function Install-DCV {
86+
param (
87+
[bool]$VirtualDisplayDriverRequired
88+
)
89+
Write-Output "[INFO] Starting Install-DCV function"
90+
91+
if (-not (Get-Service -Name "dcvserver" -ErrorAction SilentlyContinue)) {
92+
if ($VirtualDisplayDriverRequired) {
93+
Write-Output "[INFO] Installing DCV Display Driver"
94+
Start-Process "C:\Windows\System32\msiexec.exe" -ArgumentList "/I C:\Windows\Temp\DCVDisplayDriver.msi /quiet /norestart" -Wait
95+
} else {
96+
Write-Output "[INFO] DCV Display Driver installation skipped (not required)."
97+
}
98+
Write-Output "[INFO] Installing DCV Server"
99+
Start-Process "C:\Windows\System32\msiexec.exe" -ArgumentList "/I C:\Windows\Temp\DCVServer.msi ADDLOCAL=ALL /quiet /norestart /l*v C:\Windows\Temp\dcv_install_msi.log" -Wait
100+
} else {
101+
Write-Output "[INFO] DCV Server already installed, skipping installation."
102+
}
103+
104+
# Wait for the service to appear with a timeout
105+
$timeout = 10 # seconds
106+
$elapsed = 0
107+
while (-not (Get-Service -Name "dcvserver" -ErrorAction SilentlyContinue) -and ($elapsed -lt $timeout)) {
108+
Start-Sleep -Seconds 1
109+
$elapsed++
110+
}
111+
112+
if ($elapsed -ge $timeout) {
113+
Write-Output "[WARNING] Timeout waiting for dcvserver service. A restart is required to complete installation."
114+
Restart-SystemForDCV
115+
} else {
116+
Write-Output "[INFO] dcvserver service detected successfully."
117+
}
118+
}
119+
120+
function Restart-SystemForDCV {
121+
Write-Output "[INFO] The system will restart in 10 seconds to finalize DCV installation."
122+
Start-Sleep -Seconds 10
123+
124+
# Initiate restart
125+
Restart-Computer -Force
126+
127+
# Exit the script after initiating restart
128+
Write-Output "[INFO] Please wait for the system to restart..."
129+
130+
Exit 1
131+
}
132+
133+
134+
function Configure-DCV {
135+
Write-Output "[INFO] Starting Configure-DCV function"
136+
$dcvPath = "Microsoft.PowerShell.Core\Registry::\HKEY_USERS\S-1-5-18\Software\GSettings\com\nicesoftware\dcv"
137+
138+
# Create the required paths
139+
@("$dcvPath\connectivity", "$dcvPath\session-management", "$dcvPath\session-management\automatic-console-session", "$dcvPath\display") | ForEach-Object {
140+
if (-not (Test-Path $_)) {
141+
New-Item -Path $_ -Force | Out-Null
142+
}
143+
}
144+
145+
# Set registry keys
146+
New-ItemProperty -Path "$dcvPath\session-management" -Name create-session -PropertyType DWORD -Value 1 -Force
147+
New-ItemProperty -Path "$dcvPath\session-management\automatic-console-session" -Name owner -Value Administrator -Force
148+
New-ItemProperty -Path "$dcvPath\connectivity" -Name quic-port -PropertyType DWORD -Value $port -Force
149+
New-ItemProperty -Path "$dcvPath\connectivity" -Name web-port -PropertyType DWORD -Value $port -Force
150+
New-ItemProperty -Path "$dcvPath\connectivity" -Name web-url-path -PropertyType String -Value $webURLPath -Force
151+
152+
# Attempt to restart service
153+
if (Get-Service -Name "dcvserver" -ErrorAction SilentlyContinue) {
154+
Restart-Service -Name "dcvserver"
155+
} else {
156+
Write-Output "[WARNING] dcvserver service not found. Ensure the system was restarted properly."
157+
}
158+
159+
Write-Output "[INFO] DCV configuration completed"
160+
Read-Host "[DEBUG] Press Enter to proceed to the next step"
161+
}
162+
163+
# Main Script Execution
164+
Write-Output "[INFO] Starting script"
165+
$VirtualDisplayDriverRequired = [bool](Get-VirtualDisplayDriverRequired)
166+
Set-LocalAdminUser
167+
Download-DCV -VirtualDisplayDriverRequired $VirtualDisplayDriverRequired
168+
Install-DCV -VirtualDisplayDriverRequired $VirtualDisplayDriverRequired
169+
Configure-DCV
170+
Write-Output "[INFO] Script completed"

amazon-dcv-windows/main.tf

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
terraform {
2+
required_version = ">= 1.0"
3+
4+
required_providers {
5+
coder = {
6+
source = "coder/coder"
7+
version = ">= 0.17"
8+
}
9+
}
10+
}
11+
12+
variable "agent_id" {
13+
type = string
14+
description = "The ID of a Coder agent."
15+
}
16+
17+
variable "admin_password" {
18+
type = string
19+
default = "coderDCV!"
20+
sensitive = true
21+
}
22+
23+
variable "port" {
24+
type = number
25+
description = "The port number for the DCV server."
26+
default = 8443
27+
}
28+
29+
variable "subdomain" {
30+
type = bool
31+
description = "Whether to use a subdomain for the DCV server."
32+
default = true
33+
}
34+
35+
variable "slug" {
36+
type = string
37+
description = "The slug of the web-dcv coder_app resource."
38+
default = "web-dcv"
39+
}
40+
41+
resource "coder_app" "web-dcv" {
42+
agent_id = var.agent_id
43+
slug = var.slug
44+
display_name = "Web DCV"
45+
url = "https://localhost:${var.port}${local.web_url_path}?username=${local.admin_username}&password=${var.admin_password}"
46+
icon = "/icon/dcv.svg"
47+
subdomain = var.subdomain
48+
}
49+
50+
resource "coder_script" "install-dcv" {
51+
agent_id = var.agent_id
52+
display_name = "Install DCV"
53+
icon = "/icon/dcv.svg"
54+
run_on_start = true
55+
script = templatefile("${path.module}/install-dcv.ps1", {
56+
admin_password : var.admin_password,
57+
port : var.port,
58+
web_url_path : local.web_url_path
59+
})
60+
}
61+
62+
data "coder_workspace" "me" {}
63+
data "coder_workspace_owner" "me" {}
64+
65+
locals {
66+
web_url_path = var.subdomain ? "/" : format("/@%s/%s/apps/%s", data.coder_workspace_owner.me.name, data.coder_workspace.me.name, var.slug)
67+
admin_username = "Administrator"
68+
}
69+
70+
output "web_url_path" {
71+
value = local.web_url_path
72+
}
73+
74+
output "username" {
75+
value = local.admin_username
76+
}
77+
78+
output "password" {
79+
value = var.admin_password
80+
sensitive = true
81+
}
82+
83+
output "port" {
84+
value = var.port
85+
}

0 commit comments

Comments
 (0)