#Requires -Modules ActiveDirectory #Requires -Version 5.0 <# .SYNOPSIS Génère un rapport consolidé des anomalies Active Directory .DESCRIPTION Script qui regroupe tous les audits AD: - Utilisateurs avec mot de passe expirant prochainement - Comptes expirés depuis N jours - Comptes désactivés depuis N jours Génère des fichiers CSV et un rapport HTML consolid. .PARAMETER DaysAheadPassword Nombre de jours à l'avance pour les mots de passe expirant. Défaut: 30 .PARAMETER DaysBackExpired Nombre de jours à remonter pour les comptes expirés. Défaut: 60 .PARAMETER DaysBackDisabled Nombre de jours à remonter pour les comptes désactivés. Défaut: 60 .PARAMETER OutputPath Répertoire de sortie pour les rapports. Défaut: C:\Temp\AD_Reports .PARAMETER ExcludedObjectSid SID de l'objet à exclure (ex: BUILTIN\Invité). .PARAMETER ExcludedPrefixes Tableau des préfixes de compte à exclure. .EXAMPLE .\AD_Accounts_Disab_Expir_Audit.ps1 .EXAMPLE .\AD_Accounts_Disab_Expir_Audit.ps1 -DaysAheadPassword 15 -DaysBackExpired 90 -OutputPath "C:\Reports" #> param( [int]$DaysAheadPassword = 30, [int]$DaysBackExpired = 60, [int]$DaysBackDisabled = 60, [string]$OutputPath = "C:\Temp\AD_Reports", [string]$ExcludedObjectSid = "S-1-5-21-1801674531-261903793-725345543-501", [string[]]$ExcludedPrefixes = @("krbtgt", "TsInternetUser", "X_", "T_", "V_", "U_", "P_", "E_", "S_", "R_", "SERV_", "admapp_", "admpr_", "admex_") ) # ============================================================================ # FUNCTIONS # ============================================================================ function Write-Header { param([string]$Text) Write-Host "" Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Cyan Write-Host " $Text" -ForegroundColor Cyan Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Cyan } function Write-Section { param([string]$Text) Write-Host "➤ $Text" -ForegroundColor Yellow } function Write-Success { param([string]$Text) Write-Host " ✓ $Text" -ForegroundColor Green } function Write-Error-Custom { param([string]$Text) Write-Host " ✗ $Text" -ForegroundColor Red } function Build-ExclusionFilter { <# .SYNOPSIS Construit le filtre LDAP pour exclure les comptes de service #> $filterParts = @() foreach ($prefix in $ExcludedPrefixes) { $filterParts += "(!samaccountname=$prefix*)" } return ($filterParts -join "") } function Build-SelectedProperties { <# .SYNOPSIS Construit les propriétés à afficher dans le rapport #> return @( @{Name="Nom Affiche";Expression={$_."DisplayName"}}, @{Name="Nom";Expression={$_."sn"}}, @{Name="Prenom";Expression={$_."GivenName"}}, @{Name="Login";Expression={$_."SamAccountName"}}, @{Name="Description";Expression={$_."description"}}, @{Name="Adresse email";Expression={$_."Mail"}}, @{Name="Direction/Entite";Expression={$_."Division"}}, @{Name="Service";Expression={$_."department"}}, @{Name="Fonction";Expression={$_."title"}}, @{Name="Bureau";Expression={$_."PhysicalDeliveryOfficeName"}}, @{Name="Telephone fixe";Expression={$_."TelephoneNumber"}}, @{Name="Numero IP";Expression={$_."IPPhone"}}, @{Name="Telephone mobile";Expression={$_."Mobile"}}, @{Name="Code taxation societe";Expression={$_."extensionAttribute9"}}, @{Name="Code taxation service";Expression={$_."extensionAttribute10"}}, @{Name="Date modification";Expression={$_."whenChanged"}} ) } function Get-AllADProperties { <# .SYNOPSIS Retourne la liste complète des propriétés à récupérer #> return @( 'DisplayName', 'sn', 'GivenName', 'SamAccountName', 'description', 'Mail', 'Division', 'department', 'title', 'PhysicalDeliveryOfficeName', 'TelephoneNumber', 'IPPhone', 'Mobile', 'extensionAttribute9', 'extensionAttribute10', 'whenChanged', 'msDS-UserPasswordExpiryTimeComputed', 'PasswordNeverExpires', 'Enabled', 'accountExpires' ) } function Get-PasswordExpiringUsers { <# .SYNOPSIS Récupère les utilisateurs avec mots de passe expirant dans N jours #> Write-Section "Recherche des mots de passe expirant dans $DaysAheadPassword jours..." try { $DateStart = Get-Date $DateEnd = $DateStart.AddDays($DaysAheadPassword) $properties = Get-AllADProperties $exclusionFilter = Build-ExclusionFilter # Filtre pour les mots de passe expirant $filter = "(&(objectCategory=person)(objectClass=user)(enabled=TRUE)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(!(PasswordNeverExpires=TRUE))(!(objectSid=$ExcludedObjectSid))$exclusionFilter)" $allUsers = Get-ADUser -Filter {Enabled -eq $True -and PasswordNeverExpires -eq $False} -Properties $properties $results = @() foreach ($user in $allUsers) { $pwdExpiry = $user."msDS-UserPasswordExpiryTimeComputed" if ($pwdExpiry) { try { $expiryDate = [DateTime]::FromFileTime($pwdExpiry) if ($expiryDate -gt $DateStart -and $expiryDate -le $DateEnd) { $userobj = $user | Select-Object (Build-SelectedProperties) $userobj | Add-Member -MemberType NoteProperty -Name "Date expiration MdP" -Value ($expiryDate.ToString('dd/MM/yyyy')) -Force $results += $userobj } } catch { } } } Write-Success "$($results.Count) utilisateur(s) avec mot de passe expirant" return $results | Sort-Object "Date expiration MdP" } catch { Write-Error-Custom "Erreur: $_" return @() } } function Get-ExpiredAccounts { <# .SYNOPSIS Récupère les comptes expirés depuis N jours #> Write-Section "Recherche des comptes expirés depuis $DaysBackExpired jours..." try { $Date = (Get-Date).AddDays(-$DaysBackExpired) $properties = Get-AllADProperties $exclusionFilter = Build-ExclusionFilter $filter = "(&(!useraccountcontrol:1.2.840.113556.1.4.803:=2)(objectCategory=person)(objectClass=user)(accountExpires<=$($Date.ToFileTime()))(!(|(accountExpires=9223372036854775807)(accountExpires=0)))(!objectSid=$ExcludedObjectSid)$exclusionFilter)" $results = Get-ADUser -LDAPFilter $filter -Properties $properties | Where-Object { $ae = $_.accountExpires $ae -gt 0 -and $ae -ne 9223372036854775807 } | ForEach-Object { $user = $_ $expiryDate = [DateTime]::FromFiletime([Int64]($user.accountExpires)) $userobj = $user | Select-Object (Build-SelectedProperties) $userobj | Add-Member -MemberType NoteProperty -Name "Date expiration" -Value ($expiryDate.ToString('dd/MM/yyyy')) -Force $userobj } | Sort-Object "Date expiration" Write-Success "$($results.Count) compte(s) expiré(s)" return @($results) } catch { Write-Error-Custom "Erreur: $_" return @() } } function Get-DisabledAccounts { <# .SYNOPSIS Récupère les comptes désactivés depuis N jours #> Write-Section "Recherche des comptes désactivés depuis $DaysBackDisabled jours..." try { $Date = (Get-Date).AddDays(-$DaysBackDisabled) $properties = Get-AllADProperties $exclusionFilter = Build-ExclusionFilter $filterParts = @( "(&(useraccountcontrol:1.2.840.113556.1.4.803:=2)(objectclass=user)" "(!objectSid=$ExcludedObjectSid)" ) $filter = $filterParts -join "" + "$exclusionFilter)" $results = Get-ADUser -LDAPFilter $filter -Properties $properties | Select-Object (Build-SelectedProperties) | Sort-Object "Nom Affiche" Write-Success "$($results.Count) compte(s) désactivé(s)" return @($results) } catch { Write-Error-Custom "Erreur: $_" return @() } } function Export-ToCSV { <# .SYNOPSIS Exporte les données en fichier CSV #> param( [array]$Data, [string]$FileName ) $filePath = Join-Path $OutputPath $FileName if ($Data -and $Data.Count -gt 0) { $Data | Export-Csv -Encoding UTF8 -NoTypeInformation -Path $filePath -Force Write-Success "Exporté: $FileName" } else { "Aucune donnée à exporter." | Out-File -Encoding UTF8 -FilePath $filePath -Force Write-Host " ⚠ Exporté (vide): $FileName" -ForegroundColor Gray } return $filePath } function Generate-HTMLReport { <# .SYNOPSIS Génère un rapport HTML consolidé #> param( [array]$PasswordExpiringUsers, [array]$ExpiredAccounts, [array]$DisabledAccounts ) Write-Section "Génération du rapport HTML..." $timestamp = Get-Date -Format "dd/MM/yyyy HH:mm:ss" $htmlPath = Join-Path $OutputPath "AD_Consolidated_Report.html" $totalIssues = ($PasswordExpiringUsers.Count) + ($ExpiredAccounts.Count) + ($DisabledAccounts.Count) $htmlContent = @" Audit de l'expiration et desactivation des comptes Active Directory

