#Requires -version 2.0 # ----------------------------------------------------------------------------- # Script: Report-Events.ps1 # Author: Jeffery Hicks # http://jdhitsolutions.com/blog # Date: 03/08/2011 # Keywords: Eventlog, html, WinEvent, WMI # Comments: # original version first posted at # http://redmondmag.com/articles/2009/02/01/event-reporting-revisited.aspx # ----------------------------------------------------------------------------- Function New-EventReport { <# .Synopsis Create an HTML report of selected events. .Description This function will connect to one or more machines and query all eventlogs, looking for critical events, errors, warnings and audit failures that have been recorded in the last X number of hours. The default value is 24. The function defines an embedded style sheet to color code the different types of events. The function will use WMI to check the computer's operating system. Systems older than Vista don't support the Get-WinEvent cmdlet, so an older style WMI query will be used. This function now queries security logs on older systems. .Parameter Computername The name of the computer to query. The default is the local computer. .Parameter Hours The number of hours from the current date and time to search. The default is 24. .Parameter Path The filename and path for the HTML report. One file is created for all computers piped into the function. The default is EventLogReport.html in the %TEMP% folder. .Parameter Credential A stored PSCredential for alternate authentication .Example PS C:\> New-EventReport Directory: C:\Users\Jeff\AppData\Local\Temp Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 9/15/2010 11:33 AM 52526 EventLogReport.html Create a report for the local machine for the last 24 hours. .Example PS C:> get-content c:\work\computers.txt | New-EventReport -path c:\reports\daily.html -credential $Admin Using a saved PSCredential, this will create a new html report for every computer in computers.txt. .Example PS C:\> $rpt = get-content c:\work\computers.txt | New-EventReport -path c:\reports\daily.html PS C:\> Send-MailMessage -to Admins@Mycompany.com -Subject "Eventlog report" -from "jeff@mycompany.com" -body (get- content $rpt | out-string) -bodyasHTML -SmtpServer mail.mycompany.com Create an eventlog report for all the systems in Computers.txt and then email the report as an HTML message to Admins@mycompany.com. .Notes NAME: New-EventReport VERSION: 2.3 AUTHOR: Jeffery Hicks LASTEDIT: 03/08/2011 Learn more with a copy of Windows PowerShell 2.0: TFM (SAPIEN Press 2010) .Link http://jdhitsolutions.com/blog/2011/03/new-event-report-revised/ .Link Get-Eventlog Get-WinEvent Get-WMIObject .Inputs Strings for computer names .Outputs A file object for the HTML report. #> [cmdletbinding()] Param( [Parameter(Position=0,ValueFromPipeline=$True)] [string[]]$Computername=$env:computername, [Parameter(Position=1)] [int]$Hours=24, [Parameter(Position=2)] [string]$Path="$env:temp\EventLogReport.html", [object]$credential ) BEGIN { Write-Verbose "Starting $($myinvocation.mycommand)" #define some variables for Write-Progress $Activity=$myinvocation.mycommand $Status="Task Setup" $Current="defining variables" Write-Progress -Activity $Activity -Status $status -CurrentOperation $current #delete report file if it exists if ((Get-Item $Path -ea "Silentlycontinue").Exists) { Write-Verbose "Deleting $Path" Remove-Item $Path } #convert credential to a PSCredential if a string was passed. if ( $credential -is [system.management.automation.psCredential]) { Write-Verbose "Using PSCredential for $($credential.username)" } ElseIf ($Credential) { Write-Verbose "Getting PSCredential for $credential" $Credential=Get-Credential $credential } #convert hours to milliseconds for XML query $cutoff=$hours*3600*1000 #get the cutoff date $cutoffDate=(get-date).AddMilliseconds(-$cutoff) Write-Verbose "Finding events after $cutoffDate" #convert $cutoff to a DMTF format. Not necessarily required #but I think it makes the WMI query a little faster for legacy queries $dmtf=[System.Management.ManagementDateTimeConverter]::ToDmtfDateTime($cutoffDate) #this will be the report title $title="Event Log Report: {0} to {1} " -f $cutoffDate,(Get-Date) #define an embedded style sheet $style="" #insert the title into the header $header="$title $style" #create variable to hold all results $all=@() } #end Begin scriptblock PROCESS { foreach ($computer in $computername) { #my error handling traps Trap { if ($_.Exception -match "RPC server is unavailable") { Write-Warning "$computer is not available via RPC." } elseif ($_.Exception -match "access is denied") { Write-Warning "Access denied to $computer." } else { Write-Warning "There was an error with $computer" Write-Warning $_ } Write-Verbose "Continuing" Write-Progress -Activity $activity -Status "Error" } #close Trap #initialize an array $results=@() #test if computer supports Get-WinEvent which requires Vista or later $status="Verifying operating system version for $computer" Write-Verbose $status Write-Progress -Activity $activity -Status $status -CurrentOperation "Check OS" #use credentials if specified if ($os) { Clear-Variable os} if ($credential) { $os=Get-WmiObject -Query "Select Caption from Win32_OperatingSystem" -EnableAllPrivileges -Credential $credential -ComputerName $computer -ErrorAction "Stop" } else { $os=Get-WmiObject -Query "Select Caption from Win32_OperatingSystem" -EnableAllPrivileges -ComputerName $computer -ErrorAction "Stop" } if ($os) { Write-Verbose "Computer is running $($os.caption)" If ($os.caption -match "2000|XP|2003") { #if computer is running an old OS then use Get-WMIObject #define some scriptblocks Write-Verbose "Using Get-Eventlog" $query="Select ComputerName,Message,TimeGenerated,Type,SourceName,EventCode,Logfile from win32_NTLogEvent WHERE (Type='warning' OR Type='error' OR Type='Audit Failure') AND TimeWritten>'$dmtf'" Write-Verbose $query $cmd='Get-WmiObject -ComputerName $computer -query $query -enableAllPrivileges' if ($credential) { $cmd=$cmd + " -credential `$credential" } Write-Verbose $cmd $status="Getting event log data from $computer" Write-Progress -Activity $activity -Status $status -CurrentOperation "Querying logs" $results+=Invoke-Expression $cmd | select @{name="Computername";Expression={($_.ComputerName).ToUpper()}}, Type,@{name="TimeCreated";Expression={$_.ConvertToDateTime($_.TimeGenerated)}}, @{Name="ProviderName";Expression={$_.SourceName}}, @{Name="ID";Expression={$_.EventCode}},Message, @{Name="LogName";Expression={$_.Logfile}} if ($results.count -gt 0) { Write-Verbose "Returned $($results.count) events for $($computer)" $all+=$results } else { Write-Warning "No matching events found for $computer" } } #if legacy OS else { #else use the newer Get-WinEvent #define some scriptblocks $list={Param($computername) get-winevent -listlog * -computername $computername} $listPriv={Param($computername,$credential) get-winevent -listlog * -computername $computername -credential $credential} $status="Getting event log data from $computer" Write-Progress -Activity $activity -Status $status -CurrentOperation "Listing logs" Write-Verbose "Getting Event logs from $computer" if ($credential) { Write-Verbose "Executing $listpriv" $logs=&$listpriv $computer $credential } else { Write-Verbose "Executing $list" $logs=&$list $computer } Write-Verbose "Returned a total of $($logs.count) logs" $logs | where {$_.recordcount -gt 0} | foreach { $log=$_.logname write-verbose "Analyzing $log" if ($log -eq "Security") { Write-Verbose "Security log query" $xmlQuery="" } else { $xmlQuery="" } $cmd='get-winevent -computername $computer -FilterXml $xmlQuery -ErrorAction "SilentlyContinue"' if ($credential) { $cmd=$cmd + " -credential `$credential" } #get matching Event logs Write-Progress -Activity $activity -Status $status -CurrentOperation "Querying event logs" $results+=Invoke-Expression $cmd | Select @{Name="Computername";Expression={$_.Machinename}}, @{Name="Type";Expression={$_.LevelDisplayName}},TimeCreated,ProviderName,Id,Message,LogName } #foreach eventlog if ($results.count -gt 0) { Write-Verbose "Returned $($results.count) events for $($computer)" $all+=$results } else { Write-warning "No matching events found for $computer" } }#else } #if $os else { Write-Verbose "Skipping $computer" } } #foreach computer } #end Process scriptblock END { if ($all.count -gt 0) { $status="Post processing" $footer="
Report generated {0} by {1}\{2}" -f (Get-Date),$env:userdomain,$env:username #convert running results to an HTML file Write-Verbose "Converting to HTML" Write-Progress -Activity $activity -Status $status -CurrentOperation "Converting to HTML" $html = $all | ConvertTo-Html -head $header #parse HTML file and add color highlighting $colorized=@() #define a counter $i=0 foreach ($line in $html) { $i++ Write-Progress -Activity $activity -Status $status -CurrentOperation "Colorizing" -PercentComplete $($i/($html.count)*100) Switch -regex ($line) { "\w+" { Write-Verbose "Colorizing header" $colorized+=$line.Replace("","") } "Error" { Write-Verbose "Colorizing Error" $colorized+=$line.Replace("","") } "Critical" { Write-Verbose "Colorizing Critical" $colorized+=$line.Replace("","") } "Security" { Write-Verbose "Colorizing Audit Failure" $colorized+=$line.Replace("","") } "" { Write-Verbose "Adding footer $($footer)" $colorized+=$line.Replace("",$footer) } Default { $colorized+=$line } } #end Switch } Write-Verbose "Sending colorized output to $($Path)" $colorized | Out-File $Path Write-Progress -Activity $activity -Status "Finished report" -CurrentOperation $path -Complete #write the html file object to the pipeline Get-Item -Path $Path } else { Write-Host "Finished. No results found." -foregroundcolor Magenta } } #end End scriptblock } #function #get-help New-EventReport -full