Skip to content

Instantly share code, notes, and snippets.

@dadatuputi
Last active September 9, 2025 04:17
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
# To install, copy Invoke-WebRequest command below in your favorite scripts directory
# Invoke-WebRequest -Uri "https://gist.githubusercontent.com/dadatuputi/d2ead8ee219da5922bbcbf6b7325857f/raw/New-DSLoginPassword.ps1" -OutFile "New-DSLoginPassword.ps1"
param(
[int]$Length = 128,
[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
-Help
Shows this help message
BEHAVIOR
Always prompts for current password. If empty password entered,
generates completely new password. If current password provided,
ensures new password meets '8 different characters' requirement.
EXAMPLES
Generate password (will prompt for current):
.\New-DSLoginPassword.ps1 -Length 30
Show help:
.\New-DSLoginPassword.ps1 -Help
"@
exit
}
# Always prompt for current password
$currentPass = Read-Host "Enter current password (press Enter for none)" -AsSecureString
# Check if password is empty without converting to plain text
$isEmpty = $currentPass.Length -eq 0
# 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,
[System.Security.SecureString]$CurrentPassword
)
# If a current password is provided, include $newPWCharsDifferent characters to guarantee it passes
if ($CurrentPassword -and $CurrentPassword.Length -gt 0) {
# Convert SecureString to plain text only when necessary for comparison
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($CurrentPassword)
$plainCurrentPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
# Ensure we have at least $newPWCharsDifferent characters not in the old password
$charsNotInCurrent = ($lowercase + $uppercase + $numbers + $special).ToCharArray() |
Where-Object { !$plainCurrentPassword.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
# Clean up sensitive data immediately
[System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)
$plainCurrentPassword = $null
}
# 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(
[System.Security.SecureString]$CurrentPassword,
[string]$NewPassword
)
# Convert SecureString temporarily for comparison
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($CurrentPassword)
$plainCurrentPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
$differentChars = $NewPassword.ToCharArray() |
Where-Object { !$plainCurrentPassword.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)"
# Clean up
[System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)
$plainCurrentPassword = $null
}
# Execution
if ($isEmpty) {
Write-Host "No current password provided - generating completely new password"
$newPass = New-DSLoginPassword -Length $Length
} else {
Write-Host "Current password provided - ensuring new password is sufficiently different"
$newPass = New-DSLoginPassword -Length $Length -CurrentPassword $currentPass
Test-DSLoginPassword -CurrentPassword $currentPass -NewPassword $newPass
}
# Clean up SecureString
$currentPass.Dispose()
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