Thursday, July 28, 2011

PowerShell: AD DC Failed Logins Report

I wanted to get a summary view of failed login attempts on a network DCs. Came up with this script that parses Windows 2008 R2 DCs security log files for all failed login attempts in the last 24 hours. Then it compiles a totals counts for each IP and host name combo listed in the logs of all the DCs. Sends a quick summary email to the admins.

#############################################################
# Script Name: AD_DCs_Failed_Login_Report.ps1
# Version: 1.0
# Author: Dean Bunn
# Last Edited: 07/26/2011
# Description: Failed Logins Report for DCs
#############################################################

#Array for All Failed Login Entries
$arrFailures = @()
#Array for Reporting
$Summary = @()

#Domain Controller Array
$DCs = @("dc1","dc2","dc3")

foreach($DC in $DCs)
{
#Retrieve Failed Logins on Each DC for the Last 24 Hours
$failedLogins = get-eventlog -computername $DC -logname security -after (get-date).adddays(-1) | where-object {$_.instanceID -eq 4625 }

#Loop Through Each Failed Login
foreach($failedLogin in $failedLogins)
{

#Var for Workstation Name
$workstation = ""
#Var for IP Address
$networkAddress = ""

#Array of Failed Login Log Entry Message (Split by Line Break)
$flM = $failedLogin.message.Split("`n")

#Loop Through Each Line in the Log Entry Message
foreach($fl in $flM)
{
#Check to See if Line has Source Network Address Info
if($fl.Contains("Source Network Address:"))
{
#Remove Unneeded Data from Line
$fl = $fl.Replace("Source Network Address:","")
#Clean UP Network Address Info
$networkAddress = $fl.ToString().Trim()
}

#Check to See if Line has Workstation Info
if($fl.Contains("Workstation Name:"))
{
#Remove Unneeded Data from Line
$fl = $fl.Replace("Workstation Name:","")
#Clean Up Workstation Info
$workstation = $fl.ToString().ToUpper().Trim()
}

}

#Format Failed Login Entry Data Before Adding to Array
$flEntry = $networkAddress + "," + $workstation

#Quick Check to See if IP And Host Name Weren't Empty
if($flEntry.length -gt 1)
{
#Added Failed Entry to Array
$arrFailures += $flEntry
}


}

}

#Create Hashtable for Unique Check
$htReport = @{}

#Loop Through Failed Log Entries Array and Count How Many Failed Logins
foreach($flEntry in $arrFailures)
{
#Int for Counting Failed Login Attempts
$intEC = 0

if(!$htReport.ContainsKey($flEntry))
{
#Loop Again Through Array Looking for IP + Host Name Match
foreach($item in $arrFailures)
{
if($flEntry -eq $item)
{
$intEC = $intEC + 1
}
}

#After Determining Matches, See if Entry Count Added To Report Already
#And Only Report on 10 or Greater Failed Logins for IP + Host Name Pair
if($intEC -gt 10) #
{

#Split Apart IP Host Name Entry to Add It to Report Summary
$arrFlEntry = $flEntry.Split(",")

#Create New PowerShell Object and Assign Data to It
$uEntry = new-Object PSObject
$uEntry | add-Member -memberType noteProperty -name "IP" -Value $arrFlEntry[0].ToString()
$uEntry | add-Member -memberType noteProperty -name "Host Name" -Value $arrFlEntry[1].ToString()
$uEntry | add-Member -memberType noteProperty -name "Failed Logins" -Value $intEC.ToString()
#Add Entry to Summary Array
$Summary += $uEntry

}

#Add Entry Info to Reporting Hashtable
$htReport.add($flEntry,"1")

}

}

#Get Current Short Date
$rptDate = Get-Date -Format d

#Style for HTML Table in ConvertTo-HTML
$a = "<style>"
$a = $a + "TABLE{border-width: 1px;border-style: solid;border-color: black;}"
$a = $a + "TH{border-width: 1px;padding: 5px;border-style: solid;border-color: black;text-align: center;}"
$a = $a + "TD{border-width: 1px;padding: 5px;border-style: solid;border-color: black;text-align: left;}"
$a = $a + "</style>"

#Message Body (Sorted by Failed Login Attempts)
$emsg = $Summary | Sort-Object {[int]$_."Failed Logins"} -descending | ConvertTo-Html -head $a | Out-String

#Settings for Email Message
$messageParameters = @{
Subject = "DCs Failed Logins Report for " + $rptDate
Body = $emsg
From = "DCAdmins@my.company.com"
To = "DCAdmins@my.company.com"
SmtpServer = "smtp.my.company.com"
}
#Send Report Email Message
Send-MailMessage @messageParameters –BodyAsHtml

6 comments:

Anonymous said...

I'm getting the following errors:

You cannot call a method on a null-valued expression.
At C:\PS\AD_DCs_Failed_Login_Report.ps1:32 char:34
+ $flM = $failedLogin.message.Split <<<< ("'n")
+ CategoryInfo : InvalidOperation: (Split:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull

You cannot call a method on a null-valued expression.
At C:\PS\AD_DCs_Failed_Login_Report.ps1:38 char:16
+ if($fl.Contains <<<< ("Source Network Address:"))
+ CategoryInfo : InvalidOperation: (Contains:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull

You cannot call a method on a null-valued expression.
At C:\PS\AD_DCs_Failed_Login_Report.ps1:47 char:16
+ if($fl.Contains <<<< ("Workstation Name:"))
+ CategoryInfo : InvalidOperation: (Contains:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull

Dean said...

Quick update. Please ensure that you run the script in an elevated instance of PowerShell.

Anonymous said...

I also got the same error:
You cannot call a method on a null-valued expression.
At C:\scripts\AdFailedLogins.ps1:32 char:34
+ $flM = $failedLogin.message.Split <<<< ("`n")
+ CategoryInfo : InvalidOperation: (Split:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull

Dean Bunn said...

Are you running it in an elevated PowerShell session?

Rick H. said...

This is a great script. Is it possible to also get the username of the failed logon in the report?

Thanks

Dean Bunn said...

I will try and add it in the next version.