Wrapper - phbits/WebsiteFailedLogins GitHub Wiki

Using this technique, the wrapper script is launched via Scheduled Tasks which then runs Invoke-WebsiteFailedLogins. The returned object can then be processed as desired.

The following PowerShell script can be used as a wrapper. Update the "User Defined Variables" and include custom code to achieve the desired action when results are found.

Import-Module WebsiteFailedLogins

<#
    ## User Defined Variables ##
#>

# Number of seconds an IP should be blocked/limited/etc.
[Int] $MaxSeconds = 3600

# Path to ClientIp persistence file
# If no file exists it will be created
[String] $ClientIpPersistenceFile = 'D:\WebsiteFailedLogins\W3SVC1-blocked-ips.xml'

# Path to configuration file
[String] $WebsiteFailedLoginsIni = 'D:\WebsiteFailedLogins\W3SVC1.ini'

<#
    ## Start Script ##
#>

# Hashtable to hold all ClientIP currently being blocked/limited/etc.
[Hashtable] $ClientIpList = @{}

if (Test-Path -LiteralPath $ClientIpPersistenceFile)
{
    $ClientIpList = Import-Clixml -LiteralPath $ClientIpPersistenceFile
}

# IIS logs in UTC
$ExpireTimeStamp = (Get-Date).ToUniversalTime().AddSeconds($(-1 * $MaxSeconds))

$Results = Invoke-WebsiteFailedLogins -RunningConfig -Configuration $WebsiteFailedLoginsIni

if ($Results.HasError -eq $true)
{
    # process error
    # Send-MailMessage -Body $($Results.ErrorMessages -join [System.Environment]::NewLine)
} 

if ($Results.HasResults -eq $true)
{
    if ($Results.FailedLoginsPerIP.Count -gt 0)
    {
        foreach ($key in $Results.FailedLoginsPerIP.Keys)
        {
            if ($ClientIpList.ContainsKey($key) -eq $false)
            {
                # Example Hashtable data:
                #
                # $Results.FailedLoginsPerIP[$key].ClientIP       # 10.1.1.10
                # $Results.FailedLoginsPerIP[$key].FailedLogins   # 30
                # $Results.FailedLoginsPerIP[$key].Sitename       # W3SVC1
                # $Results.FailedLoginsPerIP[$key].IISLogPath     # D:\logs\W3SVC1
                # $Results.FailedLoginsPerIP[$key].Authentication # Forms
                # $Results.FailedLoginsPerIP[$key].HttpResponse   # 401
                # $Results.FailedLoginsPerIP[$key].UrlPath        # /login.aspx
                # $Results.FailedLoginsPerIP[$key].Start          # 2021-01-26T18:02:51Z
                # $Results.FailedLoginsPerIP[$key].'End~'         # 2021-01-26T18:22:51Z


                # <insert custom code here>


                # add IP to list
                $ClientIpList.Add($key, $Results.FailedLoginsPerIP[$key].'End~')
                
            } else {

                # IP is already blocked. This is expected and a result of 
                # the recommended configuration. It occurs when StartTime 
                # is much larger than how frequently the script is launched 
                # which causes the same logs to be included in WebsiteFailedLogins 
                # analysis for multiple iterations.
            }
        }
    }

    if ($Results.TotalFailedLogins.ContainsKey('TotalFailedLogins') -eq $true)
    {
        # Example Hashtable data:
        #
        # $Results.TotalFailedLogins.TotalFailedLogins # 100
        # $Results.TotalFailedLogins.ClientIpList      # hashtable
        # $Results.TotalFailedLogins.ClientIpList['1.1.1.1']      # 90
        # $Results.TotalFailedLogins.Sitename          # W3SVC1
        # $Results.TotalFailedLogins.IISLogPath        # D:\logs\W3SVC1
        # $Results.TotalFailedLogins.Authentication    # Forms
        # $Results.TotalFailedLogins.HttpResponse      # 401
        # $Results.TotalFailedLogins.UrlPath           # /login.aspx
        # $Results.TotalFailedLogins.Start             # 2021-01-26T18:02:51Z
        # $Results.TotalFailedLogins.'End~'            # 2021-01-26T18:22:51Z


        # <insert custom code here>
    }
}

# Process ClientIP list being blocked/limited/etc.
foreach ($key in $ClientIpList.Keys)
{
    $alertEnd = [System.DateTime]::Parse($ClientIpList[$key])

    if ($alertEnd -lt $ExpireTimeStamp)
    {
        # allow ip previously blocked/limited/etc.
        # remove IP from firewall rule, config, etc.
        

        # <insert custom code here>


        # remove IP from list
        $ClientIpList.Remove($key)
    }
}

# Save updated ClientIP list to persistence file
$ClientIpList | Export-Clixml -LiteralPath $ClientIpPersistenceFile -Force