Quick PowerShell Stuff 17

In this latest installment, I am going to revisit a script I had written before that is now in it’s second major iteration. The first iteration was just to simply be able to scan all event logs for a list of servers that your typed in and look for Critical, Warning and Error messages in all relevant logs. Let me review the changes that have ben made so far:

  • Choose to scan all Domain Controllers – added option
  • Choose to scan all Exchange Servers – added option
  • Use a CSV file – added option
  • Menu added for ease of use
  • Speed – the old script could take days, now it takes hours (looking to trim this in the next version)


Changes and Explanation

The most important change above was simply the speed of the script was too slow. In an environment with 8+ sites and 4+ servers per site, the script took 2-3 days to run. What was happening is that the source machine (a laptop) was talking back and forth with the remote Exchange server which was sometimes in the same region, sometimes across a WAN link. The Servers over the WAN link were VERY slow. This increased the run time dramatically:

SlowMethod

Now, in order to improve the speed of the script, I used the invoke-command so that the processing would take place on the remote server and save the results locally. Then the file would be copied back to the local machine.:

QuickMethod

This reduced the run time to 2.5 hours for 50+ servers.

The Script

Here is the new menu:

menu

<#  
.SYNOPSIS
                Analyzes server event logs for Error, Warning and Critical events.
 
.DESCRIPTION  
    Analyzes server event logs for Error, Warning and Critical events for mulptile servers and skps unneded log file types.
 
.NOTES  
    Version             : 1.7
    Change Log:         : 1.7 - Added remote connection running of the script
                        : 1.6 - Fixed some bugs, added menu, added choice for Exchange servers to be scanned, added
                            check for no results found, cleaned up some formatting               
                        : 1.5 - Added choice for all DCs, CSV file or manual entry
                        : 1.4 - added log check, if log name is valid, proceed, otherwise ignore
                        : 1.3 - added warning and critical events to the get-eventlog commands
                        : 1.2 - Added support for multiple event logs other than the standard 3
                        : 1.1 - Added support for couting events and last occured
                        : 1.0 - Created script for with basic event log screening
    Wish list           : Schedule each scan as a job to run all servers simultaneously
                        : Write to the event log or alert the administrator that jobs are done
                        : Email a link to the files or the files themselves in emails
    Rights Required     : Local admin on server
    Sched Task Req'd    : No
    Exchange Version    : N/A
    Author              : Damian Scoles
    Email/Blog/Twitter  : https://justaucguy.wordpress.com/
    Disclaimer          : You are on your own.  This was not written by, support by, or endorsed by Microsoft.
 
.LINK  
[TBD]
 
.EXAMPLE
                .\EventLogs-1.6.ps1
 
.INPUTS
                None. You cannot pipe objects to this script.
#>
 
$servers = @()
$servers = $null
$allinfo = @()
cls
write-host "Where do you want to store the generated Event Log Reports? [c:\temp\] " -foregroundcolor cyan -nonewline
$path2 = read-host
write-host " "
write-host "*******************************************" -foregroundcolor cyan
write-host "* Choose an option for Event Log Scanning *" -foregroundcolor cyan
write-host "*                                         *" -foregroundcolor cyan
write-host "* (1) Query for all Domain Controllers    *" -foregroundcolor cyan
write-host "* (2) Query for all Exchange Servers      *" -foregroundcolor cyan
write-host "* (3) manually enter server names         *" -foregroundcolor cyan
write-host "* (4) use a CSV File                      *" -foregroundcolor cyan
write-host "*******************************************" -foregroundcolor cyan
write-host " "
write-host "Choose 1-4: " -nonewline
$choice = read-host
write-host " "
 
# Look for Domain Controllers in AD
if ($choice -eq "1") {
                import-module activedirectory
                $servers2 = Get-ADDomainController -filter *
    write-host " "
    write-host "Here is the list of Domain Controllers to be checked:" -foregroundcolor yellow
    write-host " "
                foreach ($line in $servers2) {
                                $servers += ,@($line.name)
                }
    $servers
}
 
