(MS Windows Server 2012 R2)
Na Windows serverech v RDP prostředí s vynucenou NLA autentizací (Network Level Authentication) resp. CredSSP protokolem (Credential Security Support Provider) nelze u klientů mimo doménu provést změnu hesla pomocí aplikace Mstsc.exe (Připojení ke vzdálené ploše).
Tato situace je pro uživatele nepříjemná, protože nezmění-li si heslo včas sami, po expiraci hesla se do systému již nepřihlásí.
Standardní řešení
- zobrazovat výzvu (bublinu) s informací o blížící se expiraci hesla viz https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/interactive-logon-prompt-user-to-change-password-before-expiration
- zprovoznit "RD Web" roli na serveru a umožnit tak uživateli změnu hesla pomocí webové služby viz https://docs.microsoft.com/en-us/windows-server/remote/remote-desktop-services/rds-rdweb-gateway-ha
Výzva bohužel nevyžaduje akci uživatele, tudíž bývá mnohdy opomíjena a uvádí postup pomocí zkratky CTRL+ALT+END, která neplatí např. u Linuxových klientů. V případě služby RD Web je třeba uživatele předem informovat a distribuovat mu zástupce resp. URL adresu.
Vlastní řešení - zpráva pomocí PowerShell skriptu
Protože výše uvedená řešení nepovažuji za ideální, připravil jsem PowerShell skript, který zobrazí uživateli zprávu s informací o expiraci hesla a vyžaduje akci. Po kliknutí na ANO se zobrazí tzv. GINA obrazovka (CTRL+ALT+DEL).

Zprovoznění skriptu
Na RDP servery je třeba nainstalovat PowerShell modul activedirectory a v AD nad objektem Password Policy (System -> Password Settings Container -> Policy) nastavit uživatelům právo Read pro potřebné vlastnosti (MinPasswordLength, PasswordHistoryCount, MaxPasswordAge) + Read permissions.
Ve skriptu stačí definovat 2 proměnné:
- $PassPolicyName = název Password Policy v AD
- $MsgDays = počet dní pro zobrazení zprávy před expirací hesla
Skript je nutné spouštět pod účtem uživatele po přihlášení a načtení Plochy, např. v GPO zde:
User Configuration -> Policies -> Administrative Templates -> System -> Logon: "Run these programs at user logon"
Skript
##############################################################################
# The name of the password policy
# (Users need permission to read the properties of this policy.)
$PassPolicyName = "Password_Policy_name"
# Number of days to display the message (before the password expires)
$MsgDays = 14
##############################################################################
# Import ActiveDirectory PS module
import-module activedirectory
# Date the password was last set.
$PassLastSetDate = [DateTime](get-aduser $env:UserName -properties passwordlastset | % passwordlastset)
# Today date
$Today = [DateTime](Get-Date)
# Number of days since the last password was set.
$PassLastSetDays = (New-TimeSpan -Start $PassLastSetDate -End $Today).Days
# Get the properties of the password policy
$PassPolicy = (Get-ADFineGrainedPasswordPolicy -Filter {name -like $PassPolicyName} -Properties MinPasswordLength, PasswordHistoryCount, MaxPasswordAge)
# Maximum password age (days)
$MaxPassDays = $PassPolicy.MaxPasswordAge.Days
# Minimum password length
$MinPassLength = $PassPolicy.MinPasswordLength
# Number of remembered passwords
$PassHistCount = $PassPolicy.PasswordHistoryCount
# Remaining days of password validity
$RemainingDays = ($MaxPassDays - $PassLastSetDays)
# Display message only the X last days ($MsgDays) before the password expires
if ( ($RemainingDays -ge 0) -and ($RemainingDays -le $MsgDays) )
{
# Text string for the remaining days
switch ($RemainingDays)
{
{$_ -eq 1} { $MsgRemDaysText = "den" }
{ ($_ -gt 1) -and ($_ -lt 5) } { $MsgRemDaysText = "dny" }
Default {
$MsgRemDaysText = "dní"
}
}
# MessageBox settings
Add-Type -AssemblyName PresentationCore, PresentationFramework
$MsgTitle = "ZMĚNA HESLA"
$MsgBody = "Vaše heslo vyprší za $RemainingDays $MsgRemDaysText. Chcete-li heslo změnit, klikněte na ANO`na zvolte 'Změnit heslo'.`n`nMin. délka hesla: $MinPassLength (znaků)`nPočet posledních hesel, která nelze použít: $PassHistCount"
$MsgIcon = [System.Windows.MessageBoxImage]::Asterisk
$MsgButtonType = [System.Windows.MessageBoxButton]::YesNo
# MessageBox result
$MsgResult = [System.Windows.MessageBox]::Show($MsgBody,$MsgTitle,$MsgButtonType,$MsgIcon)
# If the answer ($MsgResult) = YES
if ( $MsgResult -eq 6 ) {
# Send CTRL+ALT+DEL command
(New-Object -COM Shell.Application).WindowsSecurity()
}
}