【ツールの概要】
・タスクスケジューラ上に特定のWindowsイベントログをトリガーとしたタスクを作る。
・Powershellで、フィルターされた直近数分間のイベントログのデータをメールで送る。
【環境(使っている環境)】
・Windows Server2016(+ Windows10)
・Powershell ver5.1
【ソース(AlertLoginError.ps1、メイン処理)】
# クラスファイルを読み込む using module .\class\Class_CommonFunction.psm1 using module .\class\Class_GetEvents.psm1 using module .\class\Class_Sendmail.psm1 # 各変数をセットアップする、ログの命名などにも使用 $DocRoot = Split-Path (Split-Path $MyInvocation.MyCommand.Path -Parent) -Parent $YMD = Get-Date -Format "yyyyMMdd" $yMdHms = Get-Date -Format "yyyyMMddHHmmss" # xmlにメールの設定を格納しておく $mailcfgpath = $DocRoot + '\etc\mailcfg.xml' # クラス[CommonFunction]のインスタンスを作成、ログ実行(ログ用、logfileにWrite-Outputをするメソッドをもつクラス(省略)) $CF = [CommonFunction]::new() $CF.logfile = $DocRoot + '\log\AlertLoginError_' + $YMD + '.log'; $CF.logwrite("INF", "AlertLoginError process start.") # クラス[GetEventLog]のインスタンスを作成、メソッド実行(イベントログ取得用) $GE = [GetEventLog]::new() $str = $GE.GetEvents() # 返り値(文字列)のLengthが0じゃなかったらメール送信 if ($str.Length -ne 0) { $SM = [SendMail]::new() $SM.ConfigMailProperty($str, $mailcfgpath, $logfile) $CF.logwrite("INF", "Alert mail sended.") } $CF.logwrite("INF", "AlertLoginError process Finished.")
【ソース(class\Class_GetEvents.psm1、イベントログ抽出処理)】
class GetEventLog { # Get-WinEventで得られるオブジェクトを変数に格納 [string] GetEvents() { $MESSAGES = Get-WinEvent ` -logname security ` -FilterXPath ` "Event [ ` System [ ` Provider [@Name='Microsoft-Windows-Security-Auditing'] ` and ( ` EventID='4625' ) ` and ( ` TimeCreated[timediff(@SystemTime) <= 300000] ` ) ` ] ` ]" # オブジェクトの必要な要素のみJsonとして文字列にする(流用しやすいデータにしたいため) $str = $MESSAGES | ForEach-Object { if ( $_.id -eq "4625" ) { $_ | Select-Object ` @{Name = "TimeCreated"; Expression = { $_.TimeCreated.ToString("yyyy-M-d HH:mm:ss") } }, ` Id, ` @{Name = "TargetLogonId"; Expression = { $_.properties[5].value } }, ` @{Name = "TargetDomainName"; Expression = { $_.properties[6].value } }, ` @{Name = "TargetUserSid"; Expression = { $_.properties[4].value } }, ` @{Name = "IpAddress"; Expression = { $_.properties[19].value } }, ` @{Name = "Message"; Expression = { $_.Message.Substring(0, 18) } } } } | ConvertTo-Json return $str } }
【ソース(class\Class_Sendmail.psm1、メール送信処理)】
class SendMail{ [string]ConfigMailProperty($str, $mailcfgpath, $logfile) { $Body = "Windows events LoginError catched. `r`n`r`n EventData : `r`n`r`n " + $str $Result = $this.SendmailbyPS($Body, $mailcfgpath, $logfile) return $Result } [string]SendmailbyPS($Body, $mailcfgpath, $logfile) { $Mailconf_info = [xml](Get-Content $mailcfgpath) foreach ($data in $Mailconf_info.config.data) { } foreach ($data2 in $Mailconf_info.config.data) { } $MailSv = $data.MailSv $Port = $data.Port $Encode = $data.Encode $uid = $data.uid $pwd = $data.pwd $From = $data.from $To = $data.To $Cc = $data.Cc $Subject = $data.Subject #Credential $pwd = $pwd | ConvertTo-SecureString -AsPlainText -Force $cred = New-Object System.Management.Automation.PSCredential $uid, $pwd #Send Mail try { Send-MailMessage ` -To $To ` -Cc $Cc ` -From $From ` -SmtpServer $MailSv ` -Credential $cred ` -UseSsl ` -Encoding $Encode ` -Port $Port ` -Subject $Subject ` -Body $Body $result = 'OK' } catch { $ErrorMessage = $_.Exception_Message $result = $ErrorMessage + $LASTEXITCODE Write-Output $LASTEXITCODE | Out-File -Append $logfile -encoding Default } if ($result -ne 'OK') { $MailSv = $data2.MailSv $Port = $data2.Port $Encode = $data2.Encode $uid = $data2.uid $pwd = $data2.pwd $From = $data.from $pwd = $pwd | ConvertTo-SecureString -AsPlainText -Force $cred = New-Object System.Management.Automation.PSCredential $uid, $pwd try { Send-MailMessage ` -To $To ` -Cc $Cc ` -From $From ` -SmtpServer $MailSv ` -Credential $cred ` -UseSsl ` -Encoding $Encode ` -Port $Port ` -Subject $Subject ` -Body $Body $result = 'OK' } catch { $ErrorMessage = $_.Exception_Message $result = $ErrorMessage + $LASTEXITCODE Write-Output $LASTEXITCODE | Out-File -Append $logfile -encoding Default } } return $result } }
【説明】
・タスクスケジューラでイベントIDで発火するように、上記のようなトリガ設定でPowershellスクリプトを実行します。
・PowershellスクリプトではイベントログをFilterXPathでフィルターし、ConvertTo-Jsonでイベントログオブジェクトを文字列にしてメールします。
・イベントログの仕様上、一回のログイン失敗に見えても、数回 ID4625のイベントログが記録されることがあります。
【総評】
Classで分けることである程度わかりやすいスクリプトが書けるようになっていると思います。
FilertXPathやForEach-Objectのところで条件を変えれば、いろんな要件も満たせそうですね。
まあでも、業務用途の場合ちゃんとした商用監査ソフトを使うのが一番でしょうね・・・