工作排程器的工作不執行、以 0x1 結束 ── 原因排查與安全的維運設計
· 小村 豪 · 工作排程器, Windows, PowerShell, 業務自動化, 批次處理, 維運, 疑難排解, 技術諮詢
「想讓用 PowerShell 寫的彙總腳本每天早上 6 點自動執行」「手動執行明明沒問題,一放進工作排程器就跑不動」。在接業務自動化的相談案件時,最後幾乎都會遇到這個話題。
本部落格也接連寫過幾篇自動化相關的文章,包括日誌整理的自動化、用 Pester 為腳本寫測試、從 C# 執行 PowerShell、用 Power Automate 自動化業務。這些文章最終都以「用工作排程器定期執行」為前提。然而,工作排程器本身其實是一套個性相當強烈的機制,「手動可以動,但定期執行就失敗」「不知不覺間停止運作卻沒人發現」這類事故一直沒有斷過。
本文將工作排程器機制中直接關係到維運事故的部分──執行帳戶與登入類型、「不執行」時的排查方式、傳回值 0x1 的典型原因、日誌留存方式、多重啟動的控制──依實務上容易碰壁的順序整理出來。
1. 先下結論
- 工作排程器的大多數問題,並不是出在腳本本身,而是源自對「以誰的身分、在什麼樣的工作階段中執行」理解上的落差。一旦選擇「不論使用者是否登入均執行」,就必須以「會在與互動式工作階段、登入時環境完全不同的世界中執行」為前提來設計。1
- 調查「不執行」的問題,起點是「歷程記錄(History)」分頁與事件記錄檔。但工作歷程記錄預設是停用的,因此在正式上線前,務必先啟用「啟用所有工作歷程記錄」。2
- 「上次執行結果」顯示的 0x1,並不是工作排程器本身的錯誤,而是代表所啟動的程式自己傳回了結束代碼 1。原因在腳本那一側,所以要先設計好結束代碼,並建立自行留存日誌的機制。3
- PowerShell 腳本的基本呼叫方式是
-NoProfile -NonInteractive -ExecutionPolicy Bypass -File "完整路徑",腳本內部的路徑則以$PSScriptRoot為基準。「起始位置(選用)」欄位不能加引號,這個經典陷阱也常常在這裡踩到。 - 要避免因密碼變更而讓工作悄悄失效的事故,需要在執行帳戶的設計上下功夫(盤點服務帳戶,若在網域環境則考慮採用 gMSA)。4
- 多重啟動的控制(預設為「不啟動新的執行例項」)、執行時間限制(預設 3 天)、電源條件(預設僅在 AC 電源時執行),都是最容易在沒注意到的情況下就沿用預設值上線的代表性設定。註冊工作時請務必明確地逐一決定。5
2. 工作排程器的結構 ── 觸發程序・動作・條件・設定
工作排程器的工作,大致由 4 個要素構成。
| 要素 | 內容 | 容易引發事故的地方 |
|---|---|---|
| 觸發程序 | 何時啟動(時刻、登入時、事件發生時等) | 錯過時刻觸發時的處理方式(後述的 StartWhenAvailable) |
| 動作 | 執行什麼(程式、引數、啟始資料夾) | 引數加引號的方式、啟始資料夾指定錯誤 |
| 條件 | 是否處於允許執行的狀況(電源、網路、閒置) | 預設啟用「僅限使用 AC 電源時」 |
| 設定 | 執行中的行為(多重啟動、時間限制、重試) | 未確認預設值就直接上線 |
用 GUI(taskschd.msc)建立的工作,可以匯出為 XML。如果想用 Git 管理工作定義,或是要把同一份工作部署到多台機器,建議用 XML 匯出 + schtasks /Create /XML,或是用 PowerShell 的 ScheduledTasks 模組(New-ScheduledTaskAction / New-ScheduledTaskTrigger / New-ScheduledTaskSettingsSet / Register-ScheduledTask)把它寫成腳本。5
$action = New-ScheduledTaskAction -Execute 'pwsh.exe' `
-Argument '-NoProfile -NonInteractive -ExecutionPolicy Bypass -File "C:\Jobs\Cleanup-Logs.ps1"' `
-WorkingDirectory 'C:\Jobs'
$trigger = New-ScheduledTaskTrigger -Daily -At '06:00'
# 電源條件的預設是「僅在使用 AC 電源時啟動・切換為電池供電時停止」。
# 如果是也要在筆電或現場端點上執行的工作,就要在這裡明確允許
$settings = New-ScheduledTaskSettingsSet -StartWhenAvailable `
-MultipleInstances IgnoreNew `
-ExecutionTimeLimit (New-TimeSpan -Hours 2) `
-AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
# 為避免密碼顯示在畫面上,透過 Get-Credential 取得
$cred = Get-Credential -UserName 'DOMAIN\svc-batch' -Message '執行帳戶的認證資訊'
Register-ScheduledTask -TaskName 'KS-Cleanup-Logs' `
-Action $action -Trigger $trigger -Settings $settings `
-User $cred.UserName -Password $cred.GetNetworkCredential().Password
只要工作定義變成程式碼,就能把在驗證機上測試過的內容原封不動地帶到正式環境,也能避免「不知道現在是用什麼設定在跑」這種狀態。
如果是部署到多台機器,把在 GUI 上建好的工作匯出成 XML、再用 schtasks 分發,也是實際驗證過可行的方法。
rem 在驗證機上匯出已建立好的工作
schtasks /Query /TN "KS-Cleanup-Logs" /XML > KS-Cleanup-Logs.xml
rem 在各台機器匯入(執行帳戶與密碼在註冊時指定)
schtasks /Create /TN "KS-Cleanup-Logs" /XML KS-Cleanup-Logs.xml /RU DOMAIN\svc-batch /RP *
XML 裡包含了觸發程序、條件、設定的所有內容,只要放進版本庫,就能對工作定義做審查與差異管理。反過來說,靠人工在 10 台機器上用 GUI 分別註冊同一個工作,一定會產生「只有一台設定不一樣」的落單工作。建議在台數還沒到兩位數之前,就先把它變成程式碼。
另外,本文的內容以 Windows 10 / 11、Windows Server 2016 之後的工作排程器(Task Scheduler 2.0 系列)為前提。如果環境中還殘留著更古早、源自 at 命令的工作,請先從盤點它們開始。
3. 執行帳戶與登入類型 ── 最容易出事的地方
在工作內容頁面中選擇的「僅在使用者登入時執行」「不論使用者是否登入均執行」,在內部其實對應的是登入類型(LogonType)的選擇。如果對這裡的理解有落差,就會在「手動可以動、定期執行卻不動」這類問題上,說不出真正原因而一直繞圈子。1
3.1 三種模式的差異
| 選項 | 內部機制 | 特色・限制 |
|---|---|---|
| 僅在使用者登入時執行 | 互動式權杖(InteractiveToken) | 登入期間畫面上可看到視窗。登出期間則根本不會啟動 |
| 不論使用者是否登入均執行 | 密碼儲存(Password) | 註冊時會儲存密碼。畫面看不到(非互動)。密碼變更後會導致啟動失敗 |
| 同上+勾選「不儲存密碼」 | S4U | 不儲存密碼,但相對地無法存取網路上的資源,也無法存取加密檔案(EFS)1 |
實務上常見的典型事故如下。
- 用「不儲存密碼」(S4U)註冊一支存取共用資料夾的腳本。本機測試時可以動,但正式環境中偏偏只有存取共用資料夾這一步會失敗。→ 原因是 S4U 沒有網路認證資訊。
- 用「不論使用者是否登入均執行」註冊後過了幾個月,網域密碼到期而變更了密碼。此後工作就一直卡在登入失敗(
0x8007052E),卻沒有任何人發現。 - 用「不論登入均執行」註冊一支啟動 GUI 應用程式的工作。應用程式其實已經啟動,但畫面完全不會顯示在任何地方,因而被誤以為「沒有在動」。→ 原因是它是在非互動式工作階段中執行。需要互動畫面的處理,原則上在這種設定下是無法執行的。
另外,以 Password / S4U 執行的帳戶,需要具備「以批次工作登入」的權利(SeBatchLogonRight)。Administrators 群組預設就有這項權利,但如果打算把一般使用者帳戶當作服務帳戶使用,就要另外確認本機安全性原則的設定。6
3.2 該用哪個帳戶執行
- SYSTEM:不需要管理密碼,權限強大,但也正因為權限太強而要小心。用在僅限本機的維護處理上很方便,但不應該把所有會碰觸業務資料的工作都交給 SYSTEM 執行。關於是否真的需要系統管理員權限的思考方式,已在另一篇文章「Windows 應用程式在什麼情況下需要系統管理員權限」中整理過。
- 專用的服務帳戶(一般使用者):可以做到最小權限,但相對地每次密碼變更時都需要更新工作設定。請把密碼有效期限和工作盤點綁在一起管理。
- gMSA(群組受管理服務帳戶):在網域環境中是首選。因為密碼由網域控制站自動管理,「密碼變更導致工作失效」這個問題本身就不存在了。工作排程器支援以 gMSA 執行工作。4
另外,勾選「以最高權限執行」的意思是:會使用 UAC 分割出的兩個權杖中、管理員一側(已提升權限)的權杖來執行。不需要管理員權限的工作,請不要勾選這個選項。
4. 排查「不執行」問題的步驟
4.1 先啟用歷程記錄,再開始懷疑
工作排程器右側窗格中的「啟用所有工作歷程記錄」,預設是停用的。如果歷程記錄一直保持停用,連「執行失敗」這個事實本身都不會留下任何記錄。上線前務必先啟用,並讓自己能同時查看事件記錄檔(事件檢視器 → 應用程式及服務記錄檔 → Microsoft → Windows → TaskScheduler → Operational)來確認狀況。2
排查的基本步驟,直接照著 Microsoft 疑難排解指南的流程走就已經很夠用。2
- 先單獨測試腳本 ── 在放進工作之前,先在與執行帳戶相同的條件下(可能的話用
runas或驗證機)確認腳本本身能夠順利跑完。 - 查看狀態欄與歷程記錄分頁 ── 區分究竟是根本沒被觸發,還是啟動了卻失敗。如果沒有被觸發,就要懷疑觸發程序設定或條件(電源、網路),並用手動執行(右鍵 → 執行)確認動作本身能不能跑。
- 暫時改成「僅在使用者登入時執行」試試看 ── 如果這樣就能動,就可以把原因鎖定在非互動式工作階段或認證資訊(前一章)上。
4.2 如何解讀「上次執行結果」
| 顯示值 | 意義 |
|---|---|
0x0 |
正常結束(啟動的程式傳回結束代碼 0) |
0x1 |
啟動的程式傳回結束代碼 1(並非工作排程器本身的錯誤) |
0x41300 |
正在等待下次排定的執行(SCHED_S_TASK_READY) |
0x41301 |
目前正在執行中(SCHED_S_TASK_RUNNING) |
0x41303 |
尚未執行過一次(SCHED_S_TASK_HAS_NOT_RUN) |
0x8007010B |
起始位置(「起始位置(選用)」)的指定不正確。加了引號時的典型症狀 |
0x8007052E |
登入失敗。可能是已儲存的密碼過期、權利不足等 |
0x413xx 系列是工作排程器自身的狀態代碼,0x8007xxxx 是 Windows 的錯誤代碼,而像 0x1、0x2 這種小數值,則是被啟動程式自身的結束代碼。3 只要能分清楚這幾種,就不會從一開始就選錯調查的方向(是工作設定,還是腳本本身)。
4.3 留意條件與設定的預設值
- 電源條件:預設會啟用「僅在電腦使用 AC 電源時啟動工作」。如果用筆電當驗證機,就會變成只有在電池供電時才不動、變成一個「無法重現的問題」。而且從 Windows 10 開始,電池節省模式啟用期間,許多工作的觸發程序都會被延遲。7
- 錯過開始時刻時:如果電腦當時處於關機狀態、錯過了開始時刻,預設要等到下一次排定時間才會執行。要明確地啟用「如果錯過排定時間,則立即執行工作」(
-StartWhenAvailable),或是先在設計上決定這是不是一個「可以被跳過也無妨」的工作。5 - 喚醒:如果是在夜間執行、電腦平時會進入睡眠的環境,也要決定是否需要啟用「喚醒電腦來執行此工作」(
-WakeToRun)。
4.4 觸發程序設計本身的注意事項
有時候「以為不執行」,其實是觸發程序的設計本身就和原本的意圖不一致。
- 「每月 31 日」在沒有 31 日的月份就不會執行。 如果是月底處理,比較安全的做法是改成「每月最後一天」的設計意圖(例如在月初處理上個月的資料,或是在腳本內自行判斷日期)。
- 時刻是註冊該工作的那台機器的本地時間。 如果把同一份 XML 部署到海外據點的機器,或極少數以 UTC 設定運作的伺服器上,各據點的實際執行時刻就會不一致。請事先以規格明確定義「所有據點都在日本時間早上 6 點」還是「各據點當地時間早上 6 點」。
- 如果開始用工作排程器做短間隔的重複執行(例如每 5 分鐘),就要留意。 它作為「一天一次的批次」工具非常優秀,但如果開始需要分鐘等級的輪詢或常駐監控,那就已經進入常駐處理程序的範疇了(後述第 8 章)。
- 事件觸發程序很強大,但要先確認目標事件是否真的能穩定被記錄下來。 以應用程式記錄檔中特定事件識別碼作為觸發程序的架構,一旦應用程式更新導致事件的出現方式改變,就會在毫無預警的情況下失效。改用時刻觸發程序+在腳本內自行做條件判斷,結果上往往反而更容易追蹤。
5. 以 0x1 結束的典型模式與 PowerShell 的正確呼叫方式
0x1 只是「腳本失敗了」這個結果本身,原因其實在於腳本執行環境的差異。手動執行與定期執行之間,主要有以下幾點不同。
- 目前的工作目錄不同:如果沒有指定「起始位置(選用)」,就會在
C:\Windows\System32之類的目錄下執行。用相對路徑寫的腳本會在這裡出問題。腳本內部要以$PSScriptRoot為基準組出路徑,工作那一側則在「起始位置(選用)」欄位指定工作資料夾。這時候「起始位置(選用)」欄位不能加引號,即使路徑中含有空格,也要不加引號地寫(加了就會以0x8007010B失敗)。 - 環境變數・設定檔不同:登入腳本或使用者設定檔所設定的環境變數、已對應的網路磁碟機(X: 等),請視為在非互動式工作階段中並不存在。應該直接使用 UNC 路徑(
\\server\share\...),並用-NoProfile排除設定檔造成的差異。 - 執行原則不同:即使已經為使用者設定了
RemoteSigned,服務帳戶那邊也可能完全沒有設定。要在工作的引數中明確加上-ExecutionPolicy Bypass。 - 工具本身的結束代碼規則特殊:例如
robocopy在「有檔案需要複製並成功複製」的情況下會傳回 1。如果用直接回傳結束代碼的包裝腳本,就會出現「明明正常卻看起來像0x1」,或反過來的情況。使用的外部命令,其結束代碼規則請務必事先確認。
PowerShell 呼叫的基本形式如下。
程式或腳本: pwsh.exe (若為 Windows PowerShell 則為 powershell.exe)
新增引數: -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "C:\Jobs\Cleanup-Logs.ps1"
起始位置 (選用): C:\Jobs (不加引號)
使用 -File 而不是 -Command,除了引數轉義比較單純之外,還因為腳本裡的 exit n 會直接變成處理程序的結束代碼,才能從工作排程器的「上次執行結果」判斷成敗。腳本內部也要設計成:成功時明確傳回 0,失敗時明確傳回非 0。
# 為了讓 Cmdlet 的「非終止錯誤」也能被 catch 捕捉到,設定為 Stop。
# 如果不設這個,Copy-Item 等命令失敗時可能會被直接放過,結果變成 exit 0
$ErrorActionPreference = 'Stop'
try {
Main
exit 0
}
catch {
Write-Error $_
exit 1
}
關於該在哪裡捕捉例外、又該如何記錄的思考方式,也已在另一篇文章「例外的捕捉與日誌設計」中詳細說明過。
6. 日誌要自己留存
工作排程器的歷程記錄,最多只能告訴你「有沒有啟動、結束代碼是多少」。至於腳本實際做了什麼、做到哪一步,就必須由腳本自己留下日誌記錄。
最基本的做法,不是在工作的引數欄裡做重新導向(因為工作排程器的「引數」欄並不會透過殼層執行,重新導向語法不會生效),而是在腳本內部直接取得逐字稿記錄,這樣比較省事。
$logDir = 'C:\Jobs\logs'
New-Item -ItemType Directory -Path $logDir -Force | Out-Null
Start-Transcript -Path (Join-Path $logDir ("cleanup-{0:yyyyMMdd}.log" -f (Get-Date))) -Append
try {
# 主要處理
}
finally {
Stop-Transcript
}
至於日誌本身持續累積的問題(世代管理、封存),正好可以直接沿用「PowerShell 腳本應用 ── 安全地自動化日誌調查、封存與報表化」一文中的內容。
若想再進一步,也可以考慮寫入 Windows 事件記錄檔。與檔案日誌不同的優點是,成敗結果會直接送到維運人員原本就已經在看的地方(事件檢視器、既有的監控工具)。
# --- 只在安裝時執行一次,且需要系統管理員權限(安裝程式或初始化腳本)---
if (-not [System.Diagnostics.EventLog]::SourceExists('KS-Jobs')) {
[System.Diagnostics.EventLog]::CreateEventSource('KS-Jobs', 'Application')
}
# --- 工作主體(以最小權限帳戶執行)只負責寫入。
# SourceExists 在搜尋所有記錄檔時可能需要系統管理員權限,執行時不要呼叫它。
# 以下假設是在 Main 的失敗處理器(catch)內呼叫 ---
catch {
$err = $_
try {
[System.Diagnostics.EventLog]::WriteEntry('KS-Jobs',
"Cleanup-Logs failed: $($err.Exception.Message)",
[System.Diagnostics.EventLogEntryType]::Error, 1001)
}
catch {
# 不能因為寫不進事件記錄檔,就把原始的失敗吞掉不管
Write-Warning "寫入事件記錄檔失敗: $_"
}
exit 1
}
這裡補充兩點。首先,事件來源的註冊(CreateEventSource)需要系統管理員權限。如果把註冊處理混進工作主體,正式環境中以最小權限服務帳戶執行、第一次失敗時就會變成「想註冊卻先丟出例外 → 結果連真正重要的事件都寫不進去」的雙重失敗。請像上面那樣把註冊獨立到安裝階段處理,執行時只負責寫入。其次,如果像本文的工作註冊範例一樣把執行引擎設為 pwsh.exe,就無法使用 Windows PowerShell 5.1 時代的 New-EventLog / Write-EventLog Cmdlet(會因為找不到命令而讓整支腳本失敗)。像上面那樣直接呼叫 .NET 類別,則不論 5.1 或 7 都能運作。
在這之上,再加入一套「失敗時會通知到人」的機制──例如寄送 Email 或發送 Teams / Slack 通知──就能避免「盤點時才發現已經停止好幾個月」這類事故。不需要多複雜的通知架構,光是在失敗時 POST 到 Webhook 的幾行程式碼就已經很夠用。反過來說,「每次成功都通知」遲早會沒人看,因此建議把成功通知控制在週報等級即可,並把「失敗」與「沒有執行」(上次執行時間過舊)當作偵測目標。至於日誌該寫些什麼,判斷標準也已在「例外的捕捉與日誌設計」中整理過。
7. 多重啟動與長時間執行的控制
如果上一次執行還沒結束,下一個排定時刻就到了,會發生什麼事?這由「設定」分頁中的「工作已在執行時所套用的規則」決定,對應到 PowerShell 中的 -MultipleInstances。5
| 設定值 | 動作 | 適合的用途 |
|---|---|---|
| IgnoreNew(GUI 預設:不啟動新的執行例項) | 執行中則跳過新的啟動 | 具冪等性的一般定期批次。優先選這個 |
| Queue | 執行中則在結束後依序執行 | 不容許漏算的彙總類工作 |
| Parallel | 並行啟動 | 原則上應避免。只有能保證並行安全時才用 |
同時建議把「停止之前的時間」(-ExecutionTimeLimit,預設 3 天)設成符合現實的數值(大約是預估執行時間的 2〜3 倍),可以避免處理程序卡死之後把隔天的工作也一起拖垮。5
需要注意的是,IgnoreNew 與 Queue 保護的範圍,僅限於同一個工作定義之內。如果是另一個工作呼叫了同一支腳本,或是人員在故障處理時手動執行造成的衝突,這些設定是防不了的。如果有多條路徑會碰觸同一份資源(檔案、資料庫、外部系統),就要在腳本這一側也加上互斥機制。慣用做法是使用具名的 Mutex。
$mutex = New-Object System.Threading.Mutex($false, 'Global\KS-Cleanup-Logs')
if (-not $mutex.WaitOne(0)) {
Write-Warning '因為有其他執行例項正在執行,所以結束。'
exit 0 # 如果「沒有執行」不算失敗就用 0,若要當成失敗則用非 0
}
try {
# 主要處理
}
finally {
$mutex.ReleaseMutex()
}
只要在名稱前面加上 Global\,即使是在不同的工作階段之間(例如不同使用者的工作與手動執行),互斥也能生效。是要等待取得鎖(在 WaitOne 傳入逾時時間),還是要立刻放棄,請依工作的性質決定。另外要注意,Global\ 開頭的具名物件在整台機器上任何人都看得到。在有多個使用者登入的共用伺服器上,如果因惡意或意外先搶到了同名的 Mutex,工作就會永遠被跳過(而且因為是 exit 0,看起來還會像是正常結束)。在這類環境中,建議為 Mutex 設定 ACL(MutexSecurity)以限制能取得它的帳戶,或至少把「因取得失敗而跳過」這件事,一併送進前一節提到的通知與事件記錄檔中,讓監控能偵測到連續被跳過的情況。透過檔案進行連動時的互斥控制,已在「檔案整合的互斥控制基礎知識」中詳細說明過。
8. 工作排程器的「該收手的時機」 ── 與常駐服務的取捨
工作排程器並不是萬能的。當需求逐漸成長時,有一條界線是與其硬撐著繼續用,不如換一套機制反而更好。
| 需求 | 適合的機制 |
|---|---|
| 一天數次以內的定時批次 | 工作排程器 |
| 啟動時機混雜了人・事件・時刻,且想呈現整個流程 | Power Automate(另一篇文章) |
| 以分鐘等級輪詢、常時監控、佇列處理 | Windows 服務/常駐處理程序 |
| 需要在多次處理之間保留狀態、想細緻控制重試與退避策略 | Windows 服務/常駐處理程序 |
一旦開始用「每 5 分鐘一次的工作」來做輪詢,每次啟動都要付出建立處理程序、載入模組的成本,還得額外做一套把前一次狀態存到檔案之類的機制,實質上等於是把常駐處理程序拆成一小段一小段重新實作。走到這個階段,直接改用 .NET 的 Generic Host 與 BackgroundService 做成常駐服務會比較乾脆。實作模式已在「在桌面應用程式中使用 Generic Host 與 BackgroundService」中說明過。
反過來說,把月結、日結這種批次特意改成服務、還自己管理計時器,也是一種過度設計。大致的判斷原則是:只要符合「執行間隔在一小時以上・處理彼此獨立・不需保留狀態」,就用工作排程器;一旦開始不符合這些條件,就該考慮改成常駐化,這樣的界線大致不會出錯。
9. 上線前的檢查清單
建議在註冊工作之前,依序逐一確認以下項目。
- 是否已決定執行帳戶(是不是隨手選了 SYSTEM?如果是網域環境,有沒有考慮過 gMSA?)
- 是否理解登入類型的限制(S4U 無法存取網路;若使用 Password,是否已決定好密碼變更時的維運方式)
- 是否已在等同於執行帳戶的條件下單獨測試過腳本
- 是否以
-NoProfile -NonInteractive -ExecutionPolicy Bypass -File的形式呼叫 - 腳本內的路徑是否以
$PSScriptRoot/ UNC 為基準(是否還依賴對應磁碟機或相對路徑) - 「起始位置(選用)」欄位是否沒有加引號
- 是否已設計結束代碼(成功為 0/失敗為非 0,且已確認外部命令的結束代碼規則)
- 是否已啟用工作歷程記錄?是否已有腳本自身的日誌與失敗通知
- 是否已明確設定電源條件・StartWhenAvailable・多重啟動・執行時間限制
- 工作定義是否已用 XML 或 PowerShell 腳本的形式存進版本庫
10. 總結
工作排程器並不是「寫完腳本就結束」,而是要先把執行帳戶、工作階段、預設值這三個前提都設計好,才會真正進入穩定維運的狀態。反過來說,只要在註冊時把本文提到的重點──登入類型的選擇、啟用歷程記錄、結束代碼與日誌的設計、明確指定多重啟動與時間限制──一次確實掌握好,之後就會出乎意料地不太需要再費心。
「手動可以動、但定期執行卻不動」,幾乎可以確定原因就在工作階段與環境的差異上。在盲目調整設定之前,請先在歷程記錄分頁確認執行到了哪一步,並依序試試本文提到的排查步驟。
相關文章
- PowerShell 腳本應用 ── 安全地自動化日誌調查、封存與報表化
- 用 Pester 守護 PowerShell 腳本 ── 維護階段的測試策略
- 用 Power Automate 自動化業務 ── 雲端流程・桌面流程的取捨與錯誤處理設計
- Windows 應用程式在什麼情況下需要系統管理員權限
相關諮詢領域
小村軟體有限公司承接以 PowerShell、工作排程器為基礎的業務自動化設計審查,以及協助重建那些「雖然還在動、但已經沒人能修」的定期工作。
參考連結
-
Microsoft Learn,logonType Simple Type (Task Scheduler)。登入類型的定義。關於 S4U 不儲存密碼、但相對地無法存取網路及加密檔案。 ↩ ↩2 ↩3
-
Microsoft Learn,Troubleshoot issues with scheduled tasks not running。「先單獨測試腳本 → 確認狀態與歷程記錄 → 變更安全性選項」的排查步驟,以及 TaskScheduler Operational 事件記錄檔的所在位置。 ↩ ↩2 ↩3
-
Microsoft Learn,Task Scheduler error and success constants。SCHED_S_TASK_READY (0x41300)、SCHED_S_TASK_RUNNING (0x41301)、SCHED_S_TASK_HAS_NOT_RUN (0x41303) 等狀態與錯誤代碼的定義。 ↩ ↩2
-
Microsoft Learn,Manage group Managed Service Accounts。gMSA 的密碼可由網域端自動管理,以及工作排程器支援以 gMSA 執行工作。 ↩ ↩2
-
Microsoft Learn,New-ScheduledTaskSettingsSet。工作設定物件的參數,包括 MultipleInstances(Parallel/Queue/IgnoreNew)、StartWhenAvailable、ExecutionTimeLimit(預設 3 天)等。 ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn,Security Contexts for Tasks。工作的安全性內容,以及以 Password / S4U 註冊的工作在執行時需要「以批次工作登入」權利。 ↩
-
Microsoft Learn,What’s New in Task Scheduler。關於從 Windows 10 開始,電池節省模式啟用期間,非互動式工作的觸發程序會被延遲。 ↩
相關文章
共用相同標籤的最新文章。能以相近的主題延伸理解。
業務應用程式的日期時間與時區 ── 從 DateTime 的陷阱到 UTC 儲存原則、測試設計
伺服器搬遷後時間整整差了 9 個小時、只有海外據點的日期會變成前一天──本文從 DateTime 的 Kind 與隱式轉換整理日期時間事故的根本原因。內容涵蓋與 DateTimeOffset 的取捨、UTC 儲存與 ISO 8601 原則、TimeZoneInfo 與夏令時...
在 C# 中將 SQLite 用於業務應用程式 ── WAL 模式・排他控制・損毀對策・與 EF Core 的取捨
整理用 Microsoft.Data.Sqlite 把 SQLite 整合進業務用 Windows 應用程式的實務知識:連線字串與連線池、WAL 模式的原理、SQLITE_BUSY 與寫入路徑單一化、用交易加速、型別對應的陷阱、用 VACUUM INTO 備份,一直到該在什...
Windows 服務的建立與維運 ── 從工作排程器的取捨到 BackgroundService 服務化
常駐處理該做成 Windows 服務,還是工作排程器就足夠?本文整理判斷表,以及用 .NET Worker Service(UseWindowsService)建立服務的方法、執行帳戶、復原選項、事件記錄、安全停止處理,從實務角度梳理上線維運所需的設計。
用 Power Automate 自動化業務 ── 雲端流程與桌面流程的分工,以及錯誤處理設計
本文整理 Power Automate 雲端流程與桌面流程的差異、與 PowerShell / VBA 的分工、授權、錯誤處理、UI 自動化的穩定化,以及認證資訊的安全處理,提供將業務自動化導入正式運作的實務設計指引。
如何理解 Windows 的工作階段隔離 ── Session 0・RDP・多使用者同時執行
整理 Windows 應用程式開發者容易混淆的「工作階段」概念。說明服務無法顯示 UI 的 Session 0 隔離原因、RDP 連線時工作階段的行為、具名物件的工作階段隔離,以及共用 PC・RDS 環境常見的設計失誤,從實務角度解說。
相關主題
與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。
Windows 技術主題
彙整 KomuraSoft LLC 關於 Windows 開發、故障調查與既有資產活用文章的主題中心。
與本主題相關的服務
本文連結到以下服務頁面,歡迎從最接近的入口查看。
Windows 應用程式開發
支援包含常駐處理、設備連動、運作日誌與可維護結構的 Windows 桌面應用程式。
技術諮詢 & 設計審查
協助釐清設計方向、架構邊界、生命週期責任,以及既有 Windows 資產的處理方式。
常見問題
整理諮詢這個主題時常見的問題。
- 工作排程器的「上次執行結果」顯示的 0x1 是什麼意思?
- 0x1 並不是工作排程器本身的錯誤,而是代表所啟動的程式傳回了結束代碼 1。原因在腳本那一側。手動執行與定期執行之間,典型的差異在於目前的工作目錄、環境變數與設定檔、執行原則不同。另外也要留意像 robocopy 這類即使正常結束也會傳回 1 的外部命令結束代碼規則。只要知道 0x413xx 系列是工作排程器的狀態代碼、0x8007xxxx 是 Windows 的錯誤代碼,就不會選錯調查的方向。
- 手動執行明明可以動,為什麼放進工作排程器就不動了?
- 幾乎可以確定原因就在執行帳戶與工作階段・環境的差異上。選擇「不論使用者是否登入均執行」,就會在非互動式工作階段中執行,因此對應的網路磁碟機或使用者設定檔的環境變數都不存在。而且「不儲存密碼」(S4U)也無法存取網路上的資源。有效的排查方式是:先確認歷程記錄分頁、單獨測試腳本、暫時改成「僅在使用者登入時執行」。由於工作歷程記錄預設是停用的,正式上線前請務必先啟用。
- 在工作排程器中呼叫 PowerShell 腳本的正確寫法是什麼?
- 基本做法是在「程式」指定 pwsh.exe(或 powershell.exe),引數則寫成 -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "完整路徑" 的形式。使用 -File 而不是 -Command,腳本裡的 exit n 就會直接變成處理程序的結束代碼,用來判斷成敗。腳本內部的路徑要以 $PSScriptRoot 為基準,「起始位置(選用)」欄位絕對不能加引號(加了會以 0x8007010B 失敗)。腳本那一側則要設計成:成功時明確傳回 0,失敗時明確傳回非 0。
- 該如何防止因密碼變更而導致工作停止的事故?
- 以「不論使用者是否登入均執行」註冊時會儲存密碼,因此密碼變更之後,工作就會一直卡在登入失敗(0x8007052E)。如果是網域環境,首選是密碼由網域控制站自動管理的 gMSA(群組受管理服務帳戶),這個問題本身就會消失。如果使用專用的服務帳戶,請把密碼有效期限與工作盤點綁在一起管理。同時再加入一套失敗時會寄送 Email 或發送 Teams 通知的機制,就能避免發生「已經停止好幾個月卻沒人發現」的事故。
作者檔案
本文作者的個人檔案頁面。
Go Komura
小村軟體有限公司 代表
以 Windows 軟體開發、技術諮詢與故障調查為中心,在難以重現的故障調查與既有資產仍在運作的專案上具有優勢。