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.

get eventlog system view

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.

Showing only Hyper-V event logs in get-eventlog-system

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= jhicks@jdhitsolutions.com
  From= jhicks@jdhitsolutions.com
  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.

The Hyper-V event viewer

When you expand one of the folders you’ll see that many of them have Admin and Operational logs.

Viewing admin and operational logs in the hyper-v event viewer

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.

all hyper-v logs in 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.

get winevent results

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.

out-gridview recent errors warnings event logs

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.

 

 

Is Your Office 365 Data Secure?

Did you know Microsoft does not back up Office 365 data? Most people assume their emails, contacts and calendar events are saved somewhere but they're not. Secure your Office 365 data today using Altaro Office 365 Backup - the reliable and cost-effective mailbox backup, recovery and backup storage solution for companies and MSPs

Share this post

Not a DOJO Member yet?

Join thousands of other IT pros and receive a weekly roundup email with the latest content & updates!

8 thoughts on "Monitoring Hyper-V Operational and Admin Event Logs with PowerShell"

  • Matt Browne says:

    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.

  • Matt Browne says:

    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.

  • Markus Egger says:

    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.

  • Régis says:

    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

Leave a comment or ask a question

Your email address will not be published. Required fields are marked *

Your email address will not be published. Required fields are marked *

Notify me of follow-up replies via email

Yes, I would like to receive new blog posts by email

What is the color of grass?

Please note: If you’re not already a member on the Dojo Forums you will create a new account and receive an activation email.

Related posts