🗂

windowsのタスクスケジューラ

2022/10/23に公開約9,500字

発表用の自分用調査メモです。
ゆえに間違っている可能性あります。

タスクスケジューラ一覧

powershell


# 専用のコマンドレットで調べる
# すべてのtaskpathを再帰的に調べて表示。
# ユーザーの権限で見ることが可能
Get-ScheduledTask

# レジストリで見る。
# MicrosoftとかAppleとかベンダーごとに勝手にパス作っている。
# root権限でしか見れない。
Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks"

# タスクスケジューラの履歴の有効無効
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree"


# 
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks"
    -Name ENVNAME
    -Value "%VALUE%"
    -Type ExpandString

タスクの登録

powershell由来

タスクの登録

# 
$task=New-ScheduledTaskAction
    -Execute "msinfo32.exe"
    -Argument "/report C:\Report\msinfo.txt"
    -WorkingDirectory "C:\Report"

#
$trigger=New-ScheduledTaskTrigger
    -Weekly
    -DaysOfWeek Friday
    -At 5pm

$username=katsutoshi
# 
Register-ScheduledTask
    -TaskName "GenerateReport"
    -Trigger $trigger
    -Action $task
    -User $username
    -Taskpath "\api\"

スケジュールドジョブ

.net frameworkはpowershell
dotnetのpwshにはない。

なので、廃止予定のやつでしか使えないぜ!

dotnetのpwshに実装するときにどういう実装になるか分からないから、
今からスケジュールドjobで実装するのは辞めた方が良い。
6年前powershellがOSSになる前に書いてほとんどほったらかし。
最後に触ったの3年前とかそんなもん。

いつ実装するか分からないし、廃止になって
全然違う機能になるかも。

今実装しても、新しいpowershellに移行するときに
問題になる可能性が高い。

<!--
この新しいdotnetと.net frameworkのpowershellとの違いって
日に日にどんどん大きくなっていっていて、
今の所は.net frameworkで書いた物をdotnetに以降するときに
すごく大変な事になるんじゃないかと思っている。

MSのpowershellの責任者はOSのチームとタイミングが合わないという
言い訳をしているが、
powershell単体でも.net frameworkにある
機能すべてを作れていないので今の状態ではdotnetの方をデフォルトにするのは厳しい。
バックアップとかジョブスケジューラとか
サーバーに必要な機能に限ってない...
-->

つまり話をまとめると、現時点でMS公式が提供しているタスク、ジョブスケジューラで
導入した結果、安心して年スパンで運用移行のスケジュールを立てれるものは存在しない。

さすが俺たちのMSだぜ!

<!--
お客さんが喜ぶサービスや物を作ってお金をもらっているのに、
windowsでサービスや物を作る事が目的になってしまう。

-->

争点

用途としてはサーバー用とよりも、
アプリケーション開いたら、タスクを実行するとかそういう使い方。

Visual studioを開いたときにtask実行して、だったら、
update促す。

MSのアプリでどのように実行しているか?

  1. systemdみたくユーザーの権限で登録して、ユーザーの権限で実行はできない。

  2. レジストリから触って直接変更するならpowershell由来でさえ管理者権限必須。

  3. 専用コマンドなら管理者権限いらない。

  4. ユーザーの権限ならHKLMでなくて、HKCUにいれるべきであって設計ミスの可能性が高い。

  5. systemdとtaskchedulerの違い表がいる。

ジョブスケジューラ ユーザーの設定ファイル システムの設定ファイル
systemd /home/username/systemd /lib/systemd
cron /var/spool/cron /etc/cron*
task scheduler HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks 同左
  1. anacronに近い。起動時に過ぎてたら、実行。

  2. 自分がログインしていない時に実行ならCredential必要。

  3. powwershellとtaskchedulerが相性が悪い。

直接powershellのコマンドを打てない。

やりたいなら
pwsh.exe
-File
-WorkingDirectory
-NonInteractive

  1. registerで登録するのか、New-ScheduledTaskで設定するのか
    はっきりしない...settingsしかり、principalしかり...このせいでtaskが作られる。
  2. AuthorやDescriptionを強制していない
  3. なぜか、デフォルトがVistaの設定...
  4. 時間を指定したら、登録できない...バグ臭い。
  5. Logonを指定しない。

parametersetに翻弄される...

Office ClickToRun Service Monitor

