■ PowerShellでSyslogメッセージ送信 ～RFC 3164形式対応させたよ～

![PowerShell_banner.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4049822/2fd4db5e-14e9-42b3-9e38-a3728ed83c5b.png)
<br>

こんにちは！
技術は実際に触って学ぶタイプです。零壱(ゼロイチ)テクトです。

<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4049822/ec08f703-4a5c-46d9-8c78-a7527b229e57.png">

先日、OpenWrtでSyslogサーバを立てたのですが
僕が使うWindowsではLinuxのようなLoggerコマンドがなく
スクリプトで好きな種類のSyslogを送信できないかと思った所
PowerShellで良さげなのが無かったので作成してみました。

尚、逆にSyslogの受信も考えたんですが、以下 ebi216様が
実用的なコードを書いてくださっていたので、送信だけのコードを作成しました。

　@ebi216様 / PowershellでSyslogサーバを書く
　https://qiita.com/ebi216/items/3e5d18bd33c5e47ae3b5
 
# １．概要/仕様
・PowerShellのみでSyslogを送信
・Syslogで一般的な仕様 RFC 3164 形式準拠（仕事でも使いたいので
・Facility/Severityを選べるように
・後に弄れるように生成メッセージも部品化

# ２．コード
```powershell:SyslogTestSned.ps1
# 送信先SyslogサーバーのIPアドレス/ポート番号
$syslogServer = "192.168.100.1" # SyslogサーバーのIPアドレス
$syslogPort   = 514             # Syslogのデフォルトポート (UDP)

# ===== Syslogメッセージの詳細設定 =====
# RFC 3164 形式に準拠
# PRI = (Facility * 8) + Severity

$facility = 1 
# Facility (機能) コード: メッセージを生成したプログラムの種類
# 0: kernel messages (カーネルメッセージ)
# 1: user-level messages (ユーザーレベルメッセージ)
# 2: mail system (メールシステム)
# 3: system daemons (システムデーモン)
# 4: security/authorization messages (セキュリティ/認証メッセージ)
# 5: syslogd internal messages (syslogd内部メッセージ)
# 6: line printer subsystem (ラインプリンターサブシステム)
# 7: network news subsystem (ネットワークニュースサブシステム)
# 8: UUCP subsystem (UUCPサブシステム)
# 9: clock daemon (クロックデーモン)
# 10: security/authorization messages (認証/セキュリティメッセージ)
# 11: FTP daemon (FTPデーモン)
# 12: NTP subsystem (NTPサブシステム)
# 13: log audit (ログ監査)
# 14: log alert (ログアラート)
# 15: clock daemon (クロックデーモン)
# 16-23: local use (ローカル利用)

$severity = 1 
# Severity (重要度) コード: メッセージの重要度
# 0: Emergency (緊急) - システムが使用不能
# 1: Alert (警告) - 直ちに行動が必要
# 2: Critical (致命的) - クリティカルな状態
# 3: Error (エラー) - エラー状態
# 4: Warning (注意) - 警告状態
# 5: Notice (通知) - 正常だが注目すべき状態
# 6: Informational (情報) - 情報メッセージ
# 7: Debug (デバッグ) - デバッグレベルメッセージ

# 現在時刻の取得(UTC) / RFC 3164形式のタイムスタンプを作成: "Jan 23 12:34:56"
$now       = [DateTime]::UtcNow
$month     = $now.ToString("MMM", [System.Globalization.CultureInfo]::InvariantCulture) # 月の略称
$day       = $now.Day
$dayString = " $day" # 日付生成/RFC3164準拠(3桁)/日付1桁の場合はスペースで桁調整
$dayString = $dayString.Substring($dayString.Length - 2, 2)
$time      = $now.ToString("HH:mm:ss") # 時刻 (HH:mm:ss)
$timestamp = "$month $dayString $time"

# ホスト名: メッセージを送信しているホストのコンピュータ名
$hostname = $env:COMPUTERNAME

# アプリケーション名 (Tag): メッセージを生成したアプリケーションの名前
$appName = "PowerShell"

# プロセスID (PID): メッセージを生成したプロセスのID
$processId = $PID

# 実際のメッセージ内容
$message = "Test Syslog Message from PowerShell(RFC 3164 FORMAT)"

# ===== Syslogメッセージ作成 =====
# PRIORITY (PRI) 値の計算
# PRI = (Facility * 8) + Severity
$priority = ($facility * 8) + $severity
$pri      = "<$priority>"

# RFC3164 形式/Syslogメッセージ生成
# フォーマット: <PRI>TIMESTAMP HOSTNAME APP-NAME[PROCID]: MESSAGE
$syslogMessage = "$pri$timestamp $hostname $appName[$processId]: $message"

# ===== Syslogメッセージの送信 =====
# UDPクライアント定義
$udpClient = New-Object System.Net.Sockets.UdpClient

try {
    # Syslogサーバー接続 (UDPはコネクションレスだが、Connectでデフォルトのリモートエンドポイントを設定)
    $udpClient.Connect($syslogServer, $syslogPort)

    # Syslogメッセージ/バイト配列に変換
    # RFC3164 は ASCII が一般的/日本語不可
    $bytes = [System.Text.Encoding]::ASCII.GetBytes($syslogMessage)

    # メッセージ送信
    $udpClient.Send($bytes, $bytes.Length)

    # コンソールに送信完了メッセージ表示
    Write-Host "[送信先]　　　　 $($syslogServer):$($syslogPort)  宛にSyslogメッセージを送信しました。"
    Write-Host "[メッセージ]　　 $($syslogMessage)"

}
catch {
    # エラー発生時処理
    Write-Error "Syslogメッセージ送信中エラー: $($_.Exception.Message)"
    Write-Host  "エラーの種類: $($_.Exception.GetType().FullName)"
}
finally {
    # UDPクライアントを必ずクローズ/リソース解放
    if ($udpClient -ne $null) {
        $udpClient.Close()
        Write-Host "---> UDPクライアントをクローズ"
    }
}
```
# ３．結果
⬇️PowerShell ISE側の送信結果です。これを3回送りました。

