From 469e6f7aa435460be5bf00ea40b087c2be4eeb48 Mon Sep 17 00:00:00 2001 From: P6g9YHK6 <17877371+P6g9YHK6@users.noreply.github.com> Date: Fri, 6 Jun 2025 08:44:38 +0200 Subject: [PATCH 01/23] Update file: Mail notification password expiry.ps1 --- .../Mail notification password expiry.ps1 | 967 +++++++++--------- 1 file changed, 497 insertions(+), 470 deletions(-) diff --git a/scripts_staging/Backend/Mail notification password expiry.ps1 b/scripts_staging/Backend/Mail notification password expiry.ps1 index ba4227fd..94d8c374 100644 --- a/scripts_staging/Backend/Mail notification password expiry.ps1 +++ b/scripts_staging/Backend/Mail notification password expiry.ps1 @@ -1,482 +1,509 @@ -<# +<# .SYNOPSIS - Script permettant de récupérer et analyser les utilisateurs dont le mot de passe est sur le point d'expirer dans Active Directory. + Ensures the script is executed using PowerShell 7 or higher. + .DESCRIPTION - Ce script se connecte à Active Directory pour rechercher les utilisateurs dans une unité organisationnelle spécifiée. - Il récupère la date de dernière mise à jour du mot de passe pour chaque utilisateur et la compare à la politique de mot de passe du domaine. - En fonction du nombre de jours restant avant l'expiration, les comptes sont classés en trois catégories : - - Expiré : Le mot de passe a déjà expiré. - - Critique : Le mot de passe est très proche de l'expiration, selon le seuil critique configuré. - - Avertissement : Le mot de passe approche de l'expiration, selon le seuil d'avertissement configuré. - Le script génère un rapport HTML contenant : - • Les détails de la politique de mot de passe du domaine (durée maximale, durée minimale, longueur minimale, complexité, historique et seuils de verrouillage). - • Un résumé statistique indiquant le nombre d'utilisateurs par catégorie. - • Une liste détaillée des comptes répartis par catégorie. - Les options de test ont été supprimées. -.PARAMETER TargetOU - Spécifie l'OU dans laquelle rechercher les utilisateurs. Exemple : "OU=Utilisateurs,DC=domaine,DC=local". -.PARAMETER WarningThreshold - Nombre de jours avant expiration déclenchant un avertissement (par défaut : 15). -.PARAMETER CriticalThreshold - Nombre de jours avant expiration déclenchant une alerte critique (par défaut : 7). -.PARAMETER IncludeDisabled - Indique si les comptes désactivés doivent être inclus dans le rapport (false par défaut). -.PARAMETER IncludeNeverExpires - Indique si les comptes dont le mot de passe n'expire jamais doivent être inclus dans le rapport (false par défaut). -.EXAMPLE - .\Check-PasswordExpiration.ps1 -TargetOU "OU=Utilisateurs,DC=domaine,DC=local" - Exécute le script avec l'OU spécifiée et les seuils par défaut. -.EXAMPLE - .\Check-PasswordExpiration.ps1 -TargetOU "OU=Utilisateurs,DC=domaine,DC=local" -WarningThreshold 20 -CriticalThreshold 10 - Exécute le script avec des seuils personnalisés pour les alertes d’avertissement et critiques. + This script verifies whether it is running in a PowerShell 7+ environment. + If not, and if PowerShell 7 (pwsh) is available on the system, it re-invokes itself using pwsh, passing along any parameters. + If pwsh is not found, the script outputs a message and exits with an error code. + Once running in PowerShell 7 or higher, it sets the output rendering mode to plaintext for consistent formatting. + .NOTES - Author: Peter Quellennec - Date: 27/05/25 + Author: PQU + Date: 29/04/2025 #public -#> -{{CallPowerShell7Lite}} - -$TargetOU = $env:TARGET_OU -$SmtpServer = $env:SMTP_SERVER -$SmtpPort = [int]$env:SMTP_PORT -$AdminEmail = $env:ADMIN_EMAIL -$FromEmail = $env:FROM_EMAIL -$signmail = $env:SIGNMAIL -$WarningThreshold = [int]$env:WARNING_THRESHOLD -$CriticalThreshold = [int]$env:CRITICAL_THRESHOLD -$EmailSignature = $env:EMAIL_SIGNATURE - -function Convert-ToBoolean($value) { - return $value -match '^(1|true|yes)$' -} - -$IncludeDisabled = Convert-ToBoolean $env:INCLUDE_DISABLED -$IncludeNeverExpires = Convert-ToBoolean $env:INCLUDE_NEVER_EXPIRES -$GenerateReportOnly = Convert-ToBoolean $env:GENERATE_REPORT_ONLY - -if ($env:SMTP_CREDENTIAL_USERNAME -and $env:SMTP_CREDENTIAL_PASSWORD) { - try { - $SecurePassword = ConvertTo-SecureString $env:SMTP_CREDENTIAL_PASSWORD -AsPlainText -Force - $SmtpCredential = New-Object System.Management.Automation.PSCredential ($env:SMTP_CREDENTIAL_USERNAME, $SecurePassword) - } catch { - Write-Error "Failed to create SMTP credentials: $_" - } -} - -function Test-Prerequisites { - - $adFeature = Get-WindowsFeature -Name AD-Domain-Services -ErrorAction Stop - if ($adFeature.InstallState -ne 'Installed') { - Write-Error "AD Domain Services ne sont pas installés. Arrêt du script." - exit 1 - } - - if (-not $SmtpServer -or -not $SmtpPort) { - Write-Error "Les variables `$SmtpServer et `$SmtpPort doivent être définies avant d'appeler cette fonction." - exit 1 - } - - if (-not (Get-Module -ListAvailable -Name ActiveDirectory)) { - Write-Error "Module ActiveDirectory non trouvé. Arrêt du script." - exit 1 - } - - Import-Module ActiveDirectory -ErrorAction Stop - - try { - $dc = Get-ADDomainController -Discover -ErrorAction Stop - Write-Host "Connexion réussie au contrôleur de domaine : $($dc.HostName)" - } - catch { - Write-Error "Impossible de se connecter au contrôleur de domaine. Arrêt du script." - exit 1 - } - - try { - $tcpClient = New-Object System.Net.Sockets.TcpClient - $tcpClient.Connect($SmtpServer, $SmtpPort) - $tcpClient.Close() - Write-Host "Connexion réussie au serveur SMTP : $SmtpServer":"$SmtpPort" - } - catch { - Write-Error "Impossible de se connecter au serveur SMTP : $SmtpServer sur le port $SmtpPort. Arrêt du script." - exit 1 - } -} -function Get-UserPasswordExpirationInfo { - param ( - $user, - $maxPasswordAge - ) - - $result = [PSCustomObject]@{ - Name = $user.Name - SamAccountName = $user.SamAccountName - Email = $user.EmailAddress - ExpirationDate = $null - DaysLeft = $null - Status = "OK" - Enabled = $user.Enabled - PasswordNeverExpires = $user.PasswordNeverExpires - } - - if ($user.PasswordLastSet -eq $null) { - $result.Status = "NeverLoggedIn" - return $result - } - - if ($user.PasswordNeverExpires) { - $result.Status = "NeverExpires" - return $result - } - - $passwordExpirationDate = $user.PasswordLastSet + $maxPasswordAge - $daysLeft = ($passwordExpirationDate - (Get-Date)).Days - - $result.ExpirationDate = $passwordExpirationDate - $result.DaysLeft = $daysLeft - - if ($daysLeft -lt 0) { - $result.Status = "Expired" - } - elseif ($daysLeft -le $CriticalThreshold) { - $result.Status = "Critical" - } - elseif ($daysLeft -le $WarningThreshold) { - $result.Status = "Warning" - } - - return $result -} +.CHANGELOG + 22.05.25 SAN Added UTF8 to fix encoding issue with russian & french chars + 06.06.25 PQU Added support for multiple admin emails +#> -function ConvertTo-HtmlReport { - param ( - $expiredUsers, - $criticalUsers, - $warningUsers, - $neverExpiresUsers, - $neverLoggedInUsers, - $disabledUsers, - $targetOU, - $passwordPolicy, - $warningThreshold, - $criticalThreshold - ) - $html = @" - - - - Rapport d'expiration des mots de passe - - - -

Rapport d'expiration des mots de passe

- -
-

Politique de mot de passe du domaine

-

Durée maximale du mot de passe: $($passwordPolicy.MaxPasswordAge.Days) jours

-

Durée minimale du mot de passe: $($passwordPolicy.MinPasswordAge.Days) jours

-

Longueur minimale: $($passwordPolicy.MinPasswordLength) caractères

-

Complexité requise: $($passwordPolicy.ComplexityEnabled)

-

Historique du mot de passe: $($passwordPolicy.PasswordHistoryCount) mots de passe

-

Verrouillage de compte: $($passwordPolicy.LockoutThreshold) tentatives (durée: $($passwordPolicy.LockoutDuration.Minutes) minutes, observation: $($passwordPolicy.LockoutObservationWindow.Minutes) minutes)

-
- -
-

Seuil d'avertissement : $warningThreshold jours

-

Seuil critique : $criticalThreshold jours

-

Statistiques : - Expirés: $($expiredUsers.Count) - Critiques: $($criticalUsers.Count) - Avertissement: $($warningUsers.Count) - Expirent jamais: $($neverExpiresUsers.Count) - Jamais connectés: $($neverLoggedInUsers.Count) - Désactivés: $($disabledUsers.Count) -

