Files
linux-scripts/deploy-password-expiry-checker.ps1
T
chiefgeek a1a17e81a1 Sync all scripts from website downloads — 352 scripts total
Includes updated JS challenge scripts with Claude-User whitelist,
same-site referer bypass, Blackbox-Exporter allowed bot, and all
new exporters, cheat sheets, and automation scripts.
2026-05-25 03:31:08 +02:00

390 lines
16 KiB
PowerShell

<#
.SYNOPSIS
Deploy the password expiry checker to Windows machines.
.DESCRIPTION
Downloads password-expiry-check.ps1, installs it to a configurable
directory, creates a scheduled task for recurring checks, and
optionally copies the script to NETLOGON for GPO deployment.
.NOTES
Author: Phil Connor <contact@mylinux.work>
License: MIT (https://opensource.org/licenses/MIT)
Version: 1.01
#>
param(
[string]$InstallDir = "C:\Scripts",
[int]$WarningDays = 14,
[int]$IntervalHours = 4,
[switch]$NetlogonCopy,
[switch]$CmdPrompt,
[switch]$NoProfile,
[switch]$Remove,
[switch]$DryRun,
[Alias("h")]
[switch]$Help
)
$ScriptUrl = "https://mylinux.work/downloads/password-expiry-check.ps1.zip"
$ScriptName = "password-expiry-check.ps1"
$TaskName = "PasswordExpiryCheck"
# ── Colors ────────────────────────────────────────────────────────────
function Write-OK { param([string]$Msg) Write-Host "[OK] $Msg" -ForegroundColor Green }
function Write-Warn { param([string]$Msg) Write-Host "[WARN] $Msg" -ForegroundColor Yellow }
function Write-Err { param([string]$Msg) Write-Host "[ERROR] $Msg" -ForegroundColor Red }
function Write-Info { param([string]$Msg) Write-Host "[INFO] $Msg" -ForegroundColor Cyan }
# ── Help ──────────────────────────────────────────────────────────────
if ($Help) {
Write-Host @"
Usage: .\deploy-password-expiry-checker.ps1 [OPTIONS]
Deploy password expiry notifications on Windows machines.
Installs:
1. password-expiry-check.ps1 to C:\Scripts\ (configurable)
2. Scheduled task - runs every 4 hours (configurable) under logged-on user
3. Logon-triggered task - fires on every user logon
4. PowerShell profile hook - warning banner in every new PowerShell window
5. Optional cmd.exe AutoRun hook - warning banner in every new cmd window
6. Optional NETLOGON copy for GPO deployment
Options:
-InstallDir PATH Installation directory (default: C:\Scripts)
-WarningDays N Warning threshold in days (default: 14)
-IntervalHours N Scheduled task interval in hours (default: 4)
-CmdPrompt Also add warning to cmd.exe via AutoRun registry key
-NoProfile Skip PowerShell profile hook (scheduled tasks only)
-NetlogonCopy Copy script to NETLOGON share for GPO deployment
-Remove Remove deployed components
-DryRun Show what would be done without making changes
-Help Show this help
Examples:
.\deploy-password-expiry-checker.ps1 # install with defaults
.\deploy-password-expiry-checker.ps1 -CmdPrompt # also hook into cmd.exe
.\deploy-password-expiry-checker.ps1 -NoProfile # skip profile hook
.\deploy-password-expiry-checker.ps1 -WarningDays 30 # 30-day warning threshold
.\deploy-password-expiry-checker.ps1 -IntervalHours 8 # check every 8 hours
.\deploy-password-expiry-checker.ps1 -NetlogonCopy # also copy to NETLOGON
.\deploy-password-expiry-checker.ps1 -DryRun # preview changes
.\deploy-password-expiry-checker.ps1 -Remove # uninstall
"@
exit 0
}
# ── Admin check ───────────────────────────────────────────────────────
$currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object Security.Principal.WindowsPrincipal($currentUser)
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
Write-Err "Must run as Administrator"
exit 1
}
$ScriptPath = Join-Path $InstallDir $ScriptName
# ── Remove mode ───────────────────────────────────────────────────────
if ($Remove) {
Write-Info "Removing password expiry checker deployment..."
Write-Host ""
# Remove scheduled tasks
foreach ($name in @($TaskName, "${TaskName}Logon")) {
$task = Get-ScheduledTask -TaskName $name -ErrorAction SilentlyContinue
if ($task) {
if ($DryRun) {
Write-Info "Would remove scheduled task: $name"
} else {
Unregister-ScheduledTask -TaskName $name -Confirm:$false
Write-OK "Removed scheduled task: $name"
}
} else {
Write-Info "Scheduled task '$name' not found, skipping"
}
}
# Remove script
if (Test-Path $ScriptPath) {
if ($DryRun) {
Write-Info "Would remove: $ScriptPath"
} else {
Remove-Item -Path $ScriptPath -Force
Write-OK "Removed $ScriptPath"
}
}
# Remove PowerShell profile hook
$profileMarker = "# PasswordExpiryCheck"
$allUsersProfile = $PROFILE.AllUsersAllHosts
if ((Test-Path $allUsersProfile) -and (Select-String -Path $allUsersProfile -Pattern $profileMarker -Quiet)) {
if ($DryRun) {
Write-Info "Would remove profile hook from $allUsersProfile"
} else {
$content = Get-Content $allUsersProfile | Where-Object { $_ -notmatch $profileMarker }
if ($content) {
Set-Content -Path $allUsersProfile -Value $content
} else {
Remove-Item -Path $allUsersProfile -Force
}
Write-OK "Removed PowerShell profile hook"
}
}
# Remove cmd.exe AutoRun
$cmdAutoRun = Get-ItemProperty -Path "HKLM:\Software\Microsoft\Command Processor" -Name "AutoRun" -ErrorAction SilentlyContinue
if ($cmdAutoRun -and $cmdAutoRun.AutoRun -match "password-expiry-check") {
if ($DryRun) {
Write-Info "Would remove cmd.exe AutoRun registry key"
} else {
$existing = $cmdAutoRun.AutoRun
# Remove our command, handle single command or chained with ampersand
$cleaned = ($existing -split '\s*&\s*' | Where-Object { $_ -notmatch 'password-expiry-check' }) -join ' & '
if ($cleaned.Trim()) {
Set-ItemProperty -Path "HKLM:\Software\Microsoft\Command Processor" -Name "AutoRun" -Value $cleaned.Trim()
} else {
Remove-ItemProperty -Path "HKLM:\Software\Microsoft\Command Processor" -Name "AutoRun" -ErrorAction SilentlyContinue
}
Write-OK "Removed cmd.exe AutoRun hook"
}
}
# Remove install dir if empty
if ((Test-Path $InstallDir) -and @(Get-ChildItem $InstallDir -Force).Count -eq 0) {
if ($DryRun) {
Write-Info "Would remove empty directory: $InstallDir"
} else {
Remove-Item -Path $InstallDir -Force
Write-OK "Removed empty directory: $InstallDir"
}
}
Write-Host ""
if (-not $DryRun) {
Write-OK "Removal complete."
}
exit 0
}
# ── Install mode ──────────────────────────────────────────────────────
Write-Info "Deploying password expiry checker..."
Write-Host ""
# 1. Create install directory
if (-not (Test-Path $InstallDir)) {
if ($DryRun) {
Write-Info "Would create directory: $InstallDir"
} else {
New-Item -Path $InstallDir -ItemType Directory -Force | Out-Null
Write-OK "Created directory: $InstallDir"
}
}
# 2. Download script
if (Test-Path $ScriptPath) {
Write-Info "Script already exists at $ScriptPath - downloading latest version"
}
if ($DryRun) {
Write-Info "Would download $ScriptUrl and extract to $ScriptPath"
} else {
$zipPath = Join-Path $env:TEMP "password-expiry-check.ps1.zip"
try {
Invoke-WebRequest -Uri $ScriptUrl -OutFile $zipPath -UseBasicParsing -ErrorAction Stop
Expand-Archive -Path $zipPath -DestinationPath $InstallDir -Force
Remove-Item $zipPath -Force -ErrorAction SilentlyContinue
if (Test-Path $ScriptPath) {
Write-OK "Downloaded and extracted $ScriptPath"
} else {
Write-Err "Zip extracted but $ScriptName not found in $InstallDir"
exit 1
}
} catch {
Write-Err "Failed to download: $($_.Exception.Message)"
exit 1
}
}
# 3. Scheduled task - recurring interval
$taskArgs = "-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$ScriptPath`" -Quiet -WarningDays $WarningDays"
$existingTask = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
if ($existingTask) {
Write-Info "Scheduled task '$TaskName' already exists - recreating"
if (-not $DryRun) {
Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false
}
}
if ($DryRun) {
Write-Info "Would create scheduled task: $TaskName (every ${IntervalHours}h)"
} else {
$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument $taskArgs
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).Date.AddHours(9) `
-RepetitionInterval (New-TimeSpan -Hours $IntervalHours) `
-RepetitionDuration (New-TimeSpan -Days 365)
$settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable `
-RunOnlyIfNetworkAvailable:$false
$principal = New-ScheduledTaskPrincipal -GroupId "S-1-5-32-545" -RunLevel Limited
Register-ScheduledTask -TaskName $TaskName -Action $action -Trigger $trigger `
-Settings $settings -Principal $principal `
-Description "Check password expiry every $IntervalHours hours (mylinux.work)" | Out-Null
Write-OK "Created scheduled task: $TaskName (every ${IntervalHours}h)"
}
# 4. Logon trigger task
$logonTaskName = "${TaskName}Logon"
$existingLogon = Get-ScheduledTask -TaskName $logonTaskName -ErrorAction SilentlyContinue
if ($existingLogon) {
Write-Info "Logon task '$logonTaskName' already exists - recreating"
if (-not $DryRun) {
Unregister-ScheduledTask -TaskName $logonTaskName -Confirm:$false
}
}
if ($DryRun) {
Write-Info "Would create logon trigger task: $logonTaskName"
} else {
$logonAction = New-ScheduledTaskAction -Execute "powershell.exe" -Argument $taskArgs
$logonTrigger = New-ScheduledTaskTrigger -AtLogOn
$logonSettings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable `
-ExecutionTimeLimit (New-TimeSpan -Minutes 5)
# Delay 30 seconds after logon to let the desktop load
$logonTrigger.Delay = "PT30S"
$logonPrincipal = New-ScheduledTaskPrincipal -GroupId "S-1-5-32-545" -RunLevel Limited
Register-ScheduledTask -TaskName $logonTaskName -Action $logonAction -Trigger $logonTrigger `
-Settings $logonSettings -Principal $logonPrincipal `
-Description "Check password expiry at logon (mylinux.work)" | Out-Null
Write-OK "Created logon trigger task: $logonTaskName"
}
# 5. NETLOGON copy (optional)
if ($NetlogonCopy) {
$logonServer = $env:LOGONSERVER
if ($logonServer) {
$netlogonPath = Join-Path "$logonServer\NETLOGON" $ScriptName
if ($DryRun) {
Write-Info "Would copy $ScriptPath to $netlogonPath"
} else {
try {
Copy-Item -Path $ScriptPath -Destination $netlogonPath -Force -ErrorAction Stop
Write-OK "Copied to $netlogonPath"
} catch {
Write-Warn "Could not copy to NETLOGON: $($_.Exception.Message)"
Write-Warn "Copy manually: Copy-Item '$ScriptPath' '$netlogonPath'"
}
}
} else {
Write-Warn "LOGONSERVER not set - machine may not be domain-joined"
Write-Warn "Copy manually to \\DC\NETLOGON\$ScriptName"
}
}
# 6. PowerShell profile hook (default, skip with -NoProfile)
$profileMarker = "# PasswordExpiryCheck"
$profileLine = "& `"$ScriptPath`" -Quiet -WarningDays $WarningDays $profileMarker"
if (-not $NoProfile) {
$allUsersProfile = $PROFILE.AllUsersAllHosts
$profileDir = Split-Path $allUsersProfile -Parent
# Check if hook already exists
$hookExists = (Test-Path $allUsersProfile) -and (Select-String -Path $allUsersProfile -Pattern $profileMarker -Quiet)
if ($hookExists) {
Write-Info "PowerShell profile hook already present - updating"
if (-not $DryRun) {
$content = Get-Content $allUsersProfile | Where-Object { $_ -notmatch $profileMarker }
$content += $profileLine
Set-Content -Path $allUsersProfile -Value $content
}
} else {
if ($DryRun) {
Write-Info "Would add profile hook to $allUsersProfile"
} else {
if (-not (Test-Path $profileDir)) {
New-Item -Path $profileDir -ItemType Directory -Force | Out-Null
}
Add-Content -Path $allUsersProfile -Value $profileLine
}
}
Write-OK "PowerShell profile hook: $allUsersProfile"
} else {
Write-Info "Skipping PowerShell profile hook (-NoProfile)"
}
# 7. cmd.exe AutoRun hook (optional, enable with -CmdPrompt)
if ($CmdPrompt) {
$cmdCommand = '@powershell.exe -NoProfile -ExecutionPolicy Bypass -File "' + $ScriptPath + '" -Quiet -WarningDays ' + $WarningDays
$regPath = "HKLM:\Software\Microsoft\Command Processor"
$existing = Get-ItemProperty -Path $regPath -Name "AutoRun" -ErrorAction SilentlyContinue
if ($existing -and $existing.AutoRun -match "password-expiry-check") {
Write-Info "cmd.exe AutoRun hook already present - updating"
if (-not $DryRun) {
$cleaned = ($existing.AutoRun -split '\s*&\s*' | Where-Object { $_ -notmatch 'password-expiry-check' }) -join ' & '
if ($cleaned.Trim()) {
$newValue = $cleaned.Trim() + " & " + $cmdCommand
} else {
$newValue = $cmdCommand
}
Set-ItemProperty -Path $regPath -Name "AutoRun" -Value $newValue
}
} elseif ($existing -and $existing.AutoRun.Trim()) {
if ($DryRun) {
Write-Info "Would append to existing cmd.exe AutoRun"
} else {
$newValue = $existing.AutoRun.Trim() + " & " + $cmdCommand
Set-ItemProperty -Path $regPath -Name "AutoRun" -Value $newValue
}
} else {
if ($DryRun) {
Write-Info "Would create cmd.exe AutoRun registry key"
} else {
Set-ItemProperty -Path $regPath -Name "AutoRun" -Value $cmdCommand
}
}
Write-OK "cmd.exe AutoRun hook: $regPath"
}
# ── Summary ───────────────────────────────────────────────────────────
Write-Host ""
Write-Host "Deployment summary:" -ForegroundColor White
Write-Host " Script: $ScriptPath"
Write-Host " Warning: $WarningDays days"
Write-Host " Interval task: $TaskName (every ${IntervalHours}h)"
Write-Host " Logon task: $logonTaskName (at user logon, 30s delay)"
if (-not $NoProfile) {
Write-Host " PS profile: $($PROFILE.AllUsersAllHosts) (all users)"
}
if ($CmdPrompt) {
Write-Host " cmd.exe: AutoRun registry hook (HKLM)"
}
if ($NetlogonCopy) {
Write-Host " NETLOGON: $env:LOGONSERVER\NETLOGON\$ScriptName"
}
Write-Host ""
Write-Host "Users will see warnings via:" -ForegroundColor White
Write-Host " MessageBox popup every $IntervalHours hours (scheduled task)"
Write-Host " MessageBox popup at logon (logon trigger task)"
Write-Host " Terminal banner in new PowerShell windows (profile hook)"
if ($CmdPrompt) {
Write-Host " Terminal banner in new cmd.exe windows (AutoRun hook)"
}
Write-Host ""
Write-Info "Test with: & '$ScriptPath' -Test"
Write-Info "Remove with: .\deploy-password-expiry-checker.ps1 -Remove"