适用于 PowerShell 5.1 及以上版本(Windows),部分操作需要管理员权限
Windows 计划任务(Task Scheduler)是自动化运维的基石,系统维护脚本、定时备份、健康检查、日志清理等都依赖它来定时执行。在早期,管理计划任务需要使用 schtasks.exe 命令行工具,参数繁多且语法晦涩。PowerShell 3.0 引入了 ScheduledTasks 模块,提供了面向对象的任务管理方式,可以精确控制触发条件、执行账户、重试策略等高级配置。
本文将介绍计划任务的创建、管理和监控自动化。
任务创建与配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| $action = New-ScheduledTaskAction -Execute "powershell.exe" ` -Argument "-NoProfile -ExecutionPolicy Bypass -File C:\Scripts\DailyBackup.ps1"
$trigger = New-ScheduledTaskTrigger -Daily -At "02:00"
$settings = New-ScheduledTaskSettingsSet ` -StartWhenAvailable ` -DontStopOnIdleEnd ` -ExecutionTimeLimit (New-TimeSpan -Hours 2) ` -RestartCount 3 ` -RestartInterval (New-TimeSpan -Minutes 5)
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
Register-ScheduledTask -TaskName "DailyBackup" ` -Action $action ` -Trigger $trigger ` -Settings $settings ` -Principal $principal ` -Description "每日凌晨 2:00 执行数据库备份"
Write-Host "已创建计划任务:DailyBackup" -ForegroundColor Green
$actions = @( New-ScheduledTaskAction -Execute "powershell.exe" ` -Argument "-NoProfile -File C:\Scripts\HealthCheck.ps1" )
$triggers = @( (New-ScheduledTaskTrigger -Daily -At "06:00") (New-ScheduledTaskTrigger -AtStartup) )
Register-ScheduledTask -TaskName "ServerHealthCheck" ` -Action $actions ` -Trigger $triggers ` -Settings $settings ` -Principal $principal ` -Description "每日 6:00 及开机时检查服务器健康状态"
Write-Host "已创建计划任务:ServerHealthCheck" -ForegroundColor Green
$weeklyTrigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Sunday -At "03:00" Register-ScheduledTask -TaskName "WeeklyReport" ` -Action (New-ScheduledTaskAction -Execute "powershell.exe" ` -Argument "-NoProfile -File C:\Scripts\WeeklyReport.ps1") ` -Trigger $weeklyTrigger ` -Principal $principal ` -Description "每周日凌晨 3:00 生成周报"
Write-Host "已创建计划任务:WeeklyReport" -ForegroundColor Green
|
执行结果示例:
1 2 3
| 已创建计划任务:DailyBackup 已创建计划任务:ServerHealthCheck 已创建计划任务:WeeklyReport
|
任务查询与监控
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| $tasks = Get-ScheduledTask | Where-Object { $_.TaskPath -notlike "\Microsoft\*" -and $_.State -ne "Disabled" }
$tasks | Select-Object TaskName, State, @{N='LastRun'; E={ ($_. | Get-ScheduledTaskInfo).LastRunTime.ToString('yyyy-MM-dd HH:mm:ss') }}, @{N='LastResult'; E={ ($_. | Get-ScheduledTaskInfo).LastTaskResult }}, @{N='NextRun'; E={ ($_. | Get-ScheduledTaskInfo).NextRunTime.ToString('yyyy-MM-dd HH:mm:ss') }} | Format-Table -AutoSize
$failed = Get-ScheduledTask | ForEach-Object { $info = $_ | Get-ScheduledTaskInfo if ($info.LastTaskResult -ne 0 -and $_.State -ne "Disabled") { [PSCustomObject]@{ TaskName = $_.TaskName State = $_.State LastRun = $info.LastRunTime.ToString('yyyy-MM-dd HH:mm:ss') ResultCode = $info.LastTaskResult Description = $_.Description } } }
if ($failed) { Write-Host "发现失败的任务:" -ForegroundColor Red $failed | Format-Table -AutoSize } else { Write-Host "所有任务执行正常" -ForegroundColor Green }
Get-ScheduledTask | Where-Object { $_.TaskPath -notlike "\Microsoft\*" } | ForEach-Object { $info = $_ | Get-ScheduledTaskInfo [PSCustomObject]@{ TaskName = $_.TaskName LastRun = $info.LastRunTime NextRun = $info.NextRunTime } } | Sort-Object LastRun -Descending | Format-Table -AutoSize
|
执行结果示例:
1 2 3 4 5 6 7
| TaskName State LastRun LastResult NextRun -------- ----- ------- ---------- ------- DailyBackup Ready 2025-09-03 02:00:00 0 2025-09-04 02:00:00 ServerHealthCheck Ready 2025-09-03 06:00:15 0 2025-09-04 06:00:00 WeeklyReport Ready 2025-09-01 03:00:00 0 2025-09-08 03:00:00
所有任务执行正常
|
批量任务管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
| function Register-MaintenanceTasks { param( [string]$ScriptsPath = "C:\Scripts", [string]$LogFile = "C:\Logs\TaskSetup.log" )
$taskDefs = @( @{ Name = "DiskCleanup" Script = "DiskCleanup.ps1" Schedule = "Weekly" Time = "03:00" Day = "Saturday" Description = "每周六凌晨 3:00 清理磁盘临时文件" } @{ Name = "LogRotation" Script = "LogRotation.ps1" Schedule = "Daily" Time = "00:30" Description = "每日凌晨 0:30 轮转日志文件" } @{ Name = "CertExpiryCheck" Script = "CertExpiryCheck.ps1" Schedule = "Daily" Time = "08:00" Description = "每日 8:00 检查证书到期情况" } @{ Name = "SecurityScan" Script = "SecurityScan.ps1" Schedule = "Weekly" Time = "04:00" Day = "Sunday" Description = "每周日凌晨 4:00 运行安全扫描" } )
$settings = New-ScheduledTaskSettingsSet -StartWhenAvailable -DontStopOnIdleEnd ` -ExecutionTimeLimit (New-TimeSpan -Hours 1) -RestartCount 2 ` -RestartInterval (New-TimeSpan -Minutes 10)
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
foreach ($def in $taskDefs) { $scriptPath = Join-Path $ScriptsPath $def.Script
if (-not (Test-Path $scriptPath)) { Write-Host "脚本不存在:$scriptPath" -ForegroundColor Red continue }
$action = New-ScheduledTaskAction -Execute "powershell.exe" ` -Argument "-NoProfile -ExecutionPolicy Bypass -File `"$scriptPath`""
if ($def.Schedule -eq "Daily") { $trigger = New-ScheduledTaskTrigger -Daily -At $def.Time } else { $trigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek $def.Day -At $def.Time }
$existing = Get-ScheduledTask -TaskName $def.Name -ErrorAction SilentlyContinue if ($existing) { Set-ScheduledTask -TaskName $def.Name -Action $action -Trigger $trigger -Settings $settings Write-Host "已更新任务:$($def.Name)" -ForegroundColor Yellow } else { Register-ScheduledTask -TaskName $def.Name -Action $action ` -Trigger $trigger -Settings $settings -Principal $principal ` -Description $def.Description Write-Host "已创建任务:$($def.Name)" -ForegroundColor Green }
"$((Get-Date).ToString('yyyy-MM-dd HH:mm:ss')) $($def.Name) registered" | Out-File $LogFile -Append } }
Register-MaintenanceTasks
$servers = @("SRV01", "SRV02", "SRV03")
foreach ($server in $servers) { try { Invoke-Command -ComputerName $server -ScriptBlock { $action = New-ScheduledTaskAction -Execute "powershell.exe" ` -Argument "-NoProfile -File C:\Scripts\HealthCheck.ps1" $trigger = New-ScheduledTaskTrigger -Daily -At "06:00" $settings = New-ScheduledTaskSettingsSet -StartWhenAvailable
$existing = Get-ScheduledTask -TaskName "HealthCheck" -ErrorAction SilentlyContinue if ($existing) { Set-ScheduledTask -TaskName "HealthCheck" -Action $action -Trigger $trigger } else { Register-ScheduledTask -TaskName "HealthCheck" -Action $action ` -Trigger $trigger -Settings $settings ` -Principal (New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest) } } -ErrorAction Stop Write-Host "$server :任务部署成功" -ForegroundColor Green } catch { Write-Host "$server :部署失败 - $($_.Exception.Message)" -ForegroundColor Red } }
|
执行结果示例:
1 2 3 4 5 6 7
| 已创建任务:DiskCleanup 已创建任务:LogRotation 已创建任务:CertExpiryCheck 已创建任务:SecurityScan SRV01 :任务部署成功 SRV02 :任务部署成功 SRV03 :任务部署成功
|
注意事项
- 执行账户:SYSTEM 账户无法访问网络资源,需要网络访问的任务应使用 gMSA(组托管服务账户)
- 执行策略:脚本文件可能受执行策略限制,任务中使用
-ExecutionPolicy Bypass 参数绕过
- 时区问题:计划任务的触发时间基于服务器本地时区,分布式部署时注意时区差异
- 任务超时:默认执行时间限制为 72 小时,建议根据实际需求设置合理的超时时间
- 日志记录:脚本内部应实现日志输出,方便排查任务失败原因
- 并发冲突:如果任务可能重叠执行,使用文件锁或数据库锁防止并发冲突