如何利用 PowerShell 自動將應用程式註冊到工作排程器 (Task Scheduler) | The Will Will Web

The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

如何利用 PowerShell 自動將應用程式註冊到工作排程器 (Task Scheduler)

最近一直在處理不同專案的 CI/CD 作業,有個自動化排程作業就需要將應用程式註冊到 工作排程器 (Task Scheduler) 之中,因此就研究了一下如何利用 PowerShell 來完成這個自動化工作。

基本觀念

工作排程器 (Task Scheduler) 是 Windows 內建服務之一,可以用來控制在「指定時間或區間內」進行一連串的「工作」執行,這工作可能包含一些命令或應用程式執行。

基本上,工作排程器主要可以區分兩種「觸發條件」來啟動一份排程工作:

  1. 以時間為基礎的觸發條件 (time-based trigger)

    你可以在特定時間點,或是特定時間區間,啟動一個或多個工作。可以只啟動一次工作,也可以週期性地啟動工作。

    週期性的工作可以是每天、每周、每月、每年,而最短可從「每分鐘」啟動一次工作。

  2. 以事件為基礎的觸發條件 (event-based trigger)

    你可以設定當系統發生特定事件時自動啟動工作,例如設定開機時、使用者登入時、工作站鎖定時、...等等,還可以設定事件記錄器中特定 事件來源 (Source) 或 事件編號 (Event ID) 發生時觸發。

一個「排程工作」可以設定多組「觸發條件」,任何一個觸發條件成立,就會立刻啟動工作,因此設定上非常具有彈性。例如:如果你想每隔 30 秒就執行一次工作,就可以設定兩組觸發條件,每一組都是設定「每分鐘」啟動一次,但是第一個觸發條件的開始時間可以設定為 12:00:00、另一個觸發條件就可以設定為 12:00:30,如此一來每次執行的間隔時間最小時間就可以設定到每隔 30 秒就執行一次!

基本範例

以下我對幾種常用的排程工作進行解說,解說完你就會比較清楚透過 PowerShell 建立工作的方法:

  1. 建立一個每天凌晨 4:00 執行一次的工作排程

    一個排程工作是由「觸發條件」與「執行動作」所組成,因此要透過 PowerShell 建立排程工作,必須分別由 New-ScheduledTaskTriggerNew-ScheduledTaskAction 定義,並在取得物件之後透過 Register-ScheduledTask 註冊到工作排程器之中。

    $STTrig = New-ScheduledTaskTrigger -Daily -At '4am'
    $Action = New-ScheduledTaskAction -Execute '每日商品資料批次更新.exe' -WorkingDirectory 'C:/Jobs'
    Register-ScheduledTask -TaskName '每日商品資料批次更新' -Trigger $STTrig -Action $Action
    Register-ScheduledTask -TaskName '每日商品資料批次更新' -Trigger $STTrig -Action $Action -Description ''
    

    如果你要加上詳細的描述文字,可以在最後一個 Register-ScheduledTask 命令加上 -Description 參數:

    Register-ScheduledTask -TaskName '每日商品資料批次更新' -Trigger $STTrig -Action $Action -Description '這裡可以撰寫所有跟每日商品資料批次更新的說明。'
    
  2. 刪除現有的工作排程

    $taskName = '每日商品資料批次更新'
    if (Get-ScheduledTask | Where-Object { $_.TaskName -eq $taskName }) {
      Unregister-ScheduledTask $taskName -Confirm:$false
    }
    

工作排程器的功能相當強大,因此使用案例非常多,完整的 Cmdlets 清單可以參考 ScheduledTasks 文件說明。

以時間為基礎的觸發條件 (time-based trigger)

