Skip to content

feat: keep Windows RDP sessions alive while connected#915

Open
Rafly0078 wants to merge 5 commits into
coder:mainfrom
Rafly0078:rafly0078/rdp-keepalive-200
Open

feat: keep Windows RDP sessions alive while connected#915
Rafly0078 wants to merge 5 commits into
coder:mainfrom
Rafly0078:rafly0078/rdp-keepalive-200

Conversation

@Rafly0078
Copy link
Copy Markdown

/claim #200

Summary

This PR adds an RDP keepalive monitor to the coder/windows-rdp module so a workspace can keep extending its deadline while a user is actively connected over RDP.

The change is intentionally scoped to the registry module and does not require a core coder/coder change. It follows the module-side approach discussed in #200: detect an active RDP connection inside the Windows workspace and use the workspace agent token to extend the workspace deadline.

What changed

  • Adds keepalive_enabled, keepalive_interval_seconds, and keepalive_extension_minutes variables to the Windows RDP module.
  • Generates a small rdp-keepalive.ps1 monitor from Terraform and installs it from the existing startup script.
  • Detects active RDP sessions via established local TCP connections on port 3389.
  • Calls Coder's documented PUT /api/v2/workspaces/{workspace}/extend endpoint with the workspace agent token while RDP is active.
  • Stops any previously running keepalive monitor before starting a new one so repeated workspace starts do not stack duplicate background loops.
  • Supports disabling the monitor with keepalive_enabled = false, which also removes the generated monitor script.
  • Documents the feature, disable switch, timing variables, and log path in the module README.

Safety notes

  • The implementation keeps existing Windows RDP behavior intact: the module still configures RDP, installs Devolutions Gateway, and patches the web client as before.
  • The monitor only extends the deadline when an established RDP connection is detected.
  • The extension window is validated at 30 minutes or more because Coder rejects deadlines that are too close to the current time.
  • Failures to inspect RDP connections or extend the deadline are logged to C:\ProgramData\Coder\windows-rdp\rdp-keepalive.log instead of failing the startup script.

Verification

Proof screenshot:

verification proof

Raw proof log: https://gist.githubusercontent.com/Rafly0078/0584b83b1022a9d1bc2f6d4aff099e12/raw/3d222f8f08f3e5cac23907fc780929ca8bf83bb9/verification-clean.log

Commands run locally:

bun test registry/coder/modules/windows-rdp/main.test.ts
terraform -chdir=registry/coder/modules/windows-rdp validate
bun x prettier --check registry/coder/modules/windows-rdp/README.md registry/coder/modules/windows-rdp/main.test.ts registry/coder/modules/windows-rdp/main.tf
terraform fmt -check -recursive registry/coder/modules/windows-rdp
PowerShell parser checks for rdp-keepalive.ps1.tftpl and powershell-installation-script.tftpl

The targeted module test path is green: 7 pass, 0 fail.

I also attempted the full Bun suite locally. Many unrelated container-backed tests fail on this Windows environment without a real Docker daemon, so I did not treat that as a regression from this module change.

References

AI-assisted with Codex.

Issue coder#200 asks for Windows RDP sessions to prevent a workspace from stopping while a user is still connected through RDP. The existing windows-rdp module enabled Remote Desktop and Devolutions Gateway, but it did not report activity or extend the workspace deadline after the startup script completed. That meant an active RDP session could still be shut down by the normal workspace schedule.

This change keeps the fix scoped to the registry module. It adds an opt-in-by-default PowerShell keepalive monitor that is installed by the existing startup script, checks for established local RDP connections on port 3389, and uses the workspace agent token to call Coder's documented workspace deadline extension endpoint. The monitor is written to ProgramData, logs to a module-specific log file, and the installer stops any previous monitor process before starting a new one so repeated starts do not stack duplicate loops.

The Terraform surface is intentionally small: keepalive_enabled controls the feature, keepalive_interval_seconds controls how often the RDP connection check runs, and keepalive_extension_minutes controls how far ahead the deadline is extended. The extension window is validated at 30 minutes or more because Coder's server-side deadline validation rejects deadlines that are too close to the current time. Disabling keepalive removes the generated monitor script and leaves the rest of the Windows RDP module behavior unchanged.

Tests now assert the rendered coder_script installs the monitor by default, includes the RDP connection detection logic, sends the Coder-Session-Token header, calls /api/v2/workspaces/{workspace}/extend with the deadline body, supports custom interval/extension values, and can disable the monitor cleanly. The README documents the new behavior, configuration variables, disable switch, and log path.

Validation performed locally: bun test registry/coder/modules/windows-rdp/main.test.ts; terraform -chdir=registry/coder/modules/windows-rdp validate; bun x prettier --check registry/coder/modules/windows-rdp/README.md registry/coder/modules/windows-rdp/main.test.ts registry/coder/modules/windows-rdp/main.tf; terraform fmt -check -recursive registry/coder/modules/windows-rdp; PowerShell parser checks for rdp-keepalive.ps1.tftpl and powershell-installation-script.tftpl.

AI-assisted with Codex.
@Rafly0078
Copy link
Copy Markdown
Author