```
PS C:\Powershell\SyslogTestSned.ps1
99
[送信先]　　　　 192.168.100.1:514  宛にSyslogメッセージを送信しました。
[メッセージ]　　 <9>May 24 14:33:49 01tecto PowerShell[20200]: Test Syslog Message from PowerShell(RFC 3164 FORMAT)
---> UDPクライアントをクローズ

```
<br>

⬇️OpenWrtのSyslogサーバ側のログです。
　 実際にSyslogサーバで受信を確認できました。
　（見やすさや情報隠しにログの一部を若干弄ってます）

```
BusyBox v1.36.1 (2024-09-23 12:34:46 UTC) built-in shell (ash)

  _______                     ________        __
 |       |.-----.-----.-----.|  |  |  |.----.|  |_
 |   -   ||  _  |  -__|     ||  |  |  ||   _||   _|
 |_______||   __|_____|__|__||________||__|  |____|
          |__| W I R E L E S S   F R E E D O M
 -----------------------------------------------------
 OpenWrt 23.05.5, r24106-10cc5fcd00
 -----------------------------------------------------
root@OpenWrt_FG52E:~# 
root@OpenWrt_FG52E:~# 
root@OpenWrt_FG52E:~# cat /var/log/syslog.log

May 24 14:33:21 OpenWrt_FG52E dnsmasq-dhcp[1]: DHCPDISCOVER(br-lan) xx:xx:xx:xx:xx:xx:xx
May 24 14:33:21 OpenWrt_FG52E dnsmasq-dhcp[1]: DHCPOFFER(br-lan) 192.168.xx.xx xx:xx:xx:xx:xx:xx 

May 24 14:33:41 01Tecto PowerShell[20200]: Test Syslog Message from PowerShell(RFC 3164 FORMAT)
May 24 14:33:46 01Tecto PowerShell[20200]: Test Syslog Message from PowerShell(RFC 3164 FORMAT)
May 24 14:33:50 01Tecto PowerShell[20200]: Test Syslog Message from PowerShell(RFC 3164 FORMAT)

May 24 14:33:58 OpenWrt_FG52E odhcpd[1894]: A default route is present -略- don't announce a default route by overriding ra_lifetime!

```

# ４．備考
・✅ 改良案1：1実行1送信なので、連発したい場合は回数指定してループさせる必要あり

・✅ 改良案2：同じメッセージ連発はイマイチなので
　 　　　　ランダムでFacility/Severityを変えた方がよさげ

・「**Syslog Generator for Windows**」や
　「**Kiwi SyslogGen**」なるフリーのSyslog送信ツールを見つけたので
　ちゃんとした業務で検証に使用するなら、そっちのツールの方が良いカーモ。
 
・そもそもLinuxのLoggerコマンドやその手のツールを使え、
　と思ったが方いれば仰る通りです。

・最初はもっとシンプルでとりあえず送信できるコードを書いたんですが
　せっかく投稿するなら「`RFC3164`」に準拠しようと盛ったら
　仕様の確認と理解の方にめっちゃ時間かかりました……。

<br>

それでは
　　　　　　　どこかの誰かの何かの足しになれば幸いです。
　　　　　　　　　　　　　　　　　　　　　　　　　　01000010 01011001 01000101