tech.guitarrapc.cóm

Technical updates

PowerShellでIISのログから接続元IP一覧を取得する

Windows Server 2012いいですね!もう2008R2以前に戻れません。 さて、IISですが、相変わらず読みにくいログです…。 今回、接続元のIPをログから取得、調査するする必要に迫られたので、スクリプトを書きました。 せっかくなので公開しておきます。

IISのログ場所

デフォルトのログ保存パスは以下です。
C:\inetpub\logs\LogFiles\W3SVC1\
ここに以下のファイル名規則でログが保持されています。
u_exYYMMdd.log

Git公開

コードはGitで公開しています。 最新は此処から取得してください。
https://github.com/guitarrapc/PowerShellUtil/tree/master/Get-IisLogFileCIps

コード全文

最新はGitを参照してください。(※このコードはApt/18/2013時点のものです。) ログファイルの日付指定とか、もう少し改修が欲しいですね (記事書いてて思ったので 近日更新をしましょう。 19/Apr/2013 更新しました。
#Requires -Version 3.0

[CmdletBinding(
    SupportsShouldProcess = $false,
    ConfirmImpact = "none",
    DefaultParameterSetName = "ExportAsCsv"
)]
param
(
    [Parameter(
    HelpMessage = "Input Path of IIS Log file. Default : C:\inetpub\logs\LogFiles\W3SVC1\",
    Position = 0,
    Mandatory = $false,
    ValueFromPipeline = $true,
    ValueFromPipelineByPropertyName = $true
    )]
    [ValidateNotNullOrEmpty()]
    [string]
    $IISLogPath = "C:\inetpub\logs\LogFiles\W3SVC1\",

    [Parameter(
    HelpMessage = "Input Suffix of IIS Log file. Default : u_ex",
    Position = 1,
    Mandatory = $false,
    ValueFromPipeline = $true,
    ValueFromPipelineByPropertyName = $true
    )]
    [ValidateNotNullOrEmpty()]
    [string]
    $IISLogFileHead =  "u_ex",

    [Parameter(
    HelpMessage = "Input date of IIS Log file . Default : `"`"",
    Position = 2,
    Mandatory = $false,
    ValueFromPipeline = $true,
    ValueFromPipelineByPropertyName = $true
    )]
    [string]
    $IISLogFileDate =  "",

    [Parameter(
    HelpMessage = "Input Extention of IIS Log file. Default : .log",
    Position = 3,
    Mandatory = $false,
    ValueFromPipeline = $true,
    ValueFromPipelineByPropertyName = $true
    )]
    [string]
    $IISLogFileExtention =  ".log",

    [Parameter(
    HelpMessage = "Select switch if you want to output with sort all AddressList to Unique. Default : Not Selected",
    Mandatory = $false,
    ValueFromPipeline = $true,
    ValueFromPipelineByPropertyName = $true
    )]
    [switch]
    $sortUniq,

    [Parameter(
    HelpMessage = "If you select this switch, output with csv. Default selected",
    Mandatory = $false,
    ParameterSetName="ExportAsCsv"
    )]
    [switch]
    $ExportAsCsv,

    [Parameter(
    HelpMessage = "If you select this switch, output with json. Default not selected",
    Mandatory = $false,
    ParameterSetName="ExportAsJson"
    )]
    [switch]
    $ExportAsJson
)



function Get-IisLogFileCIps{

    [CmdletBinding(
        SupportsShouldProcess = $false,
        ConfirmImpact = "none",
        DefaultParameterSetName = "ExportAsCsv"
    )]
    param
    (
        [Parameter(
        HelpMessage = "Input Path of IIS Log file. Default : C:\inetpub\logs\LogFiles\W3SVC1\",
        Position = 0,
        Mandatory = $false,
        ValueFromPipeline = $true,
        ValueFromPipelineByPropertyName = $true
        )]
        [ValidateNotNullOrEmpty()]
        [string]
        $IISLogPath = "C:\inetpub\logs\LogFiles\W3SVC1\",

        [Parameter(
        HelpMessage = "Input Suffix of IIS Log file. Default : u_ex",
        Position = 1,
        Mandatory = $false,
        ValueFromPipeline = $true,
        ValueFromPipelineByPropertyName = $true
        )]
        [ValidateNotNullOrEmpty()]
        [string]
        $IISLogFileHead =  "u_ex",

        [Parameter(
        HelpMessage = "Input date of IIS Log file . Sample : `'2013/04/17`'",
        Position = 2,
        Mandatory = $false,
        ValueFromPipeline = $true,
        ValueFromPipelineByPropertyName = $true
        )]
        [string]
        $IISLogFileDate = "",

        [Parameter(
        HelpMessage = "Input Extention of IIS Log file. Default : .log",
        Position = 3,
        Mandatory = $false,
        ValueFromPipeline = $true,
        ValueFromPipelineByPropertyName = $true
        )]
        [string]
        $IISLogFileExtention =  ".log",

        [Parameter(
        HelpMessage = "Select switch if you want to output with sort all AddressList to Unique. Default : Not Selected",
        Mandatory = $false,
        ValueFromPipeline = $true,
        ValueFromPipelineByPropertyName = $true
        )]
        [switch]
        $sortUniq,

        [Parameter(
        HelpMessage = "If you select this switch, output with csv. Default selected",
        Mandatory = $false,
        ParameterSetName="ExportAsCsv"
        )]
        [switch]
        $ExportAsCsv,

        [Parameter(
        HelpMessage = "If you select this switch, output with json. Default not selected",
        Mandatory = $false,
        ParameterSetName="ExportAsJson"
        )]
        [switch]
        $ExportAsJson

    )

    begin
    {
        $prevstatus = $true
    }

    process
    {

        # Check IIS Log file exit or not, when $IISLogFileDate had passed
        if($IISLogFileDate -ne "")
        {
            # Cast and Perse $IISLogFileDate to use for filename
            $IISLogFileDate = ([datetime]$IISLogFileDate).ToString("yyMMdd")

            $IISLogFileName = ($IISLogFileHead + $IISLogFileDate + $IISLogFileExtention)
            $IISLogFullPath = Join-Path $IISLogPath $IISLogFileName

            if(!(Test-Path $IISLogFullPath))
            {
                throw "$IISLogFileDate format was correct. But $IISLogFullPath not found. Please check $ISLogFileDate format."
            }

        }
        else
        {
            # When $IISLogFileDate not ordered, then get all Files in iis log directory.
            $IISLogFullPath = (Get-ChildItem $IISLogPath).FullName
        }

        $result = foreach ($log in $IISLogFullPath){

            [Console]::WriteLine("$log read start.")

            # Read $log file
            $IISLogFileRaw = Get-Content -Path $log

            # Set Header from log file by reading RAW Number 3
            $headers = $IISLogFileRaw[3].Replace("#Fields: ","").Replace("-","").Replace("(","").Replace(")","").split(" ")

            # Import Log file as Object
            $IISLogFileCSV = Import-Csv -Delimiter " " -Header $headers -Path $log

            # Remove #* line for date object
            $IISLogFileCSV = $IISLogFileCSV | where {$_.date -notlike "#*"}

            # Create PSCustomObject
            $IISLogFileCSV `
                | %{
                    # Input into variables to prepare making PSCustomObject
                    $date=$_.date
                    $time=$_.time
                    $sip=$_.sip
                    $csmethod=$_.csmethod
                    $csuristem=$_.csuristem
                    $csuriquery=$_.csuriquery
                    $sport=$_.sport
                    $csusername=$_.csusername
                    $cip=$_.cip
                    $csUserAgent=$_.csUserAgent
                    $csReferer=$_.csReferer
                    $scstatus=$_.scstatus
                    $scsubstatus=$_.scsubstatus
                    $scwin32status=$_.scwin32status
                    $timetaken=$_.timetaken


                    #region Debug check cip resut
                    <#
                    [Console]::WriteLine($_.cip)
                    #>
                    #endregion

                    # Check currentIP and previousIP is same or not, then check previous result. If failed, then skip.
                    # 1. Check PreviousIP and CurrentIP
                    if(($_.cip -ne $prevCip))
                    {
                        try
                        {
                            # DNS Name Resolve for IP Address who connected
                            [System.Net.Dns]::GetHostByAddress($_.cip)
                            $prevStatus=$true
                        }
                        catch
                        {
                            # Create Custom Object as blank
                            $prevStatus=$false
                            [PSCustomObject]@{
                                HostName=""
                                Aliases=""
                                AddressList=$_.cip
                            }
                        }
                        # flag for next line ip check
                        $prevCip=$_.cip
                    }
                    else
                    {
                        # 2. Check previous result was succeed or not
                        if($prevStatus -eq $false)
                        {
                        }
                        else
                        {
                            [System.Net.Dns]::GetHostByAddress($_.cip)
                            $prevStatus=$true
                        }
                    }


                    #region Debug if result
                    <#
                    [Console]::WriteLine(($_.cip -eq $prevCip) -and ($prevStatus -eq $true))
                    [Console]::WriteLine(($_.cip -eq $prevCip))
                    [Console]::WriteLine(($prevStatus -eq $true))
                    #>
                    #endregion


                } `
                | %{
                    # Output as PSCustomObejct and append all file parse data into 1 PSObject
                    $Output = [PSCustomObject]@{
                        HostName=$_.HostName
                        Aliases=[string]$_.Aliases
                        AddressList=[string]$_.AddressList
                        date=$date
                        time=$time
                        sip=$sip
                        csmethod=$csmethod
                        csuristem=$csuristem
                        csuriquery=$csuriquery
                        sport=$sport
                        csusername=$csusername
                        cip=$cip
                        csUserAgent=$csUserAgent
                        csReferer=$csReferer
                        scstatus=$scstatus
                        scsubstatus=$scsubstatus
                        scwin32status=$scwin32status
                        timetaken=$timetaken
                        iisFileName=$log
                        }

                    # Output currenct PSCustom Object to temp file.
                    switch ($true){
                        $ExportAsCsv {$Output | Out-File ./Current_Object_Status_csv.log}
                        $ExportAsJson {$Output | Out-File ./Current_Object_Status_json.log}
                        default {$Output | Out-File ./Current_Object_Status.log}
                    }

                    # Output to recieve in $result
                    $Output
                }
            [Console]::WriteLine("$log read end and go next step..")
            }
        [Console]::WriteLine("All Log files read done. Starting output...")
    }

    end
    {
        switch($true){
        $sortUniq {$result | sort AddressList -Unique}
        default {$result | sort AddressList}
        }
    }

}


