Alright, so we’re nearly finished with our PowerShell script to create users in bulk in AD with random passwords, but we want to add a last few touches to our (finger painting) work of art. Firstly, let’s add that password back into the $Users variable so we can export it with the passwords afterwards. The idea here is for us to use the same information we imported from the CSV but we want to add the password we generated for each user in a new column.


\\\Write the Password Back

We can accomplish this by using the Add-Member cmdlet inside the ForEach loop. We can pretty much put this anywhere after the $Pwd variable is defined so let’s put it at the end:

$U | Add-Member -MemberType NoteProperty -Name "Initial Password" -Value $Pwd -Force

This line will add the property “Initial Password” with the generated password back to the individual user ($U) in the loop. Once the loop is complete, our $Users variable should now have all of the passwords added for each user just waiting for us to export.

\\\Where Am I?

Now, questions we must ask ourselves (shout out to my boy Yoda)…

Wouldn’t it be nice if we were older? I mean, wouldn’t it be nice if we knew what the script was doing and where we were at? Let’s say we have thousands of users (which is definitely why we want to create users in bulk) in our CSV and we finally press “go” but have no idea if it’s doing anything or not…as the audience stares at our screen patiently waiting for the magic to happen, let’s give them (and ourselves) a little bit of an indication of where we’re at so we don’t have to keep checking task manager to make sure nothing says “Not Responding…” Oh, I’m the only who does that? It’s cool…

\\\Progress Tracking

In the beginning of the loop after we’ve defined the Name Variations (so we can use the $Display variable we’ve defined that contains the First and Last Names), let’s add this line that will tell us which user we’re working on:

Write-Host "Working on $Display..." -ForegroundColor Cyan

And right after New-ADUser (where we actually create the account), let’s add another line to let us know that we successfully created the account:

Write-Host "Successfully created: $Display..." -ForegroundColor Cyan

You don’t have to choose Cyan as the foreground color, but it helps my elder eyes to see something other than the white text. Really, anything except for the daunting and dreadful red scare that we get with PowerShell error messages.

\\\Define Success

Ask more questions we must…What if the user already exists??? Will I break the internet???


But we can wrap the actual New-ADUser cmdlet (And our new Write-Host saying successfully created) in a Try/Catch block, which is very similar to a Do/Do Not But There Is No Try block…I’ll write more on this at a later time, but if you’re familiar with the Try/Catch block, we want to use it to detect errors and tell us what happened. Additionally, we don’t want it to tell us “Hey, everything succeeded!” when, in fact, everything did not succeed. Nobody likes successful messages for unsuccessful actions. Don’t write lies to yourself.

So the Try block will do everything in it until it encounters a terminating error. If there’s a terminating error, it will stop and move to the Catch block. If there are no errors, it will skip over the catch and move right along. As a good practice, we can specify the error action on the New-ADUser cmdlet. Here’s our updated changes with the Try/Catch block, Add-Member, and Write-Host:

#Create New User in AD with the Parameters defined
    New-ADUser @Parameters -ErrorAction Stop
    Write-Host "Successfully created $Display!" -ForegroundColor Green
    $U | Add-Member -MemberType NoteProperty -Name "Initial Password" -Value $Pwd -Force
    Write-Host "Bro...something went horribly wrong. Does this user already exist?? $Display" -ForegroundColor Red

There is definitely more we can do here with writing to specific pipelines and grabbing the specific errors and doing different things based on each, but for now, this will conclude our training in adding just a smidgen of error handling so you can sleep better at night. What? You don’t go to sleep thinking about your scripts? Yeah…of course, not…me uh, neither…

\\\Export Data

Lastly, what do we do with the $Users variable that now has the passwords? At the end of our script (outside of the loop) we can choose to export to the same path and file that we imported (overwriting our original CSV, a Sith move) or we could go with a completely new file (just like a Jedi), it’s up to you. So after the curly brace at the end of our ForEach loop, I’m going to go with a new CSV file so I don’t accidentally overwrite the CSV file that contained my life’s work:

$Users | Export-Csv C:\Users\star.killer\Desktop\TooManyUsers_SamplePwds.csv -NoTypeInformation 

\\\Putting it All Together

Finished Script
Here is the finished script including the function with only a couple of changes (changed the paths for importing and exporting to be variables you can easily change at the beginning of the script). You can also get your hands on this script via GitHub!

