How to Configure Remote Desktop Services on Windows Server 2022

Remote Desktop Services (RDS) on Windows Server 2022 encompasses everything from simple single-server RDP access to full virtual desktop infrastructure with Remote Desktop Session Hosts, licensing servers, gateways, and RemoteApp programs. Whether you need to enable basic RDP for administrative access or deploy a multi-user RDS farm for application delivery, this guide covers the configuration steps using PowerShell and explains the key architectural concepts you need to understand to deploy RDS correctly.

The Difference Between RDP and RDS

Remote Desktop Protocol (RDP) is the protocol used for all remote desktop connections. Basic RDP access for administrators is always available on Windows Server (up to two concurrent admin sessions) without any RDS licensing. This is called Remote Administration mode and does not require the Remote Desktop Services role.

Remote Desktop Services is the full role you install when you need more than two concurrent users, when you want to publish RemoteApp programs, or when you need features like session brokering, gateway access, or RDS licensing management. RDS requires a licensing server and Client Access Licenses (CALs) for each user or device beyond the two free admin sessions.

Enabling Basic RDP for Administrative Access

For administrative RDP (not the full RDS role), enable Remote Desktop by modifying the registry key that controls access and configure the Windows Firewall rule to allow the connection.

# Enable Remote Desktop
Set-ItemProperty -Path "HKLM:SystemCurrentControlSetControlTerminal Server" `
    -Name "fDenyTSConnections" -Value 0

# Verify the change
$rdpEnabled = (Get-ItemProperty "HKLM:SystemCurrentControlSetControlTerminal Server").fDenyTSConnections
Write-Host "RDP Denied: $rdpEnabled"  # 0 = enabled, 1 = disabled

# Enable firewall rules for RDP
Enable-NetFirewallRule -DisplayGroup "Remote Desktop"

# Confirm the firewall rule is enabled
Get-NetFirewallRule -DisplayGroup "Remote Desktop" |
    Select-Object DisplayName, Enabled, Direction

Enabling Network Level Authentication (NLA)

Network Level Authentication (NLA) requires the user to authenticate before a full Remote Desktop session is established. Without NLA, the Windows login screen is rendered to the connecting client before authentication occurs, which consumes server resources and increases the attack surface. NLA is the recommended security configuration for all RDP deployments.

# Enable NLA via registry
Set-ItemProperty `
    -Path "HKLM:SystemCurrentControlSetControlTerminal ServerWinStationsRDP-Tcp" `
    -Name "UserAuthentication" -Value 1

# Verify NLA is enabled
(Get-ItemProperty "HKLM:SystemCurrentControlSetControlTerminal ServerWinStationsRDP-Tcp").UserAuthentication
# 1 = NLA enabled, 0 = NLA disabled

# Check using CIM (alternative method)
Get-CimInstance -ClassName Win32_TerminalServiceSetting -Namespace "RootCimV2TerminalServices" |
    Select-Object UserAuthentication

Changing the Default RDP Port

The default RDP port is TCP 3389. While security through obscurity is not a substitute for proper hardening, changing the port reduces noise from automated scanners and brute-force bots. After changing the port, update the firewall rule to match.

# Change RDP listening port to 3390 (example)
$NewPort = 3390
Set-ItemProperty `
    -Path "HKLM:SystemCurrentControlSetControlTerminal ServerWinStationsRDP-Tcp" `
    -Name "PortNumber" -Value $NewPort -Type DWord

# Verify the change
(Get-ItemProperty "HKLM:SystemCurrentControlSetControlTerminal ServerWinStationsRDP-Tcp").PortNumber

# Update Windows Firewall — remove old rule and add new one
Remove-NetFirewallRule -DisplayName "Remote Desktop - User Mode (TCP-In)" -ErrorAction SilentlyContinue

New-NetFirewallRule `
    -DisplayName "Remote Desktop Custom Port (TCP-In)" `
    -Direction Inbound `
    -Protocol TCP `
    -LocalPort $NewPort `
    -Action Allow `
    -Profile Domain,Private

# Restart the RDP service for the port change to take effect
Restart-Service -Name TermService -Force

When connecting from the client side to a non-standard port, append the port to the server address in the RDP client: server-ip:3390.

Installing the Remote Desktop Services Role

To support more than two concurrent users, install the Remote Desktop Services role. In a full RDS deployment, multiple role services work together. For a simple single-server RDS deployment (all roles on one server), install the Session Host and Licensing roles together.

# Install the Remote Desktop Session Host role
Install-WindowsFeature -Name RDS-RD-Server -IncludeManagementTools

# Install the RD Licensing role (needed to manage CALs)
Install-WindowsFeature -Name RDS-Licensing -IncludeManagementTools

# Install both at once with management tools
Install-WindowsFeature -Name RDS-RD-Server, RDS-Licensing, `
    RDS-Connection-Broker, RDS-Web-Access -IncludeManagementTools

