監視ノードからWindowsサービスを確認し、停止していたらメール通知するPowerShell
はじめに
Windows Serverを運用していると、
- 特定のサービスが落ちていないか確認したい
- 監視製品を入れる前に、最低限の状態確認だけしたい
- 移行期間中だけ、暫定的にサービス状態を見たい
- 複数サーバのサービス状態を1か所から確認したい
という場面があります。
もちろん、Zabbixなどの監視製品を利用できるなら、それを使うのが本筋です。
ただ、現場では
監視製品に載せる前に、とりあえず最低限見たい
という場面もあります。
この記事では、管理用端末や監視ノードから複数サーバのWindowsサービス状態を確認し、停止していた場合にメール通知するPowerShellスクリプトを紹介します。
このスクリプトの考え方
このスクリプトは、監視対象サーバ自身に配置するものではありません。
想定している構成は以下です。
[監視ノード]
|
| net use で監視対象へ認証接続
|
| Get-Service -ComputerName でサービス状態確認
|
| 結果をログ出力
|
| NGがあればメール通知
v
[監視対象サーバ]
つまり、各サーバにスクリプトやタスクを配置するのではなく、監視ノード側から複数サーバをまとめて確認する方式です。
この方式にすると、監視対象を増やす場合も、監視ノード側の定義を追加するだけで済みます。
やりたいこと
今回やりたいことは以下です。
- 複数サーバのWindowsサービス状態を確認する
- 確認前に
net useで対象サーバへ認証接続する - サービスが起動していればOK
- サービスが停止、または取得失敗したらNG
- 結果をログファイルに保存する
- NGがあった場合のみメール通知する
前提
この記事のサンプルでは、以下のような環境を想定します。
| 項目 | 内容 |
|---|---|
| 実行元 | 管理用Windows端末または監視ノード |
| 監視対象 | Windows Server |
| 確認方法 | Get-Service -ComputerName |
| 認証 |
net use による一時接続 |
| 通知 | SMTPサーバ経由のメール送信 |
| 実行方法 | タスクスケジューラなどで定期実行 |
スクリプト例
以下は、監視ノードから複数サーバのサービス状態を確認し、NGがあった場合のみメール通知するサンプルです。
# ==========================================
# Windowsサービス簡易監視スクリプト
# 監視ノードから複数サーバのサービス状態を確認する
# ==========================================
# ログ出力先
$savePath = "C:\Surveillance\Services\SERVICES.txt"
# 日時
$nowDate = Get-Date -Format "yyyy-MM-dd"
$nowTime = Get-Date -Format "HH:mm:ss"
# 監視対象定義
# ServerName : 監視対象サーバ名
# ServiceName : 確認するサービスの表示名
# UserName : 接続に使用するユーザー
# Password : 接続に使用するパスワード
$targets = @(
@{
ServerName = "SERVER01"
ServiceName = "Sample Service"
UserName = "Administrator"
Password = "password"
},
@{
ServerName = "SERVER02"
ServiceName = "Another Service"
UserName = "Administrator"
Password = "password"
}
)
# メール設定
$to = @("alert@example.com")
$from = "monitor@example.com"
$smtp = "smtp.example.com"
$subject = "[NG] Windows Service Check $nowDate $nowTime"
# ログ初期化
"Windows Service Check" | Out-File $savePath -Encoding UTF8
"Date : $nowDate" | Out-File $savePath -Append -Encoding UTF8
"Time : $nowTime" | Out-File $savePath -Append -Encoding UTF8
"========================================" | Out-File $savePath -Append -Encoding UTF8
# NG結果格納
$ngList = @()
foreach ($target in $targets) {
$serverName = $target.ServerName
$serviceName = $target.ServiceName
$userName = $target.UserName
$password = $target.Password
"[$serverName] $serviceName check start" | Out-File $savePath -Append -Encoding UTF8
try {
# 監視対象サーバへ一時的に認証接続
# ドメイン環境の場合は domain\user の形式に変更する
net use "\\$serverName" /user:"$serverName\$userName" "$password" | Out-File $savePath -Append -Encoding UTF8
# サービス状態取得
$service = Get-Service -ComputerName $serverName |
Where-Object { $_.DisplayName -eq $serviceName }
if ($null -eq $service) {
$message = "NG : Service not found. Server=$serverName Service=$serviceName"
$ngList += $message
$message | Out-File $savePath -Append -Encoding UTF8
}
elseif ($service.Status -eq "Running") {
"OK : Server=$serverName Service=$serviceName Status=$($service.Status)" |
Out-File $savePath -Append -Encoding UTF8
}
else {
$message = "NG : Server=$serverName Service=$serviceName Status=$($service.Status)"
$ngList += $message
$message | Out-File $savePath -Append -Encoding UTF8
}
}
catch {
$message = "NG : Failed to check service. Server=$serverName Service=$serviceName Error=$($_.Exception.Message)"
$ngList += $message
$message | Out-File $savePath -Append -Encoding UTF8
}
finally {
# 接続を削除
net use "\\$serverName" /delete /yes | Out-File $savePath -Append -Encoding UTF8
}
"----------------------------------------" | Out-File $savePath -Append -Encoding UTF8
}
# 最終結果
if ($ngList.Count -eq 0) {
$checkResult = "ALL OK"
}
else {
$checkResult = "NG"
}
"Result : $checkResult" | Out-File $savePath -Append -Encoding UTF8
"========================================" | Out-File $savePath -Append -Encoding UTF8
# NG時のみメール送信
if ($ngList.Count -gt 0) {
$body = @"
Windows Service Check Result : NG
Date : $nowDate
Time : $nowTime
NG List:
$($ngList -join "`r`n")
Please check attached log file.
"@
Send-MailMessage `
-To $to `
-From $from `
-SmtpServer $smtp `
-Subject $subject `
-Body $body `
-Attachments $savePath `
-Encoding UTF8
}
出力ログ例
すべてのサービスが起動している場合は、以下のようなログになります。
Windows Service Check
Date : 2026-06-15
Time : 10:00:00
========================================
[SERVER01] Sample Service check start
OK : Server=SERVER01 Service=Sample Service Status=Running
----------------------------------------
[SERVER02] Another Service check start
OK : Server=SERVER02 Service=Another Service Status=Running
----------------------------------------
Result : ALL OK
========================================
サービスが停止していた場合は、以下のようになります。
Windows Service Check
Date : 2026-06-15
Time : 10:00:00
========================================
[SERVER01] Sample Service check start
OK : Server=SERVER01 Service=Sample Service Status=Running
----------------------------------------
[SERVER02] Another Service check start
NG : Server=SERVER02 Service=Another Service Status=Stopped
----------------------------------------
Result : NG
========================================
タスクスケジューラで定期実行する
定期的に確認したい場合は、監視ノード側のタスクスケジューラから実行します。
プログラム
powershell.exe
引数
-ExecutionPolicy Bypass -File "C:\script\Check-Service.ps1"
開始フォルダー
C:\script
たとえば、5分ごと、10分ごと、1時間ごとなど、用途に応じて実行間隔を設定します。
なぜ監視ノード方式にするのか
単体サーバで自分自身のサービスを確認するだけであれば、各サーバ上で Get-Service を実行すれば済みます。
ただし、監視対象が複数ある場合、各サーバにスクリプトやタスクを配置すると管理が分散します。
監視ノード方式にすると、以下のようなメリットがあります。
- スクリプトを監視ノード側に集約できる
- 対象サーバの追加・削除を1か所で管理できる
- メール通知元を監視ノードに集約できる
- 対象サーバ側にタスクスケジューラを作らなくてよい
- 一時的な移行期間や小規模環境の暫定監視に使いやすい
一方で、リモート接続の権限やファイアウォール、名前解決などを考慮する必要があります。
このスクリプトでできること
このスクリプトでは、以下のようなことができます。
- 監視ノードから複数サーバのサービス状態を確認する
- サービス停止時のみメール通知する
- 確認結果をログファイルに残す
- タスクスケジューラで定期実行する
- 監視製品導入前の暫定監視として使う
注意点
監視製品の代替ではない
このスクリプトは、Zabbixなどの監視製品の代替ではありません。
以下のような用途には、監視製品を利用した方がよいです。
- 多数サーバの一元管理
- しきい値監視
- 死活監視
- リソース監視
- ログ監視
- 障害履歴の管理
- 通知抑止
- メンテナンス時間帯の制御
- グラフ化
あくまで、暫定監視や小規模環境での簡易確認として使う想定です。
net useの認証情報に注意する
サンプルでは説明を分かりやすくするため、対象定義の中にユーザー名とパスワードを書いています。
ただし、実運用でスクリプト内に平文パスワードを直接書くのは推奨しません。
可能であれば、
- タスクスケジューラの実行ユーザーに必要権限を付与する
- 管理用アカウントの権限設計で対応する
- 資格情報を安全に扱う仕組みを使う
- 閉域環境に限定する
など、環境に合わせて検討してください。
net useが失敗した場合もNG扱いにする
監視対象サーバに接続できない場合、サービス状態も確認できません。
このサンプルでは、例外が発生した場合もNGとしてログに残すようにしています。
サービス停止だけでなく、
- 名前解決失敗
- ネットワーク疎通不可
- 認証失敗
- 権限不足
- ファイアウォール
- 対象サーバ停止
なども、確認失敗として扱う必要があります。
DisplayNameとNameの違いに注意する
Windowsサービスには、表示名とサービス名があります。
このサンプルでは、表示名で確認しています。
Where-Object { $_.DisplayName -eq $serviceName }
環境によっては、表示名よりサービス名で確認した方が確実な場合があります。
その場合は、以下のように変更します。
Where-Object { $_.Name -eq $serviceName }
Send-MailMessageについて
このサンプルでは Send-MailMessage を使用しています。
既存の閉域環境や、社内SMTPサーバを使う簡易通知では利用しやすい場合があります。
一方で、新しく仕組みを作る場合は、環境のセキュリティ方針や利用可能なメール送信方式に合わせて検討してください。
ログファイルの肥大化に注意する
定期実行する場合、ログファイルが増え続けることがあります。
必要に応じて、
- 日次でログを分ける
- 月次でローテーションする
- 古いログを圧縮する
- 保存期間を決める
などを検討してください。
まとめ
PowerShellを使うことで、監視ノードから複数サーバのWindowsサービス状態を確認し、停止していた場合のみメール通知する簡易監視を作ることができます。
監視製品の代替にはなりませんが、
- 監視製品導入前の暫定確認
- 小規模環境の簡易監視
- 移行期間中の一時監視
- 特定サービスだけの死活確認
といった場面では役に立つことがあります。
大事なのは、
何となく動いているはず
で終わらせず、
確認した結果をログに残し、異常時に気づけるようにする
ことだと思います。