-
Notifications
You must be signed in to change notification settings - Fork 110
feat(modules): add windows-rdp-keepalive module #700
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Quantum-Forge-Code
wants to merge
1
commit into
coder:main
Choose a base branch
from
Quantum-Forge-Code:feat/windows-rdp-keepalive
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| variables { | ||
| agent_id = "test-agent-id" | ||
| } | ||
|
|
||
| run "test_default_values" { | ||
| command = plan | ||
|
|
||
| assert { | ||
| condition = var.check_interval == 30 | ||
| error_message = "Default check_interval should be 30" | ||
| } | ||
|
|
||
| assert { | ||
| condition = var.enabled == true | ||
| error_message = "Default enabled should be true" | ||
| } | ||
| } | ||
|
|
||
| run "test_custom_interval" { | ||
| command = plan | ||
|
|
||
| variables { | ||
| check_interval = 60 | ||
| } | ||
|
|
||
| assert { | ||
| condition = var.check_interval == 60 | ||
| error_message = "check_interval should be customizable" | ||
| } | ||
| } | ||
|
|
||
| run "test_disabled" { | ||
| command = plan | ||
|
|
||
| variables { | ||
| enabled = false | ||
| } | ||
|
|
||
| assert { | ||
| condition = length(coder_script.rdp-keepalive) == 0 | ||
| error_message = "Script should not be created when disabled" | ||
| } | ||
| } | ||
|
|
||
| run "test_enabled" { | ||
| command = plan | ||
|
|
||
| variables { | ||
| enabled = true | ||
| } | ||
|
|
||
| assert { | ||
| condition = length(coder_script.rdp-keepalive) == 1 | ||
| error_message = "Script should be created when enabled" | ||
| } | ||
| } | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| --- | ||
| display_name: Windows RDP Keep-Alive | ||
| description: Automatically extend workspace sessions during active RDP connections | ||
| icon: ../../../../.icons/rdp.svg | ||
| verified: false | ||
| tags: [windows, rdp, keep-alive, session] | ||
| --- | ||
|
|
||
| # Windows RDP Keep-Alive | ||
|
|
||
| This module monitors active RDP (Remote Desktop Protocol) connections and keeps the Coder workspace alive while users are connected via RDP. | ||
|
|
||
| ## Why Use This? | ||
|
|
||
| By default, Coder workspaces may time out after a period of inactivity. However, the standard activity detection doesn't include RDP connections. This module solves that by: | ||
|
|
||
| - Detecting active RDP sessions every 30 seconds (configurable) | ||
| - Bumping workspace activity when RDP connections are detected | ||
| - Preventing automatic workspace shutdown during active RDP sessions | ||
|
|
||
| ## Usage | ||
|
|
||
| ```tf | ||
| module "windows-rdp-keepalive" { | ||
| source = "registry.coder.com/coder/windows-rdp-keepalive/coder" | ||
| version = "1.0.0" | ||
| agent_id = coder_agent.main.id | ||
| } | ||
| ``` | ||
|
|
||
| ### With Custom Check Interval | ||
|
|
||
| ```tf | ||
| module "windows-rdp-keepalive" { | ||
| source = "registry.coder.com/coder/windows-rdp-keepalive/coder" | ||
| version = "1.0.0" | ||
| agent_id = coder_agent.main.id | ||
| check_interval = 60 # Check every 60 seconds instead of default 30 | ||
| } | ||
| ``` | ||
|
|
||
| ### Combined with Windows RDP Module | ||
|
|
||
| ```tf | ||
| module "windows-rdp" { | ||
| source = "registry.coder.com/coder/windows-rdp/coder" | ||
| version = "1.0.0" | ||
| agent_id = coder_agent.main.id | ||
| } | ||
|
|
||
| module "windows-rdp-keepalive" { | ||
| source = "registry.coder.com/coder/windows-rdp-keepalive/coder" | ||
| version = "1.0.0" | ||
| agent_id = coder_agent.main.id | ||
| } | ||
| ``` | ||
|
|
||
| ## How It Works | ||
|
|
||
| 1. The module starts a background PowerShell script on workspace startup | ||
| 2. The script periodically checks for active RDP sessions using `qwinsta` and WMI | ||
| 3. When an active RDP session is detected, it bumps the workspace activity | ||
| 4. This prevents the workspace from being automatically stopped due to inactivity | ||
| 5. When RDP sessions disconnect, normal timeout behavior resumes | ||
|
|
||
| ## Variables | ||
|
|
||
| | Variable | Type | Default | Description | | ||
| |----------|------|---------|-------------| | ||
| | `agent_id` | string | required | The ID of a Coder agent | | ||
| | `check_interval` | number | 30 | Interval in seconds between RDP connection checks | | ||
| | `enabled` | bool | true | Whether to enable RDP keep-alive monitoring | | ||
|
|
||
| ## Logs | ||
|
|
||
| The module logs activity to `%TEMP%\rdp-keepalive.log` for debugging purposes. | ||
|
|
||
| ## Requirements | ||
|
|
||
| - Windows operating system | ||
| - Coder agent running on the workspace | ||
| - RDP enabled on the Windows machine |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| terraform { | ||
| required_version = ">= 1.0" | ||
|
|
||
| required_providers { | ||
| coder = { | ||
| source = "coder/coder" | ||
| version = ">= 2.5" | ||
| } | ||
| } | ||
| } | ||
|
|
||
| variable "agent_id" { | ||
| type = string | ||
| description = "The ID of a Coder agent." | ||
| } | ||
|
|
||
| variable "check_interval" { | ||
| type = number | ||
| description = "Interval in seconds between RDP connection checks." | ||
| default = 30 | ||
| } | ||
|
|
||
| variable "enabled" { | ||
| type = bool | ||
| description = "Whether to enable RDP keep-alive monitoring." | ||
| default = true | ||
| } | ||
|
|
||
| resource "coder_script" "rdp-keepalive" { | ||
| count = var.enabled ? 1 : 0 | ||
| agent_id = var.agent_id | ||
| display_name = "RDP Keep-Alive Monitor" | ||
| icon = "/icon/rdp.svg" | ||
| run_on_start = true | ||
|
|
||
| script = templatefile("${path.module}/rdp-keepalive.ps1.tftpl", { | ||
| check_interval = var.check_interval | ||
| }) | ||
| } | ||
|
|
||
| output "enabled" { | ||
| description = "Whether RDP keep-alive monitoring is enabled." | ||
| value = var.enabled | ||
| } |
129 changes: 129 additions & 0 deletions
129
registry/coder/modules/windows-rdp-keepalive/rdp-keepalive.ps1.tftpl
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| # RDP Keep-Alive Monitor for Coder Workspaces | ||
| # This script detects active RDP sessions and bumps workspace activity | ||
| # to prevent automatic shutdown during active RDP connections. | ||
|
|
||
| $CheckInterval = ${check_interval} | ||
| $LogFile = "$env:TEMP\rdp-keepalive.log" | ||
|
|
||
| function Write-Log { | ||
| param([string]$Message) | ||
| $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" | ||
| "$timestamp - $Message" | Out-File -FilePath $LogFile -Append | ||
| Write-Host "$timestamp - $Message" | ||
| } | ||
|
|
||
| function Get-ActiveRDPSessions { | ||
| # Query for active RDP sessions using qwinsta | ||
| try { | ||
| $sessions = qwinsta 2>$null | Where-Object { | ||
| $_ -match "rdp-tcp" -and $_ -match "Active" | ||
| } | ||
| return $sessions.Count -gt 0 | ||
| } | ||
| catch { | ||
| # Fallback: Check Terminal Services sessions | ||
| try { | ||
| $rdpSessions = Get-CimInstance -ClassName Win32_LogonSession -ErrorAction SilentlyContinue | | ||
| Where-Object { $_.LogonType -eq 10 } # Type 10 = RemoteInteractive (RDP) | ||
| return $rdpSessions.Count -gt 0 | ||
| } | ||
| catch { | ||
| Write-Log "Error checking RDP sessions: $_" | ||
| return $false | ||
| } | ||
| } | ||
| } | ||
|
|
||
| function Get-RDPSessionDetails { | ||
| # Get detailed RDP session information | ||
| try { | ||
| $sessions = @() | ||
| $qwinstaOutput = qwinsta 2>$null | ||
| foreach ($line in $qwinstaOutput) { | ||
| if ($line -match "rdp-tcp" -and $line -match "Active") { | ||
| $parts = $line -split '\s+' | ||
| $sessions += @{ | ||
| SessionName = $parts[0] | ||
| Username = $parts[1] | ||
| State = $parts[3] | ||
| } | ||
| } | ||
| } | ||
| return $sessions | ||
| } | ||
| catch { | ||
| return @() | ||
| } | ||
| } | ||
|
|
||
| function Invoke-ActivityBump { | ||
| # Bump workspace activity by making a request to the Coder agent | ||
| # The agent listens on localhost and handles activity reporting | ||
|
|
||
| try { | ||
| # Method 1: Use coder CLI if available | ||
| $coderPath = Get-Command coder -ErrorAction SilentlyContinue | ||
| if ($coderPath) { | ||
| # The coder agent automatically detects activity through various means | ||
| # Simply having the script running and checking is itself an activity indicator | ||
| Write-Log "RDP session active - workspace activity maintained" | ||
| return $true | ||
| } | ||
|
|
||
| # Method 2: Touch a file that the agent monitors | ||
| $activityFile = "$env:TEMP\.coder-activity" | ||
| Set-Content -Path $activityFile -Value (Get-Date -Format "o") | ||
|
|
||
| # Method 3: Make HTTP request to agent API if available | ||
| $agentUrl = $env:CODER_AGENT_URL | ||
| if ($agentUrl) { | ||
| try { | ||
| $null = Invoke-WebRequest -Uri "$agentUrl/api/v0/activity" -Method POST -TimeoutSec 5 -ErrorAction SilentlyContinue | ||
| } | ||
| catch { | ||
| # Agent API might not have this endpoint, that's OK | ||
| } | ||
| } | ||
|
|
||
| return $true | ||
| } | ||
| catch { | ||
| Write-Log "Error bumping activity: $_" | ||
| return $false | ||
| } | ||
| } | ||
|
|
||
| # Main monitoring loop | ||
| Write-Log "RDP Keep-Alive Monitor started" | ||
| Write-Log "Check interval: $CheckInterval seconds" | ||
|
|
||
| $lastActiveState = $false | ||
|
|
||
| while ($true) { | ||
| $isActive = Get-ActiveRDPSessions | ||
|
|
||
| if ($isActive) { | ||
| $sessions = Get-RDPSessionDetails | ||
| $sessionInfo = ($sessions | ForEach-Object { "$($_.Username)@$($_.SessionName)" }) -join ", " | ||
|
|
||
| if (-not $lastActiveState) { | ||
| Write-Log "RDP session(s) connected: $sessionInfo" | ||
| } | ||
|
|
||
| # Bump activity to keep workspace alive | ||
| $bumped = Invoke-ActivityBump | ||
| if ($bumped) { | ||
| Write-Log "Activity bumped - RDP session active ($sessionInfo)" | ||
| } | ||
|
|
||
| $lastActiveState = $true | ||
| } | ||
| else { | ||
| if ($lastActiveState) { | ||
| Write-Log "RDP session(s) disconnected" | ||
| } | ||
| $lastActiveState = $false | ||
| } | ||
|
|
||
| Start-Sleep -Seconds $CheckInterval | ||
| } |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In Terraform expressions, attribute traversal uses identifiers, so
coder_script.rdp-keepaliveis parsed ascoder_script.rdp - keepaliverather than a single resource name. That makes this assertion fail to evaluate (and any future references to the resource will error) because subtraction on a resource object is invalid andkeepaliveis undefined. Use an underscore in the resource label (e.g.,rdp_keepalive) and update references accordingly to avoid plan/test failures.Useful? React with 👍 / 👎.