# Check the result
Get-WindowsFeature | Where-Object { $_.Name -like "RDS*" -and $_.Installed }

RDS Licensing Modes

Windows Server 2022 RDS supports two licensing modes: Per User and Per Device. Per User requires one CAL per user account regardless of how many devices they connect from. Per Device requires one CAL per client device regardless of how many users connect from that device. Configure the licensing mode and license server address in the registry on each Session Host.

# Set licensing mode: 2 = Per Device, 4 = Per User
Set-ItemProperty `
    -Path "HKLM:SYSTEMCurrentControlSetControlTerminal ServerRCMLicensing Core" `
    -Name "LicensingMode" -Value 4  # 4 = Per User

# Set the license server address
Set-ItemProperty `
    -Path "HKLM:SYSTEMCurrentControlSetServicesTermServiceParametersLicenseServers" `
    -Name "SpecifiedLicenseServers" -Value "rds-license.yourdomain.com"

# Verify current licensing configuration
Get-WmiObject -Namespace root/cimv2 -Class Win32_TerminalServiceSetting |
    Select-Object LicensingType, LicensingName

Configuring the Session Host

The Remote Desktop Session Host (RDSH) is the server where user sessions run. After installing the RDS-RD-Server role, configure session limits and behavior to prevent resource exhaustion.

# Configure session time limits via registry
# These settings apply to all users; per-user settings in Group Policy override these

# Set maximum idle session time (in milliseconds) — 30 minutes = 1800000
Set-ItemProperty `
    -Path "HKLM:SOFTWAREPoliciesMicrosoftWindows NTTerminal Services" `
    -Name "MaxIdleTime" -Value 1800000 -Type DWord

# Set maximum session time — 8 hours = 28800000
Set-ItemProperty `
    -Path "HKLM:SOFTWAREPoliciesMicrosoftWindows NTTerminal Services" `
    -Name "MaxConnectionTime" -Value 28800000 -Type DWord

# Set disconnected session time limit — 1 hour = 3600000
Set-ItemProperty `
    -Path "HKLM:SOFTWAREPoliciesMicrosoftWindows NTTerminal Services" `
    -Name "MaxDisconnectionTime" -Value 3600000 -Type DWord

Managing Active RDS Sessions

Use the query command-line tool and its PowerShell-friendly equivalents to view and manage active sessions on a Session Host.

# List all active sessions on the local server
query session
# or:
qwinsta

# List sessions on a remote server
query session /server:RDSH-01

# List sessions and user details
query user
quser

# Disconnect a session by session ID
logoff 3

# Disconnect a session on a remote server
logoff 3 /server:RDSH-01

# Send a message to a session
msg 3 "Server maintenance in 15 minutes. Please save your work."

# Send a message to all sessions
msg * "Server going down for maintenance at 10:00 PM."

# Reset (forcibly disconnect and delete) a session
reset session 3
rwinsta 3

Shadow Connections (Remote Control of Sessions)

Shadow connections allow an administrator to view or interact with another user’s active RDS session. This is useful for troubleshooting user issues without requiring the user to describe their problem. Shadow connections require the administrator to have appropriate permissions and the target session to be active.

# First, find the session ID of the user you want to shadow
query session /server:RDSH-01

# Shadow a session (view only — read-only mode)
mstsc /shadow:3 /v:RDSH-01 /noConsentPrompt

# Shadow with control (interactive mode — you can interact with their session)
mstsc /shadow:3 /v:RDSH-01 /control /noConsentPrompt

# Configure shadow policy via registry (0=disabled, 1=full control no consent,
# 2=full control with consent, 3=view only no consent, 4=view only with consent)
Set-ItemProperty `
    -Path "HKLM:SOFTWAREPoliciesMicrosoftWindows NTTerminal Services" `
    -Name "Shadow" -Value 2 -Type DWord  # Full control with user consent

RemoteApp Setup Overview

RemoteApp allows individual applications to be published to users so they appear to run locally on the user’s desktop, even though they are executing on the Session Host. Users launch them from a shortcut or via the RD Web Access portal. Configuring RemoteApp requires the Session Host role to be installed and uses the Remote Desktop Services Manager console or PowerShell.

