Sommaire
ToggleDans le cadre d’un projet chez un client, j’ai dû réfléchir à un process automatisé pour avertir les utilisateurs que leur mot de passe allait arriver à expiration selon un workflow défini.
Le but étant de s’affranchir de la « popup » Windows qui est éphémère, peu visible, aléatoire et faiblement impactante au regard de l’utilisateur.
Le mail personnalisé s’adresse à l’utilisateur, indique le nombre de jour restant avant expiration de son mot de passe, indique une procédure de modification (sur site, avec vpn, à distance, etc)
Le workflow
Objectif : Avertir l’utilisateur avec un mail/template personnalisé que son mot de passe va arriver à expiration
- Premier mail > 15 jours avant la date d’expiration
- Second mail > 10 jours avant la date d’expiration
- Troisième mail > 5 jours avant la date d’expiration
- En dessous de 5 jours, un mail tous les jours jusqu’à la date d’expiration
Le template HTML
Voici un extrait du body HTML, Les variables de personnalisation HTML sont les suivantes :
- INSERT_GIVENNAME
- INSERT_USERPRINCIPALNAME
- INSERT_PWDDATE
Ces variables vont être peuplés par le script powershell pour personnaliser le mail.
Le fichier HTML se situe dans un dossier TEMPLATE à la racine du script.
Exemple de construction du fichier HTML
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<body>
<span style='font-family:"Calibri",sans-serif;text-decoration:none;text-underline:none'>
<span style='color:#002060'>Bonjour INSERT_GIVENNAME, </span></p><br>
<span style='color:#002060'>Le mot de passe de ton compte INSERT_USERPRINCIPALNAME expire dans INSERT_PWDDATE jours. </span></p><br>
<span style='color:#002060'>Tu trouveras ci-dessous la procédure à suivre : </span></p><br>
</body>
</html>
Attention : Dans ce template, il est possible de rajouter des images au format PNG/JPG. Je recommande l’utilisation de Base64 pour insérer l’image directement dans ce template HTML.
Le script PowerShell
Plusieurs étapes dans le script :
- Paramètre SENDMAIL > Yes ou No
- Va permettre d’exécuter le script dans un environnement de recette/Prod
- Des boucles whatif sont insérer si le paramètre est sur NO
- Paramètre, variable et création d’une fonction « Mail »
- Obtention d’un groupe d’utilisateur dont une PSO est appliqué dessus > Permet de cibler sur quel groupe utilisateur le workflow d’avertissement de mot de passe va s’appliquer.
- Le script n’envoie pas de mail dans les conditions suivantes :
- Le compte utilisateur est désactivé
- Le compte utilisateur est arrivé à expirationLe mot de passe utilisateur est configuré sur « n’expire jamais »Le mot de passe utilisateur doit être changé à la prochaine ouverture de session
- Le compte utilisateur n’a pas d’adresse mail au format [email protected]
- Insertion des valeurs HTML personnalisé dans le body HTML
- Envoi du mail personnalisé.
#############################################################################
#
# Objet : Script de notification d'expiration de PWD par mail automatique
# Auteur : Hervé CZUBAK
# version : 1.0
#
#############################################################################
# Definition des variables
#le paramètre sendmail attend une réponse de type YES ou NO. Sert surtout pour les environnements d’automatisation de recette/prod comme un whatif#
param (
[Parameter(Mandatory)]
[Alias("MAIL")]
[string]$sendmail
)
#Module AD
import-module ActiveDirectory
#ScriptRoot
$RootFolder = Split-Path -Path $MyInvocation.MyCommand.Path
#Serveur SMTP
$SMTPhost = "monserveursmtp.metsys.test"
#port
$port = "587"
#Expediteur du mail
$FromEmail = "Mail d’envoi"
#Email Sujet
$SubjectMail = "Attention, ton mot de passe va bientôt expirer "
#Corps du mail en HTML
$encoding = [System.Text.Encoding]::UTF8
#Credential
#####Le mieux ici c’est d’utiliser un fichier clé crypter mais dans le contexte de mon client ce n’était pas possible. Je ne recommande pas d’insérer un mot de passe dans un script###
$SecPWD = ConvertTo-SecureString "password connecteur" -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential ($FromEmail,$SecPWD)
# Definition Fonction Mail
###La fonction suivante va servir pour appeler la commande d’envoi de mail
Function Mail {
[CmdletBinding(SupportsShouldProcess)]
param (
[Parameter(Mandatory)]
[string]$Usermail,
[string]$Body
)
###ici on rajoute une fonction de whatif à la commande send-mailmessage###
if ($PSCmdlet.ShouldProcess($name)) {
$encoding = [System.Text.Encoding]::UTF8
Send-MailMessage -Encoding $encoding -smtpserver $SMTPHost -Credential $Cred -Port $port -UseSSL -To $Usermail -Subject $SubjectMail -From $FromEmail -Body $Body -BodyAsHtml
} else {}
}
if ($sendmail -eq 'no')
{
# Obtention des utilisateurs dont la stratégie de mot de passe est appliqué (pas de récursivité de groupe > Une boucle par groupe)
foreach ($user in $(Get-ADGroupMember -Identity 'GroupUser' | where objectClass -eq 'user').SamAccountName)
{
$ADUser=Get-ADUser "$user" -Properties msDS-UserPasswordExpiryTimeComputed, mail, givenName, userPrincipalName, PasswordNeverExpires, pwdLastSet, AccountExpirationDate
$error.clear()
try {
$ExpDate=[DateTime]::FromFileTime($ADUser.'msDS-UserPasswordExpiryTimeComputed')
$DiffDays=$(New-TimeSpan -Start $(Get-Date) -End $($ExpDate)).Days
}
catch { "Erreur variable de temps pour $user - Valeur Non traduisible - PWD n'expire pas" }
if (!$error) { }
if ($ADUser.Enabled -eq $false){
Write-Host "Le compte utilisateur $user est désactivé"
}
elseif ($ADUser.AccountExpirationDate -NE $null -AND $ADUser.accountExpiratioNDate -lt (Get-Date)){
Write-Host "le compte utilisateur $user est arrivé à expiration"
}
elseif ($ADUser.PasswordNeverExpires)
{
write-host "le mot de passe de $user est configuré sur 'n'expire jamais' pas d'envoi de mail'"
}
elseif ($ADUser.pwdLastSet -eq 0){
Write-Host "Le mot de passe de $user doit être changé à la prochaine connexion"
}
elseif ($ADUser.mail -eq $null) {
Write-Host "l'utilisateur $user n'a pas d'adresse e-mail"
}
elseif ($ADUser.mail -notlike '*@xxx.fr'){
Write-Host "l'utilisateur $user n'a pas d'adresse au format xxx.fr"
}
elseif ( $DiffDays -eq 15 ) { #Si égal à 15 jours
$html = get-content -Path "$RootFolder\Template\HTML_PWD_.html" -Raw
$html = $html.Replace("INSERT_GIVENNAME",$ADUser.givenName)
$html = $html.Replace("INSERT_USERPRINCIPALNAME",$ADUser.UserPrincipalName)
$html = $html.Replace("INSERT_PWDDATE",$DiffDays)
Mail -Usermail $ADUser.mail -Body $html -WhatIf
Write-Host "$user J-15 Envoi du mail de rappel à" $ADUser.mail "le mot de passe arrive à expiration dans" $DiffDays "jours, le" $(get-date($ExpDate) -Format "dd.MM.yyyy")
}
elseif ( $DiffDays -eq 10 ) { #Si égal à 10 jours
$html = get-content -Path "$RootFolder\Template\HTML_PWD_.html" -Raw
$html = $html.Replace("INSERT_GIVENNAME",$ADUser.givenName)
$html = $html.Replace("INSERT_USERPRINCIPALNAME",$ADUser.UserPrincipalName)
$html = $html.Replace("INSERT_PWDDATE",$DiffDays)
Mail -Usermail $ADUser.mail -Body $html -WhatIf
Write-Host "$user J-10 Envoi du mail de rappel à" $ADUser.mail "le mot de passe arrive à expiration dans" $DiffDays "jours, le" $(get-date($ExpDate) -Format "dd.MM.yyyy")
}
elseif ( $DiffDays -lt 5 ) { #Si moins de 5 jours
$html = get-content -Path "$RootFolder\Template\HTML_PWD_.html" -Raw
$html = $html.Replace("INSERT_GIVENNAME",$ADUser.givenName)
$html = $html.Replace("INSERT_USERPRINCIPALNAME",$ADUser.UserPrincipalName)
$html = $html.Replace("INSERT_PWDDATE",$DiffDays)
Mail -Usermail $ADUser.mail -Body $html -WhatIf
Write-Host "$user J-5 Envoi du mail de rappel à" $ADUser.mail "le mot de passe arrive à expiration dans" $DiffDays "jours, le" $(get-date($ExpDate) -Format "dd.MM.yyyy")
}
else {write-host "pas de mail de rappel pour $user"}
}
}
else{
if ($sendmail -eq 'yes')
{
# Obtention des utilisateurs dont la stratégie de mot de passe est appliqué (pas de récursivité de groupe > Une boucle par groupe) / Si groupe différent, recopier la boucle.
foreach ($user in $(Get-ADGroupMember -Identity 'GroupUser' | where objectClass -eq 'user').SamAccountName)
{
$ADUser=Get-ADUser "$user" -Properties msDS-UserPasswordExpiryTimeComputed, mail, givenName, userPrincipalName, PasswordNeverExpires, pwdLastSet, AccountExpirationDate
$error.clear()
try {
$ExpDate=[DateTime]::FromFileTime($ADUser.'msDS-UserPasswordExpiryTimeComputed')
$DiffDays=$(New-TimeSpan -Start $(Get-Date) -End $($ExpDate)).Days
}
catch { "Erreur variable de temps pour $user - Valeur Non traduisible - PWD n'expire pas" }
if (!$error) { }
if ($ADUser.Enabled -eq $false){
Write-Host "Le compte utilisateur $user est désactivé"
}
elseif ($ADUser.AccountExpirationDate -NE $null -AND $ADUser.accountExpiratioNDate -lt (Get-Date)){
Write-Host "le compte utilisateur $user est arrivé à expiration"
}
elseif ($ADUser.PasswordNeverExpires)
{
write-host "le mot de passe de $user est configuré sur 'n'expire jamais' pas d'envoi de mail'"
}
elseif ($ADUser.pwdLastSet -eq 0){
Write-Host "Le mot de passe de $user doit être changé à la prochaine connexion"
}
elseif ($ADUser.mail -eq $null) {
Write-Host "l'utilisateur $user n'a pas d'adresse e-mail"
}
elseif ($ADUser.mail -notlike '*@xxx.fr'){
Write-Host "l'utilisateur $user n'a pas d'adresse au format xxx.fr"
}
elseif ( $DiffDays -eq 15 ) { #Si égal à 15 jours
$html = get-content -Path "$RootFolder\Template\HTML_PWD_.html" -Raw
$html = $html.Replace("INSERT_GIVENNAME",$ADUser.givenName)
$html = $html.Replace("INSERT_USERPRINCIPALNAME",$ADUser.UserPrincipalName)
$html = $html.Replace("INSERT_PWDDATE",$DiffDays)
Mail -Usermail $ADUser.mail -Body $html
Write-Host "$user J-15 Envoi du mail de rappel à" $ADUser.mail "le mot de passe arrive à expiration dans" $DiffDays "jours, le" $(get-date($ExpDate) -Format "dd.MM.yyyy")
}
elseif ( $DiffDays -eq 10 ) { #Si égal à 10 jours
$html = get-content -Path "$RootFolder\Template\HTML_PWD_.html" -Raw
$html = $html.Replace("INSERT_GIVENNAME",$ADUser.givenName)
$html = $html.Replace("INSERT_USERPRINCIPALNAME",$ADUser.UserPrincipalName)
$html = $html.Replace("INSERT_PWDDATE",$DiffDays)
Mail -Usermail $ADUser.mail -Body $html
Write-Host "$user J-10 Envoi du mail de rappel à" $ADUser.mail "le mot de passe arrive à expiration dans" $DiffDays "jours, le" $(get-date($ExpDate) -Format "dd.MM.yyyy")
}
elseif ( $DiffDays -lt 5 ) { #Si moins de 5 jours
$html = get-content -Path "$RootFolder\Template\HTML_PWD_.html" -Raw
$html = $html.Replace("INSERT_GIVENNAME",$ADUser.givenName)
$html = $html.Replace("INSERT_USERPRINCIPALNAME",$ADUser.UserPrincipalName)
$html = $html.Replace("INSERT_PWDDATE",$DiffDays)
Mail -Usermail $ADUser.mail -Body $html
Write-Host "$user J-5 Envoi du mail de rappel à" $ADUser.mail "le mot de passe arrive à expiration dans" $DiffDays "jours, le" $(get-date($ExpDate) -Format "dd.MM.yyyy")
}
else {write-host "pas de mail de rappel pour $user"}
}
}
}
La planification du script
Utilisation d’un ordonnanceur ou de l’outil de tâche planifié de Windows Server.
Je recommande l’utilisation d’un compte de service managé de type GMSA pour la construction de la tâche planifié. Le compte devra avoir les droits de requêtes sur l’annuaire LDAP.
Les paramètres à configurer peuvent être les suivants :
- Déclencheur :
- 1x tous les jours à 22h
- Action :
- Programme/script : C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
- Argument : -Command « & ‘C:\Scripts\monscript.ps1’ -Sendmail YES »
Lors de l’exécution de la tâche, la commande suivante sera donc exécutée :
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -Command "& 'C:\Scripts\Get-HyperVReport.ps1' -Sendmail YES”