📊 Audit de l'expiration et desactivation des comptes Active Directory

Généré le $timestamp

Nombre total de comptes détectés: $totalIssues

Mots de passe expirant

${($PasswordExpiringUsers.Count)}

Comptes expirés

${($ExpiredAccounts.Count)}

Comptes désactivés

${($DisabledAccounts.Count)}
"@ # Section Mots de passe expirant $htmlContent += "
" $htmlContent += "

🔐 Mots de passe expirant prochainement (dans $DaysAheadPassword jours)

" if ($PasswordExpiringUsers -and $PasswordExpiringUsers.Count -gt 0) { $htmlContent += "" $htmlContent += "" $htmlContent += "" foreach ($user in $PasswordExpiringUsers) { $htmlContent += "" $htmlContent += "" $htmlContent += "" $htmlContent += "" $htmlContent += "" $htmlContent += "" $htmlContent += "" } $htmlContent += "
LoginNom AfficheEmailServiceDate expiration MdP
$($user.'Login')$($user.'Nom Affiche')$($user.'Adresse email')$($user.'Service')$($user.'Date expiration MdP')
" } else { $htmlContent += "
✓ Aucun mot de passe n'expire dans les prochains $DaysAheadPassword jours
" } $htmlContent += "
" # Section Comptes expirés $htmlContent += "
" $htmlContent += "

