Search
Join the Technical Preview Program
See how NVMe-oF removes iSCSI
bottlenecks in your HCI
The Best Hyperconverged
Infrastructure
(HCI) for Enterprise
ROBO, SMB & Edge
The Best Virtual SAN
for Enterprise ROBO, SMB & Edge

Creating Bulk User Accounts in AD via PowerShell

  • January 19, 2025
  • 14 min read
StarWind Storage and Virtualization Engineer. Volodymyr specializes in solution architecture and data protection. With a technical background in applied physics, he provides unique analytical leadership in building resilient IT infrastructure. Volodymyr delivers expert guidance on optimizing virtualized environments, disaster recovery, and enterprise-scale storage systems.
StarWind Storage and Virtualization Engineer. Volodymyr specializes in solution architecture and data protection. With a technical background in applied physics, he provides unique analytical leadership in building resilient IT infrastructure. Volodymyr delivers expert guidance on optimizing virtualized environments, disaster recovery, and enterprise-scale storage systems.

Active Directory (AD) administrators often need to create multiple user accounts at once. Doing this manually for a large number of users can be time-consuming and error-prone. In this article, we review two methods for bulk user creation using PowerShell, updated to reflect best practices. All techniques leverage the standard ActiveDirectory PowerShell module, which is fully supported on modern Windows Server versions (including Windows Server 2022) and in Windows PowerShell 5.1 or PowerShell 7+. The primary cmdlets used are New-ADUser, Get-ADUser, Remove-ADUser, Export-CSV, and Import-CSV – all of which remain available and function the same in current environments. We will also use Microsoft Excel as a convenient tool for editing CSV files, though any spreadsheet or text editor could be used.

Overview: We’ll demonstrate two scenarios for bulk account creation:

  • Scenario 1 – Uniform Accounts: Creating multiple AD user accounts that share a common naming pattern and similar parameters (e.g., User1, User2, … all with a default password). This is useful for test accounts or temporary access accounts that have minimal required attributes.
  • Scenario 2 – Customized Accounts: Creating multiple AD user accounts with individual attributes by importing details from a CSV file. This approach is suited for onboarding many new employees or any situation where each account has distinct information (name, department, etc.).

Heads-up before you run anything: work in a lab first, and make sure your account has rights to create users in the target OU. Also decide on your password policy and whether you’ll force a change at first logon.

Bulk AD Account Creation with Typical Settings (Uniform Accounts)

First, we’ll create a batch of AD user accounts that have a consistent name format and identical settings. In this example, we create 15 users named Client1 through Client15, all in the OU Users_New in the Test.local domain. Each account will be enabled with a default password that varies by user (e.g., Password1 for Client1, Password2 for Client2, etc.). We will use the New-ADUser cmdlet in a loop to achieve this.

1) Check for conflicts

Verify no conflicting accounts exist: Before creating new accounts, it’s wise to check that accounts with the intended naming pattern don’t already exist, to avoid duplicates. We use Get-ADUser with an LDAP filter to search for any existing users whose name starts with “Client”:

Get-ADUser -Filter 'name -Like "Client*"' -SearchBase "OU=Users_New,DC=Test,DC=local"

Output: If that returns nothing, you’re clear.

2) Create users in a loop (with sensible defaults)

The original one-liner works, but here’s a sturdier version that sets UPNs, display names, and forces a password change on first logon. It also generates a per-user starter password (meeting complexity) instead of a guessable “Password1”.

# Target OU and UPN suffix

$TargetOU = "OU=Users_New,DC=Test,DC=local"

$UPNSuffix = "@test.local"




# Simple random password generator (letters, numbers, symbols; 14 chars)

function New-RandomPassword {

$len = 14

$lower = 97..122 | ForEach-Object {[char]$_}

$upper = 65..90 | ForEach-Object {[char]$_}

$nums = 48..57 | ForEach-Object {[char]$_}

$sym = '!@#$%^&*-_=+?'.ToCharArray()

$pool = $lower + $upper + $nums + $sym

-join (1..$len | ForEach-Object { $pool | Get-Random })

}




1..15 | ForEach-Object {

$name = "Client$_"

$pass = (New-RandomPassword)

try {

New-ADUser `

-Name $name `

-SamAccountName $name `

-GivenName $name `

-Surname "User" `

-DisplayName $name `

-UserPrincipalName ($name + $UPNSuffix) `

-Path $TargetOU `

-AccountPassword (ConvertTo-SecureString $pass -AsPlainText -Force) `

-Enabled $true `

-ChangePasswordAtLogon $true

# Optional: show what you set

[PSCustomObject]@{ User=$name; InitialPassword=$pass } | Format-Table -Auto

}

catch {

Write-Warning "Failed to create $name : $($_.Exception.Message)"

}

}

Why this way: you get a unique, complex starter password; UPNs are set to something users can actually type; first logon prompts a change. If you want to dry-run first, add -WhatIf to New-ADUser.

3) Confirm the result

Get-ADUser -Filter 'Name -like "Client*"' -SearchBase $TargetOU |

Select-Object Name,Enabled,UserPrincipalName,DistinguishedName

4) Cleanup for lab users (optional)

1..15 | ForEach-Object {

$n = "Client$_"

Remove-ADUser -Identity $n -Confirm:$false -ErrorAction SilentlyContinue

}

If you’d rather be prompted each time, drop -Confirm:$false.

Small but useful add-ons: add a home OU guardrail so you don’t splat users into the default container by mistake, and keep SamAccountName ≤ 20 characters (UPN can be longer).

Bulk AD Account Creation with Individual Settings (CSV Import)

