PowerShell pentru onboarding automat de useri – de la 45 min/user la 2 min

Configurare noua (How To)

Situatie

Context real pe care probabil mulți îl recunoașteți: companie de ~400 de angajați, HR-ul trimite pe mail „a venit Ionescu luni, îi faci cont?”. Procesul de onboarding arăta cam așa pe partea de IT:

  • Deschizi ADUC, creezi userul în OU-ul corect
  • Îl bagi în grupurile de securitate în funcție de departament (uneori 8-10 grupuri)
  • Deschizi portalul M365, îi asignezi licența potrivită
  • Creezi mailbox-ul, configurezi signature-ul cu template-ul companiei
  • Creezi folderul personal pe file server, setezi permisiuni NTFS
  • Adaugi userul în Teams în canalele departamentului
  • Trimiți credențialele temporare pe telefonul managerului (că userul nu are încă mail)
  • Updatezi un Excel cu cine a fost adăugat când și cu ce licență

Fiecare pas – click-uri, așteptări, verificări. Timpul mediu: 40-50 minute per user, și asta când totul mergea bine.Clar trebuia automatizat. Varianta complet comercială (ex: soluții de IAM/IGA) era peste budget. Am mers pe combinație de PowerShell + un CSV generat din HR + Task Scheduler.

Solutie

Ideea de bază: HR completează un template CSV cu noii angajați (nume, departament, manager,
data start, tip contract). Scriptul rulează în fiecare dimineață, citește CSV-ul, creează userii care au data de start ≤ azi și trimite un raport pe mail către IT și HR.

1. Structura CSV-ului – simplu, ca să nu sperii HR-ul:

FirstName,LastName,Department,JobTitle,ManagerUPN,StartDate,LicenseType
Ion,Popescu,Engineering,Software Developer,maria.ionescu@company.ro,2026-04-21,E3
Ana,Georgescu,Sales,Account Manager,radu.popa@company.ro,2026-04-22,E3
Mihai,Stan,Finance,Accountant,elena.vasile@company.ro,2026-04-28,E1

2. Am definit un fișier de configurare JSON cu mapping-ul departament → OU + grupuri + canale Teams. Asta ca să nu hardcodez nimic în script:

{
"Engineering": {
"OU": "OU=Engineering,OU=Users,DC=company,DC=ro",
"Groups": ["SG-Engineering-All", "SG-VPN-Users", "SG-GitHub-Access"],
"TeamsChannels": ["Engineering General", "Dev Announcements"],
"SharedFolders": ["\\\\fs01\\Engineering\\Shared"]
},
"Sales": {
"OU": "OU=Sales,OU=Users,DC=company,DC=ro",
"Groups": ["SG-Sales-All", "SG-CRM-Users", "SG-VPN-Users"],
"TeamsChannels": ["Sales General", "Pipeline Updates"],
"SharedFolders": ["\\\\fs01\\Sales\\Shared"]
}
}

3. Funcția care face toată munca – simplificată, dar asta e scheletul:

function New-CompanyUser {
[CmdletBinding()]
param(
[Parameter(Mandatory)]$UserData,
[Parameter(Mandatory)]$DeptConfig
)

# Generăm UPN și password inițial
$upn = "$($UserData.FirstName.ToLower()).$($UserData.LastName.ToLower())@company.ro"
$tempPassword = [System.Web.Security.Membership]::GeneratePassword(14, 3)
$securePass = ConvertTo-SecureString $tempPassword -AsPlainText -Force

try {
# 1. Creăm userul în AD
New-ADUser `
-Name "$($UserData.FirstName) $($UserData.LastName)" `
-GivenName $UserData.FirstName `
-Surname $UserData.LastName `
-UserPrincipalName $upn `
-SamAccountName "$($UserData.FirstName.ToLower()).$($UserData.LastName.ToLower())" `
-Path $DeptConfig.OU `
-Title $UserData.JobTitle `
-Department $UserData.Department `
-Manager $UserData.ManagerUPN `
-AccountPassword $securePass `
-Enabled $true `
-ChangePasswordAtLogon $true

Write-Log "AD user creat: $upn" -Level Info

# 2. Adăugăm în grupuri
foreach ($group in $DeptConfig.Groups) {
Add-ADGroupMember -Identity $group -Members "$($UserData.FirstName.ToLower()).$($UserData.LastName.ToLower())"
}

# 3. Așteptăm sync-ul cu Entra ID (Azure AD Connect)
Write-Log "Aștept sync AAD Connect..." -Level Info
Start-ADSyncSyncCycle -PolicyType Delta
Start-Sleep -Seconds 90

# 4. Asignăm licența M365
Connect-MgGraph -Scopes "User.ReadWrite.All", "Organization.Read.All" -NoWelcome
$licenseSku = Get-MgSubscribedSku | Where-Object SkuPartNumber -eq $UserData.LicenseType
Set-MgUserLicense -UserId $upn -AddLicenses @{SkuId = $licenseSku.SkuId} -RemoveLicenses @()

# 5. Creăm folder personal + permisiuni NTFS
$userFolder = "\\fs01\Users\$($UserData.FirstName.ToLower()).$($UserData.LastName.ToLower())"
New-Item -Path $userFolder -ItemType Directory -Force | Out-Null
$acl = Get-Acl $userFolder
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
$upn, "Modify", "ContainerInherit,ObjectInherit", "None", "Allow")
$acl.SetAccessRule($rule)
Set-Acl $userFolder $acl

return [PSCustomObject]@{
Success = $true
UPN = $upn
TempPassword = $tempPassword
Folder = $userFolder
}
}
catch {
Write-Log "EROARE la creare $upn : $_" -Level Error
return [PSCustomObject]@{ Success = $false; UPN = $upn; Error = $_.Exception.Message }
}
}