# ユーザーの権限で登録できる。
# 他のユーザーが実行者のジョブは定義できない。
# 
# defaultの設定だとVistaとWindows Server 2008の設定で作られる
$action=New-ScheduledTaskAction `
    -Execute "pwsh.exe" `
    -Argument "-File ${HOME}\pwsh\hello.ps1 -WorkingDirectory ${HOME}\pwsh\ -NonInteractive" `
    -WorkingDirectory "${HOME}\pwsh\"

# 定期実行を指定したら、なぜかログイン時にできるかどうか指定ができない。
$trigger=New-ScheduledTaskTrigger `
    -Weekly `
    -DaysOfWeek Friday `
    -At 5pm
    # -Logon
# powershell実行者=登録者自身の場合
$username="$($Env:computername)\$($env:username)"
# 編集するときに毎回パスワードがいる...なんでこんな作りにした...
$password = ""
$taskname = "Hello"

# batteryの状態とか、何分たって終わらなかったら中止とか。
# compatibility
# Win8って書くけどGUI上ではWin10って表示される。
$settings = New-ScheduledTaskSettingsSet `
    -Compatibility Win8 `
    -WakeToRun
$task = New-ScheduledTask `
    -Description "ようやくをここに入れてください。" `
    -Action $action `
    -Trigger $trigger `
    -Settings $settings

# authorの名前も設定する。
$task.Author = "otowakka"

# versionの設定
$task.Version = "16.0.0"

# インストール日でなくてリリース日
# まさかのdateじゃなくても入れれる。
# 2017-01-01T00:00:00 みたいな形式が妥当
$task.Date = "2022-01-01T00:00:00"

# Userとpasswordがあったら暗黙的にログイン
# User名だけだったら、ログイン時のみに実行される。
Register-ScheduledTask `
    $task `
    -TaskName $taskname `
    -User $username `
    -Password $password `
    -Taskpath "\api\" 

# ユーザーの権限で登録できる。
# 他のユーザーが実行者のジョブは定義できない。
# 
# defaultの設定だとVistaとWindows Server 2008の設定で作られる
$action=New-ScheduledTaskAction `
    -Execute "pwsh.exe" `
    -Argument "-File -WorkingDirectory -NonInteractive" `
    -WorkingDirectory "C:\Report"

# 定期実行を指定したら、なぜかログイン時にできるかどうか指定ができない。
$trigger=New-ScheduledTaskTrigger `
    -Weekly `
    -DaysOfWeek Friday `
    -At 5pm
# powershell実行者=登録者自身の場合
$username="$($Env:computername)\$($env:username)"

# ログインしなくても実行するのはServiceAccount
$principal=New-ScheduledTaskPrincipal `
    -UserId $username `
    -RunLevel Highest

    
# batteryの状態とか、何分たって終わらなかったら中止とか。
# compatibility
# Win8って書くけどGUI上ではWin10って表示される。
$settings = New-ScheduledTaskSettingsSet `
    -Compatibility Win8
$task = New-ScheduledTask `
    -Description "ようやくをここに入れてください。" `
    -Action $action `
    -Trigger $trigger `
    -Settings $settings

# authorの名前も設定する。
$task.Author = "otowakka"

# versionの設定
$task.Version = "16.0.0"

# インストール日でなくてリリース日
# まさかのdateじゃなくても入れれる。
# 2017-01-01T00:00:00 みたいな形式が妥当
# Dateオブジェクトでないとダメだが、エラーにならない。
$task.Date = "2022-01-01T00:00:00"

Register-ScheduledTask `
    $task `
    -TaskName "GenerateReport" `
    -Taskpath "\api\" 

# 他のユーザーの登録か、
$action=New-ScheduledTaskAction `
    -Execute "pwsh.exe" `
    -Argument "-File -WorkingDirectory -NonInteractive" `
    -WorkingDirectory "C:\Report"

#
$trigger=New-ScheduledTaskTrigger `
    -AtLogon:$false `
    -Weekly `
    -DaysOfWeek Friday `
    -At 5pm

# powershell実行者=登録者以外の場合。
$username=$env:username

$principal=New-ScheduledTaskPrincipal `
    -UserId $username `
    -LogonType Password

$task = New-ScheduledTask `
    -Trigger $trigger `
    -Principal $principal `
    -Action $action 

# こうする。
$task.Auhtor = "otowakka"

Register-ScheduledTask `
    $task `
    -TaskName "GenerateReport" `
    -Taskpath "\api\"
# 通知のモジュールインストール
Install-Module -Name BurntToast -RequiredVersion 0.8.5


Import-Module BurntToast

# 終了通知とか。
$title = "Don't forget to smile!"
$text = 'Your script ran successfully, celebrate!'

# Powershell由来のという風に書かれる。
New-BurntToastNotification -AppLogo "${HOME}\Pictures\6dc77b22caee44416a169a3a31ef6392.jpg" -Text $title, $text