"Now Running Scripts, please wait...."


switch ($true)
{
    $ExportAsCsv
    {
        if($true -eq $sortUniq)
        {
            Get-IisLogFileCIps -IISLogPath "C:\inetpub\logs\LogFiles\W3SVC1\" -sortUniq -ExportAsCsv | Export-csv ./IIS_c-IP_lists_$((Get-Date).ToString("yyyyMMdd")).csv -NoTypeInformation
        }
        else
        {
            Get-IisLogFileCIps -IISLogPath "C:\inetpub\logs\LogFiles\W3SVC1\" -ExportAsCsv | Export-csv ./IIS_c-IP_lists_$((Get-Date).ToString("yyyyMMdd")).csv -NoTypeInformation
        }
    }

    $ExportAsJson
    {
        if($true -eq $sortUniq)
        {
            Get-IisLogFileCIps -IISLogPath "C:\inetpub\logs\LogFiles\W3SVC1\" -sortUniq -ExportAsJson | ConvertTo-Json -Compress | Out-File ./IIS_c-IP_lists_$((Get-Date).ToString("yyyyMMdd")).json
        }
        else
        {
            Get-IisLogFileCIps -IISLogPath "C:\inetpub\logs\LogFiles\W3SVC1\" -ExportAsJson | ConvertTo-Json -Compress | Out-File ./IIS_c-IP_lists_$((Get-Date).ToString("yyyyMMdd")).json
        }
    }

    default { Get-IisLogFileCIps -IISLogPath "C:\inetpub\logs\LogFiles\W3SVC1\" | Export-csv ./IIS_c-IP_lists_$((Get-Date).ToString("yyyyMMdd")).csv -NoTypeInformation}
}

"End Scripts. Please check ./IIS_c-IP_lists.log"
pause

利用例

フォルダ内の全ログを取得、分析する場合はこれで大丈夫です。 (デフォルトでcsv取得)
# バッチで実行の場合
PowerShell .\Get-IisLogFileCIps.ps1 -IISLogPath "C:\inetpub\logs\LogFiles\W3SVC1\"

# PowerShell.exeで実行
.\Get-IisLogFileCIps.ps1 -IISLogPath "C:\inetpub\logs\LogFiles\W3SVC1\"
出力をjsonにする場合は以下のように、-ExportAsJsonをつけます。
# バッチで実行の場合
PowerShell .\Get-IisLogFileCIps.ps1 -IISLogPath "C:\inetpub\logs\LogFiles\W3SVC1\" -ExportAsJson

# PowerShell.exeで実行
.\Get-IisLogFileCIps.ps1 -IISLogPath "C:\inetpub\logs\LogFiles\W3SVC1\"  -ExportAsJson
更に出力結果をユニーク(一意)なもののみにする場合は、-sortUniq を付けます。
# バッチで実行の場合(csvでuniq)
PowerShell .\Get-IisLogFileCIps.ps1 -IISLogPath "C:\inetpub\logs\LogFiles\W3SVC1\" -ExportAsCsv -sortUniq

# PowerShell.exeで実行の場合(csvでuniq)
.\Get-IisLogFileCIps.ps1 -IISLogPath "C:\inetpub\logs\LogFiles\W3SVC1\"  -ExportAsCsv -sortUniq
日付の指定も可能です。調査したい日付がファイル名を、-IISLogFileDate に渡します。 もし何も渡さなければ、IISログフォルダ全てのログを走査します。 datetime型にキャストするので、所謂日付や、Get-Dateで走査した日付をパイプから渡しても大丈夫です。
# バッチで実行の場合(csvでuniq)
PowerShell .\Get-IisLogFileCIps.ps1 -IISLogPath "C:\inetpub\logs\LogFiles\W3SVC1\" -ExportAsCsv -sortUniq -IISLogFileDate "2013/03/07"

# PowerShell.exeで実行の場合(csvでuniq)
.\Get-IisLogFileCIps.ps1 -IISLogPath "C:\inetpub\logs\LogFiles\W3SVC1\"  -ExportAsCsv -sortUniq -ExportAsCsv -sortUniq -IISLogFileDate "2013/03/07"

(Get-Date).AddDays("-30") | %{ .\Get-IisLogFileCIps.ps1 -IISLogPath "C:\inetpub\logs\LogFiles\W3SVC1\"  -ExportAsCsv -sortUniq -ExportAsCsv -sortUniq -IISLogFileDate "2013/03/07" }
ベースができているので、取得したいパラメーターの調整も簡単に調整できますね。 → リクエストがあり、IISのログも全て出力するようにしています。 別コマンドレットでもいいですし、パラメーターで渡しても、Module化しても良さそうです。 欲しい人はリクエストをどうぞ。書きますよん。 更新点は、GitHubでどうぞ。