a1a17e81a1
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.
390 lines
16 KiB
PowerShell
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"
|