Clonar usuaris a Active Directory amb PowerShell
Hi ha tasques a Active Directory que són més mecàniques que complicades, però tot i així es presten a errors tontos que acaben donant problemes. Clonar un usuari —és a dir, crear un nou compte prenent-ne una altra com a plantilla— és una d’aquestes tasques. Sembla trivial fins que algú et demana que mantinguis els grups, els atributs personalitzats, el home folder a la ruta correcta i que el logon script no dispari qualsevol cosa fora de lloc.
Lo bàsic: no copiïs només els grups
Una de les formes més habituals de “clonar” un usuari és copiar els seus grups i assignar-los al nou. Ho he fet, tots ho hem fet. I funciona… si l’únic que importa és el control d’accés. Però clonar només els grups no cobreix coses com:
- Ubicació en OU, que moltes vegades defineix polítiques i scripts.
- Atributs com Department, Title o Manager, que afecten eines com Teams, Delve o sistemes externs que fan sync amb AD.
- HomeDirectory, ProfilePath i LogonScript, que poden heretar rutes específiques.
- Memberships ocults en grups niats o dinàmics.
Si només còpies els grups, et deixes tot això fora. I algú acabarà preguntant per què a “Joan Nou” no li mapeja la unitat Z o per què no pot iniciar sessió en una aplicació que tira d’un atribut concret.
Decisions clau que convé prendre abans
Hi ha un parell de decisions que convé prendre abans de posar-se a picar codi:
- Des d’on clones?
Alguns scripts clonen des d’un compte viu; d’altres fan servir un usuari “plantilla” específic, que no té login habilitat i es manté amb atributs nets. Si treballes en un entorn amb canvis freqüents en els usuaris, el compte viu pot ser més representatiu. Si vols consistència, la plantilla dedicada evita sorpreses. - Quins atributs?
No tots els atributs s’haurien de clonar a cegues. El mail, per exemple, cal regenerar-lo. Altres com sAMAccountName, userPrincipalName o displayName s’han de generar segons convenció. Però molts altres (department, title, company, physicalDeliveryOfficeName, etc.) poden copiar-se directament. - Com gestiones els errors i els duplicats?
Res pitjor que crear un usuari a mà i després veure que l’script falla perquè ja existeix el sAMAccountName. Si l’script no valida això bé, estàs generant feina addicional.
Un enfocament que funciona
Aquest és l’enfocament que aplico quan necessito una cosa fiable, sense ficar-me en scripts mastodòntics ni dependre de mòduls externs:
- Obtenir l’ usuari plantilla.
- Generar els atributs únics del nou usuari.
- Crear l’ objecte AD amb els atributs bàsics.
- Copiar la pertinença a grups.
- Aplicar atributs addicionals si cal.
- Habilitar el compte i establir la contrasenya.
L’ script base
# Paràmetres d' entrada
$TemplateUser = "j.antic"
$NewUserCN = "j.nou"
$OU = "OU=Users,DC=contoso,DC=local"
$Password = ConvertTo-SecureString "P@ssword1234" -AsPlainText -Force
# Carregar l’ usuari plantilla
$Template = Get-ADUser -Identity $TemplateUser -Properties *
if (!$Template) {
Write-Error "L'usuari no existeix"
exit
}
# Generar valors únics
$NewSamAccountName = $NewUserCN
$NewUserPrincipalName = "$NewSamAccountName@contoso.local"
$DisplayName = "Joan Nou"
$GivenName = "Joan"
$Surname = "Nou"
# Crear nou usuari
New-ADUser `
-Name $DisplayName `
-GivenName $GivenName `
-Surname $Surname `
-SamAccountName $NewSamAccountName `
-UserPrincipalName $NewUserPrincipalName `
-Path $OU `
-AccountPassword $Password `
-Enabled $false `
-DisplayName $DisplayName `
-Department $Template.Department `
-Title $Template.Title `
-Company $Template.Company `
-Office $Template.PhysicalDeliveryOfficeName `
-Description $Template.Description `
-ProfilePath $Template.ProfilePath `
-HomeDirectory $Template.HomeDirectory `
-HomeDrive $Template.HomeDrive `
-ScriptPath $Template.ScriptPath `
-EmailAddress "$NewSamAccountName@contoso.local"
# Copiar grups (sense Domain Users)
$Groups = Get-ADUser $TemplateUser -Properties MemberOf | Select-Object -ExpandProperty MemberOf
foreach ($GroupDN in $Groups) {
if ($GroupDN -notlike "*CN=Domain Users,*") {
Add-ADGroupMember -Identity $GroupDN -Members $NewSamAccountName
}
}
# Habilitar compte
Enable-ADAccount -Identity $NewSamAccountName
El que aquest script fa bé
- Manté la coherència amb els camps que sovint importen a IT i HR.
- Omet Domain Users en la còpia de grups, perquè ja ve per defecte.
- Respecta l’OU, que sovint té GPOs aplicades.
- Permet control total sobre els valors únics com a nom, login i correu.
- Fa servir atributs estesos que no sempre es veuen als exemples (HomeDrive, ScriptPath, etc.).
Detalls que sovint donen problemes
El perfil i les rutes UNC
Si estàs clonant un usuari que té ProfilePath o HomeDirectory apuntant a \\srv01\users\j.antic, més val que aquests atributs no es copiïn tal qual. A l’script anterior, això es fa —i és intencionat—, però només si la teva infraestructura ja genera rutes automàticament amb DFS o polítiques de redirecció.
Si no és així, convé posar una lògica que reemplaci el nom a la ruta:
$NewProfilePath = $Template.ProfilePath -replace $Template.sAMAccountName, $NewSamAccountName
Correu duplicat o mal assignat
He vist scripts que copien l’atribut mail sense tocar-lo. Resultat: dos usuaris amb el mateix correu. Si estàs sincronitzant amb Azure AD o fas servir Exchange híbrid, això acaba en errors de replicació o bústies fantasmes. La regla és clara: si l’ atribut és únic en el domini, genera un de nou encara que sigui provisional.
Manager i relacions jeràrquiques
Clonar el camp Manager té sentit si estàs replicant una estructura similar. Però si estàs creant usuaris genèrics o plantilles per a diferents departaments, pot portar a relacions incorrectes en eines que llegeixen aquesta jerarquia (com Teams o certes vistes d’HRIS). Considera ometre-ho si no ho tens clar.
Polítiques d’ expiració de contrasenyes
Molts scripts creen el compte amb PasswordNeverExpires activat sense que ningú ho noti. O al revés: sense activar-lo quan sí que s’esperava. Si treballes amb polítiques estrictes, assegure’t de definir-lo:
Set-ADUser -Identity $NewSamAccountName -PasswordNeverExpires $false
També pots forçar el canvi de contrasenya en el primer inici:
Set-ADUser -Identity $NewSamAccountName -ChangePasswordAtLogon $true
Casos reals on clonar malament costa temps
1. El cas del “clon sense llicència”
En una organització amb sincronització a Azure AD i aprovisionament automàtic de llicències basat en grups, van clonar un usuari sense replicar bé la pertinença als grups correctes. En el sistema on-prem tot semblava estar bé, però en sincronitzar, el compte no rebia cap llicència. Per què? Perquè el grup clau per a llicències estava fora de l’scope de l’script de clonació.
Solució: afegeix un pas als scripts que logueja tots els grups assignats a l’usuari nou, i una llista blanca de grups “crítics” que han d’estar presents (correu, Teams, MFA, etc.). Si en falta un, es llença una alerta.
2. Herència de polítiques no desitjades
En un altre cas, el clon es va crear en una OU diferent per accident (l’script tenia una OU per defecte si no s’especificava). Resultat: l’ usuari rebia un escriptori genèric, sense les GPOs del departament, sense mapejar les unitats, sense bloqueig de pantalla, etc. Tècnicament, el compte estava ben creat. Operativament, no servia.
Solució: fer que l’OU sigui un paràmetre obligatori o es derivi de l’usuari original. Si l’usuari original és a OU=Vendes,DC=empresa,DC=local, el nou també, llevat que s’especifiqui el contrari.
$OU = ($Template.DistinguishedName -split ",", 2)[1]
Això extreu el DN menys el CN, que és suficient per replicar la mateixa OU.
3. Els atributs que trenquen integracions
Hi ha camps que semblen inofensius, però si estàs connectat amb sistemes com SAP, ServiceNow, o portals d’autoservei, hi ha valors que han de tenir un format específic o ser únics. Això va passar amb employeeID: es va clonar amb el mateix valor i, com que aquest camp era clau en el sistema de RRHH, l’alta va fallar silenciosament. El clon apareixia en blanc o sense dades, i ningú sabia per què.
Solució: exclou explícitament atributs com employeeID, employeeNumber, mailNickname, proxyAddresses i extensionAttribute* si l’entorn té sincronització externa.
# No copiar aquests camps
$CampsExclossos = @("employeeID", "employeeNumber", "proxyAddresses", "mailNickname")
Bones pràctiques
Assegura un naming únic des del començament
Encara que l’AD no impedeixi DisplayName duplicats, els sAMAccountName i userPrincipalName sí que han de ser únics. Si generes noms automàticament, és millor validar abans:
if (Get-ADUser -Filter {SamAccountName -eq $NewSamAccountName}) {
Write-Error "Ya existeix un compte amb aquest SamAccountName"
exit
}
Si l’entorn ho permet, pots afegir sufixos numèrics incrementals. Però millor si tens una convenció de noms clara des de RRHH.
Logging també des del començament
Un script que clona usuaris sense deixar registre és una trampa mortal. Si el procés falla o genera un error operatiu dies després, ningú sabrà què va passar. Jo afegeixo sempre un log CSV per data:
$LogEntry = [PSCustomObject]@{
Data = (Get-Date).ToString("yyyy-MM-dd HH:mm")
Plantilla = $TemplateUser
NouUsuari = $NewSamAccountName
Nom = $DisplayName
Resultat = "OK"
}
$LogEntry | Export-Csv -Path "C:\Scripts\Logs\ClonacioUsuaris-$(Get-Date -Format yyyyMMdd).csv" -Append -NoTypeInformation
Pots afegir camps com errors capturats o diferències de grups.
Scripts versionats, no editats sobre la marxa
Un altre error comú: tocar l’ script directament a l’ entorn de producció. És millor versionar-lo amb Git, encara que sigui en un repositori compartit de xarxa, i tenir clar quina versió s’està executant. Això permet revisar canvis, revertir regressions i mantenir coherència quan hi ha més d’un administrador.
Evita hardcodejar el domini
Molts scripts antics tenen coses com @elmeudomini.local o rutes LDAP fixes. Això falla si tens múltiples dominis o si canvies el nom d’usuari per política. Fes servir variables globals o detecta el domini dinàmicament:
$Domain = (Get-ADDomain).DNSRoot
$NewUserPrincipalName = "$NewSamAccountName@$Domain"
Això fa que l’script sigui més portable i menys depenent de supòsits.
Gestiona bé la contrasenya
Si l’script fixa una contrasenya genèrica (com als exemples), assegura’t que estigui dins de les polítiques de complexitat. Idealment, hauries de tenir integració amb un sistema que generi contrasenyes temporals i les comuniqui de forma segura (o amb autoservei de restabliment). Però mentrestant:
- Forçar canvi en iniciar sessió.
- Evitar contrasenyes que apareixen en llistes comunes.
- Registrar a qui se li va lliurar la contrasenya inicial.
Ajustos útils que s’acaben automatitzant
- Autoexpiració: per a comptes temporals (becaris, externs), pots afegir una data d’expiració:
Set-ADAccountExpiration -Identity $NewSamAccountName -DateTime (Get-Date). AddDays(90)
- Fotos d’usuari: si estàs en un entorn que fa servir Outlook/Teams i vols estandarditzar, pots deixar el camp preparat o automatitzar la càrrega posterior.
- Assignació automàtica a grups base: alguns grups haurien d’anar sempre en tots els usuaris nous (VPN, WiFi, portal intern). Millor declarar-los com a llista fixa en l’ script.
- Xec post-creació: incloure una funció que verifiqui que el nou usuari:
- Pot iniciar sessió.
- Té tots els grups requerits.
- Es sincronitza correctament amb AzureAD si aplica.
Conclusió
Clonar usuaris a Active Directory amb PowerShell pot ser ràpid, però si no es fa bé, pot donar lloc a comptes inconsistents que trenquen fluxos, creen confusió o fan perdre temps més endavant. El més útil que he après no és sobre comandaments nous, sinó sobre decisions petites: com anomenes coses, com gestiones els atributs únics, com evites copiar més del compte o menys del necessari.

