Last active
April 7, 2025 09:51
-
-
Save dadatuputi/d2ead8ee219da5922bbcbf6b7325857f to your computer and use it in GitHub Desktop.
Powershell Script to generate new DS Login password
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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