
⚠️ ATTENZIONE️ ⚠️
LA PROCEDURA DESCRITTA IN QUESTO ARTICOLO NON E’ SUPPORTATA DA MICROSOFT E NON SOSTITUISCE L’ACQUISTO E L’INSTALLAZIONE DELLE LICENZE RDS E VA UTILIZZATA ESCLUSIVAMENTE IN AMBIENTI DI LAB O TEST E NON IN PRODUZIONE!
L’UTLIZZO IMPROPRIO DI QUESTA PROCEDURA PUO’ COMPORTARE VIOLAZIONI DELLE CONDIZIONI DI LICENZA E PROBLEMI DI CONFORMITA’.
Durante la configurazione di un ambiente Remote Desktop Services (RDS), Windows Server concede un periodo di grazia (“Grace Period”) di 120 giorni in cui è possibile consentire le connessioni Remote Desktop anche senza aver ancora configurato un server licenze attivo.
Al termine di questo periodo, se non è stato installato e attivato correttamente un Remote Desktop License Server, gli utenti non potranno più connettersi alle sessioni RDS.
In alcuni scenari di laboratorio, testing o ambiente di sviluppo temporaneo, può essere utile reimpostare il Grace Period per estendere la possibilità di testare la configurazione senza dover attivare subito le licenze CAL.
Questo articolo illustra come farlo in modo controllato, spiegando i passaggi tecnici, i rischi e le implicazioni del reset.
PRODOTTI INTERESSATI
Microsoft Windows 2012 Server
Microsoft Windows 2012 Server R2
Microsoft Windows Server 2016
Microsoft Windows Server 2019
Microsoft Windows Server 2022
Microsoft Windows Server 2025
CONTROLLO DEI GIORNI RIMANENTI ALLA SCADENZA DEL GRACE PERIOD DI 120 GIORNI
Per verificare i giorni rimanenti eseguire il seguente comando in una shell powershell con diritti amministrativi:
|
0 |
(invoke-cimmethod -inputobject (get-ciminstance -namespace root/CIMV2/TerminalServices -classname Win32_TerminalServiceSetting) -methodname GetGracePeriodDays).DaysLeft
|
Nel mio caso ho il server RDS a cui mancano 61 giorni per la scadenza della licenza
Per l’esercizio di reimpostazione del periodo di tolleranza di 120 giorni per il ruolo RDS, l’editor del Registro di sistema semplifica questo processo.
NOTA BENE: La modifica del registro può comportare la distruzione totale di un sistema Windows quindi procedere con cautela con qualsiasi modifica del registro di basso livello.
Per reimpostare il periodo di tolleranza, in realtà sono necessari solo 3 passaggi:
- Modificare le autorizzazioni per la chiave GracePeriod di RCM
- EliminaRE la voce del Registro di sistema Timebomb
- Riavviare il server
MODIFICA DELLE AUTORIZZAZIONI DELLA CHIAVE RDS GRACE PERIOD E RELATIVA CANCELLAZIONE
Ora che abbiamo impostato il livello, una volta creato lo snapshot o un altro backup, è necessario aprire il registro di sistema con diritti amministrativi:
Individuare la chiave seguente:
|
0 |
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\RCM\GracePeriod |
Quindi posizionarsi sulla chiave GracePeriod come mostrato nell’imamgine sovrastante
Cliccare sulla Chiave GracePeriod con il tasto destro e selezionare la voce Permissions…
Cliccare su Adavanced
Cliccare Change
Selezionare il gruppo di amministratori locali come proprietario della chiave quindi cliccare OK
Selezionare l’opzione Replace owner on subcontainers and objects e l’opzione Replace all child object permissions entries with inheritable permissions entries from this object quindi cliccare OK
Cliccare yes per procedere alla modifica delle ACL della chiave
Selezionare Administrators quindi impostare il Full Control nelle permissions. Cliccare OK per applicare
A questo punto è possibile selezionare la REG_BINARY TimeBomb e cancellarla cliccando su Delete
Confermare la cancellazione del REG_BINARY cliccando Yes
A questo punto non ci resta che riavviare il server
CONTROLLO DEI GIORNI RIMANENTI ALLA SCADENZA DEL GRACE PERIOD
Per verificare i giorni rimanenti eseguire il seguente comando in una powershell con diritti amministrativi:
|
0 |
(invoke-cimmethod -inputobject (get-ciminstance -namespace root/CIMV2/TerminalServices -classname Win32_TerminalServiceSetting) -methodname GetGracePeriodDays).DaysLeft
|
Come mostrato nell’immagine sovrastante dovremmo visualizzare il numero di giorni a 119
SCRIPT PER IL RESET DEL GRACE PERIOD DI 120 GIORNI
Di seguito lo script in Powershell che fai il reset del Grace Period di 120 Giorni con relativo riavvio del server:
|
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
<#
RESET GRACE PERIOD RDS - SOLO LAB/TEST
- Legge giorni residui
- Take ownership + FullControl su HKLM\...\RCM\GracePeriod
- Elimina la chiave (reset a 120 gg)
- Riavvia TermService
- (Opzionale) Riavvio server con conferma
#>
Clear-Host
$ErrorActionPreference = 'Stop'
Write-Host '======================================================' -ForegroundColor Yellow
Write-Host 'ATTENZIONE: SOLO PER LAB/TEST — NON IN PRODUZIONE' -ForegroundColor Yellow
Write-Host '======================================================' -ForegroundColor Yellow
function Get-GraceDays {
try {
$obj = Get-WmiObject -Namespace root\cimv2\terminalservices -Class Win32_TerminalServiceSetting
$res = Invoke-WmiMethod -Path $obj.__PATH -Name GetGracePeriodDays
return [int]$res.DaysLeft
} catch { return $null }
}
# Stato iniziale
$before = Get-GraceDays
Write-Host "`nGiorni di GRACE rimanenti (prima): $before" -ForegroundColor Green
# Conferma reset
$resp = Read-Host "Reimpostare il Grace Period a 120 giorni? (Y/N)"
if ($resp -notmatch '^(y|s)$') {
Write-Host "`nOperazione annullata." -ForegroundColor Yellow
return
}
# Abilita SeTakeOwnershipPrivilege (fallback)
$enablePrivilege = @"
using System;
using System.Runtime.InteropServices;
public static class NtPriv {
[DllImport("ntdll.dll", EntryPoint="RtlAdjustPrivilege")]
public static extern int RtlAdjustPrivilege(ulong Privilege, bool Enable, bool CurrentThread, ref bool Enabled);
}
"@
try { Add-Type -TypeDefinition $enablePrivilege -ErrorAction SilentlyContinue } catch {}
$dummy = $false
try { [NtPriv]::RtlAdjustPrivilege(9, $true, $false, [ref]$dummy) | Out-Null } catch {}
# Take ownership + FullControl + delete key
$regPath = 'SYSTEM\CurrentControlSet\Control\Terminal Server\RCM\GracePeriod'
try {
$key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey(
$regPath,
[Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree,
[System.Security.AccessControl.RegistryRights]::TakeOwnership
)
if (-not $key) { throw "Chiave non trovata: HKLM:\$regPath" }
$acl = $key.GetAccessControl()
$admSid = New-Object System.Security.Principal.SecurityIdentifier([System.Security.Principal.WellKnownSidType]::BuiltinAdministratorsSid, $null)
$admAcc = $admSid.Translate([System.Security.Principal.NTAccount])
$acl.SetOwner($admAcc)
$key.SetAccessControl($acl)
$acl = $key.GetAccessControl()
$rule = New-Object System.Security.AccessControl.RegistryAccessRule($admAcc, 'FullControl', 'ContainerInherit', 'None', 'Allow')
$acl.SetAccessRule($rule)
$key.SetAccessControl($acl)
$key.Close()
Remove-Item -Path "HKLM:\$regPath" -Recurse -Force
Write-Host "`nChiave GracePeriod eliminata." -ForegroundColor Cyan
}
catch {
Write-Error "Errore durante il reset del Grace Period: $($_.Exception.Message)"
return
}
# Riavvio servizio RDP
try {
Write-Host "Riavvio del servizio TermService..." -ForegroundColor Cyan
Restart-Service -Name TermService -Force -ErrorAction Stop
Start-Sleep -Seconds 5
} catch {
Write-Warning "Impossibile riavviare TermService automaticamente. Potrebbe essere necessario un riavvio."
}
# Stato post-reset (prima del reboot)
$after = Get-GraceDays
Write-Host "`nGiorni di GRACE rimanenti (dopo reset): $after" -ForegroundColor Yellow
# --- OPZIONALE: riavvio del server ---
$reboot = Read-Host "Vuoi riavviare ORA il server per applicare completamente la modifica? (Y/N)"
if ($reboot -match '^(y|s)$') {
Write-Host "`nIl server si riavvierà tra 30 secondi. Salva il lavoro aperto." -ForegroundColor Red
shutdown.exe /r /t 30 /c "Riavvio per reset Grace Period RDS (solo LAB/TEST)" /f
# In alternativa: Restart-Computer -Force
} else {
Write-Host "`nNessun riavvio eseguito. Se il valore dei giorni non si aggiorna, riavvia manualmente." -ForegroundColor Cyan
}
Write-Host "`nOperazione completata (solo LAB/TEST)." -ForegroundColor Green
|
NOTE PER LO SCRIPT
Il reset funziona solo sul Session Host che sta consumando il grace period (non sul License Server).
Se il tuo RDSH ha già un License Server configurato e valido, il DaysLeft può risultare 0 anche dopo il reset (perché non è in grace).
In alcuni casi il contatore si aggiorna dopo un reboot: se vedi null o valori inattesi, riavviare il server.
L’opzione di riavvio usa shutdown /r /t 30 /f (timer 30s + force chiusura). Se preferisci immediato, metti t 0; se vuoi usare PowerShell puro, sostituisci con Restart-Computer -Force.
Dopo il reboot, se vuoi verificare: riesegui solo la funzione Get-GraceDays o l’intero script scegliendo N al reset.
SCRIPT PER GENERAZIONE REPORT DELLE LICENZE PRESENTI SU UNO O PIU’ REMOTE DESKTOP LICENSE SERVER
Il seguente script genera un report CSV delle licenze RDS presenti su uno o più Remote Desktop License Server:
- pacchetti CAL effettivamente installati (quantità, disponibili, eventuale scadenza);
- (opzionale) elenco delle CAL Per Device emesse ai client.
Lo script interroga prima root\cimv2 (corretto), se non trova nulla, prova comunque anche root\cimv2\TerminalServices per compatibilità ed esporta tutto in un unico CSV.
Copiare-incollare in Export-RDS-CalReport.ps1 ed eseguire come amministratore sul License Server.
|
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
[CmdletBinding()]
param(
[string[]]$ComputerName = @($env:SERVER-NAME),
[string]$OutputPath = "C:\Temp",
[switch]$IncludeIssued = $true
)
$ProductTypeMap = @{ 2='Per Device'; 4='Per User' }
$ProductVersionMap = @{ 2='Windows Server 2008'; 4='Windows Server 2012'; 8='Windows Server 2016/2019'; 16='Windows Server 2022/2025' }
function Convert-DmtfDate([string]$d){ if([string]::IsNullOrWhiteSpace($d)){return $null}; try{ [Management.ManagementDateTimeConverter]::ToDateTime($d) }catch{ $null } }
$null = New-Item -ItemType Directory -Path $OutputPath -Force -ErrorAction SilentlyContinue
$ts = Get-Date -Format "yyyyMMdd_HHmmss"
$outFile = Join-Path $OutputPath ("RDS_CAL_Report_{0}_{1}.csv" -f ($ComputerName -join '_'), $ts)
$rows = New-Object System.Collections.Generic.List[object]
function Get-LsData {
param([string]$Server,[string]$NsPrimary='root\cimv2',[string]$NsFallback='root\cimv2\TerminalServices')
$packs = @()
$issued = @()
foreach($ns in @($NsPrimary,$NsFallback)){
if(-not $packs){
try { $packs = Get-CimInstance -ComputerName $Server -Namespace $ns -ClassName Win32_TSLicenseKeyPack -ErrorAction Stop } catch {}
}
if($IncludeIssued -and -not $issued){
try { $issued = Get-CimInstance -ComputerName $Server -Namespace $ns -ClassName Win32_TSIssuedLicense -ErrorAction Stop } catch {}
}
}
[pscustomobject]@{ Packs=$packs; Issued=$issued }
}
foreach ($server in $ComputerName) {
Write-Host ("`n>> Server: {0}" -f $server) -ForegroundColor Cyan
$data = Get-LsData -Server $server
$packs = $data.Packs
$issued = $data.Issued
if(-not $packs -and -not $issued -and $IncludeIssued){
Write-Warning (" Nessun dato trovato su {0}. Verifica che sia il License Server giusto e riprova in PowerShell 64-bit." -f $server)
}
foreach ($p in $packs) {
$exp = Convert-DmtfDate $p.ExpirationDate
$days = if($exp){ [int][Math]::Floor(($exp - (Get-Date)).TotalDays) } else { $null }
$rows.Add([pscustomobject]@{
Server=$server; RecordType='Pack'; KeyPackId=$p.KeyPackId; Description=$p.Description
ProductType=$p.ProductType; ProductTypeText=$ProductTypeMap[$p.ProductType]
ProductVersion=$p.ProductVersion; ProductVersionText=$ProductVersionMap[$p.ProductVersion]
KeyPackType=$p.KeyPackType; TotalLicenses=$p.TotalLicenses; AvailableLicenses=$p.AvailableLicenses
IssuedCount=($p.TotalLicenses - $p.AvailableLicenses)
IssueDate=$null; ExpirationDate=$exp; DaysToExpire=$days; MachineName=$null; LicenseStatus=$null
})
}
if ($IncludeIssued) {
foreach ($i in $issued) {
$issue = Convert-DmtfDate $i.IssueDate
$exp2 = Convert-DmtfDate $i.ExpirationDate
$days2 = if($exp2){ [int][Math]::Floor(($exp2 - (Get-Date)).TotalDays) } else { $null }
$rows.Add([pscustomobject]@{
Server=$server; RecordType='IssuedDevice'; KeyPackId=$i.KeyPackId; Description=$null
ProductType=$null; ProductTypeText=$null; ProductVersion=$null; ProductVersionText=$null
KeyPackType=$null; TotalLicenses=$null; AvailableLicenses=$null; IssuedCount=$null
IssueDate=$issue; ExpirationDate=$exp2; DaysToExpire=$days2; MachineName=$i.MachineName; LicenseStatus=$i.LicenseStatus
})
}
}
}
if ($rows.Count -gt 0) {
$csv = $rows | Select-Object * | ConvertTo-Csv -NoTypeInformation
$Utf8NoBom = New-Object System.Text.UTF8Encoding($false)
[System.IO.File]::WriteAllLines($outFile, $csv, $Utf8NoBom)
Write-Host ("`nReport creato: {0}" -f $outFile) -ForegroundColor Green
$outFile
} else {
Write-Warning "Nessun dato da esportare."
}
|
Download “Reset-Grace-Period-120-RDS-License.zip” Reset-Grace-Period-120-RDS-License.zip – Scaricato 0 volte – 1,96 KB
Download “Export-RDS-CalReport.ps1_.zip” Export-RDS-CalReport.ps1_.zip – Scaricato 0 volte – 1,65 KB



0 commenti