Skip to content

Commit

Permalink
feat: add Amazon DCV Windows module (#345)
Browse files Browse the repository at this point in the history
  • Loading branch information
matifali authored Nov 27, 2024
1 parent 6597a2d commit b345e62
Show file tree
Hide file tree
Showing 5 changed files with 305 additions and 0 deletions.
1 change: 1 addition & 0 deletions .icons/dcv.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .images/amazon-dcv-windows.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 49 additions & 0 deletions amazon-dcv-windows/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
display_name: Amazon DCV Windows
description: Amazon DCV Server and Web Client for Windows
icon: ../.icons/dcv.svg
maintainer_github: coder
verified: true
tags: [windows, amazon, dcv, web, desktop]
---

# Amazon DCV Windows

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.

![Amazon DCV on a Windows workspace](../.images/amazon-dcv-windows.png)

Enable DCV Server and Web Client on Windows workspaces.

```tf
module "dcv" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/amazon-dcv-windows/coder"
version = "1.0.24"
agent_id = resource.coder_agent.main.id
}
resource "coder_metadata" "dcv" {
count = data.coder_workspace.me.start_count
resource_id = aws_instance.dev.id # id of the instance resource
item {
key = "DCV client instructions"
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}**"
}
item {
key = "username"
value = module.dcv[count.index].username
}
item {
key = "password"
value = module.dcv[count.index].password
sensitive = true
}
}
```

## License

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.
170 changes: 170 additions & 0 deletions amazon-dcv-windows/install-dcv.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
# Terraform variables
$adminPassword = "${admin_password}"
$port = "${port}"
$webURLPath = "${web_url_path}"

function Set-LocalAdminUser {
Write-Output "[INFO] Starting Set-LocalAdminUser function"
$securePassword = ConvertTo-SecureString $adminPassword -AsPlainText -Force
Write-Output "[DEBUG] Secure password created"
Get-LocalUser -Name Administrator | Set-LocalUser -Password $securePassword
Write-Output "[INFO] Administrator password set"
Get-LocalUser -Name Administrator | Enable-LocalUser
Write-Output "[INFO] User Administrator enabled successfully"
Read-Host "[DEBUG] Press Enter to proceed to the next step"
}

function Get-VirtualDisplayDriverRequired {
Write-Output "[INFO] Starting Get-VirtualDisplayDriverRequired function"
$token = Invoke-RestMethod -Headers @{'X-aws-ec2-metadata-token-ttl-seconds' = '21600'} -Method PUT -Uri http://169.254.169.254/latest/api/token
Write-Output "[DEBUG] Token acquired: $token"
$instanceType = Invoke-RestMethod -Headers @{'X-aws-ec2-metadata-token' = $token} -Method GET -Uri http://169.254.169.254/latest/meta-data/instance-type
Write-Output "[DEBUG] Instance type: $instanceType"
$OSVersion = ((Get-ItemProperty -Path "Microsoft.PowerShell.Core\Registry::\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ProductName).ProductName) -replace "[^0-9]", ''
Write-Output "[DEBUG] OS version: $OSVersion"

# Force boolean result
$result = (($OSVersion -ne "2019") -and ($OSVersion -ne "2022") -and ($OSVersion -ne "2025")) -and (($instanceType[0] -ne 'g') -and ($instanceType[0] -ne 'p'))
Write-Output "[INFO] VirtualDisplayDriverRequired result: $result"
Read-Host "[DEBUG] Press Enter to proceed to the next step"
return [bool]$result
}

function Download-DCV {
param (
[bool]$VirtualDisplayDriverRequired
)
Write-Output "[INFO] Starting Download-DCV function"

$downloads = @(
@{
Name = "DCV Display Driver"
Required = $VirtualDisplayDriverRequired
Path = "C:\Windows\Temp\DCVDisplayDriver.msi"
Uri = "https://d1uj6qtbmh3dt5.cloudfront.net/nice-dcv-virtual-display-x64-Release.msi"
},
@{
Name = "DCV Server"
Required = $true
Path = "C:\Windows\Temp\DCVServer.msi"
Uri = "https://d1uj6qtbmh3dt5.cloudfront.net/nice-dcv-server-x64-Release.msi"
}
)

foreach ($download in $downloads) {
if ($download.Required -and -not (Test-Path $download.Path)) {
try {
Write-Output "[INFO] Downloading $($download.Name)"

# Display progress manually (no events)
$progressActivity = "Downloading $($download.Name)"
$progressStatus = "Starting download..."
Write-Progress -Activity $progressActivity -Status $progressStatus -PercentComplete 0

# Synchronously download the file
$webClient = New-Object System.Net.WebClient
$webClient.DownloadFile($download.Uri, $download.Path)

# Update progress
Write-Progress -Activity $progressActivity -Status "Completed" -PercentComplete 100

Write-Output "[INFO] $($download.Name) downloaded successfully."
} catch {
Write-Output "[ERROR] Failed to download $($download.Name): $_"
throw
}
} else {
Write-Output "[INFO] $($download.Name) already exists. Skipping download."
}
}

Write-Output "[INFO] All downloads completed"
Read-Host "[DEBUG] Press Enter to proceed to the next step"
}

function Install-DCV {
param (
[bool]$VirtualDisplayDriverRequired
)
Write-Output "[INFO] Starting Install-DCV function"

if (-not (Get-Service -Name "dcvserver" -ErrorAction SilentlyContinue)) {
if ($VirtualDisplayDriverRequired) {
Write-Output "[INFO] Installing DCV Display Driver"
Start-Process "C:\Windows\System32\msiexec.exe" -ArgumentList "/I C:\Windows\Temp\DCVDisplayDriver.msi /quiet /norestart" -Wait
} else {
Write-Output "[INFO] DCV Display Driver installation skipped (not required)."
}
Write-Output "[INFO] Installing DCV Server"
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
} else {
Write-Output "[INFO] DCV Server already installed, skipping installation."
}

# Wait for the service to appear with a timeout
$timeout = 10 # seconds
$elapsed = 0
while (-not (Get-Service -Name "dcvserver" -ErrorAction SilentlyContinue) -and ($elapsed -lt $timeout)) {
Start-Sleep -Seconds 1
$elapsed++
}

if ($elapsed -ge $timeout) {
Write-Output "[WARNING] Timeout waiting for dcvserver service. A restart is required to complete installation."
Restart-SystemForDCV
} else {
Write-Output "[INFO] dcvserver service detected successfully."
}
}

function Restart-SystemForDCV {
Write-Output "[INFO] The system will restart in 10 seconds to finalize DCV installation."
Start-Sleep -Seconds 10

# Initiate restart
Restart-Computer -Force

# Exit the script after initiating restart
Write-Output "[INFO] Please wait for the system to restart..."

Exit 1
}


function Configure-DCV {
Write-Output "[INFO] Starting Configure-DCV function"
$dcvPath = "Microsoft.PowerShell.Core\Registry::\HKEY_USERS\S-1-5-18\Software\GSettings\com\nicesoftware\dcv"

# Create the required paths
@("$dcvPath\connectivity", "$dcvPath\session-management", "$dcvPath\session-management\automatic-console-session", "$dcvPath\display") | ForEach-Object {
if (-not (Test-Path $_)) {
New-Item -Path $_ -Force | Out-Null
}
}

# Set registry keys
New-ItemProperty -Path "$dcvPath\session-management" -Name create-session -PropertyType DWORD -Value 1 -Force
New-ItemProperty -Path "$dcvPath\session-management\automatic-console-session" -Name owner -Value Administrator -Force
New-ItemProperty -Path "$dcvPath\connectivity" -Name quic-port -PropertyType DWORD -Value $port -Force
New-ItemProperty -Path "$dcvPath\connectivity" -Name web-port -PropertyType DWORD -Value $port -Force
New-ItemProperty -Path "$dcvPath\connectivity" -Name web-url-path -PropertyType String -Value $webURLPath -Force

# Attempt to restart service
if (Get-Service -Name "dcvserver" -ErrorAction SilentlyContinue) {
Restart-Service -Name "dcvserver"
} else {
Write-Output "[WARNING] dcvserver service not found. Ensure the system was restarted properly."
}

Write-Output "[INFO] DCV configuration completed"
Read-Host "[DEBUG] Press Enter to proceed to the next step"
}

# Main Script Execution
Write-Output "[INFO] Starting script"
$VirtualDisplayDriverRequired = [bool](Get-VirtualDisplayDriverRequired)
Set-LocalAdminUser
Download-DCV -VirtualDisplayDriverRequired $VirtualDisplayDriverRequired
Install-DCV -VirtualDisplayDriverRequired $VirtualDisplayDriverRequired
Configure-DCV
Write-Output "[INFO] Script completed"
85 changes: 85 additions & 0 deletions amazon-dcv-windows/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
terraform {
required_version = ">= 1.0"

required_providers {
coder = {
source = "coder/coder"
version = ">= 0.17"
}
}
}

variable "agent_id" {
type = string
description = "The ID of a Coder agent."
}

variable "admin_password" {
type = string
default = "coderDCV!"
sensitive = true
}

variable "port" {
type = number
description = "The port number for the DCV server."
default = 8443
}

variable "subdomain" {
type = bool
description = "Whether to use a subdomain for the DCV server."
default = true
}

variable "slug" {
type = string
description = "The slug of the web-dcv coder_app resource."
default = "web-dcv"
}

resource "coder_app" "web-dcv" {
agent_id = var.agent_id
slug = var.slug
display_name = "Web DCV"
url = "https://localhost:${var.port}${local.web_url_path}?username=${local.admin_username}&password=${var.admin_password}"
icon = "/icon/dcv.svg"
subdomain = var.subdomain
}

resource "coder_script" "install-dcv" {
agent_id = var.agent_id
display_name = "Install DCV"
icon = "/icon/dcv.svg"
run_on_start = true
script = templatefile("${path.module}/install-dcv.ps1", {
admin_password : var.admin_password,
port : var.port,
web_url_path : local.web_url_path
})
}

data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}

locals {
web_url_path = var.subdomain ? "/" : format("/@%s/%s/apps/%s", data.coder_workspace_owner.me.name, data.coder_workspace.me.name, var.slug)
admin_username = "Administrator"
}

output "web_url_path" {
value = local.web_url_path
}

output "username" {
value = local.admin_username
}

output "password" {
value = var.admin_password
sensitive = true
}

output "port" {
value = var.port
}

0 comments on commit b345e62

Please sign in to comment.