# user一覧。
# get-WmiObjectは非推奨。
# ADUser以外。
# windowsのアプリケーションに登録するときはhost名付きで登録しないと
# 動かない時がある。taskschedulerとか。
Get-LocalUser |
    Foreach-Object {$_.Name = "$($Env:ComputerName)\$($_.Name)"; $_} |
    Select-Object Name

$password=ConvertTo-SecureString "password" -AsPlainText -Force
# user作成
New-LocalUser -Name Hello -Password $password

Start-Process -FilePath 'pwsh.exe' -Credential 'Hello'

# 
$userinstance = Get-LocalUser -Name "selectname" |
    Select-Object -Expandproperty Name
if ($userinstance -eq $null) {
    return 1;
}
Microsoft-Windows-TaskScheduler/Maintenance

# 実行
wevtutil set-log Microsoft-Windows-TaskScheduler/Operational /enabled:true

ユーザー毎にログ取るとか
そんな考えられた作りになっていない。

# 最後のタスクが失敗したかどうかの確認
Get-ScheduledTaskInfo

# ログを見る。
# Get-EventLogは非推奨。

Get-Scheduled

$FulltaskPath=Join-Path "\api" -ChildPath "Hello"

# -eqで比較できない。メッセージで探すことになるので。
Get-WinEvent `
    -LogName Microsoft-Windows-TaskScheduler/Operational |
    ?{ $_.Message -match $FullTaskPath}


# オブジェクトとしてエキスポートしたい。
# 後でローカルで見たいから。
Get-WinEvent `
    -LogName Microsoft-Windows-TaskScheduler/Operational |
    ?{ $_.Message -match $FullTaskPath} |
    Export-CliXml -Path event.xml

# ログが見れる。
Import-CliXml -path event.xml

Get-ScheduledTask

という風に登録する必要がある。

複数のクライアントサーバーに登録する場合は

ただ複雑になっても、

Get-Disk

その代わりといってはなんだが、scheduled jobという物がある。

Powershellからpwsh呼ぶ

.net frameworkのPowershellから
dotnet のPowershellを登録する

どちらかというとjobの登録というよりアプリケーションのログ取るやつだし。

# Windows11だと管理者権限がいる。
# バグの気もする。
# registry触りに言っているけど、いつのまにか触れなくなった系か。

# 保存先
Get-ChildItem $env:USERPROFILE\AppData\Local\Microsoft\Windows\PowerShell\ScheduledJobs

# 定期実行を指定したら、なぜかログイン時にできるかどうか指定ができない。
$trigger=New-JobTrigger `
    -Weekly `
    -DaysOfWeek Friday `
    -At 5pm
    # -Logon

$trigger = New-JobTrigger `
    -User $username`
    -AtLogon

$option = New-ScheduledJobOption -WakeToRun
$taskname = "Hello"

# powershell実行者=登録者自身の場合
$username="$($Env:computername)\$($env:username)"
$cred = Get-Credential -UserName $username

# Workingdirectoryも与えられない
# 割とあいまいになる。
Register-ScheduledJob `
    -Name $taskname `
    -FilePath "${HOME}\pwsh\hello_dummy.ps1" `
    -ArgumentList $null `
    -ScheduledJobOption $option `
    -Trigger $trigger

# 下のような感じに書く。
# pwsh.exe -File "${HOME}\pwsh\hello.ps1" -WorkingDirectory "${HOME}\pwsh\" -NonInteractive

なぜか登録できない...

# scheduled Jobでも結局scheduled taskで登録される。
# taskpath \Microsoft\Windows\Powershell\ScheduledJobs\
# Name

# vistaで登録される。

これがVista, Windows Server 2008で登録されたら、ScheduledTaskがちゃんと動くかどうか
わからない。
windows11で結構がりがり変わっているのに。
vistaの頃は管理者権限がいらなかったのかも。

ずっと放置されている...

余談だが、新しいpwshでまだ作られていないコマンドレットって、
Vistaや7の頃の機能が多くて、今もちゃんと動くのか保守されているのか謎なやつが多い。
まあ、現段階では古いpowershellのコマンドレットを打ちに行くという古いコマンドレットのラッパーという作りで
とりあえず作って、windows11だとこういう理由で動きませんでしたって理由がわかったら、
それを参考に書き換えるでいい気がするが。
今も、古いPowershellのコマンドレット使っているから、
いずれにせよ処理壊れているなら古いコマンドレットの方を非推奨にして
新たにコマンドレット作ったら良いし。
コマンドうちにいくだけだから、別に処理遅くならんし...

最終的に新しいPowershellのコマンドレットを使ってくださいで良いはず。

Discussion

ログインするとコメントできます