Active Directory — Gestion des utilisateurs en PowerShell
Introduction
Faitza_AD.ps1 regroupe les fonctions de gestion du cycle de vie des
comptes Active Directory on-premise : création avec home directory et ACL,
recherche par OU ou email, contrôle de l'expiration du mot de passe,
dernière connexion, désactivation et suppression.
Le module ActiveDirectory doit être disponible :
Import-Module ActiveDirectory
Le script doit s'exécuter depuis un serveur membre du domaine ou un poste avec les RSAT installés. Les fonctions nécessitent les droits de gestion d'utilisateurs dans les OUs ciblées.
New-Faitza_AD_user
Crée un compte AD à partir d'un objet PSCustomObject contenant les
propriétés utilisateur. La fonction calcule le SAMAccountName, crée le répertoire
home, applique les ACL, génère un mot de passe cryptographiquement sûr via
New-Faitza_AD_Password et ajoute l'utilisateur aux groupes spécifiés.
function New-Faitza_AD_user {
[CmdletBinding()]
param (
[PSCustomObject]$user_base,
[string[]]$groupe_liste
)
function New-Faitza_AD_Password {
[CmdletBinding()]
param([Alias("longueur")][int]$pass_longueur = 64)
try {
Write-Host "$($PSStyle.Background.Red)>>> New-Faitza_AD_Password <<<$($PSStyle.Reset)"
if ($pass_longueur -lt 8) { throw "longueur minimale = 8 (recu : $pass_longueur)" }
$caracteres_min = "abcdefghijklmnopqrstuvwxyz"
$caracteres_maj = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
$caracteres_nom = "0123456789"
$caracteres_spe = "!#%&()*+,-./:;<=>?@[]^_{|}~"
$caracteres_reqs = @($caracteres_min, $caracteres_maj, $caracteres_nom, $caracteres_spe)
$caracteres_alls = $caracteres_min + $caracteres_maj + $caracteres_nom + $caracteres_spe
$crypto_rng = [System.Security.Cryptography.RandomNumberGenerator]::Create()
$crypto_byte = New-Object byte[] 4
$pass = [System.Collections.Generic.List[char]]::new()
try {
foreach ($set in $caracteres_reqs) { $crypto_rng.GetBytes($crypto_byte); $pass.Add($set[[System.BitConverter]::ToUInt32($crypto_byte, 0) % $set.Length]) }
for ($k = $pass.Count; $k -lt $pass_longueur; $k++) { $crypto_rng.GetBytes($crypto_byte); $pass.Add($caracteres_alls[[System.BitConverter]::ToUInt32($crypto_byte, 0) % $caracteres_alls.Length]) }
for ($i = $pass.Count - 1; $i -gt 0; $i--) { $crypto_rng.GetBytes($crypto_byte); $j = [int]([System.BitConverter]::ToUInt32($crypto_byte, 0) % ($i + 1)); if ($j -ne $i) { $tmp = $pass[$i]; $pass[$i] = $pass[$j]; $pass[$j] = $tmp } }
} finally { $crypto_rng.Dispose() }
return -join $pass
} catch {
throw "Erreur New-Faitza_AD_Password : $_"
} finally {
Write-Host "$($PSStyle.Background.Red)<<< New-Faitza_AD_Password >>>$($PSStyle.Reset)"
}
}
try {
Write-Host "$($PSStyle.Background.Red)>>> New-Faitza_AD_user <<<$($PSStyle.Reset)"
if (-not $user_base) { throw "parametre -user_base manquant" }
if ([string]::IsNullOrWhiteSpace($user_base.Mail)) { throw "propriete Mail manquante dans user_base" }
if ([string]::IsNullOrWhiteSpace($user_base.Surname)) { throw "propriete Surname manquante dans user_base" }
if ([string]::IsNullOrWhiteSpace($user_base.GivenName)) { throw "propriete GivenName manquante dans user_base" }
if ([string]::IsNullOrWhiteSpace($user_base.Departement)) { throw "propriete Departement manquante dans user_base" }
if ([string]::IsNullOrWhiteSpace($user_base.Profil)) { throw "propriete Profil manquante dans user_base" }
$groupe_erreur = @()
$user_mapping = Get-Faitza_AD_Mapping -departement $user_base.Departement -profil $user_base.Profil
$user_base_Departement = $user_mapping.departement
$user_base_Profil = $user_mapping.profil
$user_ou = $user_mapping.ou
if ($user_base_Profil -match "Fonction Support|Assistant|Paralegal") {
$groupe_liste += "GRP_LICENCE_E5_AND_SHAREPOINT"
if (-not $user_base.Externe) { $groupe_liste += "Salaries" }
if ($user_base_Profil -eq "Assistant") { $groupe_liste += @("ASSISTANTES", "Secretaires$($user_base.Groupe)") }
if ($user_base_Profil -eq "Paralegal") { $groupe_liste += "Corporate-Paralegal" }
} else {
$groupe_liste += "GRP_LICENCE_E5"
if ($user_base.Groupe -match "Groupe[1-3]") { $groupe_liste += "Corporate-$($user_base.Groupe)" }
$groupe_liste += "$user_base_Departement-$($user_base_Profil)s"
}
function Remove-Diacritics ([string]$Text) {
if ([string]::IsNullOrWhiteSpace($Text)) { return $Text }
return ($Text.Normalize([System.Text.NormalizationForm]::FormD) -replace '\p{Mn}', '')
}
$user_sam = ((Remove-Diacritics ($user_base.GivenName + $user_base.Surname)) -replace '[^a-zA-Z0-9]', '').ToLower()
if ($user_sam.Length -gt 20) {
$initiale = if ($user_base.GivenName.Length -gt 0) { $user_base.GivenName.Substring(0, 1) } else { "" }
$user_sam = ((Remove-Diacritics ($initiale + $user_base.Surname)) -replace '[^a-zA-Z0-9]', '').ToLower()
}
if ($user_sam.Length -gt 20) { $user_sam = $user_sam.Substring(0, 20) }
if ([string]::IsNullOrWhiteSpace($user_sam)) { throw "impossible de calculer un SAMAccountName valide" }
$user_homedir = "\\srv-faitza-files01.faitza.local\UTILISATEURS\$(($user_base.Mail).Split('@')[0])"
$givenFormatted = ($user_base.GivenName) -replace '^\w|\s\w', { $_.Value.ToUpper() }
$surnameFormatted = ($user_base.Surname).ToUpper()
$user_params = @{
Name = "$surnameFormatted $givenFormatted"
DisplayName = "$surnameFormatted $givenFormatted"
GivenName = $givenFormatted
Surname = $surnameFormatted
SamAccountName = $user_sam
Description = $user_base.Description
UserPrincipalName = $user_base.Mail
EmailAddress = $user_base.Mail
Enabled = $false
Company = "Faitza"
Country = "FR"
HomeDirectory = $user_homedir
HomeDrive = "H:"
Path = $user_ou
OtherAttributes = @{
extensionAttribute15 = "CmnExcludeFromDirSync"
mAPIRecipient = $true
mDBStorageQuota = 6144000
mDBUseDefaults = $false
msExchHomeServerName = "/o=FAITZAORG/ou=Exchange Administrative Group/cn=Configuration/cn=Servers/cn=MAIL01"
msExchUserAccountControl = 0
}
}
try {
if (-not (Test-Path $user_homedir)) {
New-Item -Path $user_homedir -ItemType Directory -Force | Out-Null
Write-Host "Repertoire home cree : $user_homedir"
}
} catch { throw "erreur creation home '$user_homedir' : $_" }
$user_password = New-Faitza_AD_Password -pass_longueur 32
$user_params["AccountPassword"] = (ConvertTo-SecureString -String $user_password -AsPlainText -Force)
try {
if (-not (Get-ADOrganizationalUnit -Filter "DistinguishedName -eq '$user_ou'" -ErrorAction SilentlyContinue)) { throw "OU introuvable dans l'AD : '$user_ou'" }
$user = New-ADUser @user_params -PassThru -ErrorAction Stop
} catch { throw "creation compte AD echouee ($user_sam) : $_" }
foreach ($group in $groupe_liste) {
try {
$group_find = Get-ADGroup -Filter "Name -eq '$group'" -ErrorAction Stop
if ($null -eq $group_find) { throw "groupe '$group' introuvable dans l'AD" }
Add-ADGroupMember -Identity $group_find.distinguishedName -Members $user.SamAccountName -ErrorAction Stop
} catch {
Write-Host "Erreur ajout groupe '$group' : $_"
$groupe_erreur += "Erreur ajout groupe '$group' : $_"
}
}
try {
$userSid = [System.Security.Principal.SecurityIdentifier]$user.SID
if ($null -eq $userSid) { throw "SID de l'utilisateur introuvable" }
$homedir_acl = Get-Acl $user_homedir
$user_accessrule = New-Object System.Security.AccessControl.FileSystemAccessRule($userSid, [System.Security.AccessControl.FileSystemRights]::FullControl, [System.Security.AccessControl.InheritanceFlags]"ContainerInherit,ObjectInherit", [System.Security.AccessControl.PropagationFlags]::None, [System.Security.AccessControl.AccessControlType]::Allow)
$homedir_acl.SetAccessRule($user_accessrule)
Set-Acl $user_homedir $homedir_acl
} catch { throw "erreur application ACL home '$user_homedir' : $_" }
return [PSCustomObject]@{ User = $user; password = $user_password; Groupe_erreur = $groupe_erreur; Groupe = $groupe_liste }
} catch {
throw "Erreur New-Faitza_AD_user : $_"
} finally {
Write-Host "$($PSStyle.Background.Red)<<< New-Faitza_AD_user >>>$($PSStyle.Reset)"
}
}
Exemple d'utilisation
$user_base = [PSCustomObject]@{
Mail = "[email protected]"
GivenName = "Jean"
Surname = "Dupont"
Departement = "Corporate M&A"
Profil = "Collaborateur"
Description = "Nouveau collaborateur"
Groupe = "Groupe1"
Externe = $false
}
$result = New-Faitza_AD_user -user_base $user_base -groupe_liste @("GRP_VPN")
Write-Host "Compte cree : $($result.User.SamAccountName)"
Write-Host "Mot de passe : $($result.password)"
if ($result.Groupe_erreur) { Write-Warning "Erreurs groupes : $($result.Groupe_erreur -join ', ')" }
Get-Faitza_AD_Aduser
Recherche des utilisateurs AD par OU (filtre sur Enabled) ou
directement par adresse email / UPN. Retourne un objet
@{ retour = []; erreur = [] }.
function Get-Faitza_AD_Aduser {
param (
[string[]]$ou = @("OU=Utilisateurs,OU=T2-Utilisateurs,OU=_TIER-2,DC=faitza,DC=local"),
[string[]]$properties = @("*"),
[bool]$enabled = $true,
[string]$Email = ""
)
try {
Write-Host "$($PSStyle.Background.Red)>>> Get-Faitza_AD_Aduser <<<$($PSStyle.Reset)"
$retour = @()
$erreur = @()
if ($Email) {
$retour += Get-ADUser -Filter { EmailAddress -eq $Email -or UserPrincipalName -eq $Email } -Properties $properties -ErrorAction Stop
} else {
foreach ($searchOu in $ou) {
try { $retour += Get-ADUser -Filter { Enabled -eq $enabled } -SearchBase $searchOu -Properties $properties -ErrorAction Stop }
catch { $erreur += "OU introuvable ou inaccessible : $searchOu" }
}
}
return @{ retour = $retour; erreur = $erreur }
} catch {
throw "Erreur Get-Faitza_AD_Aduser : $_"
} finally {
Write-Host "$($PSStyle.Background.Red)<<< Get-Faitza_AD_Aduser >>>$($PSStyle.Reset)"
}
}
Exemple d'utilisation
# Tous les utilisateurs actifs de l'OU par defaut
$result = Get-Faitza_AD_Aduser
$result.retour | Select-Object DisplayName, EmailAddress
# Par email
$result = Get-Faitza_AD_Aduser -Email "[email protected]"
$user = $result.retour[0]
# OU specifique avec proprietes limitees
$result = Get-Faitza_AD_Aduser `
-ou @("OU=Avocats,OU=Utilisateurs,OU=T2-Utilisateurs,OU=_TIER-2,DC=faitza,DC=local") `
-properties @("DisplayName", "EmailAddress", "LastLogonDate")
Get-Faitza_AD_PasswordExpiration
Calcule la date d'expiration du mot de passe d'un utilisateur AD depuis
l'attribut msDS-UserPasswordExpiryTimeComputed et renvoie
le nombre de jours restants.
function Get-Faitza_AD_PasswordExpiration {
[CmdletBinding()]
param ([Parameter(ValueFromPipeline)][Microsoft.ActiveDirectory.Management.ADUser]$User)
try {
Write-Host "$($PSStyle.Background.Red)>>> Get-Faitza_AD_PasswordExpiration <<<$($PSStyle.Reset)"
if ($null -eq $User) { throw "parametre -User manquant" }
$today = Get-Date
$expiration_raw = $User."msDS-UserPasswordExpiryTimeComputed"
if ($null -eq $expiration_raw -or $expiration_raw -eq 0 -or $expiration_raw -eq [long]::MaxValue) { throw "[$($User.UserPrincipalName)] mot de passe sans expiration ou attribut non renseigne" }
$expiration_date = [datetime]::FromFileTime($expiration_raw)
return [PSCustomObject]@{ ExpirationDate = $expiration_date; DaysLeft = ($expiration_date - $today).Days }
} catch {
throw "Erreur Get-Faitza_AD_PasswordExpiration : $_"
} finally {
Write-Host "$($PSStyle.Background.Red)<<< Get-Faitza_AD_PasswordExpiration >>>$($PSStyle.Reset)"
}
}
Exemple d'utilisation
$result = Get-Faitza_AD_Aduser -Email "[email protected]" `
-properties @("msDS-UserPasswordExpiryTimeComputed")
$user = $result.retour[0]
$exp = Get-Faitza_AD_PasswordExpiration -User $user
Write-Host "Expiration : $($exp.ExpirationDate) — dans $($exp.DaysLeft) jour(s)"
Get-Faitza_AD_User_LastLogin
Retourne la dernière date de connexion d'un utilisateur AD et le nombre de
jours depuis cette connexion. Renvoie 999 jours si la propriété
LastLogonDate est nulle (compte jamais utilisé).
function Get-Faitza_AD_User_LastLogin {
[CmdletBinding()]
param ($user)
try {
Write-Host "$($PSStyle.Background.Red)>>> Get-Faitza_AD_User_LastLogin <<<$($PSStyle.Reset)"
if ($null -eq $user) { throw "parametre -user manquant" }
if ([string]::IsNullOrWhiteSpace($user.SamAccountName)) { throw "SamAccountName vide ou null (UPN=$($user.UserPrincipalName))" }
if ($null -ne $user.LastLogonDate) { $lastLogon = [datetime]$user.LastLogonDate; $joursDepuis = [math]::Round((New-TimeSpan -Start $lastLogon -End (Get-Date)).TotalDays, 1); $dateAffichee = $lastLogon.ToString("dd/MM/yyyy HH:mm") }
else { $joursDepuis = 999; $dateAffichee = "Jamais" }
return [PSCustomObject]@{ SamAccountName = $user.SamAccountName; UPN = $user.UserPrincipalName; DerniereConnexion = $dateAffichee; JoursDepuis = $joursDepuis }
} catch {
throw "Erreur Get-Faitza_AD_User_LastLogin : $_"
} finally {
Write-Host "$($PSStyle.Background.Red)<<< Get-Faitza_AD_User_LastLogin >>>$($PSStyle.Reset)"
}
}
Exemple d'utilisation
$result = Get-Faitza_AD_Aduser -properties @("LastLogonDate")
$inactifs = foreach ($user in $result.retour) {
$login = Get-Faitza_AD_User_LastLogin -user $user
if ($login.JoursDepuis -gt 90) { $login }
}
$inactifs | Sort-Object JoursDepuis -Descending | Format-Table
Disable-Faitza_AD_user
Désactive un compte AD par SamAccountName, UPN ou email. En cas d'échec de la première tentative, recherche le compte par email et réessaie avec son GUID. Vérifie ensuite que le compte est bien désactivé.
function Disable-Faitza_AD_user {
[CmdletBinding()]
param ([string]$user)
try {
Write-Host "$($PSStyle.Background.Red)>>> Disable-Faitza_AD_user <<<$($PSStyle.Reset)"
if ([string]::IsNullOrWhiteSpace($user)) { throw "Le parametre -user est obligatoire pour la desactivation AD." }
$TargetId = $user.Trim()
try { Disable-ADAccount -Identity $TargetId -ErrorAction Stop }
catch {
$SearchResult = Get-Faitza_AD_Aduser -Email $TargetId -properties @("ObjectGUID")
if ($SearchResult.erreur.Count -gt 0 -or $null -eq $SearchResult.retour -or $SearchResult.retour.Count -eq 0) { throw "Utilisateur definitivement introuvable dans l'Active Directory avec la valeur : $TargetId" }
$AdUser = if ($SearchResult.retour -is [array]) { $SearchResult.retour[0] } else { $SearchResult.retour }
$TargetId = $AdUser.ObjectGUID.ToString()
Disable-ADAccount -Identity $TargetId -ErrorAction Stop
}
$user_test = Get-ADUser -Identity $TargetId -Properties Enabled
if ($user_test.Enabled -eq $true) { throw "Test de verification echoue pour $TargetId, le compte AD est toujours actif." }
return $true
} catch {
throw "Erreur Disable-Faitza_AD_user : $_"
} finally {
Write-Host "$($PSStyle.Background.Red)<<< Disable-Faitza_AD_user >>>$($PSStyle.Reset)"
}
}
Exemple d'utilisation
# Par SamAccountName
Disable-Faitza_AD_user -user "jdupont"
# Par email
Disable-Faitza_AD_user -user "[email protected]"
Remove-Faitza_AD_user
Supprime définitivement un compte AD. Même logique de fallback que
Disable-Faitza_AD_user : en cas d'échec, recherche le compte par
email et supprime via son GUID.
function Remove-Faitza_AD_user {
[CmdletBinding()]
param ([string]$userId)
try {
Write-Host "$($PSStyle.Background.Red)>>> Remove-Faitza_AD_user <<<$($PSStyle.Reset)"
if ([string]::IsNullOrWhiteSpace($userId)) { throw "Le parametre -userId est obligatoire pour la suppression AD." }
$TargetId = $userId.Trim()
try { Remove-ADUser -Identity $TargetId -Confirm:$false -ErrorAction Stop }
catch {
$SearchResult = Get-Faitza_AD_Aduser -Email $TargetId -properties @("ObjectGUID")
if ($SearchResult.erreur.Count -gt 0 -or $null -eq $SearchResult.retour -or $SearchResult.retour.Count -eq 0) { throw "Utilisateur definitivement introuvable dans l'Active Directory avec la valeur : $TargetId" }
$AdUser = if ($SearchResult.retour -is [array]) { $SearchResult.retour[0] } else { $SearchResult.retour }
Remove-ADUser -Identity $AdUser.ObjectGUID -Confirm:$false -ErrorAction Stop
}
return $true
} catch {
throw "Erreur Remove-Faitza_AD_user : $_"
} finally {
Write-Host "$($PSStyle.Background.Red)<<< Remove-Faitza_AD_user >>>$($PSStyle.Reset)"
}
}
Exemple d'utilisation
# Par SamAccountName
Remove-Faitza_AD_user -userId "jdupont"
# Par email
Remove-Faitza_AD_user -userId "[email protected]"
// Commentaires
Aucun commentaire pour l'instant.