Skip to content
64 changes: 36 additions & 28 deletions scripts_staging/Backend/Mail notification password expiry.ps1
Original file line number Diff line number Diff line change
@@ -1,41 +1,49 @@
<#
.SYNOPSIS
Analyse et notification avancée des utilisateurs dont le mot de passe Active Directory approche de l’expiration.
Analyzes Active Directory user accounts for upcoming password expiration and optionally sends notifications.

.DESCRIPTION
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)
This script is configured entirely through environment variables and performs the following:
- Targets a specific Organizational Unit (OU) for user account analysis
- Uses configurable thresholds to classify accounts as warning or critical
- Optionally includes disabled accounts and accounts with passwords set to never expire
- Sends email reports to a list of administrator recipients or can generate reports only
- Supports customizable email signature and SMTP configuration for email delivery

Accounts are classified based on password expiration:
- Warning: password is approaching expiration (WarningThreshold)
- Critical: password is close to expiring (CriticalThreshold)

.NOTES
Prérequis :
- Module ActiveDirectory
- Accès SMTP pour l’envoi d’emails
- Droits d’administration pour les tâches planifiées
Dependency:
CallPowerShell7 snippet
Author: PQU
Date: 29/04/2025
#public

.EXAMPLE
# Example usage with environment variables set before running the script:

TARGET_OU=OU=Employees,DC=example,DC=local
SMTP_SERVER=smtp.example.com
SMTP_PORT=587
[email protected],[email protected]
[email protected]
WARNING_THRESHOLD=14
CRITICAL_THRESHOLD=7
EMAIL_SIGNATURE=Best regards,<br>IT Department
INCLUDE_DISABLED=true
INCLUDE_NEVER_EXPIRES=false
GENERATE_REPORT_ONLY=false

.CHANGELOG
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.
03.07.25 SAN - Update docs

.TODO
Multiple Locale support

#>


Expand Down
145 changes: 108 additions & 37 deletions scripts_staging/Checks/Active Directory Health.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -13,43 +13,113 @@
#public

.CHANGELOG
17.07.25 SAN Big cleanup of bug fixes for the dcdiag function, fixes of error codes, output in stderr of all errors for readability

.TODO
Do a breakdown at the top of the output for easy read with ok/ko returns from functions

#>

# Initialize exit code
$exitCode = 0
$global:exitCode = 0

