In Windows 7 la notifica della scadenza della password è visualizzata per qualche secondo nella barra inferiore dello schermo, in genere cinque giorni prima come default. Purtroppo la notifica non è molto visibile e se non si nota immediatamente, inevitabilmente la password di sistema scade pregiudicando la funzionalità dei servizi di rete.
Per risolvere questo possibile inconveniente ed avere una notifica più chiara, è possibile visualizzare dei popup durante la fase di login e/o inviare una email direttamente nella casella dell’utente.
Visualizzare un popup al logon
Visualizzare un popup al logon può essere una prima soluzione. Questo può essere effettuato con uno script .vbs lanciato tramite GPO. Nello script utilizzato nell’esempio, la variabile warningDays determina quanti giorni prima visualizzare il popup di notifica.
'======================================== ' First, get the domain policy. '======================================== Dim oDomain Dim oUser Dim maxPwdAge Dim numDaysA Dim warningDays warningDays = 14 Set LoginInfo = CreateObject("ADSystemInfo") Set objUser = GetObject("LDAP://" & LoginInfo.UserName & "") strDomainDN = UCase(LoginInfo.DomainDNSName) strUserDN = LoginInfo.UserName Set oDomain = GetObject("LDAP://" & strDomainDN) Set maxPwdAge = oDomain.Get("maxPwdAge") '======================================== ' Calculate the number of days that are ' held in this value. '======================================== numDays = CCur((maxPwdAge.HighPart * 2 ^ 32) + _ maxPwdAge.LowPart) / CCur(-864000000000) 'WScript.Echo "Maximum Password Age: " & numDays '======================================== ' Determine the last time that the user ' changed his or her password. '======================================== Set oUser = GetObject("LDAP://" & strUserDN) '======================================== ' Add the number of days to the last time ' the password was set. '======================================== whenPasswordExpires = DateAdd("d", numDays, oUser.PasswordLastChanged) fromDate = Date daysLeft = DateDiff("d",fromDate,whenPasswordExpires) 'WScript.Echo "Password Last Changed: " & oUser.PasswordLastChanged if (daysLeft < warningDays) and (daysLeft > -1) then Msgbox "La password scade tra " & daysLeft & " giorni" & " il " & _ whenPasswordExpires &chr(13) & chr(13) & "Premi CTRL + ALT + CANC " &_ "e seleziona l'opzione 'Cambia password'.", 0, "AVVISO SCADENZA PASSWORD" End if '======================================== ' Clean up. '======================================== Set oUser = Nothing Set maxPwdAge = Nothing Set oDomain = Nothing
Per eseguire lo script durante la fase di login, creare una nuova GPO tramite il tool Group Policy Management. Selezionare con il tasto destro del mouse la voce Group Policy Objects > New.
Assegnare un nome alla nuova GPO e cliccare su OK.
Editare la GPO creata e posizionarsi sull’opzione User Configuration > Policies > Windows Settings > Scripts (Logon/Logoff) e cliccare con il tasto destro del mouse su Logon > Properties.
Nella sezione Scripts, cliccare il bottone Add e tramite Browse specificare il file .vbs da utilizzare.
Selezionare lo script e cliccare su Open.
Impostato lo script .vbs, cliccare su OK per salvare la configurazione.
Effettuare il link della nuova GPO creata alla OU corretta.
Effettuato il logon al sistema, l’esecuzione dello script visualizza il popup di notifica.
Invio email di notifica
Per rendere la notifica di scadenza della password più chiara e completa, è possibile associare al logon, oltre al popup, anche l’invio di una email all’indirizzo dell’utente. L’email viene inviata tramite uno script in PowerShell eseguito al logon in aggiunta al precedente script .vbs. Lo script utilizzato nell’esempio è stato prelevato dal sito www.rlmueller.net e customizzato in base alle specifiche esigenze.
Trap {"Error: $_"; Break;} # Specify number of days. Any users whose passwords expire within # this many days after today will be processed. $intDays = 14 # Email settings. $Script:From = "myemailaddress@mydomain.com" $Script:Subject = "Password Expiration Notice" $Server = "smtp.mydomain.com" $Port = 25 $Client = New-Object System.Net.Mail.SmtpClient $Server, $Port # You may need to provide credentials. $Client.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials Function SendEmail($To, $Body) { $Message = New-Object System.Net.Mail.MailMessage ` $Script:From, $To, $Script:Subject, $Body $Client.Send($Message) } # Retrieve Domain maximum password age policy, in days. $D = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() $Domain = [ADSI]"LDAP://$D" $MPA = $Domain.maxPwdAge.Value # Convert to Int64 ticks (100-nanosecond intervals). $lngMaxPwdAge = $Domain.ConvertLargeIntegerToInt64($MPA) # Convert to days. $MaxPwdAge = -$lngMaxPwdAge/(600000000 * 1440) # Determine the password last changed date such that the password # would just now be expired. We will not process any users whose # password has already expired. $Now = Get-Date $Date1 = $Now.AddDays(-$MaxPwdAge) # Determine the password last changed date such the password # will expire $intDays in the future. $Date2 = $Now.AddDays($intDays - $MaxPwdAge) # Convert from PowerShell ticks to Active Directory ticks. $64Bit1 = $Date1.Ticks - 504911232000000000 $64Bit2 = $Date2.Ticks - 504911232000000000 $Searcher = New-Object System.DirectoryServices.DirectorySearcher $Searcher.PageSize = 200 $Searcher.SearchScope = "subtree" # Filter on user objects where the password expires between the # dates specified, the account is not disabled, password never # expires is not set, password not required is not set. # and password cannot change is not set. $Searcher.Filter = "(&(objectCategory=person)(objectClass=user)" ` + "(pwdLastSet>=" + $($64Bit1) + ")" ` + "(pwdLastSet<=" + $($64Bit2) + ")" ` + "(!userAccountControl:1.2.840.113556.1.4.803:=2)" ` + "(!userAccountControl:1.2.840.113556.1.4.803:=65536)" ` + "(!userAccountControl:1.2.840.113556.1.4.803:=32)" ` + "(!userAccountControl:1.2.840.113556.1.4.803:=48))" $Searcher.PropertiesToLoad.Add("sAMAccountName") > $Null $Searcher.PropertiesToLoad.Add("pwdLastSet") > $Null $Searcher.PropertiesToLoad.Add("mail") > $Null $Searcher.PropertiesToLoad.Add("proxyAddresses") > $Null $Searcher.SearchRoot = "LDAP://" + $Domain.distinguishedName $Results = $Searcher.FindAll() ForEach ($Result In $Results) { $Name = $Result.Properties.Item("sAMAccountName") $PLS = $Result.Properties.Item("pwdLastSet") $Mail = $Result.Properties.Item("mail") $Addresses = $Result.Properties.Item("proxyAddresses") If ($PLS.Count -eq 0) { $Date = [DateTime]0 } Else { # Interpret 64-bit integer as a date. $Date = [DateTime]$PLS.Item(0) } # Convert from .NET ticks to Active Directory Integer8 ticks. # Also, convert from UTC to local time. $PwdLastSet = $Date.AddYears(1600).ToLocalTime() # Determine when password expires. $PwdExpires = $PwdLastSet.AddDays($MaxPwdAge) # Format date to dd/mm/yyyy $PwdWarning = ‘{0:dd/MM/yyyy}’ -f $PwdExpires # Determine email address. If ("$Mail" -eq "") { ForEach ($Address In $Addresses) { $Prefix = $Address.SubString(0, 5) If (($Prefix -ceq "SMTP:") -or ($Prefix -ceq "X400:")) { $Mail = $Address.SubString(5) Break } } } If ("$Mail" -ne "") { $Notice = "Password for user $Name must be changed by $PwdWarning" SendEmail $Mail $Notice "Email sent to $Name ($Mail), password expires $PwdWarning" } Else { "$Name has no email, but password expires $PwdWarning" "DN: $DN" } }
Come già visto in precedenza, editare la GPO creata in precedenza e posizionarsi sull’opzione User Configuration > Policies > Windows Settings > Scripts (Logon/Logoff) e cliccare con il tasto destro del mouse su Logon > Properties. Selezionare la sezione PowerShell Scripts e cliccare sul bottone Add. Tramite Browse specificare, in questo caso, lo script .ps1 da utilizzare.
Selezionare lo script .ps1 e cliccare su Open.
Per specificare eventualmente una sequenza di esecuzione degli script, configurare l’opzione “For this GPO, run scripts in the following order”. Cliccare su OK per salvare le impostazioni.
Effettuando il logon alla rete, oltre alla visualizzazione del popup configurato inizialmente, l’utente riceverà nella propria casella di posta una email di notifica.
La scelta di notificare la scadenza della password tramite popup, tramite email o utilizzando entrambe le soluzioni dipende dalle esigenze o dalle policy aziendali. La notifica potrebbe essere effettuata visualizzando inizialmente il popup e successivamente anche con la email.
Aggiornamento 25/08/2014
Per far funzionare correttamente lo script powershell senza avere il problema delle email generate per ogni utente che effettua il login, eseguire lo script giornalmente tramite il Task Scheduler di Windows rimuovendo lo script dalla GPO. Creare un nuovo task nel Task Scheduler ed imposta i parametri in questo modo:
- Action: Start a program
- Program/Script: powershell.exe
- Add arguments (optional): -f “C:\Scripts\psw_warning_email.ps1?
Configurare l’esecuzione specificando le seguenti opzioni:
- Use the following user account: a Domain Admin account
- Run whether user is logged on or not
- Run with highest privileges
Angus Young! !!!
Semplicemente mitico!
Grazie x queste chicche.
Ciao
Ciao Paolo,
una domanda: avendo un rete mista tra windows 7 e Windows Xp, posso applicare comunque la policy senza creare problemi?
Cioè, quelli con Xp che impatto avranno con questo script di Logon?
Grazie
Lo script è indipendente dalla versione di Windows utilizzata perchè agisce a livello di Active Directory. Funziona sia con XP che con Windows 7.
Ciao ho fatto un test con lo script vbs per il popup ma a me da errore se lanciato:
Windows Script Host
Script: percorso di rete
Riga: 44
Carattere: 71
Errore: Errore di sintassi
Codice: 800A033EA
Origine: Errore di compilazione di Microsoft VBScript
😐 dove sbaglio?
Grazie bellissimo blog
Verifica la sintassi perchè basta un ' in meno che lo script non funziona.
La riga 44 completa è:
Msgbox "La password scade tra " & daysLeft & " giorni" & " il " &
whenPasswordExpires &chr(13) & chr(13) & "Premi CTRL + ALT + CANC
e seleziona l'opzione 'Cambia password'.", 0, "AVVISO SCADENZA PASSWORD"
E' un' istruzione unica quindi deve stare su una sola riga.
Ho controllato lo script utilizzato in produzione con quanto riportato nell'articolo ed è corretto.
Grazie Paolo per la risposta.
Anche secondo me lo script è giusto perchè l'errore cambia ogni volta. ora da riga 9 carettere 17 il resto uguale.
non risco a capire se manca qualcosa nella mia configurazione di AD o altro 😐
metti l'istruzione
Msgbox “La password scade tra ” & daysLeft & ” giorni” & ” il ” & whenPasswordExpires & chr(13) & chr(13) & “Premi CTRL + ALT + CANC
e seleziona l’opzione ‘Cambia password’.”,0, “AVVISO SCADENZA PASSWORD”
su una sola riga!
Grazie per la segnalazione.
Ho aggiunto i caratteri di concatenazione per rendere lo script funzionale anche facendo copy & paste.
Allora correggi anche la riga
Dim warningDays
warningDays = 14Set LoginInfo = CreateObject("ADSystemInfo")
in :
Dim warningDays
warningDays = 14
Set LoginInfo = CreateObject("ADSystemInfo")
Complimenti per l'articolo e per tutto il blog!
Solo una domanda, come si fa ad escludere una OU?
Dipende da come viene applicata la GPO. Si può fare in due modi:
1) Attivi la funzione "Block Inheritance" se l'OU interessata è sotto l'OU a cui hai applicato la GPO (però blocca tutte le GPO) oppure semplicemente disattivi il link alla GPO stessa nel caso sia applicata allo stesso livello.
es. 1
OU (GPO script)
--> OU - Block Inheritance
es. 2
OU1 (GPO script)
OU2 (GPO script) --> Disable link
2) Assegni la GPO ad un gruppo AD dedicato e solo gli appartenenti a quel gruppo saranno soggetti alla GPO.
lo script powershell che manda l'email, la manda a tutti gli utenti con password in scadenza. ogni utente che fa login genera un'email indirizzata a tutti quelli nella medesima situazione. se ci sono 10 utenti con password in scadenza che fanno login vengono mandate 10x10=100 email al giorno (10 a testa). Occorre una modifica allo script per generare email solo per l'utente corrente.