# 如何利用 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-ScheduledTaskTrigger](https://learn.microsoft.com/en-us/powershell/module/scheduledtasks/new-scheduledtasktrigger?view=windowsserver2019-ps&WT.mc_id=DT-MVP-4015686) 與 [New-ScheduledTaskAction](https://learn.microsoft.com/en-us/powershell/module/scheduledtasks/new-scheduledtaskaction?view=windowsserver2019-ps&WT.mc_id=DT-MVP-4015686) 定義，並在取得物件之後透過 [Register-ScheduledTask](https://learn.microsoft.com/en-us/powershell/module/scheduledtasks/register-scheduledtask?view=windowsserver2019-ps&WT.mc_id=DT-MVP-4015686) 註冊到工作排程器之中。
    
    ```powershell
    $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` 參數：
    
    ```powershell
    Register-ScheduledTask -TaskName '每日商品資料批次更新' -Trigger $STTrig -Action $Action -Description '這裡可以撰寫所有跟每日商品資料批次更新的說明。'
    ```
    
2.  刪除現有的工作排程
    
    ```powershell
    $taskName = '每日商品資料批次更新'
    if (Get-ScheduledTask | Where-Object { $_.TaskName -eq $taskName }) {
      Unregister-ScheduledTask $taskName -Confirm:$false
    }
    ```
    

> 工作排程器的功能相當強大，因此使用案例非常多，完整的 Cmdlets 清單可以參考 [ScheduledTasks](https://learn.microsoft.com/en-us/powershell/module/scheduledtasks/?view=windowsserver2019-ps&WT.mc_id=DT-MVP-4015686) 文件說明。

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

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

1.  設定當天下午 3:00 只執行一次工作
    
    ```powershell
    $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 自動執行工作
    
    ```powershell
    $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 個別執行一次工作
    
    ```powershell
    $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 個小時自動執行工作
    
    ```powershell
    $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 自動執行工作
    
    ```powershell
    $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 自動執行工作
    
    ```powershell
    $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 自動執行工作
    
    ```powershell
    $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.  當使用者登入時執行
    
    ```powershell
    $STTrig = New-ScheduledTaskTrigger -AtLogon
    $Action = New-ScheduledTaskAction -Execute '自動檢查代理程式更新.exe' -WorkingDirectory 'C:/Jobs'
    Register-ScheduledTask -TaskName '自動檢查代理程式更新' -Trigger $STTrig -Action $Action
    ```
    
2.  當**指定使用者**登入時執行
    
    ```powershell
    $STTrig = New-ScheduledTaskTrigger -AtLogon -User 'ADName\Username'
    $Action = New-ScheduledTaskAction -Execute '自動檢查代理程式更新.exe' -WorkingDirectory 'C:/Jobs'
    Register-ScheduledTask -TaskName '自動檢查代理程式更新' -Trigger $STTrig -Action $Action
    ```
    
3.  當作業系統開機時執行
    
    ```powershell
    $STTrig = New-ScheduledTaskTrigger -AtStartup
    $Action = New-ScheduledTaskAction -Execute '自動檢查代理程式更新.exe' -WorkingDirectory 'C:/Jobs'
    Register-ScheduledTask -TaskName '自動檢查代理程式更新' -Trigger $STTrig -Action $Action
    ```
    

### 關於執行身份與權限等級

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

1.  使用**當前使用者**但透過 `-RunLevel Highest` 提高為**以系統管理員身份執行**工作
    
    ```powershell
    $STTrig = New-ScheduledTaskTrigger -AtLogon
    $Action = New-ScheduledTaskAction -Execute '自動檢查代理程式更新.exe' -WorkingDirectory 'C:/Jobs'
    Register-ScheduledTask -TaskName '自動檢查代理程式更新' -Trigger $STTrig -Action $Action -RunLevel Highest
    ```
    
    > 若要用 [Unregister-ScheduledTask](https://learn.microsoft.com/en-us/powershell/module/scheduledtasks/unregister-scheduledtask?view=windowsserver2019-ps&WT.mc_id=DT-MVP-4015686) 刪除排程工作，也必須用相同的權限才能刪除。
    
2.  使用 `BUILTIN\Administrators` 身份並以**以系統管理員身份執行**工作
    
    ```powershell
    $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 個排程工作，並放在 `電商背景工作排程` 目錄下
    
    ```powershell
    $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.  批次刪除整個排程工作目錄
    
    ```powershell
    $folderName = '電商背景工作排程'
    $tasks = Get-ScheduledTask | Where-Object { $_.TaskPath -eq "\$folderName\" }
    foreach ($task in $tasks) {
      Unregister-ScheduledTask $task.TaskName -Confirm:$false
    }
    ```
    
    [照理說](https://stackoverflow.com/a/56929373/910074)你只要執行以下兩個命令，就可以刪除排程工作目錄，但是工作排程目錄的 **機碼**(Registry) 我目前找不到刪除方法，會有權限不足的問題，所以目前只能開啟「工作排程器」應用程式來刪除目錄。
    
    ```powershell
    $folderName = '電商背景工作排程'
    Remove-Item -Path "C:\Windows\System32\Tasks\$folderName"
    Remove-Item -Path "HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\$folderName" -Force
    ```
    

### 相關連結

-   [ScheduledTasks Module | Microsoft Docs](https://learn.microsoft.com/en-us/powershell/module/scheduledtasks/?view=windowsserver2019-ps&WT.mc_id=DT-MVP-4015686)
-   [Task Scheduler for developers - Win32 apps | Microsoft Docs](https://learn.microsoft.com/en-us/windows/win32/taskschd/task-scheduler-start-page?WT.mc_id=DT-MVP-4015686)
-   [schtasks 命令 | Microsoft Docs](https://learn.microsoft.com/zh-tw/windows-server/administration/windows-commands/schtasks?WT.mc_id=DT-MVP-4015686)
-   [如何讓 Windows 工作排程器（Task Scheduler）以秒為單位來觸發程序？ | 軟體主廚的程式料理廚房 - 點部落](https://dotblogs.com.tw/supershowwei/2018/10/29/161209)
-   [powershell - Create multiple triggers in scheduled task - Server Fault](https://serverfault.com/a/869238/234528)
