Passwords. Who Needs Them? NIST Guidelines, Banned Password Lists, and the Goblet of Fire.

Passwords: everyone’s favorite IT topic, I say sarcastically. Users hate ’em. IT hates resetting them. Let’s just do away with them, your C-suite says naively. We’ll you can’t, obviously, because then there’d be no multifactor in multifactor authentication. But, you can do away with password resets according to NIST.

How is this possible? Don’t password rotations help enforce strong authentication? Nope! According to NIST 800-63B, unless there is evidence of a compromised account, they do not recommend password rotations. [1] Studies find that weak passwords are derived from routine resets; with the mentality of “If I have to keep remembering passwords, I’m just going to make it easy!”

The compliance requirements as of Spring 2026 are as follows:

  • Minimum 15+ characters
  • All printable ASCII characters
  • Check against password dictionaries for known “bad” passwords
  • Enforce MFA

So that begs the question: My org just isn’t up to par with their policies, how do I go about updating them safely?

I recommend a gradual rollout mixed with some PowerShelling to keep track of your passwortd rotations.

Well, I’m here to explain the do’s and don’ts of just that! Read on!

Caution: Hybrid Environments Are a Pain

Did you know that password hashes don’t just naturally sync between cloud and on-prem? That means, if you reset your password via a cloud app, its not going to register on your Domain Controllers. I like to call this “getting split-brained”; where your cloud based hashed password differs from your local client’s hashed password — meaning two separate passwords: one for Outlook mobile and one for logging into your device. Ugh! What a pain! This happens often to off-domain computers, and is a byproduct of the Work-From-Home era. Keep this in mind when figuring out your strategy, and consult Microsoft’s documentation on Password Hash Synchronization (PHS) [2].

GPOs Are the Way to Go

…for hybrid and on-prem environments, of course. If you’ve already got a password policy GPO then modify yours. If you don’t have one (what is this, 1992?) then make one. Here’s the settings that you want to implement:

Navigate to Computer Configuration > Policies > Windows Settings > Security Settings > Account Policies > Password Policy

  • Enforce password history – use your own judgement
    • keeps a record of your last X amount of passwords and does not allow your users to use them
  • Maximum a password age – set this to 0
    • Setting this to zero will ensure that your passwords do not expire per NIST guidelines
  • Minimum password age – read below
    • This is a tricky one. If you want self-service password resets, this has to be zero. However that allows for passwords to be continuously reset. The number you set corresponds with how many days until the user can reset their password again. This can be abused by bad actors!
  • Minimum password length – 15+
    • NIST guidelines recommend 15 or more characters
    • To exceed 12 characters, you will need to enable “Relax minimum password length limits”
  • Password must meet complexity requirements – use your own judgement
    • NIST actually recommends against enforcing complexity requirements such as “must include a special character”
  • Relax minimum password length limits – set to enabled for modern Windows environments
    • Must be set to allow for 15+ character passwords

Hybrid Identities: Banned Password List

A P1 or P2 license grants you the “Banned Password List” in Microsoft Entra. Using this feature you can establish your own list of no-no words that users cannot include in their password. There is some prep work for hybrid environments [3]. Cited to the left of this sentence is the documentation for deploying Microsoft Entra Password Protection. There is an agent that needs to be installed on your Domain Controllers that allows the DCs to check the password against your banned list at time of entry. This component will log to event viewer whether a banned word was used.

Once that feature is installed on the DCs, head over to Microsoft Entra > Authentication methods > Password protection and craft your list of banned words/character combinations. I won’t spill all of my secrets on this one, but think about common words and maybe do some research online for recommendations. The gotcha is that three-letter words are not acceptable in this list.

If you’re ever curious if this is really working or not, pop into the Event Viewer for one of your DCs and review the “Applications and Services Logs\Microsoft\AzureADPasswordProtection\DCAgent\Admin” logs. This screenshot is from Audit mode. Notice that Event ID 10024 is “rejection in audit mode”, the other Event IDs are as follows:

  • Failed/blocked by banned list = 10016
  • Failed/blocked by Microsoft policy = 10016
  • Pass = 10014

Workstation Based MFA

I’ll keep this one a secret, but I highly encourage not just cloud based MFA (Microsoft Authenticator), but deploy a workstation MFA client on your endpoints this way you don’t completely compromise your environment by writing a blog post about a thing you did.

There are several notable workstation MFA clients, and while I won’t give away what I’m using, you can do some research and spin one up in your environment.