以下我列出常用的 PowerShell 命令範例,大部分應該都非常淺顯易懂:

  1. 設定當天下午 3:00 只執行一次工作

    $STTrig = New-ScheduledTaskTrigger -Once -At 3pm
    $Action = New-ScheduledTaskAction -Execute '每日商品資料批次更新.exe' -WorkingDirectory 'C:/Jobs'
    Register-ScheduledTask -TaskName '每日商品資料批次更新' -Trigger $STTrig -Action $Action
    

    如果在下午 3:00 之後才執行這個命令,這個工作就不會被自動執行

  2. 設定每天凌晨 2:00 自動執行工作

    $STTrig = New-ScheduledTaskTrigger -Daily -At 2am
    $Action = New-ScheduledTaskAction -Execute '每日商品資料批次更新.exe' -WorkingDirectory 'C:/Jobs'
    Register-ScheduledTask -TaskName '每日商品資料批次更新' -Trigger $STTrig -Action $Action
    
  3. 設定每天早上 6:00 與下午 6:00 個別執行一次工作

    $STTrig = @(
        $(New-ScheduledTaskTrigger -Daily -At '06:00'),
        $(New-ScheduledTaskTrigger -Daily -At '18:00')
    )
    $Action = New-ScheduledTaskAction -Execute '每日商品資料批次更新.exe' -WorkingDirectory 'C:/Jobs'
    Register-ScheduledTask -TaskName '每日商品資料批次更新' -Trigger $STTrig -Action $Action
    
  4. 設定每隔 1 個小時自動執行工作

    $STTrig = New-ScheduledTaskTrigger -Once -At 12am -RepetitionInterval (New-TimeSpan -Hours 1)
    $Action = New-ScheduledTaskAction -Execute '每日商品資料批次更新.exe' -WorkingDirectory 'C:/Jobs'
    Register-ScheduledTask -TaskName '每日商品資料批次更新' -Trigger $STTrig -Action $Action
    
  5. 設定每隔 2 天的凌晨 3:00 自動執行工作

    $STTrig = New-ScheduledTaskTrigger -Daily -At 03:00 -DaysInterval 2
    $Action = New-ScheduledTaskAction -Execute '每日商品資料批次更新.exe' -WorkingDirectory 'C:/Jobs'
    Register-ScheduledTask -TaskName '每日商品資料批次更新' -Trigger $STTrig -Action $Action
    
  6. 設定每週週日的凌晨 3:00 自動執行工作

    $STTrig =New-ScheduledTaskTrigger -Weekly -At 3am -DaysOfWeek Sunday
    $Action = New-ScheduledTaskAction -Execute '每日商品資料批次更新.exe' -WorkingDirectory 'C:/Jobs'
    Register-ScheduledTask -TaskName '每日商品資料批次更新' -Trigger $STTrig -Action $Action
    
  7. 設定每間隔 1 週的週日 (兩週一次) 的凌晨 3:00 自動執行工作

    $STTrig =New-ScheduledTaskTrigger -Weekly -At 3am -WeeksInterval 2 -DaysOfWeek Sunday
    $Action = New-ScheduledTaskAction -Execute '每日商品資料批次更新.exe' -WorkingDirectory 'C:/Jobs'
    Register-ScheduledTask -TaskName '每日商品資料批次更新' -Trigger $STTrig -Action $Action
    

以事件為基礎的觸發條件 (event-based trigger)

以下我列出常用的 PowerShell 命令範例,大部分應該都非常淺顯易懂:

  1. 當使用者登入時執行

    $STTrig = New-ScheduledTaskTrigger -AtLogon
    $Action = New-ScheduledTaskAction -Execute '自動檢查代理程式更新.exe' -WorkingDirectory 'C:/Jobs'
    Register-ScheduledTask -TaskName '自動檢查代理程式更新' -Trigger $STTrig -Action $Action
    
  2. 指定使用者登入時執行

    $STTrig = New-ScheduledTaskTrigger -AtLogon -User 'ADName\Username'
    $Action = New-ScheduledTaskAction -Execute '自動檢查代理程式更新.exe' -WorkingDirectory 'C:/Jobs'
    Register-ScheduledTask -TaskName '自動檢查代理程式更新' -Trigger $STTrig -Action $Action
    
  3. 當作業系統開機時執行

    $STTrig = New-ScheduledTaskTrigger -AtStartup
    $Action = New-ScheduledTaskAction -Execute '自動檢查代理程式更新.exe' -WorkingDirectory 'C:/Jobs'
    Register-ScheduledTask -TaskName '自動檢查代理程式更新' -Trigger $STTrig -Action $Action
    

