Windows 10のタスク スケジューラでRDPログオン通知を確実に行う方法|「ユーザー セッションへの接続時」が発火しない原因と回避策

Windows 10 Proで「RDPで誰かがログオンした瞬間にメール通知したい」のに、タスク スケジューラのトリガー「ユーザー セッションへの接続時(リモートからの接続)」が“新規RDPログオン”では動かない――この挙動は珍しくありません。原因を整理しつつ、確実に拾うための現実的な回避策(GUI設定/PowerShell自動登録/RDPだけに絞る方法)までまとめます。

目次

起きている現象を整理(再接続はOK、新規ログオンはNG)

質問の状況をもう少し正確に言い換えると、だいたい次の状態です。

  • Windows 10 Pro(ホスト側)に対して、RDPで接続する
  • タスク スケジューラのトリガーを「ユーザー セッションへの接続時」→「リモート コンピューターからの接続」に設定している
  • いったんRDPセッションを「切断(Disconnect)」して残しておき、同じユーザーで同じセッションへ「再接続(Reconnect)」するとタスクが実行される
  • ところが、ホストにそのユーザーの既存セッションがない状態(再起動後、ログオフ後、あるいは初回接続など)で“新規にRDPログオン”するとタスクが実行されない

この差がハマりポイントで、ここを理解すると回避策が一直線になります。

原因:「ユーザー セッションへの接続時」は“ログオン”ではなく“接続/再接続”寄りに振れることがある

タスク スケジューラのトリガーには、似ているようで目的が違うものが混在しています。

トリガー拾うタイミングのイメージ得意なケース苦手になりやすいケース
ログオン時(At log on)ユーザーのログオン セッションが作られた瞬間新規ログオン(ローカル/Remote両方)「接続/再接続」と完全一致させたい用途(RDP限定など)は工夫が必要
ユーザー セッションへの接続時(On connection to user session)“セッションへ接続した/戻ってきた”瞬間切断済みセッションへの再接続、ユーザー切替の戻り新規ログオン(セッション新規作成)の瞬間を取りこぼすことがある

RDPは、ユーザー体験としては「接続してログオン」ですが、内部的には「認証」「セッション作成」「ログオン」「デスクトップ表示」「切断/再接続」など複数段階に分かれます。トリガー「ユーザー セッションへの接続時」が“既存セッションへの接続/再接続”のイベントに寄ってしまうと、「セッションが存在しない状態からの新規ログオン」を確実に拾えないことがあります。

また、「再起動後から挙動が変わったように見える」という点も説明がつきます。再起動直後は当然ながら“切断済みの既存セッション”が存在しないため、再接続イベントが発生しません。結果として「再接続なら動くのに、新規ログオンでは動かない」という差がはっきり観測されます。

最短の解決:目的が“RDPログオン検知”なら「ログオン時(At log on)」へ寄せる

結論として、狙いが「RDPでのログオン検知」なら、まずはトリガーを「ログオン時(At log on)」へ切り替えるのが堅いです。これで新規ログオンを取りこぼしにくくなります。

GUIでのおすすめ設定(失敗しにくい形)

タスク スケジューラ(taskschd.msc)で新規タスクを作る場合、以下の考え方が安定します。

項目推奨理由
全般「ユーザーがログオンしているときのみ実行」RDP判定に環境変数(SESSIONNAME/CLIENTNAME)を使う場合、ユーザーコンテキストで動かした方が確実
全般必要に応じて「最上位の特権で実行」ログ参照やネットワーク操作など、環境によっては権限が必要
トリガー「ログオン時」新規ログオンを確実に拾う
トリガー「タスクを開始するまでの遅延:30秒」ログオン直後はネットワークやプロファイルがまだ不安定な場合があるため(メール送信失敗の軽減)
操作PowerShellスクリプトを起動メール送信やRDP判定など、柔軟に制御できる
条件「ネットワーク接続が利用可能な場合のみ開始」SMTP/Graph/Teams Webhook等、通知はネットワーク依存が多い
設定「既に実行中の場合:新しいインスタンスを開始しない」短時間に複数回ログオンした場合の重複通知を抑制

タスク スケジューラには以前「電子メールの送信」アクションがありましたが、現在は非推奨の扱いです。現実的にはPowerShellで通知を送るのが安全です。

PowerShellで「ログオン時」タスクを作る例(ローカルPC向け)

ここでは「ログオン時にスクリプトを実行するタスク」をPowerShellで登録します。実運用では、実行するスクリプト(NotifyRdp.ps1など)を“メール通知処理”に差し替えます。

# 管理者として PowerShell を起動して実行する想定