4. Scriptul principal (orchestrator) citește CSV-ul, iterează userii
cu data de start ≤ azi și apelează funcția pentru fiecare:

$config = Get-Content ".\dept-config.json" | ConvertFrom-Json
$users = Import-Csv ".\new-hires.csv"
$today = Get-Date
$results = @()

foreach ($u in $users) {
if ([datetime]$u.StartDate -le $today -and -not (Get-ADUser -Filter "UserPrincipalName -eq '$($u.FirstName.ToLower()).$($u.LastName.ToLower())@company.ro'" -ErrorAction SilentlyContinue)) {
$deptCfg = $config.($u.Department)
if (-not $deptCfg) {
Write-Log "Departament necunoscut: $($u.Department)" -Level Warning
continue
}
$result = New-CompanyUser -UserData $u -DeptConfig $deptCfg
$results += $result
}
}

# Raport final pe mail către IT + HR
Send-OnboardingReport -Results $results -Recipients @("it@company.ro", "hr@company.ro")

5. Task Scheduler rulează scriptul zilnic la 07:00, cu un gMSA (group Managed Service Account) care are permisiunile minime necesare în AD. Logging-ul merge într-un fișier rotativ + Event Log Windows custom.

După 2 luni de rulare în producție, rezultatele concrete:

  • Timp per user: de la ~45 minute manual la ~2 minute auto. Pe un lot de 20 useri pe lună, asta înseamnă ~14 ore recuperate.
  • Zero useri uitați fără licență. Asta era cea mai frecventă greșeală manuală.
  • Consistență totală: fiecare user e în grupurile corecte, fiecare folder are permisiunile corecte. Auditul intern de anul ăsta a fost mult mai ușor.
  • HR-ul e mai fericit: completează un CSV, nu mai trimit mailuri individuale. Managerii primesc automat pe mail credențialele temporare pentru angajații lor.

Ce nu a mers din prima și cum am rezolvat:

  • Sync-ul cu Entra ID / Azure AD Connect – inițial apelam
    Set-MgUserLicense imediat după New-ADUser și eșua pentru că
    userul nu era sync-uit încă. Rezolvare: forțez sync delta și aștept 90s. Nu e elegant,
    dar e fiabil. Pentru cloud-only users nu ai problema asta.
  • Credențialele temporare pe mail – nu vrei parolele în clear în log-uri
    și nici pe mailul managerului. Am folosit un link către o pagină internă care expiră
    în 24h (one-time view), generat de un endpoint simplu în Azure Functions.
  • Gestionarea erorilor parțiale – ce faci dacă userul s-a creat în AD
    dar a eșuat asignarea licenței? Inițial scriptul lăsa „zombi” în AD. Acum am un mod
    de „dry run” + cleanup automat dacă eșuează după un anumit pas critic. Oricum, raportul
    de mail evidențiază clar userii cu status „parțial” pentru intervenție manuală.
  • Teams channelsAdd-TeamUser funcționează, dar există
    throttling dacă faci multe într-o rafală. Am adăugat retry cu exponential backoff.
  • Departamente noi – prima dată când HR a scris „Marketing” în CSV și
    configul meu nu avea secțiunea respectivă, scriptul a sărit userul. Acum trimit alertă
    către IT când apare un departament necunoscut, în loc să ignor silent.

Bonus: am făcut și scriptul de offboarding (dezactivare cont, revocare sesiuni M365, conversie mailbox în shared, mutare în OU-ul de „Disabled”, păstrare date 90 zile înainte de ștergere). Rulează din același CSV cu o coloană EndDate completată.

Tip solutie

Permanent

Voteaza

(2 din 4 persoane apreciaza acest articol)

Despre Autor

Leave A Comment?