關於執行身份與權限等級

你必須以系統管理員身份執行 PowerShell 才能執行以下命令:

  1. 使用當前使用者但透過 -RunLevel Highest 提高為以系統管理員身份執行工作

    $STTrig = New-ScheduledTaskTrigger -AtLogon
    $Action = New-ScheduledTaskAction -Execute '自動檢查代理程式更新.exe' -WorkingDirectory 'C:/Jobs'
    Register-ScheduledTask -TaskName '自動檢查代理程式更新' -Trigger $STTrig -Action $Action -RunLevel Highest
    

    若要用 Unregister-ScheduledTask 刪除排程工作,也必須用相同的權限才能刪除。

  2. 使用 BUILTIN\Administrators 身份並以以系統管理員身份執行工作

    $STTrig = New-ScheduledTaskTrigger -AtStartup
    $STPrin = New-ScheduledTaskPrincipal -GroupId "BUILTIN\Administrators" -RunLevel Highest
    $Action = New-ScheduledTaskAction -Execute '自動檢查代理程式更新.exe' -WorkingDirectory 'C:/Jobs'
    Register-ScheduledTask -TaskName '自動檢查代理程式更新' -Trigger $STTrig -Action $Action -Principal $STPrin
    

善加利用 -TaskPath 設定排程工作目錄

如果你一個專案有好幾個工作排程要設定,可以善加利用目錄的方式整理這些排程工作。

  1. 批次建立 3 個排程工作,並放在 電商背景工作排程 目錄下

    $stt1 = New-ScheduledTaskTrigger -Once -At 3pm
    $sta1 = New-ScheduledTaskAction -Execute '每日商品資料批次更新1.exe' -WorkingDirectory 'C:/Jobs'
    Register-ScheduledTask -TaskName '每日商品資料批次更新1' -TaskPath '電商背景工作排程' -Trigger $stt1 -Action $sta1
    
    $stt2 = New-ScheduledTaskTrigger -Once -At 4pm
    $sta2 = New-ScheduledTaskAction -Execute '每日商品資料批次更新2.exe' -WorkingDirectory 'C:/Jobs'
    Register-ScheduledTask -TaskName '每日商品資料批次更新2' -TaskPath '電商背景工作排程' -Trigger $stt2 -Action $sta2
    
    $stt3 = New-ScheduledTaskTrigger -Once -At 5pm
    $sta3 = New-ScheduledTaskAction -Execute '每日商品資料批次更新3.exe' -WorkingDirectory 'C:/Jobs'
    Register-ScheduledTask -TaskName '每日商品資料批次更新3' -TaskPath '電商背景工作排程' -Trigger $stt3 -Action $sta3
    
  2. 批次刪除整個排程工作目錄

    $folderName = '電商背景工作排程'
    $tasks = Get-ScheduledTask | Where-Object { $_.TaskPath -eq "\$folderName\" }
    foreach ($task in $tasks) {
      Unregister-ScheduledTask $task.TaskName -Confirm:$false
    }
    

    照理說你只要執行以下兩個命令,就可以刪除排程工作目錄,但是工作排程目錄的 機碼(Registry) 我目前找不到刪除方法,會有權限不足的問題,所以目前只能開啟「工作排程器」應用程式來刪除目錄。

    $folderName = '電商背景工作排程'
    Remove-Item -Path "C:\Windows\System32\Tasks\$folderName"
    Remove-Item -Path "HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\$folderName" -Force
    

相關連結