-
+if (!($PSVersionTable.PSVersion.Major -ge 7)) { + if (Get-Command pwsh -ErrorAction SilentlyContinue) { + pwsh -File "`"$PSCommandPath`"" @PSBoundParameters + exit $LASTEXITCODE + } else { + Write-Output "ERROR: PowerShell 7 is not available. Exiting." + exit 1 + } + } + [Console]::OutputEncoding = [Text.Encoding]::UTF8 + $PSStyle.OutputRendering = "plaintext" + + + $TargetOU = $env:TARGET_OU + $SmtpServer = $env:SMTP_SERVER + $SmtpPort = [int]$env:SMTP_PORT + $AdminEmails = $env:ADMIN_EMAIL -split '[,;]' | ForEach-Object { $_.Trim() } | Where-Object { $_ } + $FromEmail = $env:FROM_EMAIL + $WarningThreshold = [int]$env:WARNING_THRESHOLD + $CriticalThreshold = [int]$env:CRITICAL_THRESHOLD + $EmailSignature = $env:EMAIL_SIGNATURE + + function Convert-ToBoolean($value) { + return $value -match '^(1|true|yes)$' + } + + $IncludeDisabled = Convert-ToBoolean $env:INCLUDE_DISABLED + $IncludeNeverExpires = Convert-ToBoolean $env:INCLUDE_NEVER_EXPIRES + $GenerateReportOnly = Convert-ToBoolean $env:GENERATE_REPORT_ONLY + + if ($env:SMTP_CREDENTIAL_USERNAME -and $env:SMTP_CREDENTIAL_PASSWORD) { + try { + $SecurePassword = ConvertTo-SecureString $env:SMTP_CREDENTIAL_PASSWORD -AsPlainText -Force + $SmtpCredential = New-Object System.Management.Automation.PSCredential ($env:SMTP_CREDENTIAL_USERNAME, $SecurePassword) + } catch { + Write-Error "Failed to create SMTP credentials: $_" + } + } + + function Test-Prerequisites { + + $adFeature = Get-WindowsFeature -Name AD-Domain-Services -ErrorAction Stop + if ($adFeature.InstallState -ne 'Installed') { + Write-Error "AD Domain Services ne sont pas installés. Arrêt du script." + exit 1 + } + + if (-not $SmtpServer -or -not $SmtpPort) { + Write-Error "Les variables `$SmtpServer et `$SmtpPort doivent être définies avant d'appeler cette fonction." + exit 1 + } + + if (-not (Get-Module -ListAvailable -Name ActiveDirectory)) { + Write-Error "Module ActiveDirectory non trouvé. Arrêt du script." + exit 1 + } + + Import-Module ActiveDirectory -ErrorAction Stop + + try { + $dc = Get-ADDomainController -Discover -ErrorAction Stop + Write-Host "Connexion réussie au contrôleur de domaine : $($dc.HostName)" + } + catch { + Write-Error "Impossible de se connecter au contrôleur de domaine. Arrêt du script." + exit 1 + } + + try { + $tcpClient = New-Object System.Net.Sockets.TcpClient + $tcpClient.Connect($SmtpServer, $SmtpPort) + $tcpClient.Close() + Write-Host "Connexion réussie au serveur SMTP : $SmtpServer":"$SmtpPort" + } + catch { + Write-Error "Impossible de se connecter au serveur SMTP : $SmtpServer sur le port $SmtpPort. Arrêt du script." + exit 1 + } + } + + function Get-UserPasswordExpirationInfo { + param ( + $user, + $maxPasswordAge + ) + + $result = [PSCustomObject]@{ + Name = $user.Name + SamAccountName = $user.SamAccountName + Email = $user.EmailAddress + ExpirationDate = $null + DaysLeft = $null + Status = "OK" + Enabled = $user.Enabled + PasswordNeverExpires = $user.PasswordNeverExpires + } + + if ($user.PasswordLastSet -eq $null) { + $result.Status = "NeverLoggedIn" + return $result + } + + if ($user.PasswordNeverExpires) { + $result.Status = "NeverExpires" + return $result + } + + $passwordExpirationDate = $user.PasswordLastSet + $maxPasswordAge + $daysLeft = ($passwordExpirationDate - (Get-Date)).Days + + $result.ExpirationDate = $passwordExpirationDate + $result.DaysLeft = $daysLeft + + if ($daysLeft -lt 0) { + $result.Status = "Expired" + } + elseif ($daysLeft -le $CriticalThreshold) { + $result.Status = "Critical" + } + elseif ($daysLeft -le $WarningThreshold) { + $result.Status = "Warning" + } + + return $result + } + + function ConvertTo-HtmlReport { + param ( + $expiredUsers, + $criticalUsers, + $warningUsers, + $neverExpiresUsers, + $neverLoggedInUsers, + $disabledUsers, + $targetOU, + $passwordPolicy, + $warningThreshold, + $criticalThreshold + ) + + $html = @" + + + + Rapport d'expiration des mots de passe + + + +

Rapport d'expiration des mots de passe

+ +
+

Politique de mot de passe du domaine

+

Durée maximale du mot de passe: $($passwordPolicy.MaxPasswordAge.Days) jours

+

Durée minimale du mot de passe: $($passwordPolicy.MinPasswordAge.Days) jours

+

Longueur minimale: $($passwordPolicy.MinPasswordLength) caractères

+

Complexité requise: $($passwordPolicy.ComplexityEnabled)

+

Historique du mot de passe: $($passwordPolicy.PasswordHistoryCount) mots de passe

+

Verrouillage de compte: $($passwordPolicy.LockoutThreshold) tentatives (durée: $($passwordPolicy.LockoutDuration.Minutes) minutes, observation: $($passwordPolicy.LockoutObservationWindow.Minutes) minutes)

+
+ +
+

Seuil d'avertissement : $warningThreshold jours

+

Seuil critique : $criticalThreshold jours

+

Statistiques : + Expirés: $($expiredUsers.Count) + Critiques: $($criticalUsers.Count) + Avertissement: $($warningUsers.Count) + Expirent jamais: $($neverExpiresUsers.Count) + Jamais connectés: $($neverLoggedInUsers.Count) + Désactivés: $($disabledUsers.Count) +

+
+ "@ + + if ($expiredUsers) { + $html += "

Comptes expirés $($expiredUsers.Count)

" + $html += $expiredUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={$_.ExpirationDate.ToString("dd/MM/yyyy")}}, DaysLeft, Enabled | ConvertTo-Html -Fragment + } + + if ($criticalUsers) { + $html += "

Comptes critiques $($criticalUsers.Count)

" + $html += $criticalUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={$_.ExpirationDate.ToString("dd/MM/yyyy")}}, DaysLeft, Enabled | ConvertTo-Html -Fragment + } + + if ($warningUsers) { + $html += "

Comptes en avertissement $($warningUsers.Count)

" + $html += $warningUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={$_.ExpirationDate.ToString("dd/MM/yyyy")}}, DaysLeft, Enabled | ConvertTo-Html -Fragment + } + + if ($IncludeNeverExpires -and $neverExpiresUsers) { + $html += "

Comptes avec mot de passe n expirant jamais $($neverExpiresUsers.Count)

" + $html += $neverExpiresUsers | Select-Object Name, SamAccountName, Email, Enabled | ConvertTo-Html -Fragment + } + + if ($neverLoggedInUsers) { + $html += "

Comptes jamais connectés $($neverLoggedInUsers.Count)

" + $html += $neverLoggedInUsers | Select-Object Name, SamAccountName, Email, Enabled | ConvertTo-Html -Fragment + } + + if ($IncludeDisabled -and $disabledUsers) { + $html += "

Comptes désactivés $($disabledUsers.Count)

" + $html += $disabledUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={if($_.ExpirationDate){$_.ExpirationDate.ToString("dd/MM/yyyy")}else{"N/A"}}}, DaysLeft | ConvertTo-Html -Fragment + } + + $html += @" +

Généré le : $(Get-Date -Format "dd/MM/yyyy HH:mm")

+ + "@ - - if ($expiredUsers) { - $html += "

Comptes expirés $($expiredUsers.Count)

" - $html += $expiredUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={$_.ExpirationDate.ToString("dd/MM/yyyy")}}, DaysLeft, Enabled | ConvertTo-Html -Fragment - } - - if ($criticalUsers) { - $html += "

Comptes critiques $($criticalUsers.Count)

" - $html += $criticalUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={$_.ExpirationDate.ToString("dd/MM/yyyy")}}, DaysLeft, Enabled | ConvertTo-Html -Fragment - } - - if ($warningUsers) { - $html += "

Comptes en avertissement $($warningUsers.Count)

" - $html += $warningUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={$_.ExpirationDate.ToString("dd/MM/yyyy")}}, DaysLeft, Enabled | ConvertTo-Html -Fragment - } - - if ($IncludeNeverExpires -and $neverExpiresUsers) { - $html += "

Comptes avec mot de passe n expirant jamais $($neverExpiresUsers.Count)

" - $html += $neverExpiresUsers | Select-Object Name, SamAccountName, Email, Enabled | ConvertTo-Html -Fragment - } - - if ($neverLoggedInUsers) { - $html += "

Comptes jamais connectés $($neverLoggedInUsers.Count)

" - $html += $neverLoggedInUsers | Select-Object Name, SamAccountName, Email, Enabled | ConvertTo-Html -Fragment - } - - if ($IncludeDisabled -and $disabledUsers) { - $html += "

Comptes désactivés $($disabledUsers.Count)

" - $html += $disabledUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={if($_.ExpirationDate){$_.ExpirationDate.ToString("dd/MM/yyyy")}else{"N/A"}}}, DaysLeft | ConvertTo-Html -Fragment - } - - $html += @" -

Généré le : $(Get-Date -Format "dd/MM/yyyy HH:mm")

- - + + return $html + } + + function Get-EmailSignature { + if ($EmailSignature) { + return "
$EmailSignature
" + } + + return @" +
+

+ Service Informatique
+ Téléphone : +33 (0)1 XX XX XX XX
+ Email : support@domain.com
+ Ce message est généré automatiquement, merci de ne pas y répondre directement. +

+
"@ - - return $html -} -function Get-EmailSignature { - if ($EmailSignature) { - return $EmailSignature - } - - return @" -
-

- Service Informatique
- Téléphone : +33 (0)1 XX XX XX XX
- Email : support@domain.com
- Ce message est généré automatiquement, merci de ne pas y répondre directement. -

-
+ } + + function Send-EmailReport { + param( + [string[]]$Recipients, + [string]$Subject, + [string]$Body, + [string]$SmtpServer, + [int]$Port = 25, + [string]$FromAddress, + [string[]]$Attachments + ) + + if ((Get-Date).DayOfWeek -ne 'Monday') { + Write-Host "Les emails ne sont envoyés que le lundi. Arrêt de l'envoi." + return + } + + $signature = Get-EmailSignature + $bodyWithSignature = $Body + if ($Body -match '(?i)') { + $bodyWithSignature = $Body -replace '(?i)', "$signature" + } else { + $bodyWithSignature = "$Body$signature" + } + + $mailMessage = New-Object System.Net.Mail.MailMessage + $mailMessage.From = $FromAddress + foreach ($recipient in $Recipients) { $mailMessage.To.Add($recipient) } + $mailMessage.Subject = $Subject + $mailMessage.Body = $bodyWithSignature + $mailMessage.IsBodyHtml = $true + if ($Attachments) { + foreach ($att in $Attachments) { + $mailMessage.Attachments.Add((New-Object System.Net.Mail.Attachment($att))) + } + } + $smtpClient = New-Object System.Net.Mail.SmtpClient($SmtpServer, $Port) + if ($SmtpCredential) { + $smtpClient.Credentials = $SmtpCredential + } + try { + $smtpClient.Send($mailMessage) + Write-Host "Email sent successfully." + } + catch { + Write-Error "Failed to send email: $_" + } + } + + function Send-UserNotification { + param( + [string]$Recipient, + [string]$Subject, + [string]$Body, + [string]$SmtpServer, + [int]$Port = 25, + [string]$FromAddress + ) + + $signature = Get-EmailSignature + $bodyWithSignature = $Body + if ($Body -match '(?i)') { + $bodyWithSignature = $Body -replace '(?i)', "$signature" + } else { + $bodyWithSignature = @" + + + + + + + $Body + $signature + + "@ -} - -function Send-EmailReport { - param( - [string[]]$Recipients, - [string]$Subject, - [string]$Body, - [string]$SmtpServer, - [int]$Port = 25, - [string]$FromAddress, - [string[]]$Attachments - ) - - if ((Get-Date).DayOfWeek -ne 'Monday') { - Write-Host "Les emails ne sont envoyés que le lundi. Arrêt de l'envoi." - return - } - $signature = Get-EmailSignature - $mailMessage = New-Object System.Net.Mail.MailMessage - $mailMessage.From = $FromAddress - foreach ($recipient in $Recipients) { $mailMessage.To.Add($recipient) } - $mailMessage.Subject = $Subject - $mailMessage.Body = $Body - $mailMessage.IsBodyHtml = $true - if ($Attachments) { - foreach ($att in $Attachments) { - $mailMessage.Attachments.Add((New-Object System.Net.Mail.Attachment($att))) - } - } - $smtpClient = New-Object System.Net.Mail.SmtpClient($SmtpServer, $Port) - try { - $smtpClient.Send($mailMessage) - Write-Host "Email sent successfully." - } - catch { - Write-Error "Failed to send email: $_" - } -} - -function Send-UserNotification { - param( - [string]$Recipient, - [string]$Subject, - [string]$Body, - [string]$SmtpServer, - [int]$Port = 25, - [string]$FromAddress - ) - $signature = Get-EmailSignature - if ($Body -match '') { - } else { - $bodyWithSignature = "$Body$signature" - } - $mailMessage = New-Object System.Net.Mail.MailMessage - $mailMessage.From = $FromAddress - $mailMessage.To.Add($Recipient) - $mailMessage.Subject = $Subject - $mailMessage.Body = $Body - $mailMessage.signmail= $signmail - $mailMessage.IsBodyHtml = $true - $smtpClient = New-Object System.Net.Mail.SmtpClient($SmtpServer, $Port) - try { - $smtpClient.Send($mailMessage) - Write-Host "Notification sent to $Recipient." } - catch { - Write-Error "Failed to send notification to ${Recipient}: $_" - } -} - -try { - $passwordPolicy = Get-ADDefaultDomainPasswordPolicy - $maxPasswordAge = $passwordPolicy.MaxPasswordAge - - Write-Host "Politique de mot de passe du domaine:" - Write-Host " - Durée maximale: $($maxPasswordAge.Days) jours" - Write-Host " - Durée minimale: $($passwordPolicy.MinPasswordAge.Days) jours" - Write-Host " - Longueur minimale: $($passwordPolicy.MinPasswordLength) caractères" - Write-Host " - Complexité: $($passwordPolicy.ComplexityEnabled)" -} -catch { - Write-Error "Erreur lors de la récupération de la politique de mot de passe : $_" - exit 1 -} - -try { - $ouExists = Get-ADOrganizationalUnit -Identity $TargetOU -ErrorAction Stop -} -catch { - Write-Error "L'OU spécifiée n'existe pas ou est inaccessible : $TargetOU" - exit 1 -} - -$filter = "PasswordNeverExpires -eq `$false" -if ($IncludeDisabled) { - $filter = "($filter) -or (Enabled -eq `$false)" -} -if ($IncludeNeverExpires) { - $filter = "PasswordNeverExpires -eq `$true -or ($filter)" -} - -try { - Write-Host "Recherche des utilisateurs dans l'OU: $TargetOU" - $users = Get-ADUser -SearchBase $TargetOU -Filter * -Properties Name, SamAccountName, EmailAddress, PasswordLastSet, PasswordNeverExpires, Enabled | Where-Object { - if ($IncludeDisabled -and $IncludeNeverExpires) { $true } - elseif ($IncludeDisabled) { -not $_.PasswordNeverExpires } - elseif ($IncludeNeverExpires) { $_.Enabled } - else { $_.Enabled -and (-not $_.PasswordNeverExpires) } - } - - Write-Host "Nombre d'utilisateurs trouvés: $($users.Count)" -} -catch { - Write-Error "Erreur lors de la récupération des utilisateurs : $_" - exit 1 -} - -if (-not $users) { - Write-Host "Aucun utilisateur trouvé dans l'OU spécifiée avec les critères actuels." - exit -} - -$reportData = foreach ($user in $users) { - if ($user.PasswordNeverExpires -or ($user.PasswordLastSet -eq $null -and -not $IncludeNeverExpires)) { - [PSCustomObject]@{ - Name = $user.Name - SamAccountName = $user.SamAccountName - Email = $user.EmailAddress - ExpirationDate = $null - DaysLeft = $null - Status = if ($user.PasswordNeverExpires) { "NeverExpires" } else { "NeverLoggedIn" } - Enabled = $user.Enabled - PasswordNeverExpires = $user.PasswordNeverExpires - } - } - else { - Get-UserPasswordExpirationInfo -user $user -maxPasswordAge $maxPasswordAge - } -} - -$expiredUsers = $reportData | Where-Object { $_.Status -eq "Expired" } | Sort-Object DaysLeft -$criticalUsers = $reportData | Where-Object { $_.Status -eq "Critical" } | Sort-Object DaysLeft -$warningUsers = $reportData | Where-Object { $_.Status -eq "Warning" } | Sort-Object DaysLeft -$neverExpiresUsers = $reportData | Where-Object { $_.Status -eq "NeverExpires" } -$neverLoggedInUsers = $reportData | Where-Object { $_.Status -eq "NeverLoggedIn" } -$disabledUsers = $reportData | Where-Object { $_.Enabled -eq $false } - -$reportFileName = "PasswordExpirationReport_$(Get-Date -Format 'yyyyMMdd_HHmm').html" -$htmlReport = ConvertTo-HtmlReport -expiredUsers $expiredUsers -criticalUsers $criticalUsers -warningUsers $warningUsers -neverExpiresUsers $neverExpiresUsers -neverLoggedInUsers $neverLoggedInUsers -disabledUsers $disabledUsers -targetOU $TargetOU -passwordPolicy $passwordPolicy -warningThreshold $WarningThreshold -criticalThreshold $CriticalThreshold -$htmlReport | Out-File $reportFileName -Encoding UTF8 - -Write-Host "Rapport généré avec succès : $reportFileName" -Write-Host "Résumé :" -Write-Host " - Comptes expirés: $($expiredUsers.Count)" -Write-Host " - Comptes critiques: $($criticalUsers.Count)" -Write-Host " - Comptes en avertissement: $($warningUsers.Count)" -Write-Host " - Comptes expirant jamais: $($neverExpiresUsers.Count)" -Write-Host " - Comptes jamais connectés: $($neverLoggedInUsers.Count)" -Write-Host " - Comptes désactivés: $($disabledUsers.Count)" - -if ($GenerateReportOnly) { - Write-Host "Option GenerateReportOnly activée, rapport généré uniquement. Arrêt du script." - exit 0 -} - -foreach ($user in $reportData | Where-Object { $_.Status -in @("Warning", "Critical", "Expired") }) { - if ($user.Email) { - $expirationDate = if ($user.ExpirationDate) { $user.ExpirationDate.ToString("dd/MM/yyyy") } else { "N/A" } - $subject = "Avertissement: Expiration de votre mot de passe" - $body = @" - - - - - - - -

Bonjour $($user.Name),

-

Votre mot de passe est dans un état $($user.Status).

-

Date d'expiration: $expirationDate

-

Veuillez mettre à jour votre mot de passe dès que possible pour éviter tout problème d'accès.

- - - + + $mailMessage = New-Object System.Net.Mail.MailMessage + $mailMessage.From = $FromAddress + $mailMessage.To.Add($Recipient) + $mailMessage.Subject = $Subject + $mailMessage.Body = $bodyWithSignature + $mailMessage.IsBodyHtml = $true + + $smtpClient = New-Object System.Net.Mail.SmtpClient($SmtpServer, $Port) + if ($SmtpCredential) { + $smtpClient.Credentials = $SmtpCredential + } + try { + $smtpClient.Send($mailMessage) + Write-Host "Notification sent to $Recipient." + } + catch { + Write-Error "Failed to send notification to ${Recipient}: $_" + } + } + + try { + $passwordPolicy = Get-ADDefaultDomainPasswordPolicy + $maxPasswordAge = $passwordPolicy.MaxPasswordAge + + Write-Host "Politique de mot de passe du domaine:" + Write-Host " - Durée maximale: $($maxPasswordAge.Days) jours" + Write-Host " - Durée minimale: $($passwordPolicy.MinPasswordAge.Days) jours" + Write-Host " - Longueur minimale: $($passwordPolicy.MinPasswordLength) caractères" + Write-Host " - Complexité: $($passwordPolicy.ComplexityEnabled)" + } + catch { + Write-Error "Erreur lors de la récupération de la politique de mot de passe : $_" + exit 1 + } + + try { + $ouExists = Get-ADOrganizationalUnit -Identity $TargetOU -ErrorAction Stop + } + catch { + Write-Error "L'OU spécifiée n'existe pas ou est inaccessible : $TargetOU" + exit 1 + } + + $filter = "PasswordNeverExpires -eq `$false" + if ($IncludeDisabled) { + $filter = "($filter) -or (Enabled -eq `$false)" + } + if ($IncludeNeverExpires) { + $filter = "PasswordNeverExpires -eq `$true -or ($filter)" + } + + try { + Write-Host "Recherche des utilisateurs dans l'OU: $TargetOU" + $users = Get-ADUser -SearchBase $TargetOU -Filter * -Properties Name, SamAccountName, EmailAddress, PasswordLastSet, PasswordNeverExpires, Enabled | Where-Object { + if ($IncludeDisabled -and $IncludeNeverExpires) { $true } + elseif ($IncludeDisabled) { -not $_.PasswordNeverExpires } + elseif ($IncludeNeverExpires) { $_.Enabled } + else { $_.Enabled -and (-not $_.PasswordNeverExpires) } + } + + Write-Host "Nombre d'utilisateurs trouvés: $($users.Count)" + } + catch { + Write-Error "Erreur lors de la récupération des utilisateurs : $_" + exit 1 + } + + if (-not $users) { + Write-Host "Aucun utilisateur trouvé dans l'OU spécifiée avec les critères actuels." + exit + } + + $reportData = foreach ($user in $users) { + if ($user.PasswordNeverExpires -or ($user.PasswordLastSet -eq $null -and -not $IncludeNeverExpires)) { + [PSCustomObject]@{ + Name = $user.Name + SamAccountName = $user.SamAccountName + Email = $user.EmailAddress + ExpirationDate = $null + DaysLeft = $null + Status = if ($user.PasswordNeverExpires) { "NeverExpires" } else { "NeverLoggedIn" } + Enabled = $user.Enabled + PasswordNeverExpires = $user.PasswordNeverExpires + } + } + else { + Get-UserPasswordExpirationInfo -user $user -maxPasswordAge $maxPasswordAge + } + } + + $expiredUsers = $reportData | Where-Object { $_.Status -eq "Expired" } | Sort-Object DaysLeft + $criticalUsers = $reportData | Where-Object { $_.Status -eq "Critical" } | Sort-Object DaysLeft + $warningUsers = $reportData | Where-Object { $_.Status -eq "Warning" } | Sort-Object DaysLeft + $neverExpiresUsers = $reportData | Where-Object { $_.Status -eq "NeverExpires" } + $neverLoggedInUsers = $reportData | Where-Object { $_.Status -eq "NeverLoggedIn" } + $disabledUsers = $reportData | Where-Object { $_.Enabled -eq $false } + + $reportFileName = "PasswordExpirationReport_$(Get-Date -Format 'yyyyMMdd_HHmm').html" + $htmlReport = ConvertTo-HtmlReport -expiredUsers $expiredUsers -criticalUsers $criticalUsers -warningUsers $warningUsers -neverExpiresUsers $neverExpiresUsers -neverLoggedInUsers $neverLoggedInUsers -disabledUsers $disabledUsers -targetOU $TargetOU -passwordPolicy $passwordPolicy -warningThreshold $WarningThreshold -criticalThreshold $CriticalThreshold + $htmlReport | Out-File $reportFileName -Encoding UTF8 + + Write-Host "Rapport généré avec succès : $reportFileName" + Write-Host "Résumé :" + Write-Host " - Comptes expirés: $($expiredUsers.Count)" + Write-Host " - Comptes critiques: $($criticalUsers.Count)" + Write-Host " - Comptes en avertissement: $($warningUsers.Count)" + Write-Host " - Comptes expirant jamais: $($neverExpiresUsers.Count)" + Write-Host " - Comptes jamais connectés: $($neverLoggedInUsers.Count)" + Write-Host " - Comptes désactivés: $($disabledUsers.Count)" + + if ($GenerateReportOnly) { + Write-Host "Option GenerateReportOnly activée, rapport généré uniquement. Arrêt du script." + exit 0 + } + + foreach ($user in $reportData | Where-Object { $_.Status -in @("Warning", "Critical", "Expired") }) { + if ($user.Email) { + $expirationDate = if ($user.ExpirationDate) { $user.ExpirationDate.ToString("dd/MM/yyyy") } else { "N/A" } + $subject = "Avertissement: Expiration de votre mot de passe" + $body = @" + + + + + + + +

Bonjour $($user.Name),

+

Votre mot de passe est dans un état $($user.Status).

+

Date d'expiration: $expirationDate

+

Veuillez mettre à jour votre mot de passe dès que possible pour éviter tout problème d'accès.

+

Cordialement,

+ + "@ - Send-UserNotification -Recipient $user.Email -Subject $subject -Body $body -SmtpServer $SmtpServer -Port $SmtpPort -FromAddress $FromEmail - } - else { - Write-Warning "L'utilisateur $($user.Name) n'a pas d'adresse email définie dans Active Directory." - } -} - -if ($reportData.Count -gt 0) { - $adminEmails = $AdminEmail - $smtpServer = $SmtpServer - $smtpPort = $SmtpPort - $fromAddress = $FromEmail - $subject = "Rapport hebdomadaire d'expiration des mots de passe" - $body = $htmlReport - Send-EmailReport -Recipients $adminEmails -Subject $subject -Body $body -SmtpServer $smtpServer -Port $smtpPort -FromAddress $fromAddress -Attachments @() -} - + Send-UserNotification -Recipient $user.Email -Subject $subject -Body $body -SmtpServer $SmtpServer -Port $SmtpPort -FromAddress $FromEmail + } + else { + Write-Warning "L'utilisateur $($user.Name) n'a pas d'adresse email définie dans Active Directory." + } + } + + if ($AdminEmails) { + if ($reportData.Count -gt 0) { + $smtpServer = $SmtpServer + $smtpPort = $SmtpPort + $fromAddress = $FromEmail + $subject = "Rapport hebdomadaire d'expiration des mots de passe" + $body = $htmlReport + Send-EmailReport -Recipients $AdminEmails -Subject $subject -Body $body -SmtpServer $smtpServer -Port $smtpPort -FromAddress $fromAddress -Attachments @() + } + } else { + Write-Warning "ADMIN_EMAIL n'est pas défini. Aucun email administrateur ne sera envoyé." + } From ac803924ebb71fc7840339f46900802125104b49 Mon Sep 17 00:00:00 2001 From: P6g9YHK6 <17877371+P6g9YHK6@users.noreply.github.com> Date: Fri, 6 Jun 2025 11:50:51 +0200 Subject: [PATCH 02/23] Update file: Change user password.ps1 --- .../Tasks/Change user password.ps1 | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/scripts_staging/Tasks/Change user password.ps1 b/scripts_staging/Tasks/Change user password.ps1 index 2638cb7a..c927c076 100644 --- a/scripts_staging/Tasks/Change user password.ps1 +++ b/scripts_staging/Tasks/Change user password.ps1 @@ -12,8 +12,10 @@ GeneratedPassphrase snippet #public +.CHANGELOG + 06.06.25 SAN added not allow to change the password on non primary DC it causes conflicts if run on multiple DC + .TODO - Do not allow to change the password on non primary DC it causes conflicts move param to env #> @@ -22,17 +24,38 @@ param( [string]$username ) -#Call snippet +# Check if the machine is not a Primary Domain Controller +# this script should not run on multiple DC as it would cause syncronisation issues so for the sake of simplicity it's only allowed to run on PDC +$domainRole = (Get-WmiObject Win32_ComputerSystem).DomainRole +$isDomainController = $domainRole -ge 4 # 4 = Backup DC, 5 = Primary DC +if ($isDomainController) { + try { + Write-Host "Domain Controller detected" + $domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() + $pdc = $domain.PdcRoleOwner.Name.Split('.')[0] + $localComputer = $env:COMPUTERNAME + + if ($pdc -ine $localComputer) { + Write-Host "Not the Primary DC" + exit 0 + } + Write-Host "Primary DC detected" + } catch { + Write-Host "Error determining PDC role. Aborting." + exit 1 + } +} + +# Snippet for passphrase {{GeneratedPassphrase}} -$newPassword = $GeneratedPassphrase # Set the new password for the user -net user $username $newPassword +net user $username $GeneratedPassphrase # Check if the password change was successful if ($LASTEXITCODE -eq 0) { - Write-Host "$newPassword" + Write-Host "$GeneratedPassphrase" } else { - Write-Host "Password change for $username failed. Please check for errors." + Write-Host "Password change for $username failed." exit 1 -} \ No newline at end of file +} From 911f42457667d4fc1597cb860e92871e24a0d071 Mon Sep 17 00:00:00 2001 From: P6g9YHK6 <17877371+P6g9YHK6@users.noreply.github.com> Date: Mon, 9 Jun 2025 09:45:08 +0200 Subject: [PATCH 03/23] Update file: Mail notification password expiry.ps1 --- .../Mail notification password expiry.ps1 | 937 +++++++++--------- 1 file changed, 452 insertions(+), 485 deletions(-) diff --git a/scripts_staging/Backend/Mail notification password expiry.ps1 b/scripts_staging/Backend/Mail notification password expiry.ps1 index 94d8c374..1e05d928 100644 --- a/scripts_staging/Backend/Mail notification password expiry.ps1 +++ b/scripts_staging/Backend/Mail notification password expiry.ps1 @@ -1,509 +1,476 @@ <# .SYNOPSIS Ensures the script is executed using PowerShell 7 or higher. - .DESCRIPTION This script verifies whether it is running in a PowerShell 7+ environment. If not, and if PowerShell 7 (pwsh) is available on the system, it re-invokes itself using pwsh, passing along any parameters. If pwsh is not found, the script outputs a message and exits with an error code. Once running in PowerShell 7 or higher, it sets the output rendering mode to plaintext for consistent formatting. - .NOTES Author: PQU Date: 29/04/2025 #public - .CHANGELOG 22.05.25 SAN Added UTF8 to fix encoding issue with russian & french chars 06.06.25 PQU Added support for multiple admin emails #> - - if (!($PSVersionTable.PSVersion.Major -ge 7)) { if (Get-Command pwsh -ErrorAction SilentlyContinue) { - pwsh -File "`"$PSCommandPath`"" @PSBoundParameters - exit $LASTEXITCODE + pwsh -File "`"$PSCommandPath`"" @PSBoundParameters + exit $LASTEXITCODE } else { - Write-Output "ERROR: PowerShell 7 is not available. Exiting." - exit 1 - } - } - [Console]::OutputEncoding = [Text.Encoding]::UTF8 - $PSStyle.OutputRendering = "plaintext" - - - $TargetOU = $env:TARGET_OU - $SmtpServer = $env:SMTP_SERVER - $SmtpPort = [int]$env:SMTP_PORT - $AdminEmails = $env:ADMIN_EMAIL -split '[,;]' | ForEach-Object { $_.Trim() } | Where-Object { $_ } - $FromEmail = $env:FROM_EMAIL - $WarningThreshold = [int]$env:WARNING_THRESHOLD - $CriticalThreshold = [int]$env:CRITICAL_THRESHOLD - $EmailSignature = $env:EMAIL_SIGNATURE - - function Convert-ToBoolean($value) { - return $value -match '^(1|true|yes)$' - } - - $IncludeDisabled = Convert-ToBoolean $env:INCLUDE_DISABLED - $IncludeNeverExpires = Convert-ToBoolean $env:INCLUDE_NEVER_EXPIRES - $GenerateReportOnly = Convert-ToBoolean $env:GENERATE_REPORT_ONLY - - if ($env:SMTP_CREDENTIAL_USERNAME -and $env:SMTP_CREDENTIAL_PASSWORD) { - try { - $SecurePassword = ConvertTo-SecureString $env:SMTP_CREDENTIAL_PASSWORD -AsPlainText -Force - $SmtpCredential = New-Object System.Management.Automation.PSCredential ($env:SMTP_CREDENTIAL_USERNAME, $SecurePassword) - } catch { - Write-Error "Failed to create SMTP credentials: $_" - } - } - - function Test-Prerequisites { - - $adFeature = Get-WindowsFeature -Name AD-Domain-Services -ErrorAction Stop - if ($adFeature.InstallState -ne 'Installed') { - Write-Error "AD Domain Services ne sont pas installés. Arrêt du script." - exit 1 - } - - if (-not $SmtpServer -or -not $SmtpPort) { - Write-Error "Les variables `$SmtpServer et `$SmtpPort doivent être définies avant d'appeler cette fonction." - exit 1 - } - - if (-not (Get-Module -ListAvailable -Name ActiveDirectory)) { - Write-Error "Module ActiveDirectory non trouvé. Arrêt du script." - exit 1 - } - - Import-Module ActiveDirectory -ErrorAction Stop - - try { - $dc = Get-ADDomainController -Discover -ErrorAction Stop - Write-Host "Connexion réussie au contrôleur de domaine : $($dc.HostName)" - } - catch { - Write-Error "Impossible de se connecter au contrôleur de domaine. Arrêt du script." - exit 1 - } - - try { - $tcpClient = New-Object System.Net.Sockets.TcpClient - $tcpClient.Connect($SmtpServer, $SmtpPort) - $tcpClient.Close() - Write-Host "Connexion réussie au serveur SMTP : $SmtpServer":"$SmtpPort" - } - catch { - Write-Error "Impossible de se connecter au serveur SMTP : $SmtpServer sur le port $SmtpPort. Arrêt du script." - exit 1 - } - } - - function Get-UserPasswordExpirationInfo { - param ( - $user, - $maxPasswordAge - ) - - $result = [PSCustomObject]@{ - Name = $user.Name - SamAccountName = $user.SamAccountName - Email = $user.EmailAddress - ExpirationDate = $null - DaysLeft = $null - Status = "OK" - Enabled = $user.Enabled - PasswordNeverExpires = $user.PasswordNeverExpires - } - - if ($user.PasswordLastSet -eq $null) { - $result.Status = "NeverLoggedIn" - return $result - } - - if ($user.PasswordNeverExpires) { - $result.Status = "NeverExpires" - return $result - } - - $passwordExpirationDate = $user.PasswordLastSet + $maxPasswordAge - $daysLeft = ($passwordExpirationDate - (Get-Date)).Days - - $result.ExpirationDate = $passwordExpirationDate - $result.DaysLeft = $daysLeft - - if ($daysLeft -lt 0) { - $result.Status = "Expired" - } - elseif ($daysLeft -le $CriticalThreshold) { - $result.Status = "Critical" - } - elseif ($daysLeft -le $WarningThreshold) { - $result.Status = "Warning" - } - - return $result - } - - function ConvertTo-HtmlReport { - param ( - $expiredUsers, - $criticalUsers, - $warningUsers, - $neverExpiresUsers, - $neverLoggedInUsers, - $disabledUsers, - $targetOU, - $passwordPolicy, - $warningThreshold, - $criticalThreshold - ) - - $html = @" - - - - Rapport d'expiration des mots de passe - - - -

Rapport d'expiration des mots de passe

- -
-

Politique de mot de passe du domaine

-

Durée maximale du mot de passe: $($passwordPolicy.MaxPasswordAge.Days) jours

-

Durée minimale du mot de passe: $($passwordPolicy.MinPasswordAge.Days) jours

-

Longueur minimale: $($passwordPolicy.MinPasswordLength) caractères

-

Complexité requise: $($passwordPolicy.ComplexityEnabled)

-

Historique du mot de passe: $($passwordPolicy.PasswordHistoryCount) mots de passe

-

Verrouillage de compte: $($passwordPolicy.LockoutThreshold) tentatives (durée: $($passwordPolicy.LockoutDuration.Minutes) minutes, observation: $($passwordPolicy.LockoutObservationWindow.Minutes) minutes)

-
- -
-

Seuil d'avertissement : $warningThreshold jours

-

Seuil critique : $criticalThreshold jours

-

Statistiques : - Expirés: $($expiredUsers.Count) - Critiques: $($criticalUsers.Count) - Avertissement: $($warningUsers.Count) - Expirent jamais: $($neverExpiresUsers.Count) - Jamais connectés: $($neverLoggedInUsers.Count) - Désactivés: $($disabledUsers.Count) -

-
- "@ - - if ($expiredUsers) { - $html += "

Comptes expirés $($expiredUsers.Count)

" - $html += $expiredUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={$_.ExpirationDate.ToString("dd/MM/yyyy")}}, DaysLeft, Enabled | ConvertTo-Html -Fragment - } - - if ($criticalUsers) { - $html += "

Comptes critiques $($criticalUsers.Count)

" - $html += $criticalUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={$_.ExpirationDate.ToString("dd/MM/yyyy")}}, DaysLeft, Enabled | ConvertTo-Html -Fragment - } - - if ($warningUsers) { - $html += "

Comptes en avertissement $($warningUsers.Count)

" - $html += $warningUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={$_.ExpirationDate.ToString("dd/MM/yyyy")}}, DaysLeft, Enabled | ConvertTo-Html -Fragment - } - - if ($IncludeNeverExpires -and $neverExpiresUsers) { - $html += "

Comptes avec mot de passe n expirant jamais $($neverExpiresUsers.Count)

" - $html += $neverExpiresUsers | Select-Object Name, SamAccountName, Email, Enabled | ConvertTo-Html -Fragment - } - - if ($neverLoggedInUsers) { - $html += "

Comptes jamais connectés $($neverLoggedInUsers.Count)

" - $html += $neverLoggedInUsers | Select-Object Name, SamAccountName, Email, Enabled | ConvertTo-Html -Fragment - } - - if ($IncludeDisabled -and $disabledUsers) { - $html += "

Comptes désactivés $($disabledUsers.Count)

" - $html += $disabledUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={if($_.ExpirationDate){$_.ExpirationDate.ToString("dd/MM/yyyy")}else{"N/A"}}}, DaysLeft | ConvertTo-Html -Fragment - } - - $html += @" -

Généré le : $(Get-Date -Format "dd/MM/yyyy HH:mm")

- - + Write-Output "ERROR: PowerShell 7 is not available. Exiting." + exit 1 + } +} +[Console]::OutputEncoding = [Text.Encoding]::UTF8 +$PSStyle.OutputRendering = "plaintext" + +$TargetOU = $env:TARGET_OU +$SmtpServer = $env:SMTP_SERVER +$SmtpPort = [int]$env:SMTP_PORT +$AdminEmails = $env:ADMIN_EMAIL -split '[,;]' | ForEach-Object { $_.Trim() } | Where-Object { $_ } +$FromEmail = $env:FROM_EMAIL +$WarningThreshold = [int]$env:WARNING_THRESHOLD +$CriticalThreshold = [int]$env:CRITICAL_THRESHOLD +$EmailSignature = $env:EMAIL_SIGNATURE + +function Convert-ToBoolean($value) { + return $value -match '^(1|true|yes)$' +} + +$IncludeDisabled = Convert-ToBoolean $env:INCLUDE_DISABLED +$IncludeNeverExpires = Convert-ToBoolean $env:INCLUDE_NEVER_EXPIRES +$GenerateReportOnly = Convert-ToBoolean $env:GENERATE_REPORT_ONLY + +if ($env:SMTP_CREDENTIAL_USERNAME -and $env:SMTP_CREDENTIAL_PASSWORD) { + try { + $SecurePassword = ConvertTo-SecureString $env:SMTP_CREDENTIAL_PASSWORD -AsPlainText -Force + $SmtpCredential = New-Object System.Management.Automation.PSCredential ($env:SMTP_CREDENTIAL_USERNAME, $SecurePassword) + } catch { + Write-Error "Failed to create SMTP credentials: $_" + } +} + +function Test-Prerequisites { + $adFeature = Get-WindowsFeature -Name AD-Domain-Services -ErrorAction Stop + if ($adFeature.InstallState -ne 'Installed') { + Write-Error "AD Domain Services ne sont pas installés. Arrêt du script." + exit 1 + } + if (-not $SmtpServer -or -not $SmtpPort) { + Write-Error "Les variables `$SmtpServer et `$SmtpPort doivent être définies avant d'appeler cette fonction." + exit 1 + } + if (-not (Get-Module -ListAvailable -Name ActiveDirectory)) { + Write-Error "Module ActiveDirectory non trouvé. Arrêt du script." + exit 1 + } + Import-Module ActiveDirectory -ErrorAction Stop + try { + $dc = Get-ADDomainController -Discover -ErrorAction Stop + Write-Host "Connexion réussie au contrôleur de domaine : $($dc.HostName)" + } + catch { + Write-Error "Impossible de se connecter au contrôleur de domaine. Arrêt du script." + exit 1 + } + try { + $tcpClient = New-Object System.Net.Sockets.TcpClient + $tcpClient.Connect($SmtpServer, $SmtpPort) + $tcpClient.Close() + Write-Host "Connexion réussie au serveur SMTP : $SmtpServer":"$SmtpPort" + } + catch { + Write-Error "Impossible de se connecter au serveur SMTP : $SmtpServer sur le port $SmtpPort. Arrêt du script." + exit 1 + } +} + +function Get-UserPasswordExpirationInfo { + param ( + $user, + $maxPasswordAge + ) + $result = [PSCustomObject]@{ + Name = $user.Name + SamAccountName = $user.SamAccountName + Email = $user.EmailAddress + ExpirationDate = $null + DaysLeft = $null + Status = "OK" + Enabled = $user.Enabled + PasswordNeverExpires = $user.PasswordNeverExpires + } + if ($user.PasswordLastSet -eq $null) { + $result.Status = "NeverLoggedIn" + return $result + } + if ($user.PasswordNeverExpires) { + $result.Status = "NeverExpires" + return $result + } + $passwordExpirationDate = $user.PasswordLastSet + $maxPasswordAge + $daysLeft = ($passwordExpirationDate - (Get-Date)).Days + $result.ExpirationDate = $passwordExpirationDate + $result.DaysLeft = $daysLeft + if ($daysLeft -lt 0) { + $result.Status = "Expired" + } + elseif ($daysLeft -le $CriticalThreshold) { + $result.Status = "Critical" + } + elseif ($daysLeft -le $WarningThreshold) { + $result.Status = "Warning" + } + return $result +} + +function ConvertTo-HtmlReport { + param ( + $expiredUsers, + $criticalUsers, + $warningUsers, + $neverExpiresUsers, + $neverLoggedInUsers, + $disabledUsers, + $targetOU, + $passwordPolicy, + $warningThreshold, + $criticalThreshold + ) + $html = @" + + + + Rapport d'expiration des mots de passe + + + +

Rapport d'expiration des mots de passe

+
+

Politique de mot de passe du domaine

+

Durée maximale du mot de passe: $($passwordPolicy.MaxPasswordAge.Days) jours

+

Durée minimale du mot de passe: $($passwordPolicy.MinPasswordAge.Days) jours

+

Longueur minimale: $($passwordPolicy.MinPasswordLength) caractères

+

Complexité requise: $($passwordPolicy.ComplexityEnabled)

+

Historique du mot de passe: $($passwordPolicy.PasswordHistoryCount) mots de passe

+

Verrouillage de compte: $($passwordPolicy.LockoutThreshold) tentatives (durée: $($passwordPolicy.LockoutDuration.Minutes) minutes, observation: $($passwordPolicy.LockoutObservationWindow.Minutes) minutes)

+
+
+

Seuil d'avertissement : $warningThreshold jours

+

Seuil critique : $criticalThreshold jours

+

Statistiques : + Expirés: $($expiredUsers.Count) + Critiques: $($criticalUsers.Count) + Avertissement: $($warningUsers.Count) + Expirent jamais: $($neverExpiresUsers.Count) + Jamais connectés: $($neverLoggedInUsers.Count) + Désactivés: $($disabledUsers.Count) +

+
"@ - - return $html - } - - function Get-EmailSignature { - if ($EmailSignature) { - return "
$EmailSignature
" - } - - return @" -
-

- Service Informatique
- Téléphone : +33 (0)1 XX XX XX XX
- Email : support@domain.com
- Ce message est généré automatiquement, merci de ne pas y répondre directement. -

-
+ + if ($expiredUsers) { + $html += "

Comptes expirés $($expiredUsers.Count)

" + $html += $expiredUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={$_.ExpirationDate.ToString("dd/MM/yyyy")}}, DaysLeft, Enabled | ConvertTo-Html -Fragment + } + + if ($criticalUsers) { + $html += "

Comptes critiques $($criticalUsers.Count)

" + $html += $criticalUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={$_.ExpirationDate.ToString("dd/MM/yyyy")}}, DaysLeft, Enabled | ConvertTo-Html -Fragment + } + + if ($warningUsers) { + $html += "

Comptes en avertissement $($warningUsers.Count)

" + $html += $warningUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={$_.ExpirationDate.ToString("dd/MM/yyyy")}}, DaysLeft, Enabled | ConvertTo-Html -Fragment + } + + if ($IncludeNeverExpires -and $neverExpiresUsers) { + $html += "

Comptes avec mot de passe n'expirant jamais $($neverExpiresUsers.Count)

" + $html += $neverExpiresUsers | Select-Object Name, SamAccountName, Email, Enabled | ConvertTo-Html -Fragment + } + + if ($neverLoggedInUsers) { + $html += "

Comptes jamais connectés $($neverLoggedInUsers.Count)

" + $html += $neverLoggedInUsers | Select-Object Name, SamAccountName, Email, Enabled | ConvertTo-Html -Fragment + } + + if ($IncludeDisabled -and $disabledUsers) { + $html += "

Comptes désactivés $($disabledUsers.Count)

" + $html += $disabledUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={if($_.ExpirationDate){$_.ExpirationDate.ToString("dd/MM/yyyy")}else{"N/A"}}}, DaysLeft | ConvertTo-Html -Fragment + } + + $html += @" +

Généré le : $(Get-Date -Format "dd/MM/yyyy HH:mm")

+ + "@ - } - - function Send-EmailReport { - param( - [string[]]$Recipients, - [string]$Subject, - [string]$Body, - [string]$SmtpServer, - [int]$Port = 25, - [string]$FromAddress, - [string[]]$Attachments - ) - - if ((Get-Date).DayOfWeek -ne 'Monday') { - Write-Host "Les emails ne sont envoyés que le lundi. Arrêt de l'envoi." - return - } - - $signature = Get-EmailSignature - $bodyWithSignature = $Body - if ($Body -match '(?i)') { - $bodyWithSignature = $Body -replace '(?i)', "$signature" - } else { - $bodyWithSignature = "$Body$signature" - } - - $mailMessage = New-Object System.Net.Mail.MailMessage - $mailMessage.From = $FromAddress - foreach ($recipient in $Recipients) { $mailMessage.To.Add($recipient) } - $mailMessage.Subject = $Subject - $mailMessage.Body = $bodyWithSignature - $mailMessage.IsBodyHtml = $true - if ($Attachments) { - foreach ($att in $Attachments) { - $mailMessage.Attachments.Add((New-Object System.Net.Mail.Attachment($att))) - } - } - $smtpClient = New-Object System.Net.Mail.SmtpClient($SmtpServer, $Port) - if ($SmtpCredential) { - $smtpClient.Credentials = $SmtpCredential - } - try { - $smtpClient.Send($mailMessage) - Write-Host "Email sent successfully." - } - catch { - Write-Error "Failed to send email: $_" - } - } - - function Send-UserNotification { - param( - [string]$Recipient, - [string]$Subject, - [string]$Body, - [string]$SmtpServer, - [int]$Port = 25, - [string]$FromAddress - ) - - $signature = Get-EmailSignature - $bodyWithSignature = $Body - if ($Body -match '(?i)') { - $bodyWithSignature = $Body -replace '(?i)', "$signature" - } else { - $bodyWithSignature = @" - - - - - - - $Body - $signature - - + return $html +} + +function Get-EmailSignature { + if ($EmailSignature) { + return "
$EmailSignature
" + } + return @" +
+

+ Service Informatique
+ Téléphone : +33 (0)1 XX XX XX XX
+ Email : support@domain.com
+ Ce message est généré automatiquement, merci de ne pas y répondre directement. +

+
+"@ +} + +function Send-EmailReport { + param( + [string[]]$Recipients, + [string]$Subject, + [string]$Body, + [string]$SmtpServer, + [int]$Port = 25, + [string]$FromAddress, + [string[]]$Attachments + ) + if ((Get-Date).DayOfWeek -ne 'Monday') { + Write-Host "Les emails ne sont envoyés que le lundi. Arrêt de l'envoi." + return + } + $signature = Get-EmailSignature + $bodyWithSignature = $Body + if ($Body -match '(?i)') { + $bodyWithSignature = $Body -replace '(?i)', "$signature" + } else { + $bodyWithSignature = "$Body$signature" + } + $mailMessage = New-Object System.Net.Mail.MailMessage + $mailMessage.From = $FromAddress + foreach ($recipient in $Recipients) { $mailMessage.To.Add($recipient) } + $mailMessage.Subject = $Subject + $mailMessage.Body = $bodyWithSignature + $mailMessage.IsBodyHtml = $true + if ($Attachments) { + foreach ($att in $Attachments) { + $mailMessage.Attachments.Add((New-Object System.Net.Mail.Attachment($att))) + } + } + $smtpClient = New-Object System.Net.Mail.SmtpClient($SmtpServer, $Port) + if ($SmtpCredential) { + $smtpClient.Credentials = $SmtpCredential + } + try { + $smtpClient.Send($mailMessage) + Write-Host "Email sent successfully." + } + catch { + Write-Error "Failed to send email: $_" + } +} + +function Send-UserNotification { + param( + [string]$Recipient, + [string]$Subject, + [string]$Body, + [string]$SmtpServer, + [int]$Port = 25, + [string]$FromAddress + ) + $signature = Get-EmailSignature + $bodyWithSignature = $Body + if ($Body -match '(?i)') { + $bodyWithSignature = $Body -replace '(?i)', "$signature" + } else { + $bodyWithSignature = @" + + + + + + +$body +$signature + + "@ } - - $mailMessage = New-Object System.Net.Mail.MailMessage - $mailMessage.From = $FromAddress - $mailMessage.To.Add($Recipient) - $mailMessage.Subject = $Subject - $mailMessage.Body = $bodyWithSignature - $mailMessage.IsBodyHtml = $true - - $smtpClient = New-Object System.Net.Mail.SmtpClient($SmtpServer, $Port) - if ($SmtpCredential) { - $smtpClient.Credentials = $SmtpCredential - } - try { - $smtpClient.Send($mailMessage) - Write-Host "Notification sent to $Recipient." - } - catch { - Write-Error "Failed to send notification to ${Recipient}: $_" - } - } - - try { - $passwordPolicy = Get-ADDefaultDomainPasswordPolicy - $maxPasswordAge = $passwordPolicy.MaxPasswordAge - - Write-Host "Politique de mot de passe du domaine:" - Write-Host " - Durée maximale: $($maxPasswordAge.Days) jours" - Write-Host " - Durée minimale: $($passwordPolicy.MinPasswordAge.Days) jours" - Write-Host " - Longueur minimale: $($passwordPolicy.MinPasswordLength) caractères" - Write-Host " - Complexité: $($passwordPolicy.ComplexityEnabled)" - } - catch { - Write-Error "Erreur lors de la récupération de la politique de mot de passe : $_" - exit 1 - } - - try { - $ouExists = Get-ADOrganizationalUnit -Identity $TargetOU -ErrorAction Stop - } - catch { - Write-Error "L'OU spécifiée n'existe pas ou est inaccessible : $TargetOU" - exit 1 - } - - $filter = "PasswordNeverExpires -eq `$false" - if ($IncludeDisabled) { - $filter = "($filter) -or (Enabled -eq `$false)" - } - if ($IncludeNeverExpires) { - $filter = "PasswordNeverExpires -eq `$true -or ($filter)" - } - - try { - Write-Host "Recherche des utilisateurs dans l'OU: $TargetOU" - $users = Get-ADUser -SearchBase $TargetOU -Filter * -Properties Name, SamAccountName, EmailAddress, PasswordLastSet, PasswordNeverExpires, Enabled | Where-Object { - if ($IncludeDisabled -and $IncludeNeverExpires) { $true } - elseif ($IncludeDisabled) { -not $_.PasswordNeverExpires } - elseif ($IncludeNeverExpires) { $_.Enabled } - else { $_.Enabled -and (-not $_.PasswordNeverExpires) } - } - - Write-Host "Nombre d'utilisateurs trouvés: $($users.Count)" - } - catch { - Write-Error "Erreur lors de la récupération des utilisateurs : $_" - exit 1 - } - - if (-not $users) { - Write-Host "Aucun utilisateur trouvé dans l'OU spécifiée avec les critères actuels." - exit - } - - $reportData = foreach ($user in $users) { - if ($user.PasswordNeverExpires -or ($user.PasswordLastSet -eq $null -and -not $IncludeNeverExpires)) { - [PSCustomObject]@{ - Name = $user.Name - SamAccountName = $user.SamAccountName - Email = $user.EmailAddress - ExpirationDate = $null - DaysLeft = $null - Status = if ($user.PasswordNeverExpires) { "NeverExpires" } else { "NeverLoggedIn" } - Enabled = $user.Enabled - PasswordNeverExpires = $user.PasswordNeverExpires - } - } - else { - Get-UserPasswordExpirationInfo -user $user -maxPasswordAge $maxPasswordAge - } - } - - $expiredUsers = $reportData | Where-Object { $_.Status -eq "Expired" } | Sort-Object DaysLeft - $criticalUsers = $reportData | Where-Object { $_.Status -eq "Critical" } | Sort-Object DaysLeft - $warningUsers = $reportData | Where-Object { $_.Status -eq "Warning" } | Sort-Object DaysLeft - $neverExpiresUsers = $reportData | Where-Object { $_.Status -eq "NeverExpires" } - $neverLoggedInUsers = $reportData | Where-Object { $_.Status -eq "NeverLoggedIn" } - $disabledUsers = $reportData | Where-Object { $_.Enabled -eq $false } - - $reportFileName = "PasswordExpirationReport_$(Get-Date -Format 'yyyyMMdd_HHmm').html" - $htmlReport = ConvertTo-HtmlReport -expiredUsers $expiredUsers -criticalUsers $criticalUsers -warningUsers $warningUsers -neverExpiresUsers $neverExpiresUsers -neverLoggedInUsers $neverLoggedInUsers -disabledUsers $disabledUsers -targetOU $TargetOU -passwordPolicy $passwordPolicy -warningThreshold $WarningThreshold -criticalThreshold $CriticalThreshold - $htmlReport | Out-File $reportFileName -Encoding UTF8 - - Write-Host "Rapport généré avec succès : $reportFileName" - Write-Host "Résumé :" - Write-Host " - Comptes expirés: $($expiredUsers.Count)" - Write-Host " - Comptes critiques: $($criticalUsers.Count)" - Write-Host " - Comptes en avertissement: $($warningUsers.Count)" - Write-Host " - Comptes expirant jamais: $($neverExpiresUsers.Count)" - Write-Host " - Comptes jamais connectés: $($neverLoggedInUsers.Count)" - Write-Host " - Comptes désactivés: $($disabledUsers.Count)" - - if ($GenerateReportOnly) { - Write-Host "Option GenerateReportOnly activée, rapport généré uniquement. Arrêt du script." - exit 0 - } - - foreach ($user in $reportData | Where-Object { $_.Status -in @("Warning", "Critical", "Expired") }) { - if ($user.Email) { - $expirationDate = if ($user.ExpirationDate) { $user.ExpirationDate.ToString("dd/MM/yyyy") } else { "N/A" } - $subject = "Avertissement: Expiration de votre mot de passe" - $body = @" - - - - - - - -

Bonjour $($user.Name),

-

Votre mot de passe est dans un état $($user.Status).

-

Date d'expiration: $expirationDate

-

Veuillez mettre à jour votre mot de passe dès que possible pour éviter tout problème d'accès.

-

Cordialement,

- - + $mailMessage = New-Object System.Net.Mail.MailMessage + $mailMessage.From = $FromAddress + $mailMessage.To.Add($Recipient) + $mailMessage.Subject = $Subject + $mailMessage.Body = $bodyWithSignature + $mailMessage.IsBodyHtml = $true + $smtpClient = New-Object System.Net.Mail.SmtpClient($SmtpServer, $Port) + if ($SmtpCredential) { + $smtpClient.Credentials = $SmtpCredential + } + try { + $smtpClient.Send($mailMessage) + Write-Host "Notification sent to $Recipient." + } + catch { + Write-Error "Failed to send notification to ${Recipient}: $_" + } +} + +try { + $passwordPolicy = Get-ADDefaultDomainPasswordPolicy + $maxPasswordAge = $passwordPolicy.MaxPasswordAge + Write-Host "Politique de mot de passe du domaine:" + Write-Host " - Durée maximale: $($maxPasswordAge.Days) jours" + Write-Host " - Durée minimale: $($passwordPolicy.MinPasswordAge.Days) jours" + Write-Host " - Longueur minimale: $($passwordPolicy.MinPasswordLength) caractères" + Write-Host " - Complexité: $($passwordPolicy.ComplexityEnabled)" +} +catch { + Write-Error "Erreur lors de la récupération de la politique de mot de passe : $_" + exit 1 +} + +try { + $ouExists = Get-ADOrganizationalUnit -Identity $TargetOU -ErrorAction Stop +} +catch { + Write-Error "L'OU spécifiée n'existe pas ou est inaccessible : $TargetOU" + exit 1 +} + +$filter = "PasswordNeverExpires -eq `$false" +if ($IncludeDisabled) { + $filter = "($filter) -or (Enabled -eq `$false)" +} +if ($IncludeNeverExpires) { + $filter = "PasswordNeverExpires -eq `$true -or ($filter)" +} + +try { + Write-Host "Recherche des utilisateurs dans l'OU: $TargetOU" + $users = Get-ADUser -SearchBase $TargetOU -Filter * -Properties Name, SamAccountName, EmailAddress, PasswordLastSet, PasswordNeverExpires, Enabled | Where-Object { + if ($IncludeDisabled -and $IncludeNeverExpires) { $true } + elseif ($IncludeDisabled) { -not $_.PasswordNeverExpires } + elseif ($IncludeNeverExpires) { $_.Enabled } + else { $_.Enabled -and (-not $_.PasswordNeverExpires) } + } + Write-Host "Nombre d'utilisateurs trouvés: $($users.Count)" +} +catch { + Write-Error "Erreur lors de la récupération des utilisateurs : $_" + exit 1 +} + +if (-not $users) { + Write-Host "Aucun utilisateur trouvé dans l'OU spécifiée avec les critères actuels." + exit +} + +$reportData = foreach ($user in $users) { + if ($user.PasswordNeverExpires -or ($user.PasswordLastSet -eq $null -and -not $IncludeNeverExpires)) { + [PSCustomObject]@{ + Name = $user.Name + SamAccountName = $user.SamAccountName + Email = $user.EmailAddress + ExpirationDate = $null + DaysLeft = $null + Status = if ($user.PasswordNeverExpires) { "NeverExpires" } else { "NeverLoggedIn" } + Enabled = $user.Enabled + PasswordNeverExpires = $user.PasswordNeverExpires + } + } + else { + Get-UserPasswordExpirationInfo -user $user -maxPasswordAge $maxPasswordAge + } +} + +$expiredUsers = $reportData | Where-Object { $_.Status -eq "Expired" } | Sort-Object DaysLeft +$criticalUsers = $reportData | Where-Object { $_.Status -eq "Critical" } | Sort-Object DaysLeft +$warningUsers = $reportData | Where-Object { $_.Status -eq "Warning" } | Sort-Object DaysLeft +$neverExpiresUsers = $reportData | Where-Object { $_.Status -eq "NeverExpires" } +$neverLoggedInUsers = $reportData | Where-Object { $_.Status -eq "NeverLoggedIn" } +$disabledUsers = $reportData | Where-Object { $_.Enabled -eq $false } + +$reportFileName = "PasswordExpirationReport_$(Get-Date -Format 'yyyyMMdd_HHmm').html" +$htmlReport = ConvertTo-HtmlReport -expiredUsers $expiredUsers -criticalUsers $criticalUsers -warningUsers $warningUsers -neverExpiresUsers $neverExpiresUsers -neverLoggedInUsers $neverLoggedInUsers -disabledUsers $disabledUsers -targetOU $TargetOU -passwordPolicy $passwordPolicy -warningThreshold $WarningThreshold -criticalThreshold $CriticalThreshold +$htmlReport | Out-File $reportFileName -Encoding UTF8 +Write-Host "Rapport généré avec succès : $reportFileName" +Write-Host "Résumé :" +Write-Host " - Comptes expirés: $($expiredUsers.Count)" +Write-Host " - Comptes critiques: $($criticalUsers.Count)" +Write-Host " - Comptes en avertissement: $($warningUsers.Count)" +Write-Host " - Comptes expirant jamais: $($neverExpiresUsers.Count)" +Write-Host " - Comptes jamais connectés: $($neverLoggedInUsers.Count)" +Write-Host " - Comptes désactivés: $($disabledUsers.Count)" + +if ($GenerateReportOnly) { + Write-Host "Option GenerateReportOnly activée, rapport généré uniquement. Arrêt du script." + exit 0 +} + +foreach ($user in $reportData | Where-Object { $_.Status -in @("Warning", "Critical", "Expired") }) { + if ($user.Email) { + $expirationDate = if ($user.ExpirationDate) { $user.ExpirationDate.ToString("dd/MM/yyyy") } else { "N/A" } + $subject = "Avertissement: Expiration de votre mot de passe" + $body = @" + + + + + + + +

Bonjour $($user.Name),

+

Votre mot de passe est dans un état $($user.Status).

+

Date d'expiration: $expirationDate

+

Veuillez mettre à jour votre mot de passe dès que possible pour éviter tout problème d'accès.

+

Cordialement,

+ + "@ - Send-UserNotification -Recipient $user.Email -Subject $subject -Body $body -SmtpServer $SmtpServer -Port $SmtpPort -FromAddress $FromEmail - } - else { - Write-Warning "L'utilisateur $($user.Name) n'a pas d'adresse email définie dans Active Directory." - } - } - - if ($AdminEmails) { - if ($reportData.Count -gt 0) { - $smtpServer = $SmtpServer - $smtpPort = $SmtpPort - $fromAddress = $FromEmail - $subject = "Rapport hebdomadaire d'expiration des mots de passe" - $body = $htmlReport - Send-EmailReport -Recipients $AdminEmails -Subject $subject -Body $body -SmtpServer $smtpServer -Port $smtpPort -FromAddress $fromAddress -Attachments @() - } - } else { - Write-Warning "ADMIN_EMAIL n'est pas défini. Aucun email administrateur ne sera envoyé." - } + Send-UserNotification -Recipient $user.Email -Subject $subject -Body $body -SmtpServer $SmtpServer -Port $SmtpPort -FromAddress $FromEmail + } + else { + Write-Warning "L'utilisateur $($user.Name) n'a pas d'adresse email définie dans Active Directory." + } +} + +if ($AdminEmails) { + if ($reportData.Count -gt 0) { + $smtpServer = $SmtpServer + $smtpPort = $SmtpPort + $fromAddress = $FromEmail + $subject = "Rapport hebdomadaire d'expiration des mots de passe" + $body = $htmlReport + Send-EmailReport -Recipients $AdminEmails -Subject $subject -Body $body -SmtpServer $smtpServer -Port $smtpPort -FromAddress $fromAddress -Attachments @() + } +} else { + Write-Warning "ADMIN_EMAIL n'est pas défini. Aucun email administrateur ne sera envoyé." +} \ No newline at end of file From 3ccb4671f4b0e8ac3d7c6448f443ad0ec647d210 Mon Sep 17 00:00:00 2001 From: P6g9YHK6 <17877371+P6g9YHK6@users.noreply.github.com> Date: Mon, 9 Jun 2025 09:50:31 +0200 Subject: [PATCH 04/23] Update file: Mail notification password expiry.ps1 --- .../Mail notification password expiry.ps1 | 236 ++++++++++++++---- 1 file changed, 182 insertions(+), 54 deletions(-) diff --git a/scripts_staging/Backend/Mail notification password expiry.ps1 b/scripts_staging/Backend/Mail notification password expiry.ps1 index 1e05d928..83d0563d 100644 --- a/scripts_staging/Backend/Mail notification password expiry.ps1 +++ b/scripts_staging/Backend/Mail notification password expiry.ps1 @@ -139,92 +139,146 @@ function ConvertTo-HtmlReport { $warningThreshold, $criticalThreshold ) + $html = @" + + Rapport d'expiration des mots de passe +

Rapport d'expiration des mots de passe

-
+ +

Politique de mot de passe du domaine

-

Durée maximale du mot de passe: $($passwordPolicy.MaxPasswordAge.Days) jours

-

Durée minimale du mot de passe: $($passwordPolicy.MinPasswordAge.Days) jours

+

Durée maximale: $($passwordPolicy.MaxPasswordAge.Days) jours

+

Durée minimale: $($passwordPolicy.MinPasswordAge.Days) jours

Longueur minimale: $($passwordPolicy.MinPasswordLength) caractères

Complexité requise: $($passwordPolicy.ComplexityEnabled)

-

Historique du mot de passe: $($passwordPolicy.PasswordHistoryCount) mots de passe

-

Verrouillage de compte: $($passwordPolicy.LockoutThreshold) tentatives (durée: $($passwordPolicy.LockoutDuration.Minutes) minutes, observation: $($passwordPolicy.LockoutObservationWindow.Minutes) minutes)

+

Historique: $($passwordPolicy.PasswordHistoryCount) mots de passe

+

Verrouillage: $($passwordPolicy.LockoutThreshold) tentatives (durée: $($passwordPolicy.LockoutDuration.Minutes) min)

-
-

Seuil d'avertissement : $warningThreshold jours

-

Seuil critique : $criticalThreshold jours

-

Statistiques : + +

+

Statistiques globales

+

Expirés: $($expiredUsers.Count) Critiques: $($criticalUsers.Count) - Avertissement: $($warningUsers.Count) + Avertissements: $($warningUsers.Count) Expirent jamais: $($neverExpiresUsers.Count) Jamais connectés: $($neverLoggedInUsers.Count) Désactivés: $($disabledUsers.Count)

-"@ - if ($expiredUsers) { - $html += "

Comptes expirés $($expiredUsers.Count)

" - $html += $expiredUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={$_.ExpirationDate.ToString("dd/MM/yyyy")}}, DaysLeft, Enabled | ConvertTo-Html -Fragment + @if ($expiredUsers) { +
+

Comptes expirés

+ $expiredUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={$_.ExpirationDate.ToString("dd/MM/yyyy")}}, DaysLeft, Enabled | ConvertTo-Html -Fragment +
} - if ($criticalUsers) { - $html += "

Comptes critiques $($criticalUsers.Count)

" - $html += $criticalUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={$_.ExpirationDate.ToString("dd/MM/yyyy")}}, DaysLeft, Enabled | ConvertTo-Html -Fragment + @if ($criticalUsers) { +
+

Comptes critiques

+ $criticalUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={$_.ExpirationDate.ToString("dd/MM/yyyy")}}, DaysLeft, Enabled | ConvertTo-Html -Fragment +
} - if ($warningUsers) { - $html += "

Comptes en avertissement $($warningUsers.Count)

" - $html += $warningUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={$_.ExpirationDate.ToString("dd/MM/yyyy")}}, DaysLeft, Enabled | ConvertTo-Html -Fragment + @if ($warningUsers) { +
+

Comptes en avertissement

+ $warningUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={$_.ExpirationDate.ToString("dd/MM/yyyy")}}, DaysLeft, Enabled | ConvertTo-Html -Fragment +
} - if ($IncludeNeverExpires -and $neverExpiresUsers) { - $html += "

Comptes avec mot de passe n'expirant jamais $($neverExpiresUsers.Count)

" - $html += $neverExpiresUsers | Select-Object Name, SamAccountName, Email, Enabled | ConvertTo-Html -Fragment + @if ($IncludeNeverExpires -and $neverExpiresUsers) { +
+

Comptes avec mot de passe n'expirant jamais

+ $neverExpiresUsers | Select-Object Name, SamAccountName, Email, Enabled | ConvertTo-Html -Fragment +
} - if ($neverLoggedInUsers) { - $html += "

Comptes jamais connectés $($neverLoggedInUsers.Count)

" - $html += $neverLoggedInUsers | Select-Object Name, SamAccountName, Email, Enabled | ConvertTo-Html -Fragment + @if ($neverLoggedInUsers) { +
+

Comptes jamais connectés

+ $neverLoggedInUsers | Select-Object Name, SamAccountName, Email, Enabled | ConvertTo-Html -Fragment +
} - if ($IncludeDisabled -and $disabledUsers) { - $html += "

Comptes désactivés $($disabledUsers.Count)

" - $html += $disabledUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={if($_.ExpirationDate){$_.ExpirationDate.ToString("dd/MM/yyyy")}else{"N/A"}}}, DaysLeft | ConvertTo-Html -Fragment + @if ($IncludeDisabled -and $disabledUsers) { +
+

Comptes désactivés

+ $disabledUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={if($_.ExpirationDate){$_.ExpirationDate.ToString("dd/MM/yyyy")}else{"N/A"}}}, DaysLeft | ConvertTo-Html -Fragment +
} - $html += @" -