# Function to perform Active Directory tests
function CheckAD {
[CmdletBinding()]
param (
[Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[string[]]$Tests
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[string[]]$Tests,

[Parameter()]
[hashtable]$SuccessPatterns = @{
'en' = @('passed test')
'fr' = @('a réussi', 'a reussi', 'a russi', 'ussi')
},

[Parameter()]
[int]$MinimumMatches = 2
)

process {
$results = @{}
$DebugMode = $false
$global:exitCode = 0

foreach ($test in $Tests) {
$output = dcdiag /test:$test
# Combine all success patterns from all languages into a single list
$allPatterns = @()
foreach ($lang in $SuccessPatterns.Keys) {
$allPatterns += $SuccessPatterns[$lang]
}

if ($output -notmatch "chou") {
$results[$test] = "OK"
} else {
$results[$test] = "Failed!"
$global:exitCode++
if ($DebugMode) {
Write-Host "`n[DEBUG] Loaded Success Patterns:"
foreach ($p in $allPatterns) {
Write-Host " - $p"
}
Write-Host ""
}

$results = @{}

foreach ($test in $Tests) {
Write-Host "`nRunning DCDIAG test: $test"

# Start dcdiag process and redirect output
$startInfo = New-Object System.Diagnostics.ProcessStartInfo
$startInfo.FileName = "dcdiag.exe"
$startInfo.Arguments = "/test:$test"
$startInfo.RedirectStandardOutput = $true
$startInfo.RedirectStandardError = $true
$startInfo.UseShellExecute = $false
$startInfo.CreateNoWindow = $true
$process = New-Object System.Diagnostics.Process
$process.StartInfo = $startInfo
$process.Start() | Out-Null
$stream = $process.StandardOutput.BaseStream
$memoryStream = New-Object System.IO.MemoryStream
$buffer = New-Object byte[] 4096
while (($read = $stream.Read($buffer, 0, $buffer.Length)) -gt 0) {
$memoryStream.Write($buffer, 0, $read)
}
$process.WaitForExit()

$bytes = $memoryStream.ToArray()
$output = [System.Text.Encoding]::GetEncoding(1252).GetString($bytes)

if ($DebugMode) {
$preview = if ($output.Length -gt 800) { $output.Substring(0,800) + "`n..." } else { $output }
Write-Host "[DEBUG] DCDIAG Output Preview:"
Write-Host $preview
Write-Host ""
}

$matchCount = 0
foreach ($pattern in $allPatterns) {
$count = ([regex]::Matches($output, [regex]::Escape($pattern))).Count
$matchCount += $count

if ($DebugMode) {
Write-Host "[DEBUG] Pattern '$pattern' matched $count time(s)."
}
}

if ($DebugMode) {
Write-Host "[DEBUG] Total success match count: $matchCount`n"
}

# Output individual test result
Write-Host "DCDIAG Test: $test Result: $($results[$test])"
if ($matchCount -ge $MinimumMatches) {
$results[$test] = "OK"
} else {
$results[$test] = "Failed!"
Write-Error "$results[$test] = Failed!"
$global:exitCode++
}

$results
Write-Host "DCDIAG Test: $test Result: $($results[$test])"
}

return $results
}

# Function to compare GPO version numbers

function Compare-GPOVersions {
[CmdletBinding()]
param ()
Expand All @@ -72,34 +142,36 @@ function Compare-GPOVersions {

# USER - Compare version numbers
if ($NumUserSysvol -ne $NumUserAD) {
Write-Host "$GPOName ($GPOId) : USER Versions différentes (Sysvol : $NumUserSysvol | AD : $NumUserAD)" -ForegroundColor Red
Write-Host "$GPOName ($GPOId) : USER Versions différentes (Sysvol : $NumUserSysvol | AD : $NumUserAD)"
Write-Error "$GPOName ($GPOId) : USER Versions différentes (Sysvol : $NumUserSysvol | AD : $NumUserAD)"
$global:exitCode++
} else {
Write-Host "$GPOName : USER Versions identiques" -ForegroundColor Green
Write-Host "$GPOName : USER Versions identiques"
}

# COMPUTER - Compare version numbers
if ($NumComputerSysvol -ne $NumComputerAD) {
Write-Host "$GPOName ($GPOId) : COMPUTER Versions différentes (Sysvol : $NumComputerSysvol | AD : $NumComputerAD)" -ForegroundColor Red
if ($NumComputerSysvol -ne $NumComputerAD) {Health
Write-Host "$GPOName ($GPOId) : COMPUTER Versions différentes (Sysvol : $NumComputerSysvol | AD : $NumComputerAD)"
Write-Error "$GPOName ($GPOId) : COMPUTER Versions différentes (Sysvol : $NumComputerSysvol | AD : $NumComputerAD)"
$global:exitCode++
} else {
Write-Host "$GPOName : COMPUTER Versions identiques" -ForegroundColor Green
Write-Host "$GPOName : COMPUTER Versions identiques"
}
}
Write-Host "GPO USER/COMPUTER Version OK" -ForegroundColor Green
Write-Host "GPO USER/COMPUTER Version OK"
}
}

# Function to check if the Recycle Bin in enabled

function Check-ADRecycleBin {
$recycleFeatures = Get-ADOptionalFeature -Filter {name -like "recycle bin feature"}

foreach ($feature in $recycleFeatures) {
if ($null -ne $feature.EnabledScopes) {
Write-Output "OK: Recycle Bin enabled"
Write-Host "OK: Recycle Bin enabled"
} else {
Write-Output "KO: Recycle Bin disabled"
Write-Host "KO: Recycle Bin disabled"
Write-Error "KO: Recycle Bin disabled"
$global:exitCode++
}
}
Expand All @@ -110,31 +182,29 @@ try {
$adFeature = Get-WindowsFeature -Name AD-Domain-Services -ErrorAction Stop

if ($adFeature.InstallState -eq "Installed") {
# Specify your AD tests

# function with the AD tests
$tests = ("Advertising", "FrsSysVol", "MachineAccount", "Replications", "RidManager", "Services", "FsmoCheck", "SysVolCheck")
# Call the function with the AD tests
Write-Host "DCDIAG"
Write-Host "DCDIAG tests: $tests"
$testResults = CheckAD -Tests $tests

$failedTests = $testResults.GetEnumerator() | Where-Object { $_.Value -eq "Failed!" }

if ($failedTests) {
Write-Error "Some Active Directory tests failed."
$failedTests | ForEach-Object { Write-Error "$($_.Key) test failed." }
$global:exitCode += $failedTests.Count
} else {
Write-Host "All Active Directory tests passed successfully."
}
Write-Host ""

# function to compare GPO versions
Write-Host "GPO Versions checks"
# Call the function to compare GPO versions
Compare-GPOVersions

Write-Host ""

# function to check the Recycle Bin
Write-Host "Recycle Bin checks"
# Call the function to check the Recycle Bin
Check-ADRecycleBin

Write-Host ""

} else {
Write-Host "Active Directory Domain Services feature is not installed or not in the 'Installed' state."
exit
Expand All @@ -144,4 +214,5 @@ try {
$global:exitCode++
}

exit $exitCode
$host.SetShouldExit($global:exitCode)
exit $global:exitCode
53 changes: 40 additions & 13 deletions scripts_staging/Checks/Disk Free Space.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,38 @@
#public

.CHANGELOG

17.07.25 SAN Added debug flag, taken into account cases where all drives are ignored.


.TODO
Add debug flag
move flags to env

#>


param(
[int]$warningThreshold = 10,
[int]$errorThreshold = 5,
[string[]]$ignoreDisks = @()
[string[]]$ignoreDisks = @(),
[bool]$DebugOutput = $false
)

function CheckDiskSpace {
[CmdletBinding()]
param()

# Get all local drives excluding network drives and the ones specified to ignore
$drives = Get-WmiObject Win32_LogicalDisk | Where-Object { $_.DriveType -eq 3 -and $_.DeviceID -notin $ignoreDisks }
$allDrives = Get-WmiObject Win32_LogicalDisk | Where-Object { $_.DriveType -eq 3 }
$drives = $allDrives | Where-Object { $_.DeviceID -notin $ignoreDisks }

if ($drives.Count -eq 0) {
Write-Host "OK: disks $($ignoreDisks -join ', ') are ignored"
if ($DebugOutput) {
Write-Host "[DEBUG] Total drives found: $($allDrives.Count)"
Write-Host "[DEBUG] Ignored drives: $($ignoreDisks -join ', ')"
}
$host.SetShouldExit(0)
return
}

$failedDrives = @()
$warningDrives = @()
Expand Down Expand Up @@ -77,23 +89,38 @@ function CheckDiskSpace {
Write-Host "OK: $($drive.DeviceID) has $($freeSpacePercent)% free space."
}
}

if ($DebugOutput) {
if ($failedDrives.Count -gt 0) {
Write-Host "DEBUG: The following drives failed:"
$failedDrives | ForEach-Object {
$p = [math]::Round(($_.FreeSpace / $_.Size) * 100, 2)
Write-Host "DEBUG: $($_.DeviceID): $p%"
}
} elseif ($warningDrives.Count -gt 0) {
Write-Host "DEBUG: The following drives are in warning:"
$warningDrives | ForEach-Object {
$p = [math]::Round(($_.FreeSpace / $_.Size) * 100, 2)
Write-Host "DEBUG: $($_.DeviceID): $p%"
}
} else {
Write-Host "DEBUG: All drives have sufficient free space."
}
}

if ($failedDrives.Count -gt 0) {
# Write-Host "ERROR: The following drives have less than $($errorThreshold)% free space:"
# $failedDrives | ForEach-Object { Write-Host "$($_.DeviceID): $([math]::Round(($_.FreeSpace / $_.Size) * 100, 2))%" }
# Write-Host "ERROR: exit 2"
if ($DebugOutput) { Write-Host "DEBUG: exit code 2" }
$host.SetShouldExit(2)
}
elseif ($warningDrives.Count -gt 0) {
# Write-Host "WARNING: The following drives have less than $($warningThreshold)% free space:"
# $warningDrives | ForEach-Object { Write-Host "$($_.DeviceID): $([math]::Round(($_.FreeSpace / $_.Size) * 100, 2))%" }
# Write-Host "Warning: exit 1"
if ($DebugOutput) { Write-Host "DEBUG: exit code 1" }
$host.SetShouldExit(1)
}
else {
# Write-Host "OK: All drives have sufficient free space."
if ($DebugOutput) { Write-Host "DEBUG: exit code 0" }
$host.SetShouldExit(0)
}
}

# Execute the function
CheckDiskSpace
CheckDiskSpace
Loading