<# .SYNOPSIS Wrapper for running Invoke-RemoteMaintenance.ps1 unattended via Task Scheduler. .DESCRIPTION Stores credentials securely using Windows DPAPI (encrypted to your user account) and runs maintenance tasks without interactive prompts. First run: use -SaveCredential to store your username/password. Subsequent runs (or Task Scheduler): credentials load automatically. .PARAMETER SaveCredential Prompt to save credentials for future unattended use. .PARAMETER Task Maintenance task to run (passed to Invoke-RemoteMaintenance.ps1). .PARAMETER ComputerListFile Path to PC list file (passed to Invoke-RemoteMaintenance.ps1). .PARAMETER CreateScheduledTask Register a Windows Scheduled Task to run this script on a schedule. .PARAMETER TaskTime Time for the scheduled task (default: 03:00 AM). .PARAMETER TaskFrequency How often to run: Daily, Weekly, or Once (default: Weekly). .PARAMETER TaskDay Day of week for Weekly frequency (default: Sunday). .EXAMPLE # Step 1: Save your credentials (one time, interactive) .\Schedule-Maintenance.ps1 -SaveCredential .EXAMPLE # Step 2: Test unattended run .\Schedule-Maintenance.ps1 -ComputerListFile ".\shopfloor-pcs.txt" -Task Reboot .EXAMPLE # Step 3: Create a scheduled task for weekly reboots .\Schedule-Maintenance.ps1 -CreateScheduledTask -ComputerListFile ".\shopfloor-pcs.txt" -Task Reboot -TaskFrequency Weekly -TaskDay Sunday -TaskTime "03:00" #> [CmdletBinding()] param( [switch]$SaveCredential, [string]$Task, [string]$ComputerListFile, [switch]$CreateScheduledTask, [string]$TaskTime = "03:00", [ValidateSet('Daily','Weekly','Once')] [string]$TaskFrequency = "Weekly", [ValidateSet('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday')] [string]$TaskDay = "Sunday", [string]$TaskDate, [string]$Username, [string]$Password ) $credFile = Join-Path $PSScriptRoot ".maintenance-cred.xml" $scriptDir = $PSScriptRoot # --------------------------------------------------------------------------- # Save credentials (DPAPI - only decryptable by this user on this machine) # --------------------------------------------------------------------------- if ($SaveCredential) { if ($Username -and $Password) { $secPass = ConvertTo-SecureString $Password -AsPlainText -Force $cred = New-Object System.Management.Automation.PSCredential($Username, $secPass) } else { Write-Host "Enter the credentials used to connect to remote shopfloor PCs:" -ForegroundColor Cyan Write-Host "If no prompt appears, re-run with: -Username 'DOMAIN\user' -Password 'pass'" -ForegroundColor Yellow $cred = Get-Credential -Message "Enter admin credentials for remote shopfloor PCs" } if (-not $cred) { Write-Host "No credentials provided. Exiting." -ForegroundColor Red exit 1 } $cred | Export-Clixml -Path $credFile Write-Host "Credentials saved to: $credFile" -ForegroundColor Green Write-Host "Encrypted with DPAPI - only YOUR user account on THIS machine can decrypt them." -ForegroundColor Yellow Write-Host "" Write-Host "You can now run tasks unattended:" -ForegroundColor Cyan Write-Host " .\Schedule-Maintenance.ps1 -ComputerListFile '.\shopfloor-pcs.txt' -Task Reboot" exit 0 } # --------------------------------------------------------------------------- # Create Scheduled Task # --------------------------------------------------------------------------- if ($CreateScheduledTask) { if (-not $Task) { Write-Host "ERROR: -Task is required when creating a scheduled task." -ForegroundColor Red exit 1 } if (-not $ComputerListFile) { Write-Host "ERROR: -ComputerListFile is required when creating a scheduled task." -ForegroundColor Red exit 1 } # Resolve to absolute paths $absListFile = (Resolve-Path $ComputerListFile -ErrorAction Stop).Path $absScript = Join-Path $scriptDir "Schedule-Maintenance.ps1" if (-not (Test-Path $credFile)) { Write-Host "ERROR: No saved credentials found. Run with -SaveCredential first." -ForegroundColor Red exit 1 } $taskName = "ShopfloorMaintenance-$Task" $psArgs = "-NoProfile -ExecutionPolicy Bypass -File `"$absScript`" -Task `"$Task`" -ComputerListFile `"$absListFile`"" $action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument $psArgs -WorkingDirectory $scriptDir $triggerTime = if ($TaskDate) { [DateTime]::Parse("$TaskDate $TaskTime") } else { [DateTime]::Parse($TaskTime) } $trigger = switch ($TaskFrequency) { 'Daily' { New-ScheduledTaskTrigger -Daily -At $triggerTime } 'Weekly' { New-ScheduledTaskTrigger -Weekly -DaysOfWeek $TaskDay -At $triggerTime } 'Once' { New-ScheduledTaskTrigger -Once -At $triggerTime } } $settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -RunOnlyIfNetworkAvailable # Register as current user (needed for DPAPI credential decryption) $currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name Write-Host "Creating scheduled task '$taskName' as $currentUser..." -ForegroundColor Cyan Write-Host " Schedule: $TaskFrequency at $TaskTime$(if ($TaskFrequency -eq 'Weekly') { " ($TaskDay)" })" -ForegroundColor Gray Write-Host " Task: $Task" -ForegroundColor Gray Write-Host " PC List: $absListFile" -ForegroundColor Gray $existingTask = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue if ($existingTask) { Write-Host "Task '$taskName' already exists. Updating..." -ForegroundColor Yellow Set-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Settings $settings | Out-Null } else { Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Settings $settings -User $currentUser -RunLevel Highest -Description "Shopfloor PC Maintenance - $Task (via Invoke-RemoteMaintenance.ps1)" | Out-Null } Write-Host "Scheduled task '$taskName' created successfully." -ForegroundColor Green Write-Host "" Write-Host "Manage in Task Scheduler or with:" -ForegroundColor Cyan Write-Host " Get-ScheduledTask -TaskName '$taskName'" Write-Host " Start-ScheduledTask -TaskName '$taskName' # run now" Write-Host " Unregister-ScheduledTask -TaskName '$taskName' # delete" exit 0 } # --------------------------------------------------------------------------- # Run maintenance (unattended) # --------------------------------------------------------------------------- if (-not $Task) { Write-Host "ERROR: -Task is required. Example: -Task Reboot" -ForegroundColor Red Write-Host " Or use -SaveCredential to store credentials first." -ForegroundColor Yellow Write-Host " Or use -CreateScheduledTask to set up a scheduled run." -ForegroundColor Yellow exit 1 } if (-not $ComputerListFile) { Write-Host "ERROR: -ComputerListFile is required." -ForegroundColor Red exit 1 } # Load saved credentials if (-not (Test-Path $credFile)) { Write-Host "ERROR: No saved credentials found at $credFile" -ForegroundColor Red Write-Host "Run with -SaveCredential first to store credentials." -ForegroundColor Yellow exit 1 } try { $cred = Import-Clixml -Path $credFile Write-Host "Loaded saved credentials for: $($cred.UserName)" -ForegroundColor Green } catch { Write-Host "ERROR: Failed to load credentials: $_" -ForegroundColor Red Write-Host "Re-run with -SaveCredential to save new credentials." -ForegroundColor Yellow exit 1 } # Log output $logDir = Join-Path $scriptDir "logs" if (-not (Test-Path $logDir)) { New-Item -Path $logDir -ItemType Directory -Force | Out-Null } $logFile = Join-Path $logDir "maintenance-$(Get-Date -Format 'yyyy-MM-dd_HHmmss')-$Task.log" Write-Host "Running: Invoke-RemoteMaintenance.ps1 -Task $Task -ComputerListFile $ComputerListFile" -ForegroundColor Cyan Write-Host "Log: $logFile" -ForegroundColor Gray $mainScript = Join-Path $scriptDir "Invoke-RemoteMaintenance.ps1" & $mainScript -ComputerListFile $ComputerListFile -Task $Task -Credential $cred 2>&1 | Tee-Object -FilePath $logFile Write-Host "" Write-Host "Complete. Log saved to: $logFile" -ForegroundColor Green