#####FINAL FORM#####
$ImportPath = "C:\Users\star.killer\Desktop\EmpireUsers_EmpireOnly.csv"
$ExportPath = "C:\Users\star.killer\Desktop\TooManyUsers_WPwds.csv"
$Users = Import-Csv $ImportPath
#Randomize Passwords the hard way, Option4
function Get-RandomPassword{
        if($Length -lt 4){
        $Numbers = 1..9
        $LettersLower = 'abcdefghijklmnopqrstuvwxyz'.ToCharArray()
        $LettersUpper = 'ABCEDEFHIJKLMNOPQRSTUVWXYZ'.ToCharArray()
        $Special = '!@#$%^&*()=+[{}]/?<>'.ToCharArray()
        #For the 4 character types (upper, lower, numerical, and special), let's do a little bit of math magic
        $N_Count = [math]::Round($Length*.2)
        $L_Count = [math]::Round($Length*.4)
        $U_Count = [math]::Round($Length*.2)
        $S_Count = [math]::Round($Length*.2)
        $Pwd = $LettersLower | Get-Random -Count $L_Count
        $Pwd += $Numbers | Get-Random -Count $N_Count
        $Pwd += $LettersUpper | Get-Random -Count $U_Count
        $Pwd += $Special | Get-Random -Count $S_Count
        #If the password length isn't long enough (due to rounding), add X special characters, where X is the difference between the desired length and the current length.
        if($Pwd.length -lt $Length){
            $Pwd += $Special | Get-Random -Count ($Length - $Pwd.length)
        #Lastly, grab the $Pwd string and randomize the order
        $Pwd = ($Pwd | Get-Random -Count $Length) -join ""
#Loop through each user from the CSV
Foreach($U in $Users){
    #Define Name variations and generate a random password
    $FirstDotLast = "$($U.First).$($U.Last)"
    $Display = "$($U.First) $($U.Last)"
    $UPN = "$FirstDotLast@empire.local"
    $Pwd = Get-RandomPassword -Length 8
    Write-Host "Working on $Display..." -ForegroundColor Cyan
    #Define Parameters
    $Parameters = @{
        Name = $FirstDotLast
        GivenName = $U.First
        Surname = $U.Last
        SamAccountName = $FirstDotLast
        DisplayName = $Display
        UserPrincipalName = $UPN
        AccountPassword = (ConvertTo-SecureString $Pwd -AsPlainText -Force)
        Enabled = $true
        ChangePasswordAtLogon = $true
        Title = $U.Title
        OtherAttributes = @{"Allegiance"=$U.Allegiance;"Species"=$U.Species}
    #Create New User in AD with the Parameters defined above
        New-ADUser @Parameters -ErrorAction Stop
        Write-Host "Successfully created $Display!" -ForegroundColor Green
        $U | Add-Member -MemberType NoteProperty -Name "Initial Password" -Value $Pwd -Force
        Write-Host "Bro...something went horribly wrong. Does this user already exist?? $Display And did you run this as administrator?" -ForegroundColor Red
$Users | Export-Csv $ExportPath -NoTypeInformation

I ran this script with one user in the CSV (Palpatine) and everything looked good. Let’s try it now with multiple users to make sure we’re not building our house on shifting sand.

Here is my CSV with 9 users:


Let’s do this thang! I’ve already created the Emperor from the sample CSV (nobody wants to get on his bad side) so I should expect to see it “catch” the error trying to create the user that already exists. Here’s my output:

Almost brings tears to my eyes it’s so beautiful.

\\\Putting it All Together

And the finale, here is the new CSV file exported. Since I had already created Palpatine and it was caught in the try block, it didn’t update the password. Bigly.

This concludes our 4 part series and you should now know how to bulk create users in AD via PowerShell with random passwords. Thoughts? Stuck? Have something new to teach me? Like Star Wars? Have hands? Comment below!


Nested AD Managers (PowerShell)

So I had a somewhat odd request (isn't that how all truly great stories start) a long time ago in an IT galaxy far, far away. And it went something like this: we want to email an employee and CC their manager, but if their manager is inactive (took a one-way trip to...

6 Things I Wish I Had Known About PowerShell

About a hundred years ago when I stopped riding dinosaurs to work and started learning PowerShell, I struggled to know what to learn first and where to even begin. That blue box with white writing was intimidating to say the least. I finally worked up the courage to...

Index Scripts for Windows Search

So you just finished writing some code and you go to save your file. You summarize all of the important aspects this section of code contains into a nice, easy-to-read file name that your future self will immediately recognize. Fast forward to the future where you...

Array vs ArrayList (PowerShell)

For some tasks in life, being precise is a necessity. But most of us get away with rounding, paraphrasing, and hitting in the general vicinity most of the time. Depending on your personality, you may be one who strives for perfection and strains on every miniscule...

Spice Up HTML Emails with PowerShell – Part III

So far in this series we've fumbled our way around the kitchen and tried to wing it when sending HTML emails with PowerShell. It was clunky to say the least. We then went through our spice rack and built an HTML template, highlighting the nuances of each spicy element...