PowerShellを使ってネットワーク上の他のコンピュータにメッセージを送る方法

PowerShellを使ってネットワーク上の他のコンピュータにメッセージを送る方法という問いには、quserで対象セッションを確認し、サーバー名、ユーザー、本文を表示して承認後に一人へmsg.exeで送るという方法で答えます。msg.exeはWindowsのセッションへメッセージを送るコマンドで、メールや一般的なLANブロードキャストではない。サーバー上のセッション列挙権限も必要になる。この記事ではサーバー名、USERNAME、SESSIONNAME、ID、STATEを使い、表示名や推測したユーザーだけで送らないを判断軸にし、実行前の確認、記事固有のコード、合否判定、戻し方を一続きで示します。

SMTPやチャットではなく、Windowsリモートデスクトップ等の既存セッションへの通知を扱う。完了は「msg.exeが成功し、別経路または利用者応答で保守通知を受け取ったことを確認できる」と定義します。対象が取れない場合は「対象セッション0件ならログオフ済み、別サーバー、ユーザー名違いを確認し、全セッション宛てへ拡大しない」として切り分け、推測で成功扱いにしません。

目次

msg.exeが届くWindowsセッションを確認

msg.exeが届くWindowsセッションを確認では、リモートセッション利用者への保守メッセージの対象を「サーバー名、USERNAME、SESSIONNAME、ID、STATEを使い、表示名や推測したユーザーだけで送らない」という単位で扱います。msg.exeはWindowsのセッションへメッセージを送るコマンドで、メールや一般的なLANブロードキャストではない。サーバー上のセッション列挙権限も必要になる。対象が複数なら表示名の部分一致で先頭を採らず、一意になる条件を追加します。

リモートセッション利用者への保守メッセージを始める前に、PowerShellの版、コマンドの提供元、必要権限、管理ポリシーの有無を確認します。権限不足と対象なしは意味が異なるため、例外を0件へ置き換えません。

quserでユーザーとSession IDを取得

quserで対象セッションを確認し、サーバー名、ユーザー、本文を表示して承認後に一人へmsg.exeで送る。リモートセッション利用者への保守メッセージではこの進め方により、操作したという事実ではなく、期待する状態へ到達したかでタイトルの問いへ答えられます。SMTPやチャットではなく、Windowsリモートデスクトップ等の既存セッションへの通知を扱う。

quserでユーザーとSession IDを取得の合格条件は、msg.exeが成功し、別経路または利用者応答で保守通知を受け取ったことを確認できることです。作業時刻、実行ユーザー、端末名を添え、判断に使った値が後から追える形にします。

宛先と本文をプレビュー

宛先と本文をプレビューは変更前の基準点です。サーバー名、USERNAME、SESSIONNAME、ID、STATEを使い、表示名や推測したユーザーだけで送らないを出力に含め、取得時刻と一緒に保存します。値だけを切り取ると別対象との比較になるため、識別列を省きません。

$server = 'SERVER01'
$expectedUser = 'user01'
$sessionId = 4
function Get-IttripActiveSessionRow([string]$Output) {
  $rows = @(foreach ($line in ($Output -split '\r?\n')) {
    $normalized = $line.TrimStart([char[]]@('>',' '))
    if (-not $normalized) { continue }
    $parts = @($normalized -split '\s+')
    if ($parts.Count -lt 3 -or -not [StringComparer]::OrdinalIgnoreCase.Equals($parts[0], $expectedUser)) { continue }
    $idPositions = @(for ($i=1; $i -lt $parts.Count; $i++) { if ($parts[$i] -eq [string]$sessionId) { $i } })
    if ($idPositions.Count -eq 1 -and $idPositions[0] + 1 -lt $parts.Count -and
      [StringComparer]::OrdinalIgnoreCase.Equals($parts[$idPositions[0] + 1], 'Active')) {
      [pscustomobject]@{ Raw=$line; User=$parts[0]; SessionId=[int]$parts[$idPositions[0]]; State=$parts[$idPositions[0] + 1] }
    }
  })
  if ($rows.Count -ne 1) { throw "同じquser結果からUser/SessionId/Activeの一意な行を得られません: $($rows.Count)件" }
  $rows[0]
}
$sessionOutput = (& quser.exe $sessionId /server:$server 2>&1 | Out-String)
if ($LASTEXITCODE -ne 0) { throw "quserに失敗しました: $sessionOutput" }
$sessionRow = Get-IttripActiveSessionRow -Output $sessionOutput
$sessionRow | Format-List

