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-MgUserLicenseimediat 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 channels –
Add-TeamUserfuncț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ă.
Leave A Comment?