Généré le : $(Get-Date -Format "dd/MM/yyyy HH:mm")

+ +
"@ @@ -440,18 +494,92 @@ foreach ($user in $reportData | Where-Object { $_.Status -in @("Warning", "Criti +
+

⚠️ Avertissement : Expiration de votre mot de passe

Bonjour $($user.Name),

-

Votre mot de passe est dans un état $($user.Status).

+

Votre mot de passe est dans un état $($user.Status).

Date d'expiration: $expirationDate

Veuillez mettre à jour votre mot de passe dès que possible pour éviter tout problème d'accès.

-

Cordialement,

+ + + + + + + + + + + + + + + + + + + + + + +
Nom$($user.Name)
SAM Account Name$($user.SamAccountName)
Email$($user.Email)
Date d'expiration$expirationDate
Jours restants$($user.DaysLeft)
+ +

Cordialement,
+ Service Informatique

+
"@ From 111dde795744ff49883c3e4ff6e879b2056bb3b9 Mon Sep 17 00:00:00 2001 From: P6g9YHK6 <17877371+P6g9YHK6@users.noreply.github.com> Date: Mon, 9 Jun 2025 09:58:25 +0200 Subject: [PATCH 05/23] Update file: Mail notification password expiry.ps1 --- scripts_staging/Backend/Mail notification password expiry.ps1 | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts_staging/Backend/Mail notification password expiry.ps1 b/scripts_staging/Backend/Mail notification password expiry.ps1 index 83d0563d..77fd05dc 100644 --- a/scripts_staging/Backend/Mail notification password expiry.ps1 +++ b/scripts_staging/Backend/Mail notification password expiry.ps1 @@ -144,8 +144,6 @@ function ConvertTo-HtmlReport { - - Rapport d'expiration des mots de passe +
$body -$signature +
"@ @@ -536,12 +583,6 @@ foreach ($user in $reportData | Where-Object { $_.Status -in @("Warning", "Criti background-color: #3498db; color: white; } - .footer { - margin-top: 40px; - font-size: 0.9em; - color: #777; - text-align: center; - } @@ -574,9 +615,6 @@ foreach ($user in $reportData | Where-Object { $_.Status -in @("Warning", "Criti $($user.DaysLeft) - -