/claim #200

Copy link
Copy Markdown
Member

@matifali matifali left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your contribution. Please attach a recorded demo of your solution working.

The keepalive monitor should only extend the workspace while an established local RDP connection exists. During demo verification, the no-connection path correctly skipped the extend request, but PowerShell logged the absence of matching TCP connections as an inspection error because Get-NetTCPConnection used ErrorAction Stop.

Use ErrorAction SilentlyContinue for the normal idle case so a disconnected workspace remains quiet while still returning false from Test-RDPConnectionActive. The test now asserts the rendered script uses the idle-safe connection query.

Validation performed locally: bun test registry/coder/modules/windows-rdp/main.test.ts; terraform -chdir=registry/coder/modules/windows-rdp validate; prettier check for changed formatted files; terraform fmt check for the module; PowerShell parser check for rdp-keepalive.ps1.tftpl.
@Rafly0078
Copy link
Copy Markdown
Author

Hi @matifali, I attached a recorded demo of the keepalive flow working.

Demo GIF:

RDP keepalive demo

The demo runs the rendered rdp-keepalive.ps1.tftpl from this PR branch and shows:

  • an established local RDP-style TCP connection on port 3389,
  • the keepalive script detecting that connection,
  • the script calling PUT /api/v2/workspaces/{workspace}/extend with Coder-Session-Token,
  • the keepalive log writing Extended workspace deadline ...,
  • the RDP-style connection being closed,
  • no additional extend request being sent after one keepalive interval with no RDP connection.

Raw evidence:

This is a local recorded demo using the exact rendered keepalive script and a local mock Coder Agent API endpoint so the API call can be shown deterministically. If you specifically need a full Coder deployment recording with a real Windows workspace, I can provide that once I have access to a suitable Coder Windows environment.

Copy link
Copy Markdown
Member

matifali commented Jun 5, 2026

Please share recording with a real Coder Instance (tell me if you are AI) and a real Windows based Template on some Cloud provider like AWS or GCP or Azure. Where user connects to RDP from the official RDP client.

@Rafly0078
Copy link
Copy Markdown
Author

Hi @matifali, thanks for the clarification. I recorded a new demo using a real Coder instance, a real Azure Windows Server workspace/template, and the official Microsoft Remote Desktop client.

Recorded demo (MOV): https://github.com/Rafly0078/registry/releases/download/rdp-keepalive-real-demo-915-v1/rdp-keepalive-real-coder-azure-demo.mov

Release page: https://github.com/Rafly0078/registry/releases/tag/rdp-keepalive-real-demo-915-v1

Summary of what the recording shows:

  • the workspace is opened through the official Microsoft Remote Desktop client (mstsc), with the window title showing rdp-demo - 127.0.0.1:13391 - Remote Desktop Connection;
  • inside the real Windows Server VM, Get-NetTCPConnection -LocalPort 3389 -State Established shows an established RDP session;
  • the keepalive log at C:\ProgramData\Coder\windows-rdp\rdp-keepalive.log shows repeated Extended workspace deadline ... entries while the RDP session is connected;
  • the recorded log does not show the previous 401 Unauthorized failure.

Transparency note: this contribution and the demo/debugging work were AI-assisted with Codex. I am the PR author, and I used Codex as an assistant for implementation, testing, debugging, and preparing the demo evidence.

Comment on lines 101 to 125
@@ -111,9 +118,10 @@ resource "coder_script" "windows-rdp" {
devolutions_gateway_version = var.devolutions_gateway_version
keepalive_enabled = var.keepalive_enabled
keepalive_script_contents = templatefile("${path.module}/rdp-keepalive.ps1.tftpl", {
workspace_id = data.coder_workspace.me.id
interval_seconds = var.keepalive_interval_seconds
extension_minutes = var.keepalive_extension_minutes
workspace_id = data.coder_workspace.me.id
interval_seconds = var.keepalive_interval_seconds
extension_minutes = var.keepalive_extension_minutes
coder_session_token = var.keepalive_coder_session_token != null ? var.keepalive_coder_session_token : ""
})
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use the coder-login module to get a token

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just pushed a new commit to address this:

  • Dropped the custom keepalive_coder_session_token variable entirely from the module.
  • The keepalive script now simply reads from $env:CODER_SESSION_TOKEN (populated by coder-login) if it's available, and falls back to $env:CODER_AGENT_TOKEN if not.
  • Updated the README and tests to reflect this new flow.

Let me know if this looks good to you now!

Copy link
Copy Markdown
Member

@matifali matifali Jun 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you also attach the video inline or Loom or Youtube? the MOV file doesn't play.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sorry about that! I've uploaded the demo video to YouTube so it should play perfectly inline now.

You can watch the full RDP connection demo and the deadline extension process here: https://youtu.be/5BTZdUdit_Y
Let me know if you need anything else to proceed!

@matifali
Copy link
Copy Markdown
Member

matifali commented Jun 6, 2026

Hi @Rafly0078 just FYI, We have now stopped the bounty program.
I will test and review this sometimes next week.
Thanks for your contribution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants