Remote Desktop Services¶
Ce que vous allez apprendre
- Les cles registre qui controlent le listener RDP et ses parametres de connexion
- Comment configurer les limites de session et les timeouts via le registre
- La configuration du licensing RDS, du Gateway et du Connection Broker
- Les parametres de publication RemoteApp dans le registre
- Le durcissement NLA (Network Level Authentication) et les redirections de profil
- Comment diagnostiquer et corriger le probleme "licensing mode not configured" sur une ferme RDS
Configuration du listener RDP¶
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#bd93f9', 'primaryTextColor': '#f8f8f2', 'primaryBorderColor': '#bd93f9', 'lineColor': '#ff79c6', 'secondaryColor': '#44475a', 'tertiaryColor': '#282a36', 'edgeLabelBackground': '#282a36', 'clusterBkg': '#282a36', 'clusterBorder': '#6272a4', 'titleColor': '#f8f8f2'}}}%%
flowchart TB
A["Client RDP"]:::pink
B["RD Gateway\n(HTTPS 443)"]:::orange
C["RD Connection\nBroker"]:::cyan
D["RD Session Host 1"]:::green
E["RD Session Host 2"]:::green
F["Serveur de\nlicences RDS"]:::purple
A -->|HTTPS| B
B --> C
C -->|"Répartition\nde charge"| D
C -->|"Répartition\nde charge"| E
F -.->|CAL| D
F -.->|CAL| E
classDef pink fill:#ff79c6,stroke:#ff79c6,color:#282a36
classDef green fill:#50fa7b,stroke:#50fa7b,color:#282a36
classDef cyan fill:#8be9fd,stroke:#8be9fd,color:#282a36
classDef orange fill:#ffb86c,stroke:#ffb86c,color:#282a36
classDef purple fill:#bd93f9,stroke:#bd93f9,color:#282a36 Le listener RDP est le composant qui ecoute les connexions entrantes sur le port TCP 3389 (par defaut). Sa configuration complete reside sous :
Et plus specifiquement, les parametres du listener lui-meme :
Activer ou desactiver le Bureau a distance¶
# Check if Remote Desktop is enabled
Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server" -Name "fDenyTSConnections"
Valeur fDenyTSConnections | Signification |
|---|---|
0 | Bureau a distance active (connexions autorisees) |
1 | Bureau a distance desactive (connexions refusees) |
# Enable Remote Desktop via registry
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server" `
-Name "fDenyTSConnections" -Value 0 -Type DWord
# Also open the firewall rule
Enable-NetFirewallRule -DisplayGroup "Remote Desktop"
Aucune sortie pour Set-ItemProperty.
La regle de pare-feu est activee immediatement.
Changer le port d'ecoute RDP¶
Le port d'ecoute par defaut (3389) est stocke dans la cle du listener :
# Read current RDP listening port
Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" `
-Name "PortNumber"
# Change RDP port to 3390 (for security through obscurity — not a replacement for NLA)
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" `
-Name "PortNumber" -Value 3390 -Type DWord
# Update the firewall to allow the new port
New-NetFirewallRule -DisplayName "RDP Custom Port 3390" `
-Direction Inbound -Protocol TCP -LocalPort 3390 -Action Allow
Apres un changement de port
Redemarrez le service TermService pour que le nouveau port soit pris en compte. N'oubliez pas de mettre a jour la regle de pare-feu avant de redemarrer le service, sinon vous perdez l'acces.
Parametres du listener RDP-Tcp¶
La cle RDP-Tcp contient des dizaines de valeurs. Voici les plus importantes pour un administrateur :
| Valeur | Type | Description |
|---|---|---|
PortNumber | REG_DWORD | Port d'ecoute TCP |
SecurityLayer | REG_DWORD | 0 = RDP, 1 = Negotiate, 2 = TLS |
UserAuthentication | REG_DWORD | 1 = NLA obligatoire |
MinEncryptionLevel | REG_DWORD | 1 = Low, 2 = Client-compatible, 3 = High, 4 = FIPS |
fInheritColorDepth | REG_DWORD | Heriter de la profondeur de couleur client |
ColorDepth | REG_DWORD | Profondeur de couleur forcee (1=8bit, 2=15bit, 3=16bit, 4=24bit) |
MaxInstanceCount | REG_DWORD | Nombre maximal de connexions simultanees sur ce listener |
# Read all RDP-Tcp listener settings
Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" |
Select-Object PortNumber, SecurityLayer, UserAuthentication, MinEncryptionLevel, MaxInstanceCount
PortNumber : 3389
SecurityLayer : 2
UserAuthentication : 1
MinEncryptionLevel : 3
MaxInstanceCount : 4294967295
En resume
- Le listener RDP est configure sous
HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp fDenyTSConnectionscontrole l'activation du Bureau a distance (0 = actif, 1 = desactive)SecurityLayeretUserAuthenticationdefinissent le niveau de securite de la connexion- Toute modification du port necessite une mise a jour du pare-feu et un redemarrage de TermService
Limites de session et timeouts¶
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#bd93f9', 'primaryTextColor': '#f8f8f2', 'primaryBorderColor': '#bd93f9', 'lineColor': '#ff79c6', 'secondaryColor': '#44475a', 'tertiaryColor': '#282a36'}}}%%
sequenceDiagram
participant C as Client RDP
participant GW as RD Gateway
participant CB as Connection Broker
participant SH as Session Host
C->>GW: Connexion HTTPS (port 443)
GW->>GW: Vérification NLA + autorisation
GW->>CB: Demande de session
CB->>CB: Recherche session existante
CB-->>GW: Redirection vers Session Host
GW->>SH: Tunnel RDP via HTTPS
SH-->>C: Session Bureau à distance active Les timeouts de session RDS sont critiques en environnement multi-utilisateurs. Ils evitent l'accumulation de sessions orphelines qui consomment des ressources. Ces parametres se configurent a deux niveaux : par serveur (registre) et par GPO.
Emplacement registre¶
Les timeouts sont sous la cle du listener ou sous la configuration de la session :
Ou par GPO :
Les trois timeouts fondamentaux¶
| Valeur | Type | Unite | Description |
|---|---|---|---|
MaxIdleTime | REG_DWORD | Millisecondes | Duree maximale d'inactivite avant deconnexion |
MaxConnectionTime | REG_DWORD | Millisecondes | Duree maximale totale d'une session (active ou non) |
MaxDisconnectionTime | REG_DWORD | Millisecondes | Duree maximale d'une session en etat deconnecte avant fermeture |
Conversion millisecondes
- 15 minutes = 900000 ms
- 30 minutes = 1800000 ms
- 1 heure = 3600000 ms
- 8 heures = 28800000 ms
- 0 = Pas de limite (valeur par defaut)
# Configure session timeouts on the RDP listener
$rdpPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp"
# Idle timeout: 30 minutes
Set-ItemProperty -Path $rdpPath -Name "MaxIdleTime" -Value 1800000 -Type DWord
# Max session duration: 8 hours
Set-ItemProperty -Path $rdpPath -Name "MaxConnectionTime" -Value 28800000 -Type DWord
# Disconnected session timeout: 15 minutes
Set-ItemProperty -Path $rdpPath -Name "MaxDisconnectionTime" -Value 900000 -Type DWord
Actions en fin de session¶
Deux valeurs controlent ce qui se passe quand un timeout est atteint :
| Valeur | Type | Options |
|---|---|---|
fResetBroken | REG_DWORD | 0 = Deconnecter la session, 1 = Terminer la session |
fReconnectSame | REG_DWORD | 0 = Reconnexion depuis n'importe quel client, 1 = Uniquement depuis le client d'origine |
# End session (logoff) when connection is broken, allow reconnect from any client
Set-ItemProperty -Path $rdpPath -Name "fResetBroken" -Value 0 -Type DWord
Set-ItemProperty -Path $rdpPath -Name "fReconnectSame" -Value 0 -Type DWord
Lire les timeouts actuels¶
# Audit current timeout configuration
$rdpPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp"
$props = Get-ItemProperty $rdpPath
[PSCustomObject]@{
MaxIdleTime = if ($props.MaxIdleTime -gt 0) { "$($props.MaxIdleTime / 60000) min" } else { "Unlimited" }
MaxConnectionTime = if ($props.MaxConnectionTime -gt 0) { "$($props.MaxConnectionTime / 60000) min" } else { "Unlimited" }
MaxDisconnectionTime = if ($props.MaxDisconnectionTime -gt 0) { "$($props.MaxDisconnectionTime / 60000) min" } else { "Unlimited" }
ResetBroken = $props.fResetBroken
ReconnectSame = $props.fReconnectSame
} | Format-List
MaxIdleTime : 30 min
MaxConnectionTime : 480 min
MaxDisconnectionTime : 15 min
ResetBroken : 0
ReconnectSame : 0
En resume
- Les trois timeouts cles sont
MaxIdleTime,MaxConnectionTimeetMaxDisconnectionTime(en millisecondes) - La valeur 0 signifie "pas de limite" — dangereux en production car les sessions s'accumulent
fResetBrokencontrole si une session est deconnectee (0) ou terminee (1) quand le lien est rompu- Les GPO ecrasent les valeurs locales si configurees sous
HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services
Configuration du serveur de licences RDS¶
Le licensing RDS est l'une des sources les plus frequentes de problemes sur les fermes RDS. La configuration est stockee dans le registre et doit correspondre aux CAL (Client Access Licenses) achetees.
Cles de licensing¶
Et pour la configuration GPO :
| Valeur | Cle | Type | Description |
|---|---|---|---|
LicensingMode | Licensing Core | REG_DWORD | 2 = Per Device, 4 = Per User |
LicenseServers | Licensing Core | REG_SZ | Nom du serveur de licences |
LicensingMode | Terminal Services (GPO) | REG_DWORD | Ecrase la valeur locale |
LicenseServers | Terminal Services (GPO) | REG_SZ | Ecrase la valeur locale |
# Check current licensing configuration
$licensePath = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\RCM\Licensing Core"
Get-ItemProperty $licensePath -ErrorAction SilentlyContinue |
Select-Object LicensingMode, LicenseServers
# Check if GPO overrides are in place
$gpoPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services"
Get-ItemProperty $gpoPath -Name "LicensingMode", "LicenseServers" -ErrorAction SilentlyContinue
Configurer le mode de licensing¶
# Set licensing mode to Per User and specify license server
$licensePath = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\RCM\Licensing Core"
Set-ItemProperty -Path $licensePath -Name "LicensingMode" -Value 4 -Type DWord
Set-ItemProperty -Path $licensePath -Name "LicenseServers" -Value "RDLIC01.contoso.com" -Type String
En resume
- Le licensing RDS est configure sous
HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\RCM\Licensing Core LicensingMode= 2 (Per Device) ou 4 (Per User)- Les GPO sous
HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Servicesecrasent les valeurs locales - L'absence de configuration de licensing entraine une periode de grace de 120 jours puis le blocage des connexions
RD Gateway et RD Connection Broker¶
RD Gateway¶
Le RD Gateway permet l'acces RDP via HTTPS, sans exposer le port 3389 sur Internet. Ses parametres registre sont sous :
Et pour la configuration du service :
# Check RD Gateway configuration
$gwPath = "HKLM:\SOFTWARE\Microsoft\Terminal Server Gateway\Config\Core"
Get-ItemProperty $gwPath -ErrorAction SilentlyContinue
RD Connection Broker¶
Le Connection Broker gere la repartition des sessions entre les serveurs d'une ferme RDS. Sa configuration est sous :
# Check Connection Broker cluster settings
$cbPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\ClusterSettings"
Get-ItemProperty $cbPath -ErrorAction SilentlyContinue |
Select-Object SessionDirectoryActive, SessionDirectoryLocation, SessionDirectoryClusterName
SessionDirectoryActive : 1
SessionDirectoryLocation : RDCB01.contoso.com
SessionDirectoryClusterName : RDS-Farm-01
| Valeur | Type | Description |
|---|---|---|
SessionDirectoryActive | REG_DWORD | 1 = Participe a la ferme RDS |
SessionDirectoryLocation | REG_SZ | FQDN du serveur Connection Broker |
SessionDirectoryClusterName | REG_SZ | Nom de la collection/ferme RDS |
ParticipateInLoadBalancing | REG_DWORD | 1 = Participe a la repartition de charge |
RelativeWeight | REG_DWORD | Poids relatif pour la repartition (100 = normal) |
# Enable load balancing participation with weight 100
Set-ItemProperty -Path $cbPath -Name "ParticipateInLoadBalancing" -Value 1 -Type DWord
Set-ItemProperty -Path $cbPath -Name "RelativeWeight" -Value 100 -Type DWord
En resume
- Le RD Gateway est configure sous
HKLM\SOFTWARE\Microsoft\Terminal Server Gateway - Le Connection Broker utilise
HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\ClusterSettings SessionDirectoryActive= 1 inscrit le serveur dans la ferme RDSRelativeWeightcontrole la proportion de sessions dirigees vers chaque serveur
Publication RemoteApp¶
Les RemoteApp sont des applications publiees sur un serveur RDS et affichees dans une fenetre locale chez l'utilisateur, comme si elles etaient installees localement. La configuration de publication est stockee dans le registre.
Emplacement des RemoteApp publiees¶
Chaque application est une sous-cle avec ses parametres :
# List all published RemoteApp applications
$appPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server\TSAppAllowList\Applications"
Get-ChildItem $appPath | ForEach-Object {
$props = Get-ItemProperty $_.PSPath
[PSCustomObject]@{
Name = $_.PSChildName
Path = $props.Path
CommandLine = $props.CommandLineSetting
IconPath = $props.IconPath
}
}
Name Path CommandLine IconPath
---- ---- ----------- --------
Notepad C:\Windows\system32\notepad.exe 0 C:\Windows\system32\notepad.exe
Calculator C:\Windows\system32\calc.exe 0 C:\Windows\system32\calc.exe
Excel C:\Program Files\Microsoft Office\... 1 C:\Program Files\Microsoft Office\...
Parametres d'une RemoteApp¶
| Valeur | Type | Description |
|---|---|---|
Name | REG_SZ | Nom affiche de l'application |
Path | REG_SZ | Chemin complet vers l'executable |
VPath | REG_SZ | Chemin virtuel (utilise par le Web Access) |
CommandLineSetting | REG_DWORD | 0 = Pas d'arguments, 1 = Arguments optionnels, 2 = Arguments obligatoires |
RequiredCommandLine | REG_SZ | Arguments par defaut |
IconPath | REG_SZ | Chemin vers l'icone |
IconIndex | REG_DWORD | Index de l'icone dans le fichier |
ShowInTSWA | REG_DWORD | 1 = Visible dans RD Web Access |
# Publish a new RemoteApp
$appBasePath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server\TSAppAllowList\Applications"
$appName = "WordPad"
$appPath = Join-Path $appBasePath $appName
New-Item -Path $appPath -Force | Out-Null
Set-ItemProperty -Path $appPath -Name "Name" -Value "WordPad" -Type String
Set-ItemProperty -Path $appPath -Name "Path" -Value "C:\Program Files\Windows NT\Accessories\wordpad.exe" -Type String
Set-ItemProperty -Path $appPath -Name "CommandLineSetting" -Value 0 -Type DWord
Set-ItemProperty -Path $appPath -Name "IconPath" -Value "C:\Program Files\Windows NT\Accessories\wordpad.exe" -Type String
Set-ItemProperty -Path $appPath -Name "IconIndex" -Value 0 -Type DWord
Set-ItemProperty -Path $appPath -Name "ShowInTSWA" -Value 1 -Type DWord
Autoriser les RemoteApp non listees¶
Par defaut, seules les applications explicitement publiees sont autorisees. Ce comportement est controle par :
# Check the TSAppAllowList policy
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server\TSAppAllowList" `
-Name "fDisabledAllowList" -ErrorAction SilentlyContinue
Valeur fDisabledAllowList | Signification |
|---|---|
0 | Seules les RemoteApp de la liste sont autorisees (securise) |
1 | Toutes les applications sont autorisees en RemoteApp (dangereux) |
En resume
- Les RemoteApp sont enregistrees sous
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server\TSAppAllowList\Applications - Chaque application est une sous-cle avec
Path,Name,CommandLineSettingetShowInTSWA fDisabledAllowList= 0 restreint l'execution aux seules applications publiees (recommande)
NLA (Network Level Authentication)¶
NLA force l'authentification de l'utilisateur avant l'etablissement complet de la session RDP. C'est une mesure de securite essentielle qui protege contre les attaques par force brute et l'exploitation de vulnerabilites pre-authentification (comme BlueKeep).
Activer NLA¶
# Enable NLA on the RDP listener
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" `
-Name "UserAuthentication" -Value 1 -Type DWord
# Also set the security layer to TLS
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" `
-Name "SecurityLayer" -Value 2 -Type DWord
Verifier NLA via GPO¶
# Check if NLA is enforced by Group Policy
$gpoPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services"
Get-ItemProperty $gpoPath -Name "UserAuthentication" -ErrorAction SilentlyContinue
Niveaux de securite RDP¶
| SecurityLayer | UserAuthentication | Resultat |
|---|---|---|
| 0 (RDP) | 0 | Aucune securite — connexion en clair (a proscrire) |
| 1 (Negotiate) | 0 | TLS si le client le supporte, sinon RDP |
| 2 (TLS) | 0 | TLS obligatoire, mais pas de NLA |
| 2 (TLS) | 1 | TLS + NLA — configuration recommandee |
# Audit NLA and security layer on remote servers
$servers = @("RDSH01", "RDSH02", "RDSH03")
Invoke-Command -ComputerName $servers -ScriptBlock {
$rdpPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp"
$props = Get-ItemProperty $rdpPath
[PSCustomObject]@{
Server = $env:COMPUTERNAME
SecurityLayer = $props.SecurityLayer
UserAuthentication = $props.UserAuthentication
MinEncryptionLevel = $props.MinEncryptionLevel
}
} | Format-Table -AutoSize
Server SecurityLayer UserAuthentication MinEncryptionLevel
------ ------------- ------------------ ------------------
RDSH01 2 1 3
RDSH02 2 1 3
RDSH03 2 1 3
En resume
- NLA est controle par
UserAuthentication= 1 dans la cleRDP-Tcp - La configuration recommandee est
SecurityLayer= 2 (TLS) +UserAuthentication= 1 (NLA) - Les GPO sous
HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Servicesecrasent la configuration locale - NLA protege contre BlueKeep et les attaques par force brute pre-authentification
Profils utilisateur et redirection de session¶
Profils itinerants RDS¶
Les profils itinerants specifiques aux sessions RDS sont configures separement des profils itinerants Windows classiques :
| Valeur | Type | Description |
|---|---|---|
WFProfilePath | REG_SZ | Chemin UNC du profil itinerant RDS (ex: \\NAS01\RDSProfiles$\%username%) |
WFHomeDir | REG_SZ | Chemin du repertoire personnel RDS |
WFHomeDirDrive | REG_SZ | Lettre de lecteur pour le repertoire personnel (ex: H:) |
DeleteRoamingProfilesOnLogoff | REG_DWORD | 1 = Supprimer le profil local a la deconnexion |
MaxProfileSize | REG_DWORD | Taille maximale du profil en Ko |
# Configure RDS roaming profile path via registry
$tsPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services"
if (-not (Test-Path $tsPath)) {
New-Item -Path $tsPath -Force | Out-Null
}
Set-ItemProperty -Path $tsPath -Name "WFProfilePath" -Value "\\NAS01\RDSProfiles$\%username%" -Type String
Set-ItemProperty -Path $tsPath -Name "WFHomeDirDrive" -Value "H:" -Type String
Set-ItemProperty -Path $tsPath -Name "WFHomeDir" -Value "\\NAS01\RDSHome$\%username%" -Type String
User Profile Disks (UPD)¶
Les UPD sont une alternative aux profils itinerants, ou chaque utilisateur a un disque virtuel VHDX monte a la connexion. La configuration est sous :
# Check UPD configuration on the RDS session host
$clusterPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\ClusterSettings"
Get-ItemProperty $clusterPath -Name "UvhdEnabled", "UvhdShareUrl" -ErrorAction SilentlyContinue
Redirection de peripheriques¶
Les redirections (presse-papier, imprimantes, lecteurs, ports COM) sont controlees dans la cle du listener :
| Valeur | Type | Effet si = 1 |
|---|---|---|
fDisableClip | REG_DWORD | Desactive la redirection du presse-papier |
fDisableCpm | REG_DWORD | Desactive la redirection des imprimantes |
fDisableCdm | REG_DWORD | Desactive la redirection des lecteurs |
fDisableLPT | REG_DWORD | Desactive la redirection des ports LPT |
fDisableCcm | REG_DWORD | Desactive la redirection des ports COM |
fDisableAudioCapture | REG_DWORD | Desactive la capture audio (microphone) |
# Harden RDS: disable clipboard and drive redirection (prevent data exfiltration)
$rdpPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp"
Set-ItemProperty -Path $rdpPath -Name "fDisableClip" -Value 1 -Type DWord
Set-ItemProperty -Path $rdpPath -Name "fDisableCdm" -Value 1 -Type DWord
En resume
- Les profils itinerants RDS sont configures via
WFProfilePathsousTerminal Services(GPO) - Les UPD (User Profile Disks) sont stockes dans le chemin defini par
UvhdShareUrl - Les redirections de peripheriques se controlent par les valeurs
fDisable*dans la cleRDP-Tcp - Desactiver le presse-papier et les lecteurs est une mesure anti-exfiltration courante
Scenario reel : corriger "licensing mode not configured" sur une ferme RDS¶
Contexte¶
Votre ferme RDS de 5 serveurs Session Host affiche le message "The Remote Desktop Session Host server does not have a Remote Desktop license server specified" dans l'Observateur d'evenements (Event ID 1128). Les utilisateurs voient un bandeau rouge indiquant que la periode de grace expire dans 30 jours. Vous disposez d'un serveur de licences RDLIC01.contoso.com avec des CAL Per User valides.
Etape 1 : diagnostiquer l'etat du licensing¶
# Check licensing configuration on all Session Hosts
$sessionHosts = @("RDSH01", "RDSH02", "RDSH03", "RDSH04", "RDSH05")
Invoke-Command -ComputerName $sessionHosts -ScriptBlock {
# Check local registry
$localPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\RCM\Licensing Core"
$local = Get-ItemProperty $localPath -ErrorAction SilentlyContinue
# Check GPO registry
$gpoPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services"
$gpo = Get-ItemProperty $gpoPath -Name "LicensingMode", "LicenseServers" -ErrorAction SilentlyContinue
[PSCustomObject]@{
Server = $env:COMPUTERNAME
LocalMode = $local.LicensingMode
LocalLicServer = $local.LicenseServers
GPOMode = $gpo.LicensingMode
GPOLicServer = $gpo.LicenseServers
}
} | Format-Table -AutoSize
Server LocalMode LocalLicServer GPOMode GPOLicServer
------ --------- -------------- ------- ------------
RDSH01 0
RDSH02 0
RDSH03 4 RDLIC01.contoso.com
RDSH04 0
RDSH05 0
Le diagnostic revele que seul RDSH03 est correctement configure. Les autres serveurs ont un LicensingMode a 0 (non configure) et aucun serveur de licences specifie.
Etape 2 : verifier la connectivite avec le serveur de licences¶
# Test connectivity to the license server from each session host
Invoke-Command -ComputerName $sessionHosts -ScriptBlock {
$licServer = "RDLIC01.contoso.com"
$dns = Resolve-DnsName $licServer -ErrorAction SilentlyContinue
$tcp = Test-NetConnection $licServer -Port 135 -WarningAction SilentlyContinue
[PSCustomObject]@{
Server = $env:COMPUTERNAME
DNS = if ($dns) { "OK" } else { "FAIL" }
RPC_Port135 = if ($tcp.TcpTestSucceeded) { "OK" } else { "FAIL" }
}
}
Server DNS RPC_Port135
------ --- -----------
RDSH01 OK OK
RDSH02 OK OK
RDSH03 OK OK
RDSH04 OK FAIL
RDSH05 OK OK
RDSH04 a un probleme de pare-feu qui bloque le port RPC vers le serveur de licences.
Etape 3 : appliquer la configuration de licensing sur tous les serveurs¶
# Apply licensing configuration on all session hosts
$sessionHosts = @("RDSH01", "RDSH02", "RDSH03", "RDSH04", "RDSH05")
$licServer = "RDLIC01.contoso.com"
$licMode = 4 # Per User
Invoke-Command -ComputerName $sessionHosts -ScriptBlock {
param($mode, $server)
# Set local registry configuration
$localPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\RCM\Licensing Core"
Set-ItemProperty -Path $localPath -Name "LicensingMode" -Value $mode -Type DWord
Set-ItemProperty -Path $localPath -Name "LicenseServers" -Value $server -Type String
# Also set via the GPO path to ensure consistency
$gpoPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services"
if (-not (Test-Path $gpoPath)) {
New-Item -Path $gpoPath -Force | Out-Null
}
Set-ItemProperty -Path $gpoPath -Name "LicensingMode" -Value $mode -Type DWord
Set-ItemProperty -Path $gpoPath -Name "LicenseServers" -Value $server -Type String
Write-Output "$env:COMPUTERNAME : Licensing configured (Mode=$mode, Server=$server)"
} -ArgumentList $licMode, $licServer
RDSH01 : Licensing configured (Mode=4, Server=RDLIC01.contoso.com)
RDSH02 : Licensing configured (Mode=4, Server=RDLIC01.contoso.com)
RDSH03 : Licensing configured (Mode=4, Server=RDLIC01.contoso.com)
RDSH04 : Licensing configured (Mode=4, Server=RDLIC01.contoso.com)
RDSH05 : Licensing configured (Mode=4, Server=RDLIC01.contoso.com)
Etape 4 : corriger le probleme de pare-feu sur RDSH04¶
# Fix the firewall issue on RDSH04
Invoke-Command -ComputerName "RDSH04" -ScriptBlock {
# Enable RPC communication for RD Licensing
Enable-NetFirewallRule -DisplayGroup "Remote Desktop"
# Verify the license server is now reachable
$test = Test-NetConnection "RDLIC01.contoso.com" -Port 135 -WarningAction SilentlyContinue
Write-Output "RDSH04 : RPC Port 135 = $($test.TcpTestSucceeded)"
}
Etape 5 : reinitialiser la periode de grace (si necessaire)¶
Si la periode de grace a expire et que les connexions sont bloquees, il faut supprimer la cle qui stocke le compteur de grace. Cette cle est protegee et necessite des droits SYSTEM :
# Delete the grace period timer (requires SYSTEM privileges via PsExec or scheduled task)
# WARNING: only use this if the grace period has expired and you have valid CALs
Invoke-Command -ComputerName $sessionHosts -ScriptBlock {
$gracePath = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\RCM\GracePeriod"
if (Test-Path $gracePath) {
# Take ownership and grant full control
$acl = Get-Acl $gracePath
$rule = New-Object System.Security.AccessControl.RegistryAccessRule(
"BUILTIN\Administrators", "FullControl", "Allow")
$acl.SetAccessRule($rule)
Set-Acl $gracePath $acl
# Delete the grace period key
Remove-Item $gracePath -Force
Write-Output "$env:COMPUTERNAME : Grace period reset"
}
else {
Write-Output "$env:COMPUTERNAME : No grace period key found"
}
}
RDSH01 : Grace period reset
RDSH02 : Grace period reset
RDSH03 : No grace period key found
RDSH04 : Grace period reset
RDSH05 : Grace period reset
Reinitialiser la grace period n'est pas une solution permanente
Cette manipulation ne doit etre utilisee que pour debloquer temporairement l'acces pendant que vous configurez correctement le licensing. Elle ne remplace pas l'achat de CAL valides.
Etape 6 : valider la configuration¶
# Validate licensing is working after restart
Invoke-Command -ComputerName $sessionHosts -ScriptBlock {
# Check licensing events
$events = Get-WinEvent -FilterHashtable @{
LogName = 'System'
ProviderName = 'TermServLicensing'
} -MaxEvents 3 -ErrorAction SilentlyContinue
[PSCustomObject]@{
Server = $env:COMPUTERNAME
LastEvent = if ($events) { $events[0].Message.Substring(0, [Math]::Min(80, $events[0].Message.Length)) } else { "No events" }
EventId = if ($events) { $events[0].Id } else { "N/A" }
}
} | Format-Table -AutoSize
Server LastEvent EventId
------ --------- -------
RDSH01 The Remote Desktop Session Host server is properly licensed with... 1076
RDSH02 The Remote Desktop Session Host server is properly licensed with... 1076
RDSH03 The Remote Desktop Session Host server is properly licensed with... 1076
RDSH04 The Remote Desktop Session Host server is properly licensed with... 1076
RDSH05 The Remote Desktop Session Host server is properly licensed with... 1076
En resume
- Le probleme "licensing mode not configured" vient d'un
LicensingModea 0 et d'unLicenseServersvide - La solution consiste a configurer les deux valeurs dans
Licensing Coreet le chemin GPO - Verifiez la connectivite RPC (port 135) entre chaque Session Host et le serveur de licences
- La cle
GracePeriodpeut etre supprimee en urgence pour debloquer l'acces, mais ce n'est pas une solution permanente - Deployer la configuration via GPO est recommande pour garantir la coherence sur tous les serveurs de la ferme
Voir aussi
- Remote Desktop Services via GPO — GPO Admins
- FSLogix et GPO — GPO Admins