Cordialement,
- Service Informatique

From 7f9bcd6ab6d1214a2004ffcffbd2335613c2b863 Mon Sep 17 00:00:00 2001 From: P6g9YHK6 <17877371+P6g9YHK6@users.noreply.github.com> Date: Mon, 9 Jun 2025 14:45:31 +0200 Subject: [PATCH 07/23] Update file: Mail notification password expiry.ps1 --- .../Backend/Mail notification password expiry.ps1 | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts_staging/Backend/Mail notification password expiry.ps1 b/scripts_staging/Backend/Mail notification password expiry.ps1 index 13741e89..4410b0ee 100644 --- a/scripts_staging/Backend/Mail notification password expiry.ps1 +++ b/scripts_staging/Backend/Mail notification password expiry.ps1 @@ -400,13 +400,15 @@ function Send-UserNotification { margin-top: 20px; } th, td { - padding: 12px; + padding: 8px; /* Réduit l'espace */ border-bottom: 1px solid #ddd; text-align: left; + font-size: 14px; /* Police compacte */ } th { background-color: #3498db; color: white; + font-size: 14px; /* Police compacte */ } @@ -421,7 +423,7 @@ $body $mailMessage = New-Object System.Net.Mail.MailMessage $mailMessage.From = $FromAddress $mailMessage.To.Add($Recipient) - $mailMessage.Subject = $Subject + $mailMessage.Subject = $subject $mailMessage.Body = $bodyWithSignature $mailMessage.IsBodyHtml = $true $smtpClient = New-Object System.Net.Mail.SmtpClient($SmtpServer, $Port) @@ -575,13 +577,15 @@ foreach ($user in $reportData | Where-Object { $_.Status -in @("Warning", "Criti margin-top: 20px; } th, td { - padding: 12px; + padding: 8px; /* Réduit l'espace */ border-bottom: 1px solid #ddd; text-align: left; + font-size: 14px; /* Police compacte */ } th { background-color: #3498db; color: white; + font-size: 14px; /* Police compacte */ } From eae28d216f0f55e5aacc844183e41dc0f8a1717c Mon Sep 17 00:00:00 2001 From: P6g9YHK6 <17877371+P6g9YHK6@users.noreply.github.com> Date: Mon, 9 Jun 2025 14:50:43 +0200 Subject: [PATCH 08/23] Update file: Mail notification password expiry.ps1 --- .../Mail notification password expiry.ps1 | 152 ++++++++++++++++-- 1 file changed, 140 insertions(+), 12 deletions(-) diff --git a/scripts_staging/Backend/Mail notification password expiry.ps1 b/scripts_staging/Backend/Mail notification password expiry.ps1 index 4410b0ee..5f91e853 100644 --- a/scripts_staging/Backend/Mail notification password expiry.ps1 +++ b/scripts_staging/Backend/Mail notification password expiry.ps1 @@ -231,45 +231,173 @@ function ConvertTo-HtmlReport {

- @if ($expiredUsers) { + @if ($expiredUsers.Count -gt 0) {

Comptes expirés

- $expiredUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={$_.ExpirationDate.ToString("dd/MM/yyyy")}}, DaysLeft, Enabled | ConvertTo-Html -Fragment + + + + + + + + + + + + + $($expiredUsers | ForEach-Object { + " + + + + + + + " + }) + +
NomSAM Account NameEmailDate d'expirationJours restantsActivé
$($_.Name)$($_.SamAccountName)$($_.Email)$($_.ExpirationDate.ToString('dd/MM/yyyy'))$($_.DaysLeft)$($_.Enabled)
} - @if ($criticalUsers) { + @if ($criticalUsers.Count -gt 0) {

Comptes critiques

- $criticalUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={$_.ExpirationDate.ToString("dd/MM/yyyy")}}, DaysLeft, Enabled | ConvertTo-Html -Fragment + + + + + + + + + + + + + $($criticalUsers | ForEach-Object { + " + + + + + + + " + }) + +
NomSAM Account NameEmailDate d'expirationJours restantsActivé
$($_.Name)$($_.SamAccountName)$($_.Email)$($_.ExpirationDate.ToString('dd/MM/yyyy'))$($_.DaysLeft)$($_.Enabled)
} - @if ($warningUsers) { + @if ($warningUsers.Count -gt 0) {

Comptes en avertissement

- $warningUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={$_.ExpirationDate.ToString("dd/MM/yyyy")}}, DaysLeft, Enabled | ConvertTo-Html -Fragment + + + + + + + + + + + + + $($warningUsers | ForEach-Object { + " + + + + + + + " + }) + +
NomSAM Account NameEmailDate d'expirationJours restantsActivé
$($_.Name)$($_.SamAccountName)$($_.Email)$($_.ExpirationDate.ToString('dd/MM/yyyy'))$($_.DaysLeft)$($_.Enabled)
} - @if ($IncludeNeverExpires -and $neverExpiresUsers) { + @if ($IncludeNeverExpires -and $neverExpiresUsers.Count -gt 0) {

Comptes avec mot de passe n'expirant jamais

- $neverExpiresUsers | Select-Object Name, SamAccountName, Email, Enabled | ConvertTo-Html -Fragment + + + + + + + + + + + $($neverExpiresUsers | ForEach-Object { + " + + + + + " + }) + +
NomSAM Account NameEmailActivé
$($_.Name)$($_.SamAccountName)$($_.Email)$($_.Enabled)
} - @if ($neverLoggedInUsers) { + @if ($neverLoggedInUsers.Count -gt 0) {

Comptes jamais connectés

- $neverLoggedInUsers | Select-Object Name, SamAccountName, Email, Enabled | ConvertTo-Html -Fragment + + + + + + + + + + + $($neverLoggedInUsers | ForEach-Object { + " + + + + + " + }) + +
NomSAM Account NameEmailActivé
$($_.Name)$($_.SamAccountName)$($_.Email)$($_.Enabled)
} - @if ($IncludeDisabled -and $disabledUsers) { + @if ($IncludeDisabled -and $disabledUsers.Count -gt 0) {

Comptes désactivés

- $disabledUsers | Select-Object Name, SamAccountName, Email, @{Name="ExpirationDate";Expression={if($_.ExpirationDate){$_.ExpirationDate.ToString("dd/MM/yyyy")}else{"N/A"}}}, DaysLeft | ConvertTo-Html -Fragment + + + + + + + + + + + + $($disabledUsers | ForEach-Object { + " + + + + + + " + }) + +
NomSAM Account NameEmailDate d'expirationJours restants
$($_.Name)$($_.SamAccountName)$($_.Email)$($_.ExpirationDate.ToString('dd/MM/yyyy'))$($_.DaysLeft)
} From f477e086b3f30594fc4a7b4feaae450ef310ed9b Mon Sep 17 00:00:00 2001 From: P6g9YHK6 <17877371+P6g9YHK6@users.noreply.github.com> Date: Mon, 9 Jun 2025 14:56:31 +0200 Subject: [PATCH 09/23] Update file: Mail notification password expiry.ps1 --- .../Mail notification password expiry.ps1 | 348 +++++++++--------- 1 file changed, 183 insertions(+), 165 deletions(-) diff --git a/scripts_staging/Backend/Mail notification password expiry.ps1 b/scripts_staging/Backend/Mail notification password expiry.ps1 index 5f91e853..43578d00 100644 --- a/scripts_staging/Backend/Mail notification password expiry.ps1 +++ b/scripts_staging/Backend/Mail notification password expiry.ps1 @@ -139,100 +139,21 @@ function ConvertTo-HtmlReport { $warningThreshold, $criticalThreshold ) - $html = @" - - - - - - Rapport d'expiration des mots de passe - - - -
-

Rapport d'expiration des mots de passe

- -
-

Politique de mot de passe du domaine

-

Durée maximale: $($passwordPolicy.MaxPasswordAge.Days) jours

-

Durée minimale: $($passwordPolicy.MinPasswordAge.Days) jours

-

Longueur minimale: $($passwordPolicy.MinPasswordLength) caractères

-

Complexité requise: $($passwordPolicy.ComplexityEnabled)

-

Historique: $($passwordPolicy.PasswordHistoryCount) mots de passe

-

Verrouillage: $($passwordPolicy.LockoutThreshold) tentatives (durée: $($passwordPolicy.LockoutDuration.Minutes) min)

-
- -
-

Statistiques globales

-

- Expirés: $($expiredUsers.Count) - Critiques: $($criticalUsers.Count) - Avertissements: $($warningUsers.Count) - Expirent jamais: $($neverExpiresUsers.Count) - Jamais connectés: $($neverLoggedInUsers.Count) - Désactivés: $($disabledUsers.Count) -

-
- - @if ($expiredUsers.Count -gt 0) { -
+ # Génération des sections conditionnelles + $expiredSection = "" + if ($expiredUsers.Count -gt 0) { + $rows = $expiredUsers | ForEach-Object { + " + $($_.Name) + $($_.SamAccountName) + $($_.Email) + $($_.ExpirationDate.ToString('dd/MM/yyyy')) + $($_.DaysLeft) + $($_.Enabled) + " + } | Out-String + $expiredSection = @" +

Comptes expirés

@@ -246,23 +167,26 @@ function ConvertTo-HtmlReport { - $($expiredUsers | ForEach-Object { - " - - - - - - - " - }) + $rows
$($_.Name)$($_.SamAccountName)$($_.Email)$($_.ExpirationDate.ToString('dd/MM/yyyy'))$($_.DaysLeft)$($_.Enabled)
+"@ } - - @if ($criticalUsers.Count -gt 0) { -
+ $criticalSection = "" + if ($criticalUsers.Count -gt 0) { + $rows = $criticalUsers | ForEach-Object { + " + $($_.Name) + $($_.SamAccountName) + $($_.Email) + $($_.ExpirationDate.ToString('dd/MM/yyyy')) + $($_.DaysLeft) + $($_.Enabled) + " + } | Out-String + $criticalSection = @" +

Comptes critiques

@@ -276,23 +200,26 @@ function ConvertTo-HtmlReport { - $($criticalUsers | ForEach-Object { - " - - - - - - - " - }) + $rows
$($_.Name)$($_.SamAccountName)$($_.Email)$($_.ExpirationDate.ToString('dd/MM/yyyy'))$($_.DaysLeft)$($_.Enabled)
+"@ } - - @if ($warningUsers.Count -gt 0) { -
+ $warningSection = "" + if ($warningUsers.Count -gt 0) { + $rows = $warningUsers | ForEach-Object { + " + $($_.Name) + $($_.SamAccountName) + $($_.Email) + $($_.ExpirationDate.ToString('dd/MM/yyyy')) + $($_.DaysLeft) + $($_.Enabled) + " + } | Out-String + $warningSection = @" +

Comptes en avertissement

@@ -306,23 +233,24 @@ function ConvertTo-HtmlReport { - $($warningUsers | ForEach-Object { - " - - - - - - - " - }) + $rows
$($_.Name)$($_.SamAccountName)$($_.Email)$($_.ExpirationDate.ToString('dd/MM/yyyy'))$($_.DaysLeft)$($_.Enabled)
+"@ } - - @if ($IncludeNeverExpires -and $neverExpiresUsers.Count -gt 0) { -
+ $neverExpiresSection = "" + if ($IncludeNeverExpires -and $neverExpiresUsers.Count -gt 0) { + $rows = $neverExpiresUsers | ForEach-Object { + " + $($_.Name) + $($_.SamAccountName) + $($_.Email) + $($_.Enabled) + " + } | Out-String + $neverExpiresSection = @" +

Comptes avec mot de passe n'expirant jamais

@@ -334,21 +262,24 @@ function ConvertTo-HtmlReport { - $($neverExpiresUsers | ForEach-Object { - " - - - - - " - }) + $rows
$($_.Name)$($_.SamAccountName)$($_.Email)$($_.Enabled)
+"@ } - - @if ($neverLoggedInUsers.Count -gt 0) { -
+ $neverLoggedInSection = "" + if ($neverLoggedInUsers.Count -gt 0) { + $rows = $neverLoggedInUsers | ForEach-Object { + " + $($_.Name) + $($_.SamAccountName) + $($_.Email) + $($_.Enabled) + " + } | Out-String + $neverLoggedInSection = @" +

Comptes jamais connectés

@@ -360,21 +291,25 @@ function ConvertTo-HtmlReport { - $($neverLoggedInUsers | ForEach-Object { - " - - - - - " - }) + $rows
$($_.Name)$($_.SamAccountName)$($_.Email)$($_.Enabled)
+"@ } - - @if ($IncludeDisabled -and $disabledUsers.Count -gt 0) { -
+ $disabledSection = "" + if ($IncludeDisabled -and $disabledUsers.Count -gt 0) { + $rows = $disabledUsers | ForEach-Object { + " + $($_.Name) + $($_.SamAccountName) + $($_.Email) + $($_.ExpirationDate.ToString('dd/MM/yyyy')) + $($_.DaysLeft) + " + } | Out-String + $disabledSection = @" +

Comptes désactivés

@@ -387,20 +322,103 @@ function ConvertTo-HtmlReport { - $($disabledUsers | ForEach-Object { - " - - - - - - " - }) + $rows
$($_.Name)$($_.SamAccountName)$($_.Email)$($_.ExpirationDate.ToString('dd/MM/yyyy'))$($_.DaysLeft)
+"@ } - + # Assemblage du rapport HTML final + $html = @" + + + + + + Rapport d'expiration des mots de passe + + + +
+

Rapport d'expiration des mots de passe

+
+

Politique de mot de passe du domaine

+

Durée maximale: $($passwordPolicy.MaxPasswordAge.Days) jours

+

Durée minimale: $($passwordPolicy.MinPasswordAge.Days) jours

+

Longueur minimale: $($passwordPolicy.MinPasswordLength) caractères

+

Complexité requise: $($passwordPolicy.ComplexityEnabled)

+

Historique: $($passwordPolicy.PasswordHistoryCount) mots de passe

+

Verrouillage: $($passwordPolicy.LockoutThreshold) tentatives (durée: $($passwordPolicy.LockoutDuration.Minutes) min)

+
+
+

Statistiques globales

+

+ Expirés: $($expiredUsers.Count) + Critiques: $($criticalUsers.Count) + Avertissements: $($warningUsers.Count) + Expirent jamais: $($neverExpiresUsers.Count) + Jamais connectés: $($neverLoggedInUsers.Count) + Désactivés: $($disabledUsers.Count) +

+
+ $expiredSection + $criticalSection + $warningSection + $neverExpiresSection + $neverLoggedInSection + $disabledSection From d842f70db0e57507b151753e0a3686232881081a Mon Sep 17 00:00:00 2001 From: P6g9YHK6 <17877371+P6g9YHK6@users.noreply.github.com> Date: Mon, 9 Jun 2025 15:04:31 +0200 Subject: [PATCH 10/23] Update file: Mail notification password expiry.ps1 --- .../Backend/Mail notification password expiry.ps1 | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts_staging/Backend/Mail notification password expiry.ps1 b/scripts_staging/Backend/Mail notification password expiry.ps1 index 43578d00..2ef00e08 100644 --- a/scripts_staging/Backend/Mail notification password expiry.ps1 +++ b/scripts_staging/Backend/Mail notification password expiry.ps1 @@ -520,7 +520,7 @@ function Send-UserNotification { .container { max-width: 700px; margin: 0 auto; - padding: 30px; + padding: 15px; // reduced from 30px to 15px background-color: #fff; border-radius: 10px; box-shadow: 0 0 10px rgba(0,0,0,0.05); @@ -528,11 +528,11 @@ function Send-UserNotification { h1 { color: #2c3e50; border-bottom: 2px solid #3498db; - padding-bottom: 10px; + padding-bottom: 5px; // reduced from 10px to 5px } .status { font-weight: bold; - margin-top: 15px; + margin-top: 5px; // reduced from 15px to 5px display: inline-block; padding: 6px 12px; border-radius: 5px; @@ -543,18 +543,18 @@ function Send-UserNotification { table { width: 100%; border-collapse: collapse; - margin-top: 20px; + margin-top: 5px; // reduced from 10px to 5px } th, td { - padding: 8px; /* Réduit l'espace */ + padding: 2px; /* reduced from 4px to 2px */ border-bottom: 1px solid #ddd; text-align: left; - font-size: 14px; /* Police compacte */ + font-size: 14px; } th { background-color: #3498db; color: white; - font-size: 14px; /* Police compacte */ + font-size: 14px; } From 5c58624995124d64f009a457195feadb285f720e Mon Sep 17 00:00:00 2001 From: P6g9YHK6 <17877371+P6g9YHK6@users.noreply.github.com> Date: Mon, 9 Jun 2025 15:11:58 +0200 Subject: [PATCH 11/23] Update file: Mail notification password expiry.ps1 --- .../Backend/Mail notification password expiry.ps1 | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/scripts_staging/Backend/Mail notification password expiry.ps1 b/scripts_staging/Backend/Mail notification password expiry.ps1 index 2ef00e08..29b0ed73 100644 --- a/scripts_staging/Backend/Mail notification password expiry.ps1 +++ b/scripts_staging/Backend/Mail notification password expiry.ps1 @@ -520,7 +520,7 @@ function Send-UserNotification { .container { max-width: 700px; margin: 0 auto; - padding: 15px; // reduced from 30px to 15px + padding: 15px; // réduit par rapport à 30px background-color: #fff; border-radius: 10px; box-shadow: 0 0 10px rgba(0,0,0,0.05); @@ -528,11 +528,11 @@ function Send-UserNotification { h1 { color: #2c3e50; border-bottom: 2px solid #3498db; - padding-bottom: 5px; // reduced from 10px to 5px + padding-bottom: 5px; // réduit par rapport à 10px } .status { font-weight: bold; - margin-top: 5px; // reduced from 15px to 5px + margin-top: 5px; // réduit par rapport à 15px display: inline-block; padding: 6px 12px; border-radius: 5px; @@ -543,13 +543,14 @@ function Send-UserNotification { table { width: 100%; border-collapse: collapse; - margin-top: 5px; // reduced from 10px to 5px + margin-top: 2px; // espace vertical réduit } th, td { - padding: 2px; /* reduced from 4px to 2px */ + padding: 0px 2px; /* Padding réduit pour correspondre à la police */ border-bottom: 1px solid #ddd; text-align: left; font-size: 14px; + line-height: 1.0; /* Hauteur de ligne réduite */ } th { background-color: #3498db; From 8d80ca339a5e4e650ef9b6f8f71e76474d100ec0 Mon Sep 17 00:00:00 2001 From: P6g9YHK6 <17877371+P6g9YHK6@users.noreply.github.com> Date: Mon, 9 Jun 2025 15:17:24 +0200 Subject: [PATCH 12/23] Update file: Mail notification password expiry.ps1 --- scripts_staging/Backend/Mail notification password expiry.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts_staging/Backend/Mail notification password expiry.ps1 b/scripts_staging/Backend/Mail notification password expiry.ps1 index 29b0ed73..ba8b4987 100644 --- a/scripts_staging/Backend/Mail notification password expiry.ps1 +++ b/scripts_staging/Backend/Mail notification password expiry.ps1 @@ -546,11 +546,11 @@ function Send-UserNotification { margin-top: 2px; // espace vertical réduit } th, td { - padding: 0px 2px; /* Padding réduit pour correspondre à la police */ + padding: 0px 2px; /* Padding réduit pour minimiser la hauteur des lignes */ border-bottom: 1px solid #ddd; text-align: left; font-size: 14px; - line-height: 1.0; /* Hauteur de ligne réduite */ + line-height: 0.8; /* Hauteur de ligne réduite pour correspondre à la taille de la police */ } th { background-color: #3498db; From 527c98b1e79f8fc41cd0ebb3f11b37fcad4781cc Mon Sep 17 00:00:00 2001 From: P6g9YHK6 <17877371+P6g9YHK6@users.noreply.github.com> Date: Mon, 9 Jun 2025 15:22:49 +0200 Subject: [PATCH 13/23] Update file: Mail notification password expiry.ps1 --- scripts_staging/Backend/Mail notification password expiry.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts_staging/Backend/Mail notification password expiry.ps1 b/scripts_staging/Backend/Mail notification password expiry.ps1 index ba8b4987..80f2a04b 100644 --- a/scripts_staging/Backend/Mail notification password expiry.ps1 +++ b/scripts_staging/Backend/Mail notification password expiry.ps1 @@ -551,6 +551,7 @@ function Send-UserNotification { text-align: left; font-size: 14px; line-height: 0.8; /* Hauteur de ligne réduite pour correspondre à la taille de la police */ + height: 18px; // fixed row height regardless of font size } th { background-color: #3498db; From 224999dc960df857406398fab718c25b333e1afa Mon Sep 17 00:00:00 2001 From: P6g9YHK6 <17877371+P6g9YHK6@users.noreply.github.com> Date: Tue, 10 Jun 2025 08:46:01 +0200 Subject: [PATCH 14/23] Update file: Mail notification password expiry.ps1 --- .../Backend/Mail notification password expiry.ps1 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts_staging/Backend/Mail notification password expiry.ps1 b/scripts_staging/Backend/Mail notification password expiry.ps1 index 80f2a04b..a0f31222 100644 --- a/scripts_staging/Backend/Mail notification password expiry.ps1 +++ b/scripts_staging/Backend/Mail notification password expiry.ps1 @@ -13,6 +13,7 @@ .CHANGELOG 22.05.25 SAN Added UTF8 to fix encoding issue with russian & french chars 06.06.25 PQU Added support for multiple admin emails + 10.06.25 PQU Modification of the visual of the email #> if (!($PSVersionTable.PSVersion.Major -ge 7)) { if (Get-Command pwsh -ErrorAction SilentlyContinue) { @@ -139,7 +140,7 @@ function ConvertTo-HtmlReport { $warningThreshold, $criticalThreshold ) - # Génération des sections conditionnelles + $expiredSection = "" if ($expiredUsers.Count -gt 0) { $rows = $expiredUsers | ForEach-Object { @@ -328,7 +329,7 @@ function ConvertTo-HtmlReport {
"@ } - # Assemblage du rapport HTML final + $html = @" From 750a0fb9b3a4ceb22c8f6c45612e7ca8a00eda38 Mon Sep 17 00:00:00 2001 From: P6g9YHK6 <17877371+P6g9YHK6@users.noreply.github.com> Date: Wed, 11 Jun 2025 09:31:23 +0200 Subject: [PATCH 15/23] Update file: Mail notification password expiry.ps1 --- .../Mail notification password expiry.ps1 | 64 ++++++++++++------- 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/scripts_staging/Backend/Mail notification password expiry.ps1 b/scripts_staging/Backend/Mail notification password expiry.ps1 index a0f31222..10ce765f 100644 --- a/scripts_staging/Backend/Mail notification password expiry.ps1 +++ b/scripts_staging/Backend/Mail notification password expiry.ps1 @@ -1,31 +1,49 @@ <# .SYNOPSIS - Ensures the script is executed using PowerShell 7 or higher. + Analyse et notification avancée des utilisateurs dont le mot de passe Active Directory approche de l’expiration. .DESCRIPTION - This script verifies whether it is running in a PowerShell 7+ environment. - If not, and if PowerShell 7 (pwsh) is available on the system, it re-invokes itself using pwsh, passing along any parameters. - If pwsh is not found, the script outputs a message and exits with an error code. - Once running in PowerShell 7 or higher, it sets the output rendering mode to plaintext for consistent formatting. + Ce script interroge Active Directory pour lister les utilisateurs d’une OU cible et calcule la date d’expiration de leur mot de passe selon la politique du domaine. + Les comptes sont classés selon l’urgence : + - Expiré : mot de passe déjà expiré + - Critique : expiration imminente (seuil critique) + - Avertissement : expiration proche (seuil d’avertissement) + Notifications automatiques : + • Email pour tous les utilisateurs concernés + Un rapport HTML détaillé est généré : + • Politique de mot de passe du domaine + • Statistiques par catégorie + • Liste détaillée des comptes par statut +.PARAMETER TargetOU + OU cible pour la recherche des utilisateurs (ex : "OU=Utilisateurs,DC=domaine,DC=local") +.PARAMETER WarningThreshold + Jours avant expiration pour déclencher un avertissement (défaut : 15) +.PARAMETER CriticalThreshold + Jours avant expiration pour déclencher une alerte critique (défaut : 7) +.PARAMETER IncludeDisabled + Inclure les comptes désactivés dans le rapport (défaut : false) +.PARAMETER IncludeNeverExpires + Inclure les comptes dont le mot de passe n’expire jamais (défaut : false) +.PARAMETER EmailSignature + Signature personnalisée pour les emails (optionnel) .NOTES + Prérequis : + - Module ActiveDirectory + - Accès SMTP pour l’envoi d’emails + - Droits d’administration pour les tâches planifiées Author: PQU Date: 29/04/2025 #public .CHANGELOG - 22.05.25 SAN Added UTF8 to fix encoding issue with russian & french chars - 06.06.25 PQU Added support for multiple admin emails - 10.06.25 PQU Modification of the visual of the email + 22.05.25 SAN – Added UTF8 encoding to resolve issues with Russian and French characters. + 06.06.25 PQU – Added support for multiple admin emails and centralized config. #> -if (!($PSVersionTable.PSVersion.Major -ge 7)) { - if (Get-Command pwsh -ErrorAction SilentlyContinue) { - pwsh -File "`"$PSCommandPath`"" @PSBoundParameters - exit $LASTEXITCODE - } else { - Write-Output "ERROR: PowerShell 7 is not available. Exiting." - exit 1 - } + + +{{CallPowerShell7}} + +function Convert-ToBoolean($value) { + return $value -match '^(1|true|yes)$' } -[Console]::OutputEncoding = [Text.Encoding]::UTF8 -$PSStyle.OutputRendering = "plaintext" $TargetOU = $env:TARGET_OU $SmtpServer = $env:SMTP_SERVER @@ -35,15 +53,13 @@ $FromEmail = $env:FROM_EMAIL $WarningThreshold = [int]$env:WARNING_THRESHOLD $CriticalThreshold = [int]$env:CRITICAL_THRESHOLD $EmailSignature = $env:EMAIL_SIGNATURE - -function Convert-ToBoolean($value) { - return $value -match '^(1|true|yes)$' -} - $IncludeDisabled = Convert-ToBoolean $env:INCLUDE_DISABLED $IncludeNeverExpires = Convert-ToBoolean $env:INCLUDE_NEVER_EXPIRES $GenerateReportOnly = Convert-ToBoolean $env:GENERATE_REPORT_ONLY + + + if ($env:SMTP_CREDENTIAL_USERNAME -and $env:SMTP_CREDENTIAL_PASSWORD) { try { $SecurePassword = ConvertTo-SecureString $env:SMTP_CREDENTIAL_PASSWORD -AsPlainText -Force @@ -438,7 +454,7 @@ function Get-EmailSignature {