Windows Server Remote Desktop Services: Virtual Desktop Infrastructure
Introduction
Remote Desktop Services (RDS) delivers virtual desktops and applications to remote users. This guide covers RDS deployment architecture, session host configuration, RD Gateway for secure access, RemoteApp publishing, licensing management, high availability setup, and performance tuning.
RDS Deployment Architecture
Understanding RDS Roles
# RDS Core Roles:
# - RD Connection Broker: Session load balancing and reconnection
# - RD Session Host: Hosts RemoteApp programs and session-based desktops
# - RD Web Access: Web portal for RemoteApp and desktops
# - RD Gateway: Secure remote access over HTTPS
# - RD Licensing: License server for RDS CALs
# - RD Virtualization Host: Hosts VDI virtual machines (requires Hyper-V)
# Deployment scenarios:
# 1. Session-based deployment: Multiple users share session host
# 2. VDI deployment: Each user gets dedicated virtual desktop (personal or pooled)
# 3. RemoteApp deployment: Publish individual applications
Installing RDS Roles
# Install on single server (QuickStart - not for production)
Install-WindowsFeature -Name RDS-Connection-Broker, RDS-Web-Access, RDS-RD-Server, RDS-Licensing -IncludeManagementTools
# For multi-server deployment, use Server Manager or PowerShell
# Install Connection Broker
Invoke-Command -ComputerName "RDS-Broker01" -ScriptBlock {
Install-WindowsFeature -Name RDS-Connection-Broker -IncludeManagementTools
}
# Install Session Hosts
Invoke-Command -ComputerName "RDS-SH01", "RDS-SH02" -ScriptBlock {
Install-WindowsFeature -Name RDS-RD-Server -IncludeManagementTools
}
# Install Web Access
Invoke-Command -ComputerName "RDS-Web01" -ScriptBlock {
Install-WindowsFeature -Name RDS-Web-Access -IncludeManagementTools
}
# Install Gateway
Invoke-Command -ComputerName "RDS-GW01" -ScriptBlock {
Install-WindowsFeature -Name RDS-Gateway -IncludeManagementTools
}
# Install Licensing
Invoke-Command -ComputerName "RDS-Lic01" -ScriptBlock {
Install-WindowsFeature -Name RDS-Licensing -IncludeManagementTools
}
Session Host Configuration
Creating Session Collections
# Import RDS module
Import-Module RemoteDesktop
# Create session collection
New-RDSessionCollection -CollectionName "Office Apps" `
-SessionHost "RDS-SH01.contoso.com", "RDS-SH02.contoso.com" `
-CollectionDescription "Office applications for remote workers" `
-ConnectionBroker "RDS-Broker01.contoso.com"
# Configure collection properties
Set-RDSessionCollectionConfiguration -CollectionName "Office Apps" `
-ConnectionBroker "RDS-Broker01.contoso.com" `
-MaxRedirectedMonitors 2 `
-EnableUserProfileDisk -MaxUserProfileDiskSizeGB 30 `
-DiskPath "\\fileserver\UPD" `
-AuthenticateUsingNLA $true `
-EncryptionLevel ClientCompatible `
-SecurityLayer SSL
# View collection
Get-RDSessionCollection -ConnectionBroker "RDS-Broker01.contoso.com"
Get-RDSessionHost -ConnectionBroker "RDS-Broker01.contoso.com"
User Profile Disks (UPD)
# Enable User Profile Disks
Set-RDSessionCollectionConfiguration -CollectionName "Office Apps" `
-ConnectionBroker "RDS-Broker01.contoso.com" `
-EnableUserProfileDisk -MaxUserProfileDiskSizeGB 30 `
-DiskPath "\\fileserver\UPD\Office-Apps"
# Configure UPD template
Set-RDSessionCollectionConfiguration -CollectionName "Office Apps" `
-ConnectionBroker "RDS-Broker01.contoso.com" `
-CustomRdpProperty "targetisaadjoined:i:1"
# View UPD settings
Get-RDSessionCollectionConfiguration -CollectionName "Office Apps" `
-ConnectionBroker "RDS-Broker01.contoso.com" |
Select-Object *ProfileDisk*
Session Limits
# Configure session timeouts
Set-RDSessionCollectionConfiguration -CollectionName "Office Apps" `
-ConnectionBroker "RDS-Broker01.contoso.com" `
-DisconnectedSessionLimitMin 60 `
-IdleSessionLimitMin 30 `
-ActiveSessionLimitMin 0 `
-AutomaticReconnectionEnabled $true `
-BrokenConnectionAction Disconnect
# Set user limits per session host
Set-RDSessionHost -SessionHost "RDS-SH01.contoso.com" `
-ConnectionBroker "RDS-Broker01.contoso.com" `
-NewConnectionAllowed Yes `
-MaxConnections 100
Remote Desktop Gateway
Installing RD Gateway
# Install RD Gateway with management tools
Install-WindowsFeature -Name RDS-Gateway -IncludeManagementTools
# Import RD Gateway module
Import-Module RemoteDesktopServices
Configuring SSL Certificate
# Import certificate
$cert = Import-PfxCertificate -FilePath "C:\Certs\rdgateway.contoso.com.pfx" `
-CertStoreLocation "Cert:\LocalMachine\My" `
-Password (ConvertTo-SecureString "P@ssw0rd123!" -AsPlainText -Force)
# Bind certificate to RD Gateway
Set-Item -Path "RDS:\GatewayServer\SSLCertificate\Thumbprint" -Value $cert.Thumbprint
# Restart service
Restart-Service TSGateway
Creating Connection Authorization Policies (CAPs)
# CAP defines WHO can connect through RD Gateway
# Navigate to RD Gateway policies
cd RDS:\GatewayServer\CAP
# Create CAP
New-Item -Name "Remote Workers CAP" -Path . -Description "Allow remote workers"
# Configure CAP
Set-Item -Path ".\Remote Workers CAP\UserGroups" -Value "CONTOSO\Remote Workers"
Set-Item -Path ".\Remote Workers CAP\ClientAccessPolicy" -Value PasswordAndSmartCard
Set-Item -Path ".\Remote Workers CAP\Enabled" -Value $true
Set-Item -Path ".\Remote Workers CAP\IdleTimeout" -Value 30
Set-Item -Path ".\Remote Workers CAP\SessionTimeout" -Value 480
# Allow all client devices
Set-Item -Path ".\Remote Workers CAP\DeviceRedirection\Enabled" -Value $true
Creating Resource Authorization Policies (RAPs)
# RAP defines WHAT resources users can access
# Navigate to RAP
cd RDS:\GatewayServer\RAP
# Create RAP
New-Item -Name "Session Hosts RAP" -Path . -Description "Allow access to session hosts"
# Configure RAP
Set-Item -Path ".\Session Hosts RAP\UserGroups" -Value "CONTOSO\Remote Workers"
Set-Item -Path ".\Session Hosts RAP\ComputerGroups" -Value "CONTOSO\RDS Session Hosts"
Set-Item -Path ".\Session Hosts RAP\Enabled" -Value $true
Set-Item -Path ".\Session Hosts RAP\PortNumbers" -Value "3389"
# Allow connection through RD Gateway
Set-Item -Path ".\Session Hosts RAP\ConnectionMethod" -Value Direct
Monitoring RD Gateway
# View active connections
Get-EventLog -LogName Microsoft-Windows-TerminalServices-Gateway/Operational -Newest 100
# Monitor RD Gateway performance
$counters = @(
'\RD Gateway(*)\Total Active Sessions'
'\RD Gateway(*)\Total Bytes Sent/Sec'
'\RD Gateway(*)\Total Bytes Received/Sec'
'\RD Gateway(*)\Failed Authentications'
)
Get-Counter -Counter $counters
RemoteApp Publishing
Publishing RemoteApp Programs
# Publish RemoteApp
New-RDRemoteApp -CollectionName "Office Apps" `
-ConnectionBroker "RDS-Broker01.contoso.com" `
-DisplayName "Microsoft Word" `
-FilePath "C:\Program Files\Microsoft Office\root\Office16\WINWORD.EXE" `
-Alias "Word" `
-ShowInWebAccess $true `
-CommandLineSetting Require `
-RequiredCommandLine ""
# Publish with file type association
New-RDRemoteApp -CollectionName "Office Apps" `
-ConnectionBroker "RDS-Broker01.contoso.com" `
-DisplayName "Microsoft Excel" `
-FilePath "C:\Program Files\Microsoft Office\root\Office16\EXCEL.EXE" `
-Alias "Excel" `
-ShowInWebAccess $true `
-FileTypeAssociation @{".xlsx"="Excel"; ".xls"="Excel"}
# View published RemoteApps
Get-RDRemoteApp -CollectionName "Office Apps" -ConnectionBroker "RDS-Broker01.contoso.com"
Configuring RemoteApp Properties
# Configure RemoteApp
Set-RDRemoteApp -CollectionName "Office Apps" `
-ConnectionBroker "RDS-Broker01.contoso.com" `
-Alias "Word" `
-CommandLineSetting DoNotAllow `
-UserGroups "CONTOSO\Office Users"
# Set icon
Set-RDRemoteApp -CollectionName "Office Apps" `
-ConnectionBroker "RDS-Broker01.contoso.com" `
-Alias "Word" `
-IconPath "C:\Program Files\Microsoft Office\root\Office16\WINWORD.EXE" `
-IconIndex 0
RD Web Access Configuration
# Configure RD Web Access
Set-RDWebClientDeploymentSetting -Name "DefaultTsGateway" -Value "rdgateway.contoso.com"
# View RD Web Access URL
# https://rdweb.contoso.com/RDWeb
# Customize RD Web Access appearance
# Modify: C:\Windows\Web\RDWeb\Pages\en-US\login.aspx
RDS Licensing
Activating License Server
# Activate license server (must be done via Server Manager or web)
# https://activate.microsoft.com
# Configure license server
Set-RDLicenseConfiguration -ConnectionBroker "RDS-Broker01.contoso.com" `
-LicenseServer "RDS-Lic01.contoso.com" `
-Mode PerUser
# View license configuration
Get-RDLicenseConfiguration -ConnectionBroker "RDS-Broker01.contoso.com"
Installing RDS CALs
# Install CALs (requires License Key Pack)
# Done via RD Licensing Manager (licmgr.exe) or web activation
# View installed licenses
Get-RDLicenseConfiguration -ConnectionBroker "RDS-Broker01.contoso.com" |
Select-Object LicenseServer, Mode
# Check available licenses (via WMI)
$licenseServer = "RDS-Lic01.contoso.com"
Get-WmiObject -Class Win32_TSLicenseKeyPack -ComputerName $licenseServer |
Select-Object ProductVersion, TypeAndModel, TotalLicenses, AvailableLicenses, IssuedLicenses
Monitoring License Usage
# View issued licenses
Get-WmiObject -Class Win32_TSIssuedLicense -ComputerName "RDS-Lic01.contoso.com" |
Select-Object sIssuedToUser, IssueDate, ExpirationDate |
Sort-Object IssueDate -Descending |
Format-Table -AutoSize
# License usage report
$licenses = Get-WmiObject -Class Win32_TSLicenseKeyPack -ComputerName "RDS-Lic01.contoso.com"
foreach ($license in $licenses) {
[PSCustomObject]@{
ProductVersion = $license.ProductVersion
Type = $license.TypeAndModel
Total = $license.TotalLicenses
Issued = $license.IssuedLicenses
Available = $license.AvailableLicenses
PercentUsed = [math]::Round(($license.IssuedLicenses / $license.TotalLicenses) * 100, 2)
}
} | Format-Table -AutoSize
High Availability Configuration
Connection Broker High Availability
# Requirements:
# - SQL Server database
# - Multiple RD Connection Broker servers
# - DNS round-robin or load balancer
# Configure Connection Broker for HA (first broker)
Add-RDServer -Server "RDS-Broker01.contoso.com" `
-Role RDS-CONNECTION-BROKER `
-ConnectionBroker "RDS-Broker01.contoso.com"
# Create HA database
# SQL Server: Create database "RDS-HA"
# Configure HA on Connection Broker
Set-RDConnectionBrokerHighAvailability -ConnectionBroker "RDS-Broker01.contoso.com" `
-DatabaseConnectionString "DRIVER=SQL Server Native Client 11.0;SERVER=SQL01;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS-HA" `
-ClientAccessName "rds-broker.contoso.com"
# Add second broker to HA
Add-RDServer -Server "RDS-Broker02.contoso.com" `
-Role RDS-CONNECTION-BROKER `
-ConnectionBroker "rds-broker.contoso.com"
# Verify HA configuration
Get-RDConnectionBrokerHighAvailability -ConnectionBroker "rds-broker.contoso.com"
Session Host Load Balancing
# Load balancing is automatic with Connection Broker
# Configure session host drain mode (for maintenance)
Set-RDSessionHost -SessionHost "RDS-SH01.contoso.com" `
-ConnectionBroker "rds-broker.contoso.com" `
-NewConnectionAllowed No
# View session distribution
Get-RDUserSession -ConnectionBroker "rds-broker.contoso.com" |
Group-Object HostServer |
Select-Object Count, Name |
Sort-Object Count -Descending
Performance Tuning
Graphics Optimization
# Enable RemoteFX (requires GPU)
# RemoteFX deprecated in Windows Server 2019+, use GPU partitioning with VDI
# Configure graphics mode
Set-RDSessionCollectionConfiguration -CollectionName "Office Apps" `
-ConnectionBroker "rds-broker.contoso.com" `
-CustomRdpProperty "use multimon:i:1`naudiomode:i:0`naudiocapturemode:i:1"
# Optimize for VDI
Set-RDVirtualDesktopCollectionConfiguration -CollectionName "VDI Desktops" `
-ConnectionBroker "rds-broker.contoso.com" `
-CustomRdpProperty "use multimon:i:1`ndevicestoredirect:s:*`nredirectprinters:i:1"
Bandwidth Optimization
# Configure compression and caching
Set-RDSessionCollectionConfiguration -CollectionName "Office Apps" `
-ConnectionBroker "rds-broker.contoso.com" `
-CustomRdpProperty "compression:i:1`nbitmapcachesize:i:1500`nbitmappersistedcache:i:1"
# Disable unnecessary redirections for low bandwidth
Set-RDSessionCollectionConfiguration -CollectionName "Office Apps" `
-ConnectionBroker "rds-broker.contoso.com" `
-CustomRdpProperty "redirectprinters:i:0`nredirectcomports:i:0`nredirectsmartcards:i:0"
Performance Monitoring
# RDS-specific performance counters
$rdsCounters = @(
'\Terminal Services Session(*)\% Processor Time'
'\Terminal Services Session(*)\Working Set'
'\RemoteFX Network(*)\Current TCP Bandwidth'
'\RemoteFX Network(*)\Current UDP Bandwidth'
'\User Input Delay per Session(*)\Max Input Delay'
)
Get-Counter -Counter $rdsCounters -SampleInterval 5 -MaxSamples 10 |
ForEach-Object { $_.CounterSamples | Format-Table -AutoSize }
# Monitor active sessions
Get-RDUserSession -ConnectionBroker "rds-broker.contoso.com" |
Select-Object UserName, HostServer, UnifiedSessionId, SessionState, IdleTime |
Format-Table -AutoSize
Capacity Planning
# Calculate capacity
$capacityReport = @"
# RDS Capacity Planning
## Session Host Sizing:
- Light users (Office apps): 15-20 users per 8 vCPU, 16 GB RAM
- Medium users (multiple apps): 10-15 users per 8 vCPU, 32 GB RAM
- Heavy users (graphics/dev): 5-10 users per 8 vCPU, 64 GB RAM
## Storage Requirements:
- User Profile Disk: 10-30 GB per user
- Session host OS: 60 GB minimum, 100 GB recommended
- RemoteApp: Add application size + 20% overhead
## Network Bandwidth:
- Per session: 50-150 Kbps (Office apps)
- Per session: 150-300 Kbps (multimedia)
- Gateway overhead: Add 20%
## Example: 100 office users
- Session hosts: 6 servers (8 vCPU, 32 GB RAM each)
- UPD storage: 2 TB
- Network bandwidth: 15 Mbps
"@
$capacityReport | Out-File "C:\Reports\RDS-Capacity.txt"
Key Takeaways
- RDS delivers virtual desktops and RemoteApp applications
- Session hosts provide shared desktop sessions
- RD Gateway secures remote access over HTTPS
- RemoteApp publishes individual applications
- Licensing requires RDS CALs (Per User or Per Device)
- High availability uses Connection Broker with SQL Server
- Performance tuning optimizes graphics and bandwidth
- Capacity planning ensures adequate resources
Next Steps
- Deploy RDS with Connection Broker and Session Hosts
- Configure RD Gateway for secure external access
- Publish RemoteApp applications
- Activate and configure RDS licensing
- Implement high availability with multiple brokers
- Optimize performance for user experience
- Monitor RDS health and capacity
Additional Resources
Connect. Publish. Scale. Secure.