Summary: Richard Siddaway talks about using Windows PowerShell to automate scanning event logs across many remote machines. Hey, Scripting Guy! I’ve just starting using Windows PowerShell to administer my systems, and I’ve been asked to test multiple remote machines for a particular event. How can I do that? —CV Hello CV, Honorary Scripting Guy, Richard Siddaway, here today. I’m filling in for my good friend, The Scripting Guy. You’re in luck because today I’ve got just the answer you are looking for as part of my series of posts about how an administrator can start making productive use of Windows PowerShell. Event logs have been a part of Windows for more years than I care to remember. Books and courses regularly recommend that you check the event logs on a regular basis, but how many administrators have the time to perform this task? Not many, I suspect. The problem is that you have a lot of machines to look after, and it takes time to check the logs on each machine. Time you don’t have. This post will explain an easy way to monitor as many logs as you need. Event logs come in two flavors. The standard event logs include:
- System
- Application
- Security
They are joined by feature-specific logs, such as DNS and Active Directory. Windows Vista introduced the Windows Event Log technology, and then came a whole bunch of extra logs—243 of them on a Windows Surface RT alone! Each log is dedicated to a specific part of the operating system, such as:
- Microsoft-Windows-Audio/PlaybackManager
- Microsoft-Windows-PowerShell/Operational
- Microsoft-Windows-Windows Firewall With Advanced Security/Firewall
- Microsoft-Windows-WinRM/Operational
There is a wealth of data in the new logs, but I’m going to concentrate on the classic logs for the rest of this post. Windows PowerShell supplies two cmdlets for reading event logs. Get-EventLog has been with us since Windows PowerShell 1.0. It reads the classic event logs. Get-WinEvent reads both the classic and the new event logs. As an example, I’m going to look at the events that are recorded when the event log service starts. This can be taken as an indicator of a machine start up. So counting the number of events of this type in a given time period gives you an indication of how many times the machine has restarted in that period. Let’s use a week for the sake of argument:
Get-EventLog -LogName System -InstanceId 2147489653 -After (Get-Date).Adddays(-7) The log name is specified as is the InstanceId, which identifies the events you want. The –After parameter is supplied a date—in this case, one week in the past. The big question is, “How did I know that I wanted an InstanceId of 2147489653?”. Simple. I cheated. I dumped the last day’s events and scrolled through them until I found what I needed:
Get-EventLog -LogName System -After (Get-Date).Adddays(-1) One thing to beware of is that in Windows 8 and Windows 8.1 systems, when you select Shut down from the power button, the machine doesn’t actually shut down. It’s in a suspended mode, which is why computers running Windows 8 start much faster than those running Windows 7. You will only get an entry in the log when the machine actually restarts. As a quick aside, and I can never resist dipping into WMI. You can test the last boot up time of machine quite simply:
Get-CimInstance -ClassName Win32_OperatingSystem |
select -ExpandProperty LastBootUpTime I always use the CIM cmdlets if I need to access date information because they convert the date into a readable date for you. Now you know how to get the events. How do you count them? I’d like to introduce one of the most underused cmdlets: Measure-Object.
Get-EventLog -LogName System -InstanceId 2147489653 -After (Get-Date).Adddays(-7) |
Measure-Object
Count : 6
Average :
Sum :
Maximum :
Minimum :
Property : You only want the count of the objects, so change the code to:
Get-EventLog -LogName System -InstanceId 2147489653 -After (Get-Date).Adddays(-7) |
Measure-Object | select -ExpandProperty Count Or if you prefer:
(Get-EventLog -LogName System -InstanceId 2147489653 -After (Get-Date).Adddays(-7) |
Measure-Object).Count Which one should you use? It’s your decision, based on your preferred coding style. I use the first method because it’s more obvious to me what I’m doing when I come back to read the code in a few months. As an alternative, you can use Get-WinEvent:
$filter = @{
LogName = ‘System’
Id = 6005
StartTime = (Get-Date).AddDays(-7)
}
Get-WinEvent -FilterHashtable $filter The difference is that you present the filtering information as a hash table. You can find the valid keys in the filter hash table in the TechNet Library: Get-WinEvent. You can also get to them like this:
get-help get-winevent -online Unfortunately, they don’t come through with the Help file. For completeness, they are:
- LogName=<String[]>
- ProviderName=<String[]>
- Path=<String[]>
- Keywords=<Long[]>
- ID=<Int32[]>
- Level=<Int32[]>
- StartTime=<DateTime>
- EndTime=<DataTime>
- UserID=<SID>
- Data=<String[]>
- *=<String[]>
In this case, the StartTime property matches the –After parameter in Get-EventLog, and you use Id instead of InstanceId. Hang on! You’re shouting that the numbers are different. Yes, they are, because there are two different sets of identification numbers in the event log data that Get-EventLog returns:
- InstanceId – usually a big number
- EventId – usually small number
Get-WinEvent only returns the EventId, but as the Id property. Get-EventLog doesn’t let you filter the EventId property directly. You could do this:
Get-EventLog -LogName System -After (Get-Date).Adddays(-7) |
where EventId -eq 6005 However, it’s not as efficient because when you run this against remote machines, you are filtering the client rather than the server. That leads us to working with remote machines, which is where we get the real value. If Windows PowerShell is about anything, it’s about the ability for admins to administer tens, hundreds, or thousands of machines remotely. Get-WinEvent and Get-EventLog have a ComputerName parameter. Get-WinEvent doesn’t get as much air time as its older sibling, so let’s use that. You might come up with something like this:
$computers = ‘server02’, ‘server03’, ‘win12r2’
$filter = @{
LogName = ‘System’
Id = 6005
StartTime = (Get-Date).AddDays(-7)
}
foreach ($computer in $computers){
New-Object -TypeName PSObject -Property @{
Computer = $computer
Count = Get-WinEvent -FilterHashtable $filter -ComputerName $computer | Measure-Object | select -ExpandProperty Count
}
} Create a list of computers. The filter is the same as before. You use Foreach to iterate the list of computers. An object is created with two properties: the name of the computer and the count of the events that were discovered. You will get output something like this:
Count Computer
—– ——–
6 server02
1 server03
4 win12r2 Well, hopefully not just like this output because it indicates that my machines have restarted up to 6 times in the last week. These results are from my test lab, which I turn off most nights. In a production environment, you would expect 0 or possibly 1 if a restart was necessary for patching. Anything higher than that is a cause for investigation. If you are running Windows Firewall (or any other firewall software) on your servers, you need to ensure that Remote Event Log Management is enabled. As another aside, on computers running Windows Server 2012, this is the way to do it:
Get-NetFirewallRule | where DisplayName -like ‘* Event Log*’ | Enable-NetFirewallRule The three rules that get enabled are:
- Remote Event Log Management (RPC)
- Remote Event Log Management (NP-In)
- Remote Event Log Management (RPC-EPMAP)
The script is a useful tool, but its main drawback is that it tests each individual machine in sequence. Wouldn’t it be nice if you could run the script against multiple machines in parallel? Most of the work is done on the remote machine, so you won’t be overloading your admin workstation. Windows PowerShell 3.0 brought us the ability to work in parallel by using workflows. This task is ideal as a workflow. It hits multiple machines in parallel, and it returns minimal information.
workflow scaneventlogs {
param (
[string[]]$computers
)
$filter = @{
LogName = ‘System’
Id = 6005
StartTime = (Get-Date).AddDays(-7)
}
foreach -parallel ($computer in $computers){
$data = New-Object -TypeName PSObject -Property @{
Computer = $computer
Count = Get-WinEvent -FilterHashtable $filter -ComputerName $computer |
Measure-Object | select -ExpandProperty Count
}
$data
}
}
scaneventlogs -computers ‘server02’, ‘server03’, ‘win12r2’ Normally, you would expect to use –PSComputerName in a workflow, but you need –ComputerName in this instance because it is in the property hash table for New-Object…simply one of the quirks of workflows that keep us entertained. You will get back something like this for each machine:
Count : 4
Computer : win12r2
PSComputerName : localhost
PSSourceJobInstanceId : 9c655254-3e8d-420d-9d98-47ad6b34d24c You can live without the last two properties because they show the machine on which the workflow is running and the Windows PowerShell job instance. You can filter them out by changing the last line to this:
scaneventlogs -computers ‘server02’, ‘server03’, ‘win12r2’ | select Computer, Count You could also save the data to a .csv file or a database if required. For more information about workflows, read my series on the Hey, Scripting Guy! Blog:
PowerShell Workflows You could develop this idea in multiple ways:
- Bring back the date of the event
- Get the list of computers from Active Directory or a text file
- Test of Active Directory log ons that occur out of hours
- Test of the last boot up time (use Get-CimInstance in the workflow)
- Test for Active Directory replication issues
If it’s in the event logs, you can access it this way. CV, that is all there is to using Windows PowerShell to scan event logs on remote machines. Next time I’ll have another idea for you to try as you bring automation into your environment. Bye for now. ~Richard
Richard has been working with Microsoft technologies for 25 years and has spent time in most IT roles, including analyst-programmer, server administrator, support, DBA, and architect. He has been interested in automation techniques (including automating job creation and submission on main frames many years ago). He originally used VBScript and WMI since it became available on Windows NT 4.0. Windows PowerShell caught his interest during the early beta versions in 2005.
Richard blogs extensively about Windows PowerShell and founded the UK Windows PowerShell User Group in 2007. A Windows PowerShell MVP for the last six years, Richard has given numerous talks on Windows PowerShell at various events in the UK, Europe, and the USA. He is frequent speaker for Windows PowerShell User Groups worldwide.
He has published a number of posts about Windows PowerShell, including expert commentaries on the Microsoft Scripting Games, for which he has been a judge for the last four years. He has written two Windows PowerShell books: Windows PowerShell in Practice (Manning 2010) and Windows PowerShell and WMI (Manning 2012). He then collaborated with Don Jones and Jeff Hicks to write Windows PowerShell in Depth, which was published in 2013. Richard is currently writing an introductory book for Active Directory administrators that features Windows PowerShell.
Contact information: Richard Siddaway’s Blog Thanks, Richard. I invite you to follow the Scripting Guys on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace. Ed Wilson, Microsoft Scripting Guy
0 comments