msg.exeはWindowsのセッションへメッセージを送るコマンドで、メールや一般的なLANブロードキャストではない。サーバー上のセッション列挙権限も必要になる。出力が多い場合も最初から無理に一件へ絞らず、候補数と除外理由を残してから対象を決めます。

承認後に一人へ送信

承認後に一人へ送信では同じ対象を別経路でもう一度読みます。判定したいのは「コマンドが終了したか」ではなく、msg.exeが成功し、別経路または利用者応答で保守通知を受け取ったことを確認できるかどうかです。

$message = '15分後に保守を開始します。作業中のファイルを保存してください。'
$approvalToken = "PREPARE-MSG $server/$($sessionRow.SessionId)/$($sessionRow.User)/$($sessionRow.State)"
if ((Read-Host "送信候補を表示する場合は $approvalToken を入力") -ne $approvalToken) { throw '中止しました。' }
$reboundOutput = (& quser.exe $sessionId /server:$server 2>&1 | Out-String)
if ($LASTEXITCODE -ne 0) { throw '承認後のquser再照会に失敗しました。' }
$reboundSession = Get-IttripActiveSessionRow -Output $reboundOutput
if ($reboundSession.User -ne $sessionRow.User -or $reboundSession.SessionId -ne $sessionRow.SessionId -or
  $reboundSession.State -ne 'Active') { throw '同じ解析済みquser行をActive状態で再確認できません。' }
$manualSendCommand = 'msg.exe {0} /server:{1} /time:60 "{2}"' -f $reboundSession.SessionId,$server,$message
[pscustomobject]@{
  Server=$server; User=$reboundSession.User; SessionId=$reboundSession.SessionId; State=$reboundSession.State
  CharacterCount=$message.Length; ExecuteAutomatically=$false; ManualCommand=$manualSendCommand
}
Write-Warning '本文と宛先を別の担当者も確認し、表示された単発コマンドを実行する直前にこのブロックを再実行してください。'

対象セッション0件ならログオフ済み、別サーバー、ユーザー名違いを確認し、全セッション宛てへ拡大しない。リモートセッション利用者への保守メッセージの期待値と実測値が一致しないときは追加変更を重ねず、対象識別、権限、ポリシー、時間差の順で原因を分けます。

送信結果と実際の受領は分ける

送信結果と実際の受領は分けるでは、quserで対象セッションを確認し、サーバー名、ユーザー、本文を表示して承認後に一人へmsg.exeで送る。リモートセッション利用者への保守メッセージの例中にある名前、パス、ID、時刻はサンプルなので、そのまま本番へ貼らず、直前の読み取り結果から承認値を入れます。

$afterOutput = (& quser.exe $sessionId /server:$server 2>&1 | Out-String)
$afterExitCode = $LASTEXITCODE
$afterRow = if ($afterExitCode -eq 0) { Get-IttripActiveSessionRow -Output $afterOutput } else { $null }
[pscustomobject]@{
  IntendedRecipient="$server/$sessionId/$expectedUser"
  SessionStillActive=($afterRow -and $afterRow.State -eq 'Active')
  CommandWasExecutedByThisScript=$false
  DeliveryReceipt='利用者応答または別経路で確認'
}
if ($afterExitCode -ne 0) { Write-Warning 'セッションを再照会できません。追送せず、別経路で受領を確認してください。' }

機密情報や資格情報を本文へ入れない。送信先が曖昧なら実行しない。テスト文を本番利用者へ送らない。リモートセッション利用者への保守メッセージでプレビュー対応コマンドを使える場合はWhatIfを先に実行し、非対応の操作は対象一覧と引数を画面へ出して人が承認してから一度だけ実行します。

セッション不存在や権限拒否

