Save to My DOJO
Table of contents
There is a lot that can happen to your Hyper-V server and fortunately most of these events, both good and bad, are recorded in the Windows event log. But monitoring these events can be a tedious process, especially if you rely on manual processes. This is a perfect situation where automation via Windows PowerShell can make your life much easier and help keep an eye on the health of your Hyper-V servers.
In PowerShell there are two cmdlets you can use. Get-Eventlog will query the classic event logs like System, Security and Application. I’m not going to take the time to explain everything about the cmdlet since you can (and should) read full cmdlet help and examples. The System event log is a great thing to check for operating system and other general issues.
get-eventlog system -computer chi-hvr2.globomantics.local -EntryType error,warning -Newest 10 | out-gridview
This command will get the 10 most recent errors and warnings from the System eventlog on Hyper-V server CHI-HVR2 and display the results in Out-Gridview.
Or you can search by Source if you want to limit your query to say Hyper-V related entries.
get-eventlog system -computer chi-hvr2.globomantics.local -source *Hyper-V* -after "10/1/2013" | out-gridview
With this command I’ve retrieved any Hyper-V related entry since October 1, 2013.
I’m piping to Out-GridView to make it easier to read, sort or filter but you can do whatever you want with the output. I’ll show you one such idea a bit later.
In addition to the System event log, you might also find Hyper-V related errors in the Application event log. Any entries should belong to one of these sources:
- vmicheartbeat
- vmickvpexchange
- vmicrdv
- vmicshutdown
- vmictimesync
- vmicvss
Once you know this, you can modify your Get-Eventlog command to search the Application event log for any entries connected to these sources using a wildcard.
Get-EventLog -LogName Application -Source vmic* -Newest 50 -ComputerName chi-hvr2.globomantics.local
By the way, if you are using a Hyper-V backup product like Altaro’s, you might also want to search for those sources as well. For Altaro, it is probably easiest to use Altaro* as a value for –Source. Fortunately for me I don’t have any entries so I can’t show you any results but I think you get the idea. What can you do with this? How about sending yourself an email every morning of the most recent errors and warnings on your Hyper-V servers?
Here’s a script that I use.
#requires -version 2.0 #hyper-v servers to search $computers= "serenity","chi-hvr2.globomantics.local" #get entries since yesterday at midnight $Last24 = (Get-Date).AddHours(-24).Date #the types of events to get $types = "error","warning" #the event log properties to display $eventProperties= "TimeGenerated","EventID","EntryType","Source","Message","UserName","MachineName" #hashtable of parameters for Get-Eventlog $eventLogParams=@{ Computername= $computers EntryType= $types After= $Last24 LogName="System" Source= "*Hyper-V*" } #check System #add the log name to each entry $logs = Get-EventLog @eventLogParams | Select-Object -Property $eventProperties | Add-Member -membertype NoteProperty -Name "EventLog" -Value "System" -PassThru #check Application $eventLogParams.Logname="Application" $eventLogParams.Source="vmic*" $logs+= Get-EventLog @eventLogParams | Select-Object -Property $eventProperties | Add-Member -membertype NoteProperty -Name "EventLog" -Value "Application" -PassThru if ($logs) { <# only send a mail if event logs were found hashtable of parameters for Send-MailMessage I already have a global variable for the SMPTServer required for Send-MailMessage #> $mailParams=@{ To= [email protected] From= [email protected] Subject= "Hyper-V System Events" Body= ($logs | Out-String) } Send-MailMessage @mailParams }
Be sure to read help on Send-MailMessage to see how to use it. I already have a global variable for the mail server. The script gets all the Hyper-V related errors and warnings from the System and Application logs on the 2 systems in my network running Hyper-V. It only gets events that have been recorded since midnight of the previous day. If I run this first thing in the morning, in essence I get a report in my inbox of all errors and warnings from the last day or so.
This script should run on any system running PowerShell 2.0. But if you are running PowerShell 3.0 you can take things a step further and setup a PowerShell scheduled job.
$trigger = New-JobTrigger -Daily -At 7:00AM $options = New-ScheduledJobOption -RequireNetwork -RunElevated -WakeToRun #parameters for Register-ScheduledJob $params=@{ FilePath= "C:scriptsSend-HyperVEventLogs.ps1" Name= "Daily Hyper-V Event mail" Trigger= $trigger ScheduledJobOption= $options MaxResultCount= 7 } Register-ScheduledJob @params
When running as a scheduled job you will most likely have to explicitly define the SMTPServer for Send-MailMessage. But now, every morning I’ll have an email waiting for me with the latest Hyper-V issues that I need to attend to.
There is another part to the Hyper-V event log story. Windows includes a large number of diagnostic and operational logs. Many of them are created based on server roles or features. You can see them in the Event Viewer. Navigate to Applications and Services LogsMicrosoftWindows. If you scroll down you should see some Hyper-V related entries.
When you expand one of the folders you’ll see that many of them have Admin and Operational logs.
These logs too will have errors and warnings. So let’s use PowerShell to search these logs as well. To that we’ll need to use a different cmdlet, Get-WinEvent. The Get-EventLog cmdlet only works with the legacy logs like System. First, let’s see what logs exist.
Get-WinEvent -Listlog "*hyper-v*" -ComputerName chi-hvr2.globomantics.local
The figure shows all of the logs that we could also see in the event viewer.
While you could query each log individually, we can easily search with wildcards and a hash table of filtering values. First, I want to search for any error that has happened in the last 30 days, so I’ll define a variable for that date.
$days=30 $start = (Get-Date).AddDays(-$days)
Next, I’ll define a hashtable of event log entry properties to filter on.
$filter= @{ LogName= "Microsoft-Windows-Hyper-V*" Level=2 StartTime= $start }
Level 2 indicates an error. A warning would be Level 3. Now I’m ready to search.
Get-WinEvent -filterHashTable $filter -ComputerName chi-hvr2.globomantics.local
You can see my results here.
These logs are formatted as XML files so searching them is super speedy. If there were no records found, PowerShell will throw an exception which you can safely ignore. On my server, I had to go back 30 days to find some errors to display. Because this is something you will presumably want to do frequently, allow me to turn this into a PowerShell function.
#requires -version 3.0 Function Get-HVEventLog { <# .Synopsis Get errors and warnings from Hyper-V Operational logs. .Description This command will search a specified server for all Hyper-V related Windows Operational logs and get all errors that have been recorded in the specified number of days. By default, the command will search for errors and warnings that have been recently recorded within the last 30 days. The command also uses Get-WSManInstance to resolve the SID to an actual accountname. .Example PS C:> Get-HVEventLog -Days 30 -computername CHI-HVR2.Globomantics.local | Select LogName,TimeCreated,LevelDisplayname,ID,Message,Username | Out-Gridview -title "Events" Get all errors and warnings within the last 3 days on server CHI-HVR2 and display with Out-Gridview. .Notes Last Updated: October 21, 2013 Version : 1.0 Learn more: PowerShell in Depth: An Administrator's Guide (http://www.manning.com/jones2/) PowerShell Deep Dives (http://manning.com/hicks/) Learn PowerShell 3 in a Month of Lunches (http://manning.com/jones3/) Learn PowerShell Toolmaking in a Month of Lunches (http://manning.com/jones4/) PowerShell and WMI (http://www.manning.com/siddaway2/) **************************************************************** * DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED * * THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK. IF * * YOU DO NOT UNDERSTAND WHAT THIS SCRIPT DOES OR HOW IT WORKS, * * DO NOT USE IT OUTSIDE OF A SECURE, TEST SETTING. * **************************************************************** .Link Get-WinEvent Get-Eventlog .Inputs [String] .Outputs [System.Diagnostics.Eventing.Reader.EventLogRecord] #> [cmdletbinding()] Param( [Parameter(Position=0,HelpMessage="Enter the name of a Hyper-V host")] [ValidateNotNullorEmpty()] [Alias("CN","PSComputername")] [string]$Computername=$env:COMPUTERNAME, [ValidateScript({$_ -ge 1})] [int]$Days=30, [Alias("RunAs")] [object]$Credential ) Write-Verbose "Starting $($MyInvocation.MyCommand)" Write-Verbose "Querying Hyper-V logs on $($computername.ToUpper())" #get-credential if $credential is a string if ($credential -is [string]) { $myCredential = Get-Credential -UserName $Credential -Message "Enter the password" } else { $myCredential = $Credential } #define a hash table of parameters to splat to Get-WinEvent $paramHash=@{ ErrorAction="Stop" ErrorVariable="MyErr" Computername=$Computername } if ($mycredential.username) { Write-Verbose "Adding a credential for $($myCredential.username)" $ParamHash.Add("Credential",$myCredential) } #calculate the cutoff date $start = (Get-Date).AddDays(-$days) Write-Verbose "Getting errors since $start" #construct a hash table for the -FilterHashTable parameter in Get-WinEvent $filter= @{ Logname= "Microsoft-Windows-Hyper-V*" Level=2,3 StartTime= $start } #add it to the parameter hash table $paramHash.Add("FilterHashTable",$filter) #search logs for errors and warnings Try { #add a property for each entry that translates the SID into #the account name #hash table of parameters for Get-WSManInstance $script:newHash=@{ ResourceURI="wmicimv2/win32_SID" SelectorSet=$null Computername=$Computername ErrorAction="Stop" ErrorVariable="myErr" } if ($MyCredential.Username) { $script:newHash.Add("Credential",$MyCredential) } Write-Verbose ($script:newHash | Out-String) Get-WinEvent @paramHash | Add-Member -MemberType ScriptProperty -Name Username -Value { Try { #resolve the SID $script:newHash.SelectorSet=@{SID="$($this.userID)"} $resolved = Get-WSManInstance @script:newhash } Catch { Write-Warning $myerr.ErrorRecord } if ($resolved.accountname) { #write the resolved name to the pipeline "$($Resolved.ReferencedDomainName)$($Resolved.Accountname)" } else { #re-use the SID $this.userID } } -PassThru } Catch { Write-Warning $MyErr.errorRecord } #All done here Write-Verbose "Ending $($MyInvocation.MyCommand)" } #end function
Obviously this is much more complicated than the simple command I started with. When I wrote this I had two requirements. First, I wanted to be able to use alternate credentials and I wanted to resolve the user SID that is in the event log entry. Instead of seeing S-1-5-21-2552845031-2197025230-307725880-1129 I wanted you to see GLOBOMANTICSJeff. Resolving the SID, with the option of using alternate credentials, meant that I needed to run the Get-WinEvent query using a remoting session with Invoke-Command. So yes, it looks like a lot, but this is all it takes to use it:
Get-HVEventLog -computername CHI-HVR2.Globomantics.local | Select LogName,TimeCreated,LevelDisplayname,ID,Message,Username | Out-Gridview -title "Events"
The figure shows the recent errors and warnings using the default of 30 days.
I like using Out-Gridview but you can do whatever you want with the event log information, which is the entire point. If you don’t know what is happening with your Hyper-V servers, you can’t take action to resolve immediate problems, or prevent minor issues from becoming full-blown crises. PowerShell makes it much easier to keep tabs on what is happening and an informed IT Pro is a successful IT Pro.
Not a DOJO Member yet?
Join thousands of other IT pros and receive a weekly roundup email with the latest content & updates!
14 thoughts on "Monitoring Hyper-V Operational and Admin Event Logs with PowerShell"
Hi Jeffery,
Great article thanks, having fun playing around with this, very useful.
I’m not a powershell guru in the slightest so was wondering if you could provide a bit more info on how you run the script to create your function.
Is it just a matter of saving the script as a ps1 file and then running it?
Or is there more involved to it.
Cheers
Matt
Thank you.
Functions have to live in script files which are .ps1 files. Your question is really about the concept of scope, which I can’t get into here. You can learn a bit more by looking at the help topic about_scopes. That said, to run a function, put it in a script file. Then you need to have an execution policy that permits running scripts. Assuming you have that, then you can “dot source” your function.
PS C:Scripts> . .myscript.ps1
In PowerShell you need to specify the full path to the script. The first period is the dot in dot-sourcing. Then there is a space and then the path to the script file. Now I can run the function from the prompt.
PS C:Scripts> get-mysomething -name foo
You can have as many functions in a single script file. Eventually, you can learn about modules which eliminates the need to dot source. If you are new to scripting, I’d suggest grabbing a copy of Learn PowerShell Toolmaking in a Month of Lunches.
Good luck.
Hi Jeffery,
Great article thanks, having fun playing around with this, very useful.
I’m not a powershell guru in the slightest so was wondering if you could provide a bit more info on how you run the script to create your function.
Is it just a matter of saving the script as a ps1 file and then running it?
Or is there more involved to it.
Cheers
Matt
Thank you.
Functions have to live in script files which are .ps1 files. Your question is really about the concept of scope, which I can’t get into here. You can learn a bit more by looking at the help topic about_scopes. That said, to run a function, put it in a script file. Then you need to have an execution policy that permits running scripts. Assuming you have that, then you can “dot source” your function.
PS C:Scripts> . .myscript.ps1
In PowerShell you need to specify the full path to the script. The first period is the dot in dot-sourcing. Then there is a space and then the path to the script file. Now I can run the function from the prompt.
PS C:Scripts> get-mysomething -name foo
You can have as many functions in a single script file. Eventually, you can learn about modules which eliminates the need to dot source. If you are new to scripting, I’d suggest grabbing a copy of Learn PowerShell Toolmaking in a Month of Lunches.
Good luck.
Wonderful! Just what I was looking for, thanks a lot 🙂 It’s easy to get lost with all the Event-Cmdlets.
Great books you recommend but “the bible” is missing: PowerShell in Action, https://www.manning.com/books/windows-powershell-in-action-second-edition – I love the insights why PowerShell is, feels, works the way it does.
Regards, Markus
Thanks. Bruce’s book is good but not one I would recommend to a beginner IT Pro trying to learn PowerShell. But you are right, if you want to know why PowerShell is the way it is, you need an author like Bruce.
Hello Jeffery,
Congratulations on your article.
I have a question.
Where can I find in event log the messages that contains the IP or hostname of who accessed the console of any virtual machine in Hyper-V W2K8 R2?
Thanks a lot!
Régis
I don’t know if that information is captured in the eventlog.