Remember the key components of MFA:

  • Something you know
  • Something you have
  • Something you are

But Wait, There’s More!

Now that you’re relaxing your password requirements, you need to strengthen your other IAM protections. Deploy other Zero-Trust policies such as Risky Sign-in Evaluations, Privilege Identity Management, Just In-Time Access, Conditional Access, and more cybersecurity buzzwords! The point I’m trying to make is do not just focus on passwords. Its 2026, you need additional layers of protection on your identities. Its easier for a bad actor to phish out a user’s tokens and credentials than it is for them “hack the mainframe”.

Buh-Buh-Buh Bonus Round

I was tasked with doing a stupid thing, so I complied with said stupid thing. Instead of gradually rolling out new password requirements, I was directed to notify my end users of the upcoming password change, and anyone who did not comply within 14 days and change their password were hit with a forced password reset. Its not my proudest moment as a scripter, but I am kind of impressed with how it turned out.

Script is on GitHub!

Gettin’ Scripty

# Accounts to exclude
$ExcludeAccts = @(

)
# Cutoff date/time (replace X with numbers)
$CutoffTime = Get-Date -Year 202X -Month X -Day X -Hour X -Minute X

First, I made a list of accounts that I did not want the script to reset their passwords. This was mainly used for service accounts. Then I made the $CutoffTime variable to set the date of my first notice to the end users.

# Get users
$Users = Get-ADUser -Filter 'Enabled -eq $true' -Properties UserPrincipalName, SamAccountName, PasswordLastSet |
    Where-Object {
        $ExcludeAccts -notcontains $_.UserPrincipalName -and
        $_.UserPrincipalName -notlike $null
    }

We’re going to want to grab all of our AD users (excluding the sensitive accounts) by using the code above.

Now, let’s run a simple foreach loop and act on every one of the returned user accounts. We will then perform an If statement to compare the PasswordLastSet property against our $CutoffTime variable. If PasswordLastSet is less than our cutoff time, then we act against the user’s account.

We make a custom object with five properties for our logging purposes. Then we are changing two properties of the AD user object: “pwdLastSet” and “ChangePasswordAtLogon”. By changing these two properties, we are enforcing the user to be prompted for password change at next login regardless of what policies we have in place for password expiry.

foreach ($User in $Users) {

    if ($User.PasswordLastSet -lt $CutoffTime) {

        Write-Host "[$($User.SamAccountName)] PasswordLastSet $($User.PasswordLastSet) is older than cutoff $CutoffTime" -ForegroundColor Yellow

        # Log object
        $Details = [pscustomobject]@{
            SamAccountName  = $User.SamAccountName
            UserPrincipalName = $User.UserPrincipalName
            PasswordLastSet = $User.PasswordLastSet
            ActionTime      = Get-Date
            Action          = 'Force password reset'
        }

        # Actions
        Set-ADUser -Identity $User.SamAccountName -Replace @{ pwdLastSet = 0 } -Verbose -whatif
        Set-ADUser -Identity $User.SamAccountName -ChangePasswordAtLogon $true -Verbose -whatif

        $Details | Export-Csv $LogFile -Append -NoTypeInformation
    }

Our Else statement is simple: just output some text to the host, compile another custom object, and log to our .CSV file.

    else {
        Write-Host "[$($User.SamAccountName)] Password is newer than cutoff" -ForegroundColor Green
        # Log object
        $Details = [pscustomobject]@{
            SamAccountName  = $User.SamAccountName
            UserPrincipalName = $User.UserPrincipalName
            PasswordLastSet = $User.PasswordLastSet
            ActionTime      = Get-Date
            Action          = 'Skip password reset'
        }
        $Details | Export-Csv $LogFile -Append -NoTypeInformation
    }
}

And, we’re done. When you are ready for production (absolutely ready!) remove the -WhatIf switches on your Set-ADUser actions.

A Cautionary Tale

Never take the advice of a stranger online. Always do your own research! See what’s best for your environment then learn, grow, and build. Sure, I’m following NIST guidelines, but maybe they don’t meet the nature of your organization.

And for the script, while it all worked out in the end (as I smugly expected), there are better ways to enforce password compliance than what I was instructed to do.

As always:

This project is provided “as is” without any warranty of any kind, express or implied. Use it at your own risk. The authors and contributors are not responsible for any damage, data loss, or other issues that may arise from using this software. You are solely responsible for any actions taken based on this code.