⏱️ Comptes expirés (depuis $DaysBackExpired jours)

" if ($ExpiredAccounts -and $ExpiredAccounts.Count -gt 0) { $htmlContent += "" $htmlContent += "" $htmlContent += "" foreach ($user in $ExpiredAccounts) { $htmlContent += "" $htmlContent += "" $htmlContent += "" $htmlContent += "" $htmlContent += "" $htmlContent += "" $htmlContent += "" } $htmlContent += "
LoginNom AfficheEmailServiceDate expiration
$($user.'Login')$($user.'Nom Affiche')$($user.'Adresse email')$($user.'Service')$($user.'Date expiration')
" } else { $htmlContent += "
✓ Aucun compte expiré
" } $htmlContent += "
" # Section Comptes désactivés $htmlContent += "
" $htmlContent += "

🔒 Comptes désactivés (depuis $DaysBackDisabled jours)

" if ($DisabledAccounts -and $DisabledAccounts.Count -gt 0) { $htmlContent += "" $htmlContent += "" $htmlContent += "" foreach ($user in $DisabledAccounts) { $htmlContent += "" $htmlContent += "" $htmlContent += "" $htmlContent += "" $htmlContent += "" $htmlContent += "" $htmlContent += "" } $htmlContent += "
LoginNom AfficheEmailServiceDate modification
$($user.'Login')$($user.'Nom Affiche')$($user.'Adresse email')$($user.'Service')$($user.'Date modification')
" } else { $htmlContent += "
✓ Aucun compte désactivé
" } $htmlContent += "
" $htmlContent += @"
"@ $htmlContent | Out-File -Encoding UTF8 -FilePath $htmlPath -Force Write-Success "Rapport HTML généré: AD_Consolidated_Report.html" return $htmlPath } # ============================================================================ # MAIN # ============================================================================ try { # Vérification du module ActiveDirectory Write-Header "Audit de l'expiration et desactivation des comptes Active Directory" if (!(Get-Module ActiveDirectory)) { Write-Section "Import du module ActiveDirectory..." Import-Module ActiveDirectory -ErrorAction Stop Write-Success "Module ActiveDirectory importé" } # Créer le répertoire de sortie s'il n'existe pas if (!(Test-Path $OutputPath)) { New-Item -ItemType Directory -Path $OutputPath -Force | Out-Null Write-Success "Répertoire créé: $OutputPath" } else { Write-Success "Répertoire de sortie: $OutputPath" } Write-Header "Lancement des audits" # Récupérer les données $passwordExpiring = Get-PasswordExpiringUsers $expiredAccounts = Get-ExpiredAccounts $disabledAccounts = Get-DisabledAccounts Write-Header "Exportation des données" # Exporter en CSV Export-ToCSV -Data $passwordExpiring -FileName "PasswordExpiring-$($DaysAheadPassword)Days.csv" Export-ToCSV -Data $expiredAccounts -FileName "AccountExpired-$($DaysBackExpired)Days.csv" Export-ToCSV -Data $disabledAccounts -FileName "AccountDisabled-$($DaysBackDisabled)Days.csv" # Générer le rapport HTML Write-Header "Génération du rapport" $reportPath = Generate-HTMLReport -PasswordExpiringUsers $passwordExpiring -ExpiredAccounts $expiredAccounts -DisabledAccounts $disabledAccounts # Résumé final Write-Host "" Write-Host "╔════════════════════════════════════════════════════════════╗" -ForegroundColor Green Write-Host "║ ✓ AUDIT TERMINÉ AVEC SUCCÈS ║" -ForegroundColor Green Write-Host "╚════════════════════════════════════════════════════════════╝" -ForegroundColor Green Write-Host "" Write-Host "Résumé:" -ForegroundColor Cyan Write-Host " • Mots de passe expirant: $($passwordExpiring.Count)" -ForegroundColor Yellow Write-Host " • Comptes expirés: $($expiredAccounts.Count)" -ForegroundColor Red Write-Host " • Comptes désactivés: $($disabledAccounts.Count)" -ForegroundColor Red Write-Host " • Total comptes: $(($passwordExpiring.Count) + ($expiredAccounts.Count) + ($disabledAccounts.Count))" -ForegroundColor Cyan Write-Host "" Write-Host "Fichiers générés dans: $OutputPath" -ForegroundColor Green Write-Host "" # Proposer d'ouvrir le rapport $response = Read-Host "Voulez-vous ouvrir le rapport HTML? (O/N)" if ($response -eq "O" -or $response -eq "o") { Start-Process $reportPath } } catch { Write-Host "" Write-Host "╔════════════════════════════════════════════════════════════╗" -ForegroundColor Red Write-Host "║ ✗ ERREUR FATALE ║" -ForegroundColor Red Write-Host "╚════════════════════════════════════════════════════════════╝" -ForegroundColor Red Write-Host "" Write-Error $_ exit 1 }