PowerShell: Password Generation
Posted on: October 26, 2025
To wrap up Cybersecurity Awareness Month, I'd like to share some of the ways I generate passwords programmatically. Whether it's a password being created during the automatic provisioning of an admin account, generating a quick temporary password to give to an end user, or setting an insanely long password on a recently terminated employee's account. Being able to not only set the password programmatically, but also generate the password within your script, saves you valuable time. Otherwise, you're either trying to cleverly think up a password that will fit within your password policy or wildly flopping your hands onto your keyboard in Notepad and copy/pasting it.
Iterative Passwords
This first method is not something I recommend in 99% of cases, though I think we are all guilty of it from time to time. An iterative password is a reuse of the same password but incrementing it in some way. Once SecurePassword1 expires, we move onto SecurePassword2, then SecurePassword3, and so on. If you have a strong password and iterate it, password policies won't think twice about it. You could certainly get away with this if you make an effort to pay attention to password breaches. However, once one of the passwords is exposed and it looks iterative, guessing becomes trivial.
On the admin's side, iterative passwords could also include processing resets using a "canned" password. For example, an easy to communicate phrase followed by the date. If your organization doesn't have strict requirements on password resets, this could be perfectly fine. However, anyone who has had their password reset once or twice will be aware of that pattern. My opinion: I wouldn't set someone's password in this way if I was not actively speaking to them and I expected them to reset it immediately. Letting an account sit with this password could be risky. Regardless of opinion, here is how we can form one of these passwords in PowerShell, joining a string with the output of Get-Date, and apply it:
### Generate an iterative password based on the date
$password = 'ChangeThisNow' + (Get-Date -Format MMddyyyy) + '!'
# Example output
# ChangeThisNow10262025!
Set-ADAccountPassword -Identity John -Reset `
-NewPassword (ConvertTo-SecureString -AsPlainText $password -Force)
Get-Random
The second method makes use a few arrays and the Get-Random cmdlet. The Get-Random cmdlet on its own generates a number between 0 and the maximum Int32 value, 2147483647. For our purpose here, we can feed it an array of strings, integers, or both and then combine them to make a decent, albeit somewhat predictable, password.
Before we go any further with Get-Random, it is worth mentioning that a warning is noted in the documentation that Get-Random does not ensure cryptographic randomness. PowerShell 7.4 introduced the Get-SecureRandom cmdlet, which does, thanks to the RandomNumberGenerator Class. Get-Random uses the System.Random class. For this use case where we will feed the cmdlet arrays of potential values, either will work fine. If you work in an environment that must adhere to regulations that require unpredictable randomness then Get-SecureRandom will satisfy that requirement. Just be sure to install PowerShell 7.4+ first.
In the example below, we will declare two arrays. One will have four potential strings and the other will include all possible 4-digit integers, from 0000 to 9999. Get-Random will ingest those arrays and choose one from each and both will be used to create a single string; our new password.
### Generate a password using Get-Random
# Generate a string from an array of values
$arrString = @('Spr!ng','Summ3r','Au+umn','W!nter')
$string = Get-Random -InputObject $arrString
# Generate a 4-character integer
$integer = -join ((0..9) | Get-Random -Count 4)
# Combine the string and integer to form a password
$password = $string + $integer
# Set password on user John in Active Directory
Set-ADAccountPassword -Identity John -Reset `
-NewPassword (ConvertTo-SecureString -AsPlainText $password -Force)
For those of you that like a more compact package, here is how we can condense that.
### Generate a password using Get-Random
$password = (Get-Random -InputObject @('Spr!ng','Summ3r','Au+umn','W!nter')) + `
(-join (@(0..9) | Get-Random -Count 4))
While this is randomized and you can add this to automation that minimizes exposure of the password to anyone other than the recipient, it can still be predictable. Because of that, I wouldn't use this for anything other than setting a temporary password. This shouldn't fly in a scenario where a password is being set and not required to be reset at next login, like with a service account. If your service account is relying on a password, then that password must be held to a more stringent standard and should be stored in a credential vault. For that, we need a better way to generate a string.
C# Password Generator
The third method adds more complexity, but introduces cryptographic randomness and less reliance on you to define what the output should be or include. For this, we'll create an inline C# class that defines a password generator. Take a look at the codeblock below and then we'll go over each component.
Add-Type -TypeDefinition @"
using System;
using System.Security.Cryptography;
using System.Linq;
public class PasswordGenerator {
private static readonly char[] Alphanumerics = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();
private static readonly char[] nonAlphanumericChars = "!@#$%^*()_-+=?".ToCharArray();
public static string Generate(int length, int specialCharacters) {
if (specialCharacters > length) throw new ArgumentException("Too many special characters requested.");
var chars = new char[length];
int i = 0;
for (; i < length - specialCharacters; i++) {
chars[i] = Alphanumerics[RandomNumberGenerator.GetInt32(Alphanumerics.Length)];
}
for (; i < length; i++) {
chars[i] = nonAlphanumericChars[RandomNumberGenerator.GetInt32(nonAlphanumericChars.Length)];
}
return new string(chars.OrderBy(c => RandomNumberGenerator.GetInt32(int.MaxValue)).ToArray());
}
}
"@
Starting with Add-Type -TypeDefinition, we are compiling the multi-line string (everything within the @" "@) into our PowerShell session. PowerShell can call any .NET class directly, but that does not include C# classes. We could absolutely achieve this in native PowerShell, but compiled C# will outperform it and no additional modules are needed.
The first thing we'll do within our multi-line string, or the beginning of our C# code, is to define our namespaces. System is the core .NET namespace that contains the classes we'll be needing. System.Security.Cryptography provides cryptographic capabilities, like RandomNumberGenerator.GetInt32. Our last namespace, System.Linq, gives us Language Integrated Query and lets us manipulate and shuffle the contents of the array that will hold our password using OrderBy.
After loading our namespaces, we define our public class, PasswordGenerator, to be called by PowerShell. The "private static readonly" arrays define the characters that will be allowed in our passwords. Breaking that down, private makes it only available within the class it's defined in, static means there is only one copy that does not change, and readonly means it cannot be reassigned after it is initialized. Note that more special characters could fit here, but I often use this in automated account provisioning that also handles email delivery. Those emails are formatted in HTML and CSS, so I omit delimiters like > and <.
Next is our method signature, "Generate". Public refers to any code being able to access the method, including PowerShell. This is fine since it only generates a string. Static means you will call the method directly, shown in the codeblock beneath the explanation. String defines the type that this method outputs. Generate is the name of the method. The parentheses that follow the method name define the acceptable input. Length and specialCharacters must both be integers. Immediately after defining the method, we add input validation. If the number of special characters is larger than the number of overall characters, the Length, then an error is displayed and the method is aborted.
An array is created by "var chars = new char[length]" to hold the password. The char type is allowed. The first for loop fills the first part with the number of alphanumerics that will be in the final product, selected by RandomNumberGenerator.GetInt32 to ensure selection is cryptographically secure. The second for loop fills in the remainder of the length with special characters. RandomNumberGenerator.GetInt32 is used one last time in "return new string" along with OrderBy to assign each character in the array a randomized place in the final string before being displayed in the terminal.
### Generate a 32-character password using the PasswordGenerator class
### Include 12 special characters
[PasswordGenerator]::Generate(32,12)
# Example outputs
# ortB#?00q$H?#lAmv)b!8X)g
# MV-@*+()DTnnx*=Q=sPz#_GbQsFv+vom
# $5!^Y@8+^FI#QJtK*?7(7Uh_zfT_1KOk
### Force an error, caught by input validation
[PasswordGenerator]::Generate(24,31)
# Output
# MethodInvocationException: Exception calling "Generate" with "2" argument(s): "Too many special characters requested."
Its Function Time
I don't know about you, but something like this will be more usable in a function. We'll create a function called Generate-Password and define parameters that will make this a bit more human-friendly to enter.
### Define PasswordGenerator within the Generate-Password function
### Define parameters Length and SpecialCharacters for input
function Generate-Password {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[ValidateRange(1, [int]::MaxValue)]
[int]$Length,
[Parameter(Mandatory = $true)]
[ValidateRange(0, [int]::MaxValue)]
[int]$SpecialCharacters
)
if ($SpecialCharacters -gt $Length) {
throw "The number of special characters cannot exceed the total password length."
}
Add-Type -TypeDefinition @"
using System;
using System.Security.Cryptography;
using System.Linq;
public class PasswordGenerator {
private static readonly char[] Alphanumerics = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();
private static readonly char[] nonAlphanumericChars = "!@#$%^*()_-+=?".ToCharArray();
public static string Generate(int length, int specialCharacters) {
if (specialCharacters > length) throw new ArgumentException("Too many special characters requested.");
var chars = new char[length];
int i = 0;
for (; i < length - specialCharacters; i++) {
chars[i] = Alphanumerics[RandomNumberGenerator.GetInt32(Alphanumerics.Length)];
}
for (; i < length; i++) {
chars[i] = nonAlphanumericChars[RandomNumberGenerator.GetInt32(nonAlphanumericChars.Length)];
}
return new string(chars.OrderBy(c => RandomNumberGenerator.GetInt32(int.MaxValue)).ToArray());
}
}
"@
return [PasswordGenerator]::Generate($Length, $SpecialCharacters)
}
Now we can achieve the same results by entering this.
### Generate a 24-character password using Generate-Password
### Include 8 special characters
Generate-Password -Length 24 -SpecialCharacters 8
# Example outputs
# FA+X*cZl8zS3E8q$_?p_)$fu
# vrWGz^-fE_L)PL#xm@-F?xru
# Z#+$Z?c@ub$RPqN!qb+TeF1h
Why Bother?
There are several reasons to generate passwords programmatically. Doing so takes the guesswork out of security. Humans are notoriously bad at randomization. A generator takes that work from us and offers consistency on top of it. We could hypothetically define a single password and reuse it everywhere, but that's far more predictable than even our first example with Get-Random, which offers 39,996 possible combinations. The possibilities skyrocket with the Generate-Password function—even with just 8 characters and a handful of special characters. Integrating this into a large-scale solution, such as account provisioning, can greatly speed up the process, ensure secure passwords are set, and reduce the likelihood of reuse.
You might also wonder why bother, since online password generators are everywhere. A reputable online service may be fine, but you still don't know where that password is logged or what information is stored alongside it. A breach of that service or a theft of that data could lead to a password you've generated being compromised. Offline generators—local to your machine or your company's network (assuming proper security) are always safer.
Conclusion
Adding a local automated password generator to your toolkit or provisioning process can be a major benefit. Whether its a more human-readable password or a long garbled string of characters, generating them in under a second and without the potential for human error is far better than trying to think of one on the fly.
To close out, I want to add a shameless plug for my custom PowerShell module, MA.IAM.PasswordTools. It is a work in progress, currently with three cmdlets, including our Generate-Password function. I hope you all found this topic interesting. I deal a lot with authentication and likely think about passwords more than most people. A good amount of thought has gone into this and it's about time I've written about it. Connect with me on LinkedIn or email me at Michael@MAitken.Dev As always, thanks for reading and I'll see you in the next one!