For real onboarding, you want per-user attributes (name, title, department, UPN, etc.). A CSV import is the usual route.

1) Build a clean CSV (start minimal – don’t export a template user)

Heads‑up: template exports include system‑managed attributes (objectGUID, objectSID, whenCreated, lastLogonDate, UAC flags) that you shouldn’t import and can break New-ADUser. Start with a minimal CSV and add columns on purpose.

Start minimal with only the columns you’ll actually set. Here’s a starter header and two example rows:

Name,GivenName,Surname,SamAccountName,UserPrincipalName,Department,Title,Enabled,ChangePasswordAtLogon,AccountPassword,OU

James Hunter,James,Hunter,jhunter,jhunter@test.local,Sales,Account Exec,TRUE,TRUE,Temp#Jhu!2025,OU=Users_New,DC=Test,DC=local

Olivia Moore,Olivia,Moore,omoore,omoore@test.local,IT,Systems Engineer,TRUE,TRUE,Temp#Omo!2025,OU=Users_New,DC=Test,DC=local

Put this in C:\Temp\imp.csv. If you want password randomization instead, leave AccountPassword blank and have the script generate one per row (shown below).

2) Import and create users

This script accepts either a provided AccountPassword from CSV or generates one. It also validates that the OU exists and logs outcomes.

$CsvPath = "C:\Temp\imp.csv"

$Users = Import-Csv -Path $CsvPath




function New-RandomPassword {

$len = 14

$lower = 97..122 | ForEach-Object {[char]$_}

$upper = 65..90 | ForEach-Object {[char]$_}

$nums = 48..57 | ForEach-Object {[char]$_}

$sym = '!@#$%^&*-_=+?'.ToCharArray()

$pool = $lower + $upper + $nums + $sym

-join (1..$len | ForEach-Object { $pool | Get-Random })

}




$Report = foreach ($u in $Users) {

# Validate OU path per row (lets you mix OUs if needed)

try { [void](Get-ADOrganizationalUnit -Identity $u.OU) }

catch { return [PSCustomObject]@{ Sam=$u.SamAccountName; Result="FAIL"; Note="OU not found: $($u.OU)" } }




$pwd = if ([string]::IsNullOrWhiteSpace($u.AccountPassword)) { New-RandomPassword } else { $u.AccountPassword }




try {

New-ADUser `

-Name $u.Name `

-GivenName $u.GivenName `

-Surname $u.Surname `

-DisplayName $u.Name `

-SamAccountName $u.SamAccountName `

-UserPrincipalName $u.UserPrincipalName `

-Department $u.Department `

-Title $u.Title `

-Path $u.OU `

-Enabled ([bool]$u.Enabled) `

-ChangePasswordAtLogon ([bool]$u.ChangePasswordAtLogon) `

-AccountPassword (ConvertTo-SecureString $pwd -AsPlainText -Force)




[PSCustomObject]@{ Sam=$u.SamAccountName; Result="OK"; InitialPassword=$pwd }

}

catch {

[PSCustomObject]@{ Sam=$u.SamAccountName; Result="FAIL"; Note=$_.Exception.Message }

}

}




$Report | Format-Table -Auto

# Optional: save the report for the helpdesk to hand to new users (or store securely)

# $Report | Export-Csv C:\Temp\CreatedUsers.csv -NoTypeInformation

Why this pattern works well: the CSV stays predictable (only fields you actually set), per-row OU lets you drop different teams into different containers in one pass, and you get a simple success/fail report with initial passwords if you choose to generate them.

3) Verify in AD

Get-ADUser -SearchBase "OU=Users_New,DC=Test,DC=local" -Filter * |

Select-Object Name,Enabled,UserPrincipalName,DistinguishedName

Spot-check a couple of users: log on, make sure they’re prompted to change their password, and confirm group policies apply as expected.

Extra guardrails you’ll thank yourself for

  • Do a dry run first by adding -WhatIf to New-ADUser while you’re dialing in the CSV.
  • Before creating, test for existing SamAccountName or UserPrincipalName and log a “skip” instead of failing the whole batch.
  • Keep the UPN suffix consistent and ensure it exists in your domain (AD Domains and Trusts).
  • Keep SamAccountName at 20 characters or less – long names can be trimmed or templated.
  • If you export the report with initial passwords, store it somewhere secure and delete it once handed off.
  • Finally, add default groups, home folders, or licenses (if hybrid) as a second step – easy to script with the same CSV.

Conclusion

Two approaches, same goal: get a bunch of users into AD without hand-cramping your way through the GUI. Use the uniform batch loop for test and temporary accounts – fast and tidy. Use the CSV path for real onboarding – clean columns, good defaults, and a simple report. Keep OUs explicit, set UPNs users can type, generate proper starter passwords, and force a change at first logon. Test with -WhatIf, run in a lab before prod, and keep a short script around for cleanup if you’re creating temporary accounts.

Hey! Found Volodymyr’s article helpful? Looking to deploy a new, easy-to-manage, and cost-effective hyperconverged infrastructure?
Alex Bykovskyi
Alex Bykovskyi StarWind Virtual HCI Appliance Product Manager
Well, we can help you with this one! Building a new hyperconverged environment is a breeze with StarWind Virtual HCI Appliance (VHCA). It’s a complete hyperconverged infrastructure solution that combines hypervisor (vSphere, Hyper-V, Proxmox, or our custom version of KVM), software-defined storage (StarWind VSAN), and streamlined management tools. Interested in diving deeper into VHCA’s capabilities and features? Book your StarWind Virtual HCI Appliance demo today!