Skip to content

Instantly share code, notes, and snippets.

@dadatuputi
Last active April 7, 2025 09:51
Show Gist options
  • Save dadatuputi/d2ead8ee219da5922bbcbf6b7325857f to your computer and use it in GitHub Desktop.
Save dadatuputi/d2ead8ee219da5922bbcbf6b7325857f to your computer and use it in GitHub Desktop.
Powershell Script to generate new DS Login password
# Invoke-WebRequest -Uri "https://gist.github.com/dadatuputi/d2ead8ee219da5922bbcbf6b7325857f/raw/dfd62e47f3f022c3faddbf3aee5a029c770264ae/New-DSLoginPassword.ps1" -OutFile "New-DSLoginPassword.ps1"
param(
[int]$Length = 128,
[switch]$ProvideCurrentPassword,
[Parameter()]
[Alias('h', '?')]
[switch]$Help
)
if ($Help) {
Write-Host @"
Generates DS Logon compliant passwords
PARAMETERS
-Length <int>
Length of password (15-128 chars)
Default: 128
-ProvideCurrentPassword
If set, prompts for current password to ensure new password
meets the '8 different characters' requirement
-Help
Shows this help message
EXAMPLES
Generate 30-char password:
.\New-DSLoginPassword.ps1 -Length 30
Generate password different from current:
.\New-DSLoginPassword.ps1 -ProvideCurrentPassword
Show help:
.\New-DSLoginPassword.ps1 -Help
"@
exit
}
$plainCurrentPass = $null
if ($ProvideCurrentPassword) {
$currentPass = Read-Host "Enter current password" -AsSecureString
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($currentPass)
$plainCurrentPass = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
[System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)
}
# Define character sets
$lowercase = 'abcdefghijklmnopqrstuvwxyz'
$uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
$numbers = '0123456789'
$special = '@_#/,;~`%&='':!$*+().{}|?><^[]-"\' # String literal, except single quote ' is escaped with double single quotes ''
# Number of characters that must be different from the current password - not what you think, see
# https://www.reddit.com/r/VeteransBenefits/comments/1avm2wf/satisfying_ds_logon_password_requirements/
$newPWCharsDifferent = 8
function New-DSLoginPassword {
param (
[int]$Length = 128,
[string]$CurrentPassword
)
# If a current password is provided, include $newPWCharsDifferent characters to guarantee it passes
if ($CurrentPassword) {
# Ensure we have at least $newPWCharsDifferent characters not in the old password
$charsNotInCurrent = ($lowercase + $uppercase + $numbers + $special).ToCharArray() |
Where-Object { !$CurrentPassword.Contains($_) }
if ($charsNotInCurrent.Count -lt $newPWCharsDifferent) {
throw "Not enough unique characters available different from current password. You may have to reset your password through the 'Forgot Password' process."
}
# Guarantee at least $newPWCharsDifferent different characters
$guaranteedDifferent = $charsNotInCurrent | Get-Random -Count $newPWCharsDifferent
}
# Limits of when we need to reserve characters for future passwords
$upperLimit = ($lowercase + $uppercase + $numbers + $special).Length
$lowerLimit = $upperLimit - $newPWCharsDifferent
# Calculate reserve count based on length
$reserveCount = if ($Length -ge $upperLimit) {
$newPWCharsDifferent # Above $upperLimit, we need to reserve $newPWCharsDifferent characters
} elseif ($Length -lt $lowerLimit) {
0 # Below $lowerLimit, no need to reserve any
} else {
$Length - $lowerLimit # Between $lowerLimit and $upperLimit, reserve number of characters $Length goes above $lowerLimit
}
# Remove $reserveCount characters so a valid password can be generated next time
if ($reserveCount -gt 0) {
$allChars = $lowercase + $uppercase + $numbers + $special
$reservedChars = $allChars.ToCharArray() | Get-Random -Count $reserveCount
foreach ($char in $reservedChars) {
$lowercase = $lowercase.Replace($char.ToString(), '')
$uppercase = $uppercase.Replace($char.ToString(), '')
$numbers = $numbers.Replace($char.ToString(), '')
$special = $special.Replace($char.ToString(), '')
}
Write-Host "Reserved $reserveCount characters for future use: $(-join $reservedChars | Sort-Object)"
}
# Meet the password complexity requirements with 1 character from each type
$password = @(
$lowercase[(Get-Random -Maximum $lowercase.Length)],
$uppercase[(Get-Random -Maximum $uppercase.Length)],
$numbers[(Get-Random -Maximum $numbers.Length)],
$special[(Get-Random -Maximum $special.Length)]
)
# Add necessary unique characters so this will be accepted by DS Login
if ($CurrentPassword) {
$password += $guaranteedDifferent
}
# Fill the rest randomly - duplicates allowed
$allCharsArray = ($lowercase + $uppercase + $numbers + $special).ToCharArray()
while ($password.Length -lt $Length) {
$password += $allCharsArray[(Get-Random -Maximum $allCharsArray.Length)]
}
# Shuffle the password
return -join ($password | Sort-Object {Get-Random})
}
# Validation test
function Test-DSLoginPassword {
param(
[string]$CurrentPassword,
[string]$NewPassword
)
$differentChars = $NewPassword.ToCharArray() |
Where-Object { !$CurrentPassword.Contains($_) } |
Select-Object -Unique | Sort-Object
Write-Host "Number of unique characters in new password not present in current password: $($differentChars.Count)"
Write-Host "Different characters: $(-join $differentChars)"
}
# Execution
$newPass = New-DSLoginPassword -Length $Length -CurrentPassword $plainCurrentPass
if ($ProvideCurrentPassword) {
Test-DSLoginPassword -CurrentPassword $plainCurrentPass -NewPassword $newPass
}
Write-Host "New password: $newPass"
$newPass | Set-Clipboard
Write-Host "Password copied to clipboard"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment