Revizuirea periodica a conturilor inactive din Active Directory si dezactivarea controlata cu PowerShell
In orice domeniu Active Directory se aduna in timp conturi care nu mai sunt folosite: angajati plecati carora nu li s-a inchis contul, conturi de test, conturi create pentru colaboratori externi sau stagiari. Aceste conturi raman active, au parole care nu se mai schimba si reprezinta o poarta de intrare usor de exploatat, pentru ca nimeni nu observa daca sunt folosite de altcineva.
Standardul ISO/IEC 27001:2022 cere revizuirea periodica a drepturilor de acces (controlul A.5.18) si eliminarea accesului care nu mai este necesar. Procedura de mai jos produce un raport lunar cu conturile inactive de peste 90 de zile, il trece printr-o aprobare si apoi dezactiveaza conturile aprobate intr-un mod controlat si reversibil. Raportul si fisierul aprobat raman ca dovada de audit.
Backup
- Verificati daca este activat Active Directory Recycle Bin. Fara el, un cont sters ulterior nu mai poate fi recuperat cu toate atributele:
Get-ADOptionalFeature -Filter 'Name -like "Recycle Bin Feature"' | Select-Object Name, EnabledScopes
Daca EnabledScopes este gol, functia nu este activa. Activarea ei este ireversibila si se face separat, cu acordul administratorului de domeniu.
- Exportati starea actuala a tuturor conturilor de utilizator, inainte de orice modificare:
New-Item -ItemType Directory -Force -Path "C:\Audit\AD" | Out-Null
Get-ADUser -Filter * -Properties Enabled, Description, DistinguishedName, LastLogonTimestamp |
Select-Object SamAccountName, Name, Enabled, Description, DistinguishedName,
@{N='UltimaLogare';E={ if ($_.LastLogonTimestamp) { [DateTime]::FromFileTime($_.LastLogonTimestamp) } }} |
Export-Csv "C:\Audit\AD\stare-conturi-$(Get-Date -Format yyyy-MM-dd).csv" -NoTypeInformation -Encoding UTF8
- Procedura se ruleaza dintr-o consola PowerShell pornita ca Administrator, pe un server sau o statie care are modulul ActiveDirectory (RSAT), cu un cont care are drept de modificare pe conturile vizate.
Pasul 1 – Pregatim grupul de exceptii si OU-ul de carantina
Unele conturi nu se logheaza interactiv niciodata, dar sunt necesare: conturi de serviciu, conturi pentru scanere sau aplicatii, conturi de urgenta (break-glass). Le punem intr-un grup dedicat, ca scriptul sa le ignore:
New-ADGroup -Name "GRP-Exceptii-Inactivitate" -GroupScope Global -GroupCategory Security `
-Description "Conturi excluse din revizuirea conturilor inactive. Fiecare membru trebuie justificat."
Add-ADGroupMember -Identity "GRP-Exceptii-Inactivitate" -Members "svc-backup","svc-scanner"
Cream si OU-ul in care vor fi mutate conturile dezactivate (inlocuiti DC=firma,DC=local cu domeniul vostru):
New-ADOrganizationalUnit -Name "Conturi Dezactivate" -Path "DC=firma,DC=local" -ProtectedFromAccidentalDeletion $true
Important: daca domeniul este sincronizat cu Microsoft Entra ID (Entra Connect / Cloud Sync) si sincronizarea este filtrata pe OU-uri, OU-ul “Conturi Dezactivate” trebuie inclus in sincronizare. Altfel, la mutare, utilizatorul este sters din Entra ID, iar casuta lui de mail ajunge in starea soft-deleted.
Pasul 2 – Generam raportul cu conturile inactive
Salvam scriptul de mai jos ca C:\Scripts\Raport-ConturiInactive.ps1:
Import-Module ActiveDirectory
$Zile = 90
$Prag = (Get-Date).AddDays(-$Zile)
$Folder = "C:\Audit\AD"
$Raport = Join-Path $Folder "conturi-inactive-$(Get-Date -Format yyyy-MM-dd).csv"
$Sistem = @('krbtgt','Administrator','Guest','DefaultAccount')
New-Item -ItemType Directory -Force -Path $Folder | Out-Null
$Exceptii = Get-ADGroupMember -Identity "GRP-Exceptii-Inactivitate" -Recursive |
Select-Object -ExpandProperty SamAccountName
$Inactive = Get-ADUser -Filter 'Enabled -eq $true' `
-Properties LastLogonTimestamp, whenCreated, PasswordLastSet, Description, ServicePrincipalName, Manager |
Select-Object SamAccountName, Name, Description, whenCreated, PasswordLastSet, DistinguishedName,
@{N='UltimaLogare';E={ if ($_.LastLogonTimestamp) { [DateTime]::FromFileTime($_.LastLogonTimestamp) } }},
@{N='AreSPN';E={ [bool]$_.ServicePrincipalName }},
@{N='Manager';E={ if ($_.Manager) { (Get-ADUser $_.Manager).Name } }} |
Where-Object {
((-not $_.UltimaLogare) -or ($_.UltimaLogare -lt $Prag)) -and
($_.whenCreated -lt $Prag) -and
(-not $_.AreSPN) -and
($_.SamAccountName -notin $Exceptii) -and
($_.SamAccountName -notin $Sistem)
} |
Sort-Object UltimaLogare
$Inactive | Export-Csv $Raport -NoTypeInformation -Encoding UTF8
Write-Host "Conturi inactive de peste $Zile zile: $($Inactive.Count). Raport: $Raport"
Ce face filtrul:
- pastreaza doar conturile active care nu s-au logat in ultimele 90 de zile sau nu s-au logat niciodata;
- ignora conturile create in ultimele 90 de zile (angajati noi care inca nu au inceput);
- ignora conturile cu SPN (de regula conturi de serviciu), conturile din grupul de exceptii si conturile de sistem.
Rulam scriptul:
C:\Scripts\Raport-ConturiInactive.ps1
Pasul 3 – Revizuim si aprobam lista
Raportul nu se aplica automat. Il deschidem in Excel si il trimitem managerului fiecarui utilizator sau departamentului HR spre confirmare. Pentru fiecare cont se decide:
- se dezactiveaza;
- se pastreaza, cu justificare (de exemplu concediu prelungit), caz in care contul se adauga temporar in grupul de exceptii.
Liniile aprobate pentru dezactivare se salveaza intr-un fisier separat, de exemplu C:\Audit\AD\conturi-inactive-2026-10-01-aprobat.csv. Acest fisier, impreuna cu emailul de aprobare, este dovada pentru auditul ISO 27001.
Pasul 4 – Simulam dezactivarea cu -WhatIf
Inainte de modificarea reala, rulam procedura in mod simulare. PowerShell afiseaza ce ar face, fara sa schimbe nimic:
$Aprobate = Import-Csv "C:\Audit\AD\conturi-inactive-2026-10-01-aprobat.csv"
$Carantina = "OU=Conturi Dezactivate,DC=firma,DC=local"
foreach ($c in $Aprobate) {
Disable-ADAccount -Identity $c.SamAccountName -WhatIf
Move-ADObject -Identity $c.DistinguishedName -TargetPath $Carantina -WhatIf
}
Verificam ca lista afisata contine exact conturile aprobate.
Pasul 5 – Dezactivam si mutam conturile aprobate
$Aprobate = Import-Csv "C:\Audit\AD\conturi-inactive-2026-10-01-aprobat.csv"
$Carantina = "OU=Conturi Dezactivate,DC=firma,DC=local"
$Data = Get-Date -Format yyyy-MM-dd
$Jurnal = "C:\Audit\AD\jurnal-dezactivare-$Data.csv"
$Rezultat = foreach ($c in $Aprobate) {
try {
Disable-ADAccount -Identity $c.SamAccountName -ErrorAction Stop
Set-ADUser -Identity $c.SamAccountName -Description "Dezactivat $Data - inactiv peste 90 zile - revizuire periodica acces" -ErrorAction Stop
Move-ADObject -Identity $c.DistinguishedName -TargetPath $Carantina -ErrorAction Stop
[pscustomobject]@{ Cont = $c.SamAccountName; OU_Original = $c.DistinguishedName; DescriereOriginala = $c.Description; Status = 'Dezactivat'; Data = $Data }
}
catch {
[pscustomobject]@{ Cont = $c.SamAccountName; OU_Original = $c.DistinguishedName; DescriereOriginala = $c.Description; Status = "Eroare: $($_.Exception.Message)"; Data = $Data }
}
}
$Rezultat | Export-Csv $Jurnal -NoTypeInformation -Encoding UTF8
$Rezultat | Format-Table Cont, Status -AutoSize
Jurnalul pastreaza OU-ul si descrierea originala ale fiecarui cont, necesare pentru restaurare.
Pasul 6 – Programam raportul lunar
Programam doar generarea raportului, nu si dezactivarea. Dezactivarea ramane un pas manual, facut dupa aprobare:
schtasks /Create /TN "Raport conturi AD inactive" /TR "powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\Scripts\Raport-ConturiInactive.ps1" /SC MONTHLY /D 1 /ST 08:00 /RU SYSTEM
Task-ul ruleaza in prima zi a fiecarei luni la ora 08:00 si salveaza raportul in C:\Audit\AD.
Pasul 7 – Stergerea definitiva (dupa perioada de retentie)
Conturile din “Conturi Dezactivate” se pastreaza o perioada stabilita in politica interna, de regula 30-90 de zile. Abia apoi se sterg, tot pe baza unei liste aprobate. Daca Recycle Bin este activ, un cont sters mai poate fi recuperat pe durata configurata in domeniu (implicit 180 de zile).
Impact colateral
Atributul LastLogonTimestamp se replica intre controllerele de domeniu cu o intarziere de pana la aproximativ 14 zile. De aceea pragul se tine la 90 de zile sau mai mult, nu la 15-30 de zile.
Utilizatorii care lucreaza doar in Microsoft 365 (Outlook Web, Teams, OneDrive din browser) pot aparea inactivi in AD-ul local, desi lucreaza zilnic. Inainte de aprobare, verificati in Entra ID ultima autentificare in cloud pentru conturile din raport.
Un cont dezactivat in AD si sincronizat cu Entra ID este blocat si in Microsoft 365: utilizatorul nu mai are acces la mail, Teams si OneDrive. Datele raman pastrate. Conturile folosite de aplicatii, scanere sau task-uri programate, care nu au SPN si nu sunt in grupul de exceptii, pot fi dezactivate gresit si opresc serviciul respectiv. Revizuirea din Pasul 3 exista tocmai pentru a prinde aceste cazuri.
Mutarea intr-un OU nou schimba politicile GPO aplicate contului si poate scoate contul din scopul sincronizarii cu Entra ID, daca OU-ul nu este inclus.
Plan de restaurare in caz de nefunctionare
- Pentru un singur cont dezactivat gresit, il reactivam si il mutam inapoi in OU-ul original, folosind jurnalul din Pasul 5:
$Jurnal = Import-Csv "C:\Audit\AD\jurnal-dezactivare-2026-10-01.csv"
$c = $Jurnal | Where-Object Cont -eq "ion.popescu"
$OU_Parinte = ($c.OU_Original -split ',', 2)[1]
Enable-ADAccount -Identity $c.Cont
if ($c.DescriereOriginala) { Set-ADUser -Identity $c.Cont -Description $c.DescriereOriginala } else { Set-ADUser -Identity $c.Cont -Clear Description }
Move-ADObject -Identity (Get-ADUser $c.Cont).DistinguishedName -TargetPath $OU_Parinte
- Pentru toate conturile din jurnal, rulam acelasi bloc intr-o bucla
foreach ($c in $Jurnal). - Daca un cont a fost deja sters si Recycle Bin este activ, il recuperam cu
Get-ADObject -Filter 'SamAccountName -eq "ion.popescu"' -IncludeDeletedObjects | Restore-ADObject. - Dupa restaurare, contul trebuie adaugat in grupul GRP-Exceptii-Inactivitate, daca motivul inactivitatii este justificat, ca sa nu apara din nou in raportul urmator.