$taskName   = "Notify-Logon"
$scriptPath = "C:\Scripts\NotifyRdp.ps1"

# アクション:PowerShell でスクリプト実行
$action = New-ScheduledTaskAction `
  -Execute "powershell.exe" `
  -Argument "-NoProfile -ExecutionPolicy Bypass -File `"$scriptPath`""

# トリガー:ログオン時(任意ユーザー)
$trigger = New-ScheduledTaskTrigger -AtLogOn

# 設定:重複起動を抑え、失敗時の再試行も設定
$settings = New-ScheduledTaskSettingsSet `
  -AllowStartIfOnBatteries `
  -DontStopIfGoingOnBatteries `
  -StartWhenAvailable `
  -MultipleInstances IgnoreNew `
  -ExecutionTimeLimit (New-TimeSpan -Minutes 5) `
  -RestartCount 3 `
  -RestartInterval (New-TimeSpan -Minutes 1)

# タスク登録(現在のユーザーで「ログオン時に実行」)
Register-ScheduledTask `
  -TaskName $taskName `
  -Action $action `
  -Trigger $trigger `
  -Settings $settings `
  -Description "Logon notification task (RDP判定はスクリプト側で実施)"

ポイントは、「ログオン時」自体はRDP/ローカル両方で発火するので、RDPだけ通知したい場合はスクリプト側で判定することです(次章)。

補足:リモートPCに同じタスクを一括配布したい場合(Invoke-Command)

複数台に仕込みたい場合、WinRM(PowerShell Remoting)が有効なら、同様の登録をリモートで流せます。運用上は、台数が増えるほど「手作業GUI」よりもミスが減ります。

$computers = @("PC-01","PC-02")  # 対象PC名
$taskName  = "Notify-Logon"
$scriptPath = "C:\Scripts\NotifyRdp.ps1"

Invoke-Command -ComputerName $computers -ScriptBlock {
  param($taskName, $scriptPath)

  New-Item -ItemType Directory -Path (Split-Path $scriptPath) -Force | Out-Null

  $action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-NoProfile -ExecutionPolicy Bypass -File `"$scriptPath`""
  $trigger = New-ScheduledTaskTrigger -AtLogOn
  $settings = New-ScheduledTaskSettingsSet -StartWhenAvailable -MultipleInstances IgnoreNew

  Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Settings $settings -Force | Out-Null
} -ArgumentList $taskName, $scriptPath

リモート実行では、事前にWinRM有効化(Enable-PSRemoting)やファイアウォール、認証方式の整備が必要です。ここが難しい環境では、GPOや構成管理(Intune等)で配布する方が現実的です。

RDP“だけ”に絞る方法A:ログオン時に動かし、スクリプト側でRDP判定して分岐

「ログオン時」トリガーは確実性が高い反面、ローカルログオンも拾います。そこで、スクリプトでRDPかどうかを判定して、RDPのときだけ通知します。

RDP判定の実用ライン(環境変数を使う)

ユーザーコンテキストで実行される場合、次の環境変数が判断材料になります。

環境変数RDP時の典型ローカル時の典型メモ
SESSIONNAMERDP-Tcp#○○Console最も分かりやすい。RDPならだいたいRDP-Tcp系になる
CLIENTNAME接続元PC名Console / 空「どの端末から来たか」を通知文面に入れたい場合に便利

注意点として、タスクをSYSTEMなど別アカウントで動かすと、これらの値が期待通りにならないことがあります。RDP判定を環境変数で行うなら、まずは“ログオンしたユーザーとして実行”が失敗しにくいです。

RDP時だけメール送信するPowerShell例(ひな形)

以下は「RDPなら通知、ローカルなら何もしない」最小構成の例です。SMTPの設定は環境に合わせて差し替えてください。

# C:\Scripts\NotifyRdp.ps1

# --- RDP判定 ---
$sessionName = $env:SESSIONNAME
$clientName  = $env:CLIENTNAME
$userName    = $env:USERNAME
$computer    = $env:COMPUTERNAME

$isRdp = $false
if ($sessionName -and $sessionName -like "RDP*") {
  $isRdp = $true
}

if (-not $isRdp) {
  # ローカルログオンは通知しない
  exit 0
}

# --- 通知内容 ---
$subject = "[RDPログオン] $computer / $userName"
$body = @"
RDPログオンを検知しました。

PC:        $computer
ユーザー:  $userName
SESSIONNAME: $sessionName
CLIENTNAME:  $clientName
日時:      $(Get-Date -Format "yyyy/MM/dd HH:mm:ss")
"@

# --- メール送信(例)---
# 注意:Send-MailMessage は将来的に非推奨扱いが強まっています。
# まずは動かす目的で例示し、運用では別方式(Graph、SMTPリレー、Webhook等)も検討してください。

