#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
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 += "| Login | Nom Affiche | Email | Service | Date expiration MdP | "
$htmlContent += "
"
foreach ($user in $PasswordExpiringUsers) {
$htmlContent += ""
$htmlContent += "| $($user.'Login') | "
$htmlContent += "$($user.'Nom Affiche') | "
$htmlContent += "$($user.'Adresse email') | "
$htmlContent += "$($user.'Service') | "
$htmlContent += "$($user.'Date expiration MdP') | "
$htmlContent += "
"
}
$htmlContent += "
"
}
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 += "| Login | Nom Affiche | Email | Service | Date expiration | "
$htmlContent += "
"
foreach ($user in $ExpiredAccounts) {
$htmlContent += ""
$htmlContent += "| $($user.'Login') | "
$htmlContent += "$($user.'Nom Affiche') | "
$htmlContent += "$($user.'Adresse email') | "
$htmlContent += "$($user.'Service') | "
$htmlContent += "$($user.'Date expiration') | "
$htmlContent += "
"
}
$htmlContent += "
"
}
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 += "| Login | Nom Affiche | Email | Service | Date modification | "
$htmlContent += "
"
foreach ($user in $DisabledAccounts) {
$htmlContent += ""
$htmlContent += "| $($user.'Login') | "
$htmlContent += "$($user.'Nom Affiche') | "
$htmlContent += "$($user.'Adresse email') | "
$htmlContent += "$($user.'Service') | "
$htmlContent += "$($user.'Date modification') | "
$htmlContent += "
"
}
$htmlContent += "
"
}
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
}