メッセージ送信は取り消せない。誤送信時は管理者へ連絡し、訂正文を承認して一度だけ送る。復旧操作にも同じ識別条件を使い、名前が似た別対象へ戻し処理を適用しません。

  • リモートセッション利用者への保守メッセージの変更前値と取得時刻
  • 復旧対象: サーバー名、USERNAME、SESSIONNAME、ID、STATEを使い、表示名や推測したユーザーだけで送らない
  • 復旧後の判定: msg.exeが成功し、別経路または利用者応答で保守通知を受け取ったことを確認できる
  • 再実行を止める条件: 機密情報や資格情報を本文へ入れない。送信先が曖昧なら実行しない。テスト文を本番利用者へ送らない

誤送信時に追送を重ねない

機密情報や資格情報を本文へ入れない。送信先が曖昧なら実行しない。テスト文を本番利用者へ送らない。誤送信時に追送を重ねないに該当したら、警告を消して継続するのではなく、どの条件で止まったかを記録します。

対象セッション0件ならログオフ済み、別サーバー、ユーザー名違いを確認し、全セッション宛てへ拡大しない。リモートセッション利用者への保守メッセージではエラー本文、FullyQualifiedErrorId、対象ID、直前に成功した段階を残すと、別担当者が安全な地点から調査できます。

保守連絡の記録

通知時刻、対象セッション、承認者、本文版、結果を保守記録に残す。リモートセッション利用者への保守メッセージを繰り返す場合は、正常、対象なし、要承認、失敗を異なる終了状態として記録し、前回値との比較だけで異常を決めません。

保守連絡の記録の識別軸サーバー名、USERNAME、SESSIONNAME、ID、STATEを使い、表示名や推測したユーザーだけで送らない
採用する実測msg.exeが成功し、別経路または利用者応答で保守通知を受け取ったことを確認できる
0件時の扱い対象セッション0件ならログオフ済み、別サーバー、ユーザー名違いを確認し、全セッション宛てへ拡大しない
保留にする兆候機密情報や資格情報を本文へ入れない。送信先が曖昧なら実行しない。テスト文を本番利用者へ送らない

リモートセッション利用者への保守メッセージの実行記録には、開始前の対象候補、採用した識別値、実行したコード、終了後の実測、除外した候補と理由を同じ作業番号で残します。特に「サーバー名、USERNAME、SESSIONNAME、ID、STATEを使い、表示名や推測したユーザーだけで送らない」を省くと、後日の再確認で別対象の値を比較するおそれがあります。画面コピーだけでなく、日時と端末名を含む構造化した出力も保存します。

PowerShellを使ってネットワーク上の他のコンピュータにメッセージを送る方法を定期手順へ組み込む場合も、初回は対話的に候補を確認します。正常時は「msg.exeが成功し、別経路または利用者応答で保守通知を受け取ったことを確認できる」、判定不能時は「対象セッション0件ならログオフ済み、別サーバー、ユーザー名違いを確認し、全セッション宛てへ拡大しない」、中止時は「機密情報や資格情報を本文へ入れない。送信先が曖昧なら実行しない。テスト文を本番利用者へ送らない」をそれぞれ別の結果として扱います。これにより、0件や例外を都合よく成功へ丸めず、次の担当者が同じ対象と条件で追試できます。

修正後コードの合格条件:ユーザー名だけで送らず、quserのSession IDを明示して同じ行に期待ユーザーが含まれることを確認します。サーバー・ID・ユーザーを含む承認トークンが一致した場合だけ一セッションへ送り、ワイルドカードや全セッション宛てへ拡大しません。

再修正後はプレビュー承認のあと、msg.exeの直前にquserを再実行します。同じserver、Session ID、利用者を一つの再照会結果で確認できない限り送信しません。

安全版 r4b-20260719:quserの同じ解析済み行でUser、Session ID、Activeを直前確認し、msg.exeは表示された単発コマンドを人が実行します。スクリプトは送信せず、受領確認を別経路に分離します。

公式情報・参考資料

リモートセッション利用者への保守メッセージで使うコマンド名、引数、対応環境は次のMicrosoft一次資料で確認しました。記事の確認日は2026年7月17日です。OSやモジュール更新後は、実行端末のGet-Helpと併せて再確認してください。

この記事を書いた人

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

コメント

コメントする

目次