# Look for Exchange Servers in AD
if ($choice -eq "2") {
    $servers2 = Get-ADComputer -filter *
    write-host " "
    write-host "Here is the list of Exchange Servers to be checked:" -foregroundcolor cyan
    write-host " "
    foreach ($line in $servers2) {
                   $memberof = (Get-ADComputer $line |Get-ADObject -Properties memberof).memberof
                   if ($memberof -match "Exchange Install Domain Servers") {
                                   $name = [string]$line.name
                       $servers += ,@($name)
                   }
    }
    $servers
}
 
# Manually enter server names
if ($choice -eq "3") {
                write-host " "
                write-host "How many servers will need to have their Event Logs examined? " -foregroundcolor green -nonewline
                $num = read-host
                write-host " "
                $counter = 0
                do {
                    $SNo = $counter+1
                    $server = read-host "What is the name of server $SNo"
                    $server = [string]$server
                    $servers += ,@($server)
                    $counter++
                }
                while ($counter -ne $num) 
}
 
# Use a CSV file to create a server list
if ($choice -eq "4") {
                write-host " "
                write-host "enter the full path for the csv file [i.e. c:\temp\servers.csv " -foregroundcolor cyan -nonewline
                $csvname = read-host
                write-host " "
                $servers = import-csv $csvname
}
 
