
Automate Active Directory User Creation from a CSV with PowerShell
Before you run this
This guide builds a PowerShell script that reads a CSV file of people and creates a matching Active Directory user account for each row — name, logon name, OU placement, and an initial password. Its purpose is to save you from clicking through Active Directory Users and Computers a hundred times when you onboard a class, a department, or a new office.
This changes your directory. Creating accounts is not destructive in the "delete data" sense, but it is a real write to AD that replicates to every domain controller, and a badly-scoped run can litter an OU with dozens of wrong accounts you then have to clean up. Treat it with respect.
Privileges: You need a session with rights to create user objects in the target OU — in practice an account with delegated user-creation rights on that OU, or a Domain Admin. You do not normally need to open PowerShell "as Administrator" for this (AD permissions are what matter, not local elevation), but you do need the ActiveDirectory module, which comes from RSAT on a workstation or the AD DS role tools on a server.
Test first. Read the script before you run it. Every New-ADUser here supports -WhatIf, and the first thing I show you is a dry run that creates nothing. Point it at a test OU with a two-row CSV before you ever aim it at real people. Do not paste-and-run a bulk-creation script blind against a live domain.
Assumptions I'm holding to:
- Windows Server 2019 or 2022, domain-joined, run from a DC or a management box with RSAT.
- Windows PowerShell 5.1 (the default) — this works identically in PowerShell 7 with the module imported.
- A single domain,
example.com. Substitute your own everywhere you see it.
The CSV
Keep the columns explicit and boring. Create C:\path\to\newusers.csv (your path):
FirstName,LastName,SamAccountName,Department,Title,OU
Ada,Lovelace,alovelace,Engineering,Analyst,"OU=Staff,DC=example,DC=com"
Alan,Turing,aturing,Engineering,Analyst,"OU=Staff,DC=example,DC=com"
A few rules that save pain later:
SamAccountNameis the pre-Windows-2000 logon name. It must be unique in the domain and 20 characters or fewer.- Quote the
OUvalue because it contains commas. It must be a real, existing OU distinguished name — the script does not create OUs. - I'm generating the UPN and email from
SamAccountName+@example.comin the script, so they're not in the CSV. If your UPN suffix differs, that's the one line to change.
The script
Save this as New-UsersFromCsv.ps1. It reads the CSV, skips any account that already exists, and creates the rest. I've kept the password handling deliberate — see the note below the code.
#Requires -Modules ActiveDirectory
param(
[string]$CsvPath = 'C:\path\to\newusers.csv', # your CSV
[string]$UpnSuffix = 'example.com', # your UPN / email domain
[switch]$WhatIf # pass -WhatIf for a dry run
)
Import-Module ActiveDirectory
# One shared initial password for this batch. Change it, and force a reset at first logon.
$initialPassword = Read-Host -Prompt 'Enter the initial password for this batch' -AsSecureString
$users = Import-Csv -Path $CsvPath
foreach ($u in $users) {
$sam = $u.SamAccountName.Trim()
# Skip anything that already exists so a re-run is safe.
if (Get-ADUser -Filter "SamAccountName -eq '$sam'" -ErrorAction SilentlyContinue) {
Write-Warning "Skipping $sam - already exists."
continue
}
$upn = "$sam@$UpnSuffix"
$params = @{
Name = "$($u.FirstName) $($u.LastName)"
GivenName = $u.FirstName
Surname = $u.LastName
DisplayName = "$($u.FirstName) $($u.LastName)"
SamAccountName = $sam
UserPrincipalName = $upn
EmailAddress = $upn
Department = $u.Department
Title = $u.Title
Path = $u.OU
AccountPassword = $initialPassword
ChangePasswordAtLogon = $true # force a reset on first sign-in
Enabled = $true
WhatIf = $WhatIf
}
try {
New-ADUser @params
if (-not $WhatIf) { Write-Host "Created $sam" -ForegroundColor Green }
}
catch {
Write-Warning "FAILED to create $sam : $($_.Exception.Message)"
}
}
Two things worth understanding before you trust it:
- The password.
New-ADUser's-AccountPasswordtakes aSecureString, which is why I useRead-Host -AsSecureString— I'm not going to hand you a script that hard-codes a plaintext password into a.ps1on disk. Everyone in the batch gets the same initial password and is forced to change it at first logon via-ChangePasswordAtLogon $true. If your domain password policy rejects the string you type,New-ADUserwill throw, and thecatchblock prints why. -WhatIfis plumbed through. SplattingWhatIf = $WhatIfinto the parameter hashtable means the switch on the script controls the switch on the cmdlet. Run it with-WhatIfand nothing is written.
Run the dry run first
Always. This creates nothing and prints what it would do:
.\New-UsersFromCsv.ps1 -CsvPath 'C:\path\to\newusers.csv' -WhatIf
You'll see a What if: line per user. Confirm the names, the SamAccountNames, and especially the target OU are right. When you're satisfied, run it for real:
.\New-UsersFromCsv.ps1 -CsvPath 'C:\path\to\newusers.csv'
It will prompt once for the initial password, then create each account and print a green line per success.
Verify it worked
Pull the accounts back out of AD and confirm they landed where you meant them to. This lists everyone under the target OU with the fields you care about:
Get-ADUser -SearchBase 'OU=Staff,DC=example,DC=com' -Filter * `
-Properties Department, Title, EmailAddress |
Select-Object SamAccountName, Name, Department, Title, EmailAddress, Enabled |
Format-Table -AutoSize
Check a single account in detail:
Get-ADUser -Identity alovelace -Properties * | Format-List Name, SamAccountName, UserPrincipalName, Enabled, DistinguishedName
Confirm Enabled is True and the DistinguishedName shows the OU you expected. If you have more than one DC, remember replication isn't instant — query the DC you wrote to, or give it a minute.
Undo it if you got it wrong
Account creation is reversible: you delete the accounts. The safe pattern is to select exactly what you created, preview with -WhatIf, then remove. To pull the batch straight from your CSV rather than guessing:
# Preview the removal first - creates the same list, deletes nothing.
Import-Csv 'C:\path\to\newusers.csv' | ForEach-Object {
Remove-ADUser -Identity $_.SamAccountName.Trim() -WhatIf
}
Read that output carefully. If — and only if — it lists exactly the accounts you meant to undo, drop the -WhatIf to actually remove them. Remove-ADUser prompts for confirmation by default, which is a second safety net; keep it.
Import-Csv 'C:\path\to\newusers.csv' | ForEach-Object {
Remove-ADUser -Identity $_.SamAccountName.Trim()
}
Deleting a user is a real deletion. If your domain has the AD Recycle Bin enabled you can restore recently-deleted objects, but don't rely on that as a plan — get the dry run right instead.
Where to go from here
If you need to set attributes this script doesn't cover — manager, office, group membership, a home directory — the mainstream approach is to add columns to the CSV and either pass them to New-ADUser (for attributes it accepts directly) or follow up with Set-ADUser and Add-ADGroupMember in the same loop. Check the exact parameter names against the official docs before adding them; the module accepts a lot, but not everything by the name you'd guess. See Microsoft Learn for New-ADUser, Set-ADUser, and Get-ADUser for the authoritative parameter lists.
Runs enterprise networks and security for a living, and writes Shore Up to turn two decades of hands-on Linux, Windows and mail-server work into guides you can actually use.
More about the author →Was this article helpful?
Tap a star — no sign-in needed.
Be the first to rate this article.