$smtpServer = "smtp.example.com"
$smtpPort   = 587
$from = "[email protected]"
$to   = "[email protected]"

# 認証が必要なら、資格情報の安全な保管(Credential ManagerやDPAPI)を推奨
# ここでは例として対話入力を避け、あらかじめ暗号化保存したXMLを読む想定にしておく
$credPath = "C:\Scripts\smtp-cred.xml"
if (Test-Path $credPath) {
  $cred = Import-Clixml -Path $credPath
} else {
  # 初回だけ手動で作成し、ACLを絞って保存しておく(別途実施)
  $cred = $null
}

try {
  if ($cred) {
    Send-MailMessage -SmtpServer $smtpServer -Port $smtpPort -UseSsl `
      -From $from -To $to -Subject $subject -Body $body -Encoding UTF8 `
      -Credential $cred
  } else {
    Send-MailMessage -SmtpServer $smtpServer -Port $smtpPort -UseSsl `
      -From $from -To $to -Subject $subject -Body $body -Encoding UTF8
  }
} catch {
  # 失敗時にイベントログやファイルへ記録しておくと運用が楽
  $log = "C:\Scripts\NotifyRdp-error.log"
  "$(Get-Date -Format s) $($_.Exception.Message)" | Out-File -FilePath $log -Append -Encoding UTF8
}

資格情報をDPAPIで暗号化して保存する(運用で詰まりやすい所)

上の例ではImport-Clixmlで資格情報を読み込む形にしています。次のように一度だけ手動で作っておくと、以後は非対話で送れます(暗号化はWindowsの仕組みに依存します)。

# 1回だけ実行(ログオン通知を動かすユーザーで実行)
$cred = Get-Credential   # SMTPユーザー/パスワードを入力
$cred | Export-Clixml -Path "C:\Scripts\smtp-cred.xml"

# 重要:ファイルのアクセス権(ACL)を絞る
# 例:Administrators と当該ユーザーのみ読み取り可能にするなど

組織のポリシーでSMTPが使いづらい場合は、Microsoft Graph(Outlook送信)、社内SMTPリレー、Teams/SlackのWebhookなどに寄せると運用が安定しやすいです。

RDP“だけ”に絞る方法B:セキュリティログのイベントをトリガーにする(ログオン種別10)

「ローカルは除外し、RDPログオンだけ確実に拾いたい」「ユーザーが複数いても一括で拾いたい」なら、イベントログトリガーが強力です。

代表的な考え方は、セキュリティログのログオン成功イベント(イベントID 4624)から、ログオン種別(LogonType)が10(RemoteInteractive)のものだけを拾う、という方法です。

手順の全体像

  • (必要なら)監査ポリシーで「ログオン」の成功監査を有効化
  • タスク スケジューラのトリガーを「イベント時」にする
  • カスタムフィルター(XML)で「4624 かつ LogonType=10」に絞る
  • アクションで通知スクリプトを実行(メール/Webhook等)

カスタムXMLフィルター例(4624かつLogonType=10)

タスク スケジューラの「トリガー」→「新規」→「イベント時」→「カスタム」→「新しいイベント フィルター」→「XML」タブで「クエリを手動で編集する」にチェックし、次のようなフィルターを入れます。

<QueryList>
  <Query Id="0" Path="Security">
    <Select Path="Security">
      *[System[(EventID=4624)]]
      and
      *[EventData[Data[@Name='LogonType']='10']]
    </Select>
  </Query>
</QueryList>

さらにユーザーを限定したい場合は、TargetUserName(または環境によりSubjectUserName)で絞る方法もあります。ただしイベントのData名は環境差が出ることがあるため、まずはイベントビューアで実際の4624を開き、「詳細」タブでフィールド名を確認してから詰めるのが安全です。

イベントログトリガー方式のメリット/デメリット

観点メリットデメリット
確実性RDPログオン(種別10)を狙い撃ちできる監査ポリシーやログ保持の影響を受ける
RDP限定ローカルログオンを自然に除外できる運用でフィルター調整が必要になることがある(例:NLAや追加イベント)
管理ユーザーが増えても同じタスクで拾えるタスク実行ユーザーをどうするか(SYSTEMで通知する等)設計が必要

参考:RDP関連イベントは他にもある(目的別に使い分け)

「ログオンを検知したい」のか、「接続/再接続を検知したい」のか、「認証に成功した時点を拾いたい」のかで、見るログが変わります。運用で困ったときに切り替えられるよう、代表例だけ押さえておくと便利です。

目的見る候補特徴
RDPログオン(成功)Security:4624(LogonType=10)「実際にログオンした」を取りやすい。RDPだけに絞りやすい
RDPの再接続/切断Security:4778/4779(環境により)「切断した」「再接続した」を拾いやすい(監査設定に依存)
RDP認証(早い段階)TerminalServices-RemoteConnectionManager(Operational)「認証が通った」段階を拾える場合がある。ログオンとはズレることがある
セッション状態(OS側)TerminalServices-LocalSessionManager(Operational)セッションの開始/切断/再接続など、状態変化の観察に向く

今回のように「接続時トリガーが新規ログオンを拾わない」ケースでは、“ログオンを拾う仕組み(At log on / 4624)へ寄せる”のが安定します。

なぜ「再接続では動く」のかを再現して腹落ちさせる(切断とログオフの違い)

RDPでウィンドウを閉じたとき、ユーザーが意図せず“ログオフ”ではなく“切断”になっているケースが多いです。

  • 切断(Disconnect):セッションはPC側に残る。再接続が可能(このとき“接続/再接続”系のイベントが出やすい)
  • ログオフ(Sign out):セッションが終了する。再接続ではなく次回は新規ログオンになる
  • 再起動:当然セッションは消える。次回は新規ログオンになる

つまり、「ユーザー セッションへの接続時」で動いていたのは、実は“新規ログオン”ではなく“切断セッションへの復帰”を拾っていた、という構図になりがちです。

トラブルシューティング:タスクが動かないときのチェックリスト

回避策に切り替えても、メール送信やスクリプト実行で詰まることがあります。ありがちな落とし穴を先に潰しておくと運用が楽です。

タスク スケジューラ側

  • 履歴(History)を有効化:タスクが起動したか、どこで失敗したかが追える
  • 「開始するプログラム」の引数のクォート:パスに空白がある場合、-File "C:\Path With Space\script.ps1"のように必ず囲む
  • 実行ポリシー:-ExecutionPolicy Bypassを付ける(社内ルールがある場合は署名運用へ)
  • 遅延開始:ログオン直後はネットワーク未確立で通知が失敗しやすい。まずは30秒遅延で安定することが多い
  • 重複実行:短時間にログオンが重なると通知が連投される。複数インスタンス設定で抑制

スクリプト側

  • RDP判定の前提:SYSTEM実行だとSESSIONNAMEが期待通りにならないことがある。まずはユーザー実行に寄せて検証
  • メール送信の失敗ログ:失敗時にファイルへ書く(例:C:\Scripts\NotifyRdp-error.log)だけで原因究明が早くなる
  • SMTP要件:587/TLS必須、アプリパスワード必須、社内FWで外向きSMTP不可など環境差が大きい

イベントログ方式の場合

  • 4624が記録されているか:実際にSecurityログに4624が出ていないと、当然タスクは発火しない
  • LogonTypeが10か:RDPでも状況によって別の種別が混ざることがあるため、まずはフィルター無しで観察してから絞る
  • ログ保持:ログがすぐローテートする環境では、追跡や原因調査が難しくなるため保持サイズも見直す

運用を“通知がうるさくない”形に整えるコツ(重複防止・情報量)

実運用では「検知できる」だけでなく「通知が役に立つ」ことが重要です。おすすめの工夫をまとめます。

  • 重複通知の抑制:同一ユーザーが短時間に再ログオンした場合は抑制する(例:5分以内は通知しない)
  • 通知本文に入れると便利な情報:
    • PC名、ユーザー名、日時
    • SESSIONNAME(RDP-Tcp#XX)
    • CLIENTNAME(接続元端末名)
  • 「切断」も把握したい場合:ログオン通知と別に、切断(Disconnect)通知タスクも作ると監視が一気に実用的になる

監視の目的が「不正ログオン検知」寄りなら、メールだけでなく、Teamsへの投稿、Syslog/SIEMへの転送、アカウントのロックアウト連携なども視野に入ります。ただし、いきなり盛ると壊れやすいので、まずはログオン時(At log on)で確実に拾う→RDP判定を追加→イベントログ方式へ移行、の順が失敗しにくいです。

まとめ:最短は「ログオン時」へ、RDP限定は“判定かイベント”で詰める

  • 「ユーザー セッションへの接続時(リモートからの接続)」は、再接続に強いが新規ログオンを取りこぼすことがある
  • RDPログオンの“確実な検知”が目的なら、まずは「ログオン時(At log on)」へ切り替えるのが最短ルート
  • RDPだけ通知したい場合は、
    • 方法A:ログオン時に動かして、スクリプトでSESSIONNAME等からRDP判定
    • 方法B:Securityログの4624(LogonType=10)をトリガーにしてRDPログオンを狙い撃ち

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次