# Import the RemoteDesktop module
Import-Module RemoteDesktop

# Get the list of currently published RemoteApp programs
Get-RDRemoteApp -ConnectionBroker "rdbroker.yourdomain.com"

# Publish a new RemoteApp program (Notepad as an example)
New-RDRemoteApp `
    -Alias "Notepad" `
    -DisplayName "Notepad" `
    -FilePath "C:WindowsSystem32notepad.exe" `
    -ShowInWebAccess 1 `
    -ConnectionBroker "rdbroker.yourdomain.com" `
    -CollectionName "Office Apps"

# Remove a published RemoteApp
Remove-RDRemoteApp -Alias "Notepad" `
    -ConnectionBroker "rdbroker.yourdomain.com" `
    -CollectionName "Office Apps"

RD Gateway Overview

The Remote Desktop Gateway (RD Gateway) allows RDP connections from the internet over HTTPS (port 443) without exposing port 3389 directly to the internet. The gateway acts as an SSL-terminating proxy that wraps RDP traffic in HTTPS. This is the recommended way to provide remote access to RDS from outside the corporate network.

# Install the RD Gateway role service
Install-WindowsFeature -Name RDS-Gateway -IncludeManagementTools

# The RD Gateway requires a valid SSL certificate
# Import the SSL certificate (assumes PFX is already on the server)
$CertPassword = ConvertTo-SecureString "CertP@ss!" -AsPlainText -Force
Import-PfxCertificate -FilePath "C:Certsrdgateway.pfx" `
    -CertStoreLocation "Cert:LocalMachineMy" `
    -Password $CertPassword

# Get the thumbprint of the imported certificate
$Cert = Get-ChildItem -Path "Cert:LocalMachineMy" |
    Where-Object { $_.Subject -like "*rdgateway*" }
$Cert.Thumbprint

# Configure the RD Gateway with the certificate thumbprint using netsh
# (PowerShell cmdlets for RD Gateway are limited; netsh and wmic are often needed)
# The GUI (RD Gateway Manager MMC) is the most reliable configuration tool for initial setup

Firewall Rules for RDS

A properly configured RDS deployment requires specific firewall rules on each role server. Here are the standard rules:

# Allow inbound RDP (port 3389) — for Session Hosts
New-NetFirewallRule -DisplayName "RDP Inbound" `
    -Direction Inbound -Protocol TCP -LocalPort 3389 `
    -Action Allow -Profile Domain

# Allow RD Gateway HTTPS (port 443)
New-NetFirewallRule -DisplayName "RD Gateway HTTPS" `
    -Direction Inbound -Protocol TCP -LocalPort 443 `
    -Action Allow -Profile Domain,Public

# Allow RD Connection Broker (port 3389 and 443 internally)
New-NetFirewallRule -DisplayName "RD Connection Broker" `
    -Direction Inbound -Protocol TCP -LocalPort 3389,443,3388 `
    -Action Allow -Profile Domain

# Allow RD Licensing (port 135 RPC, plus dynamic ports)
Enable-NetFirewallRule -DisplayGroup "Remote Desktop - RemoteFX"

# Verify all RDS-related firewall rules
Get-NetFirewallRule | Where-Object { $_.DisplayName -like "*Remote Desktop*" } |
    Select-Object DisplayName, Enabled, Direction, Action

Granting RDP Access to Users

By default, only members of the local Administrators group can connect via RDP. To grant non-administrator users RDP access, add them to the Remote Desktop Users local group.

# Add a domain user to Remote Desktop Users group
Add-LocalGroupMember -Group "Remote Desktop Users" -Member "DOMAINjsmith"

# Add a local user
Add-LocalGroupMember -Group "Remote Desktop Users" -Member "localuser"

# Add multiple users at once
$RDPUsers = @("DOMAINjsmith", "DOMAINbjones", "DOMAINrdpgroup")
foreach ($user in $RDPUsers) {
    Add-LocalGroupMember -Group "Remote Desktop Users" -Member $user -ErrorAction SilentlyContinue
}

# View current members of the Remote Desktop Users group
Get-LocalGroupMember -Group "Remote Desktop Users"

Configuring Remote Desktop Services correctly, from basic RDP hardening with NLA and firewall rules to a full RDS deployment with licensing, session management, and RemoteApp publishing, gives you a secure and scalable remote access platform on Windows Server 2022. Always pair RDS with a proper patch management strategy, use NLA everywhere, and consider RD Gateway for any public-facing RDS deployments instead of exposing port 3389 directly.