Skip to content
This repository was archived by the owner on May 15, 2025. It is now read-only.

Commit 576d701

Browse files
committed
feat: add Amazon DCV Windows module
- Introduce Amazon DCV for remote desktop on Windows workspaces. - Script to install and configure DCV components. - Terraform setup for configuring resources like coder_app. - Ensure secure management with sensitive variable handling. chore: add health check badge (#341) chore: integrate Instatus in check script (#342)
1 parent 57d96ca commit 576d701

File tree

7 files changed

+399
-10
lines changed

7 files changed

+399
-10
lines changed

.github/scripts/check.sh

Lines changed: 84 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,85 @@
11
#!/usr/bin/env bash
22
set -o pipefail
3-
REGISTRY_BASE_URL="${REGISTRY_BASE_URL:-https://registry.coder.com}"
43
set -u
54

6-
if [[ -n "${VERBOSE:-}" ]]; then
7-
set -x
8-
fi
5+
# List of required environment variables
6+
required_vars=(
7+
"INSTATUS_API_KEY"
8+
"INSTATUS_PAGE_ID"
9+
"INSTATUS_COMPONENT_ID"
10+
)
11+
12+
# Check if each required variable is set
13+
for var in "${required_vars[@]}"; do
14+
if [[ -z "${!var:-}" ]]; then
15+
echo "Error: Environment variable '$var' is not set."
16+
exit 1
17+
fi
18+
done
19+
20+
REGISTRY_BASE_URL="${REGISTRY_BASE_URL:-https://registry.coder.com}"
921

1022
status=0
1123
declare -a modules=()
1224
declare -a failures=()
25+
26+
# Collect all module directories containing a main.tf file
1327
for path in $(find . -not -path '*/.*' -type f -name main.tf -maxdepth 2 | cut -d '/' -f 2 | sort -u); do
1428
modules+=("${path}")
1529
done
30+
1631
echo "Checking modules: ${modules[*]}"
32+
33+
# Function to update the component status on Instatus
34+
update_component_status() {
35+
local component_status=$1
36+
# see https://instatus.com/help/api/components
37+
(curl -X PUT "https://api.instatus.com/v1/$INSTATUS_PAGE_ID/components/$INSTATUS_COMPONENT_ID" \
38+
-H "Authorization: Bearer $INSTATUS_API_KEY" \
39+
-H "Content-Type: application/json" \
40+
-d "{\"status\": \"$component_status\"}")
41+
}
42+
43+
# Function to create an incident
44+
create_incident() {
45+
local incident_name="Testing Instatus"
46+
local message="The following modules are experiencing issues:\n"
47+
for i in "${!failures[@]}"; do
48+
message+="$(($i + 1)). ${failures[$i]}\n"
49+
done
50+
51+
component_status="PARTIALOUTAGE"
52+
if (( ${#failures[@]} == ${#modules[@]} )); then
53+
component_status="MAJOROUTAGE"
54+
fi
55+
# see https://instatus.com/help/api/incidents
56+
response=$(curl -s -X POST "https://api.instatus.com/v1/$INSTATUS_PAGE_ID/incidents" \
57+
-H "Authorization: Bearer $INSTATUS_API_KEY" \
58+
-H "Content-Type: application/json" \
59+
-d "{
60+
\"name\": \"$incident_name\",
61+
\"message\": \"$message\",
62+
\"components\": [\"$INSTATUS_COMPONENT_ID\"],
63+
\"status\": \"INVESTIGATING\",
64+
\"notify\": true,
65+
\"statuses\": [
66+
{
67+
\"id\": \"$INSTATUS_COMPONENT_ID\",
68+
\"status\": \"PARTIALOUTAGE\"
69+
}
70+
]
71+
}")
72+
73+
incident_id=$(echo "$response" | jq -r '.id')
74+
echo "$incident_id"
75+
}
76+
77+
# Check each module's accessibility
1778
for module in "${modules[@]}"; do
1879
# Trim leading/trailing whitespace from module name
1980
module=$(echo "${module}" | xargs)
2081
url="${REGISTRY_BASE_URL}/modules/${module}"
21-
printf "=== Check module %s at %s\n" "${module}" "${url}"
82+
printf "=== Checking module %s at %s\n" "${module}" "${url}"
2283
status_code=$(curl --output /dev/null --head --silent --fail --location "${url}" --retry 3 --write-out "%{http_code}")
2384
# shellcheck disable=SC2181
2485
if (( status_code != 200 )); then
@@ -30,7 +91,23 @@ for module in "${modules[@]}"; do
3091
fi
3192
done
3293

33-
if (( status != 0 )); then
34-
echo "The following modules appear to have issues: ${failures[*]}"
94+
# Determine overall status and update Instatus component
95+
if (( status == 0 )); then
96+
echo "All modules are operational."
97+
# set to
98+
update_component_status "OPERATIONAL"
99+
else
100+
echo "The following modules have issues: ${failures[*]}"
101+
# check if all modules are down
102+
if (( ${#failures[@]} == ${#modules[@]} )); then
103+
update_component_status "MAJOROUTAGE"
104+
else
105+
update_component_status "PARTIALOUTAGE"
106+
fi
107+
108+
# Create a new incident
109+
incident_id=$(create_incident)
110+
echo "Created incident with ID: $incident_id"
35111
fi
112+
36113
exit "${status}"

.github/workflows/check.yaml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
name: Check modules on registry.coder.com
2-
1+
name: Health
2+
# Check modules health on registry.coder.com
33
on:
44
schedule:
55
- cron: "*/13 * * * *" # Runs every 13th minute
@@ -11,8 +11,12 @@ jobs:
1111

1212
steps:
1313
- name: Checkout repository
14-
uses: actions/checkout@v2
14+
uses: actions/checkout@v4
1515

1616
- name: Run check.sh
1717
run: |
1818
./.github/scripts/check.sh
19+
env:
20+
INSTATUS_API_KEY: ${{ secrets.INSTATUS_API_KEY }}
21+
INSTATUS_PAGE_ID: ${{ secrets.INSTATUS_PAGE_ID }}
22+
INSTATUS_COMPONENT_ID: ${{ secrets.INSTATUS_COMPONENT_ID }}

.icons/dcv.svg

Lines changed: 1 addition & 0 deletions
Loading

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
[![discord](https://img.shields.io/discord/747933592273027093?label=discord)](https://discord.gg/coder)
99
[![license](https://img.shields.io/github/license/coder/modules)](./LICENSE)
10+
[![Health](https://github.com/coder/modules/actions/workflows/check.yaml/badge.svg)](https://github.com/coder/modules/actions/workflows/check.yaml)
1011

1112
</div>
1213

amazon-dcv-windows/README.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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+
partner_github: aws
7+
verified: true
8+
tags: [windows, dcv, web, desktop]
9+
---
10+
11+
# Amazon DCV Windows
12+
13+
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.
14+
15+
Enable DCV Server and Web Client on Windows workspaces.
16+
17+
```tf
18+
module "dcv" {
19+
count = data.coder_workspace.me.start_count
20+
source = "registry.coder.com/modules/amazon-dcv-windows/coder"
21+
version = "1.0.24"
22+
agent_id = resource.coder_agent.main.id
23+
}
24+
25+
26+
resource "coder_metadata" "dcv" {
27+
count = data.coder_workspace.me.start_count
28+
resource_id = aws_instance.dev.id # id of the instance resource
29+
30+
item {
31+
key = "DCV client instructions"
32+
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}**"
33+
}
34+
item {
35+
key = "username"
36+
value = module.dcv[count.index].username
37+
}
38+
item {
39+
key = "password"
40+
value = module.dcv[count.index].password
41+
sensitive = true
42+
}
43+
}
44+
```
45+
46+
## License
47+
48+
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

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
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 (($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\connectivity" -Name enable-quic-frontend -PropertyType DWORD -Value 1 -Force
147+
New-ItemProperty -Path "$dcvPath\session-management" -Name create-session -PropertyType DWORD -Value 1 -Force
148+
New-ItemProperty -Path "$dcvPath\session-management\automatic-console-session" -Name owner -Value Administrator -Force
149+
New-ItemProperty -Path "$dcvPath\display" -Name target-fps -PropertyType DWORD -Value 60 -Force
150+
New-ItemProperty -Path "$dcvPath\connectivity" -Name enable-datagrams-display -Value "always-off" -Force
151+
New-ItemProperty -Path "$dcvPath\connectivity" -Name quic-port -PropertyType DWORD -Value $port -Force
152+
New-ItemProperty -Path "$dcvPath\connectivity" -Name web-port -PropertyType DWORD -Value $port -Force
153+
New-ItemProperty -Path "$dcvPath\connectivity" -Name web-url-path -PropertyType String -Value $webURLPath -Force
154+
155+
# Attempt to restart service
156+
if (Get-Service -Name "dcvserver" -ErrorAction SilentlyContinue) {
157+
Restart-Service -Name "dcvserver"
158+
} else {
159+
Write-Output "[WARNING] dcvserver service not found. Ensure the system was restarted properly."
160+
}
161+
162+
Write-Output "[INFO] DCV configuration completed"
163+
Read-Host "[DEBUG] Press Enter to proceed to the next step"
164+
}
165+
166+
# Main Script Execution
167+
Write-Output "[INFO] Starting script"
168+
$VirtualDisplayDriverRequired = [bool](Get-VirtualDisplayDriverRequired)
169+
Set-LocalAdminUser
170+
Download-DCV -VirtualDisplayDriverRequired $VirtualDisplayDriverRequired
171+
Install-DCV -VirtualDisplayDriverRequired $VirtualDisplayDriverRequired
172+
Configure-DCV
173+
Write-Output "[INFO] Script completed"

0 commit comments

Comments
 (0)