PowerShell Active Directory Fine-Grained Password Policies

Your domain password policy is a blunt instrument: one set of rules for every account, from helpdesk contractors to privileged admins. Active Directory Fine-Grained Password Policies (PSOs) break that constraint, letting you enforce stricter lockout thresholds on admin accounts while keeping friction low for standard users — all without standing up a second domain. This guide manages PSOs entirely through PowerShell, from creation to compliance reporting.
Quick Answer
Use New-ADFineGrainedPasswordPolicy to create a PSO, Add-ADFineGrainedPasswordPolicySubject to link it to a group or user, and Get-ADUserResultantPasswordPolicy to confirm which policy actually applies to a specific user.
Prerequisites: Domain Functional Level and ActiveDirectory Module
Fine-Grained Password Policies require a domain functional level of Windows Server 2008 or higher. Before running any PSO cmdlet, confirm both the functional level and the presence of the ActiveDirectory module.
# Verify domain functional level
(Get-ADDomain).DomainMode
# Import the module (available via RSAT on Windows 10/11)
Import-Module ActiveDirectory
# Confirm PSO cmdlets are available
Get-Command -Noun ADFineGrained*
The ActiveDirectory module ships with Remote Server Administration Tools (RSAT). On Windows Server it is a Feature; on Windows 10/11 it is an Optional Feature. You do not need Domain Admin rights to read PSOs, but you need it — or delegated rights on the Password Settings Container — to create or modify them.
Creating a PSO with New-ADFineGrainedPasswordPolicy
Every PSO requires a Precedence value. When a user is subject to multiple PSOs, the one with the lowest precedence number wins. Plan your numbering scheme before you start — gaps of ten (10, 20, 30) leave room to insert policies later without renumbering.
$psoParams = @{
Name = "PSO-Admins-Strict"
Precedence = 10
MinPasswordLength = 16
PasswordHistoryCount = 24
MaxPasswordAge = (New-TimeSpan -Days 60)
MinPasswordAge = (New-TimeSpan -Days 1)
LockoutThreshold = 3
LockoutDuration = (New-TimeSpan -Minutes 30)
LockoutObservationWindow = (New-TimeSpan -Minutes 30)
ComplexityEnabled = $true
ReversibleEncryptionEnabled = $false
ProtectedFromAccidentalDeletion = $true
}
New-ADFineGrainedPasswordPolicy @psoParams
The policy is stored in the Password Settings Container (CN=Password Settings Container,CN=System,DC=domain,DC=com) in AD. It does not apply to anyone until explicitly linked.
Linking a PSO to a Group or User with Add-ADFineGrainedPasswordPolicySubject
Best practice is to link PSOs to global security groups, not directly to user accounts. Group-based assignment is easier to audit and change. Direct user assignment is available but creates management overhead.
# Link to a group (recommended)
Add-ADFineGrainedPasswordPolicySubject `
-Identity "PSO-Admins-Strict" `
-Subjects "Domain Admins", "Tier0-Admins"
# Link directly to a user (last resort)
Add-ADFineGrainedPasswordPolicySubject `
-Identity "PSO-Admins-Strict" `
-Subjects "svc.privileged"
# View all subjects currently linked
Get-ADFineGrainedPasswordPolicySubject -Identity "PSO-Admins-Strict"
Verifying Effective Policy with Get-ADUserResultantPasswordPolicy
When a user belongs to multiple groups, each with a different PSO, AD applies the policy with the lowest precedence value. Use Get-ADUserResultantPasswordPolicy to confirm which policy wins for any given account.
# Check what policy actually applies to a specific user
$resultant = Get-ADUserResultantPasswordPolicy -Identity "jsmith"
if ($null -eq $resultant) {
Write-Host "User is governed by the Default Domain Password Policy"
} else {
$resultant | Select-Object Name, Precedence, MinPasswordLength,
LockoutThreshold, MaxPasswordAge
}
A $null result means the user has no PSO and falls back to the Default Domain Policy. This is the expected result for most standard users.
Listing All PSOs and Their Subjects
Maintain a clear inventory of every PSO and what it applies to. This is the foundation of any audit.
$allPSOs = Get-ADFineGrainedPasswordPolicy -Filter * |
Sort-Object Precedence
foreach ($pso in $allPSOs) {
$subjects = Get-ADFineGrainedPasswordPolicySubject -Identity $pso.Name
[PSCustomObject]@{
PSO = $pso.Name
Precedence = $pso.Precedence
MinLength = $pso.MinPasswordLength
MaxAgeDays = $pso.MaxPasswordAge.Days
LockoutAfter = $pso.LockoutThreshold
SubjectCount = $subjects.Count
Subjects = ($subjects.Name -join "; ")
}
} | Format-Table -AutoSize
Auditing Password Policy Compliance Across User Population
Combine resultant policy retrieval with user data to produce a compliance snapshot. This is especially useful for reporting to security teams or auditors.
$users = Get-ADUser -Filter {Enabled -eq $true} -Properties PasswordLastSet, PasswordNeverExpires
$report = foreach ($user in $users) {
$rp = Get-ADUserResultantPasswordPolicy -Identity $user -ErrorAction SilentlyContinue
[PSCustomObject]@{
User = $user.SamAccountName
PasswordLastSet = $user.PasswordLastSet
NeverExpires = $user.PasswordNeverExpires
AppliedPSO = if ($rp) { $rp.Name } else { "Default Domain Policy" }
MaxAgeDays = if ($rp) { $rp.MaxPasswordAge.Days } else { "Default" }
LockoutThreshold = if ($rp) { $rp.LockoutThreshold } else { "Default" }
}
}
$report | Export-Csv "PSO-Compliance-$(Get-Date -Format yyyyMMdd).csv" -NoTypeInformation
Write-Host "Report written: $($report.Count) users processed."
Common Errors
- PSO linked to a group does not apply to a user: The user must be a direct member of the linked group. Nested group membership does not qualify — AD evaluates PSO subjects by direct membership only.
- Wrong PSO wins due to misconfigured Precedence: Lower numbers win. If you assign Precedence 100 to a strict admin policy and Precedence 10 to a relaxed policy, the relaxed policy wins. Audit all precedence values with
Get-ADFineGrainedPasswordPolicy -Filter * | Sort-Object Precedenceregularly.
Related Cmdlets / See Also
Wrapping Up
Fine-Grained Password Policies give AD environments the security granularity they need without architectural complexity. Use PowerShell to automate PSO creation, link policies to security groups rather than users directly, and run periodic resultant-policy audits to catch precedence conflicts before they become a compliance gap.