$script =  {
# foreach ($line in $servers) {
#   $server=$line.name
    $server = $args[0]
    # $path - $args[1]
    # $server = [string]$line
    write-host " "
    write-host "Analyzing event logs for server $server" -ForegroundColor white
    write-host " "
    write-host "PROCESSING" -foregroundcolor yellow
    write-host " "
    # Get a list of Critical Events
    # Application Log
    $info = $null
    
    # Application Log
    $log = "application"
    $logcheck = get-winevent -ListLog $log -ComputerName $server -erroraction SilentlyContinue
    if ($logcheck -ne $null) {
        write-host "Application Log Analysis"  -foregroundcolor cyan
        $events = get-eventlog -computername $server -logname $log | where {($_.entrytype -eq "error") -or ($_.entrytype -eq "warning") -or ($_.entrytype -eq "critical")} | sort-object eventid | group-object eventid
        if ($events -ne $null) {
            $app = foreach ($line2 in $events) {
                $evt = $line2.name
                $Info2 = get-winevent -computername $server -FilterHashtable @{Logname=$log;ID=$evt} -MaxEvents 1 -erroraction silentlycontinue 
                    New-Object PSObject -Property @{
                    LastOccured = ($info2.timecreated).datetime
                    Count = $line2.count
                    Name = $info2.providername
                    Event = $evt
                    # message = $info.message
                }
            }
        }
        if ($app -eq $null) {
            $Application = $app | ConvertTo-Html -Fragment -PreContent '<h2>Application Log</h2><BR>No critical events were found in the Application Log'  | out-string
        } else {
            $Application = $app | ConvertTo-Html -Fragment -PreContent '<h2>Application Log</h2>'  | out-string
        }
    } else {
        $Application = $null
    }
    
    # System Log
    $log = "system"
    $logcheck = get-winevent -ListLog $log -ComputerName $server -erroraction SilentlyContinue
    if ($logcheck -ne $null) {
        write-host "System Log Analysis"  -foregroundcolor cyan
        $events = get-eventlog -computername $server -logname $log | where {($_.entrytype -eq "error") -or ($_.entrytype -eq "warning") -or ($_.entrytype -eq "critical")} | sort-object eventid | group-object eventid
        if ($events -ne $null) {
            $sys = foreach ($line2 in $events) {
                $evt = $line2.name
                $Info2 = get-winevent -computername $server -FilterHashtable @{Logname=$log;ID=$evt} -MaxEvents 1 -erroraction silentlycontinue 
                New-Object PSObject -Property @{
                    LastOccured = ($info2.timecreated).datetime
                    Count = $line2.count
                    Name = $info2.providername
                    Event = $evt
                    # message = $info.message
                }
            }
        }
        if ($sys -eq $null) {
            $System = $sys | ConvertTo-Html -Fragment -PreContent '<h2>System Log</h2><BR>No critical events were found in the Systemn Log' | out-string
        } else {
            $System = $sys  | ConvertTo-Html -Fragment -PreContent '<h2>System Log</h2>' | out-string
        }
    } else {
        $System = $null
    }
    
    # DNS Server
    $log = "DNS Server"
    $logcheck = get-winevent -ListLog $log -ComputerName $server -erroraction SilentlyContinue
    if ($logcheck -ne $null) {
        write-host "DNS Server Log Analysis"  -foregroundcolor cyan
        $events = get-eventlog -computername $server -logname $log | where {($_.entrytype -eq "error") -or ($_.entrytype -eq "warning") -or ($_.entrytype -eq "critical")} | sort-object eventid | group-object eventid
        if ($events -ne $null) {
            $dns = foreach ($line2 in $events) {
                $evt = $line2.name
                $Info2 = get-winevent -computername $server -FilterHashtable @{Logname=$log;ID=$evt} -MaxEvents 1 -erroraction silentlycontinue 
                New-Object PSObject -Property @{
                    LastOccured = ($info2.timecreated).datetime
                    Count = $line2.count
                    Name = $info2.providername
                    Event = $evt
                    # message = $info.message
                }
            }
        }
        if ($dns -eq $null) {
            $DNSlog = $dns | ConvertTo-Html -Fragment -PreContent '<h2>DNS Server Log</h2><BR>No critical events were found in the DNS Server Log' | out-string
        } else {
            $DNSlog = $dns  | ConvertTo-Html -Fragment -PreContent '<h2>DNS Server Log</h2>' | out-string
        }
    } else {
        $DNSlog = $null
    }
    
    # File Replication Service
    $log = "File Replication Service"
    $logcheck = get-winevent -ListLog $log -ComputerName $server -erroraction SilentlyContinue
    if ($logcheck -ne $null) {
        write-host "FRS Log Analysis" -foregroundcolor cyan
        $events = get-eventlog -computername $server -logname $log | where {($_.entrytype -eq "error") -or ($_.entrytype -eq "warning") -or ($_.entrytype -eq "critical")} | sort-object eventid | group-object eventid
        if ($events -ne $null) {
            $frs = foreach ($line2 in $events) {
                $evt = $line2.name
                $Info2 = get-winevent -computername $server -FilterHashtable @{Logname=$log;ID=$evt} -MaxEvents 1 -erroraction silentlycontinue 
                New-Object PSObject -Property @{
                    LastOccured = ($info2.timecreated).datetime
                    Count = $line2.count
                    Name = $info2.providername
                    Event = $evt
                    # message = $info.message
                }
            }
        }
        if ($frs -eq $null) {
            $FRSLog = $frs | ConvertTo-Html -Fragment -PreContent '<h2>File Replication Service Log</h2><BR>No critical events were found in the FRS Log' | out-string
        } else {
            $FRSLog = $frs | ConvertTo-Html -Fragment -PreContent '<h2>File Replication Service Log</h2>' | out-string
        }
    } else {
        $FRSlog = $null
    }
 
    # Security
    $log = "Security"
    $logcheck = get-winevent -ListLog $log -ComputerName $server -erroraction SilentlyContinue
    if ($logcheck -ne $null) {
        write-host "Security Log Analysis" -foregroundcolor cyan
        $events = get-eventlog -computername $server -logname $log | where {($_.entrytype -eq "error") -or ($_.entrytype -eq "warning") -or ($_.entrytype -eq "critical")} | sort-object eventid | group-object eventid
        if ($events -ne $null) {
            $sec = foreach ($line2 in $events) {
                $evt = $line2.name
                $Info2 = get-winevent -computername $server -FilterHashtable @{Logname=$log;ID=$evt} -MaxEvents 1 -erroraction silentlycontinue 
                New-Object PSObject -Property @{
                    LastOccured = ($info2.timecreated).datetime
                    Count = $line2.count
                    Name = $info2.providername
                    Event = $evt
                    # message = $info.message
                }
            }
        }
        if ($sec -eq $null) {
            $Security = $sec | ConvertTo-Html -Fragment -PreContent '<h2>Security Log</h2><BR>No critical events were found in the Security Log' | out-string
        } else {
            $Security = $sec | ConvertTo-Html -Fragment -PreContent '<h2>Security Log</h2>' | out-string
        }
    } else {
        $Security = $null
    }
 
    # Directory Service
    $log = "Directory Service"
    $logcheck = get-winevent -ListLog $log -ComputerName $server -erroraction SilentlyContinue
    if ($logcheck -ne $null) {
        write-host "Directory Service Log Analysis" -foregroundcolor cyan
        $events = get-eventlog -computername $server -logname $log | where {($_.entrytype -eq "error") -or ($_.entrytype -eq "warning") -or ($_.entrytype -eq "critical")} | sort-object eventid | group-object eventid
        if ($events -ne $null) {
            $dir = foreach ($line2 in $events) {
                $evt = $line2.name
                $Info2 = get-winevent -computername $server -FilterHashtable @{Logname=$log;ID=$evt} -MaxEvents 1 -erroraction silentlycontinue 
                New-Object PSObject -Property @{
                    LastOccured = ($info2.timecreated).datetime
                    Count = $line2.count
                    Name = $info2.providername
                    Event = $evt
                    # message = $info.message
                }
            }
        }
        if ($dir -eq $null) {
            $Directory = $dir | ConvertTo-Html -Fragment -PreContent '<h2>Directory Service Log</h2><BR>No critical events were found in the Directory Service Log' | out-string
        } else {
            $Directory = $dir | ConvertTo-Html -Fragment -PreContent '<h2>Directory Service Log</h2>' | out-string
        }
    } else {
        $Directory = $null
    }
 
    $path = "c:\temp\"

    # Format HTML
    $Header = @"
    <style>
    TABLE {border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}
    TH {border-width: 1px;padding: 3px;border-style: solid;border-color: black;background-color: #FF0000;}
    TD {border-width: 1px;padding: 3px;border-style: solid;border-color: black;}
    .odd  { background-color:#ffffff; }
    .even { background-color:#dddddd; }
    </style>
    <title>
    Critical Events Report for $server
    </title>
"@
    $Pre = "Critical Events Report in the Application Log for "+$server
    $dircheck = Get-Item c:\temp -erroraction silentlycontinue
    if ($dircheck -eq $null) {md "c:\temp"}
    $path = "c:\temp\"
    $name = $path+"CriticalEvents-"+$server+".html"

#   OLD CODE
#
#   $allinfo | ConvertTo-HTML -Head $Header -PreContent $Pre | out-file $name
#   $info | ConvertTo-HTML -Head $Header -PreContent $Pre | out-file $name
#   $body = $application+$system+$DNSLog+$FRSLog+$Security+$Directory
#   ConvertTo-HTML -head $header -Body $body -Title "Critical Events in Event Logs" |  Out-File $name
#   ConvertTo-HTML -head $header -Body $application -Title "Critical Events in Event Logs" |  Out-File $name
#   ConvertTo-HTML -head $header -Body "$application $system $DNSLog $FRSLog $Security $Directory" -Title "Critical Events in Event Logs Logs" |  Out-File $name
    
    ConvertTo-HTML -head $header -Body "$application $system $DNSLog $FRSLog $Security $Directory" -Title "Critical Events in Event Logs" |  Out-File $name

    write-host "Generated an Event log report for " -nonewline
    write-host $server -foregroundcolor green

    $info = $null
}
# }

foreach ($line in $servers) {
    $computer = [string]$line
    Invoke-Command -ComputerName $computer -ScriptBlock $script -Args $computer
    $localhost = $env:computername
    $LOCATION = "\\localhost\c$\netrix\evtlogs2"
    write-host "Placing HTML in $LOCATION."
    copy "\\$computer\c$\temp\CriticalEvents-$computer.html" "\\localhost\c$\netrix\evtlogs2"
    del  "\\$computer\c$\temp\CriticalEvents-$computer.html"
}

The run through is exactly the same as my Exchange Health Check – Event Logs. Please review that article to see what it does.

Further Reading

Invoke-Command

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s