Hyper-V replica monitor

Hi everyone,

I have the following script to monitor Hyper-V replica on multiple clusters from same domain:

function get_info{
#Add some Styles to the Table
$head = ’
BODY{
font-family:Arial;
font-size: 12px;
background-color:#FFFFFF;
}
TABLE{
border-width: 1px;
border-style: solid;
border-collapse: collapse;
}
TH{
border-width: 1px;
border-color: #0070A8;
background-color: #006699;
color: #FFFFFF;
font-size: 15px;
}
TD{
border-width: 1px;
border-style: solid;
border-color: #0070A8;
color: #00496B;
}

#Get Hyper-V Replica Status
$replica_body = Get-VM –ComputerName (Get-ClusterNode –Cluster pcaxxclus204) | Where-Object { $.Name -like ‘FS’ } | Get-VMReplication |
Select Name, State, Health, PrimaryServer
$replica_body = Get-VM –ComputerName (Get-ClusterNode –Cluster pcxclus203) | Where-Object { $
.Name -like ‘FS’ } | Get-VMReplication|
Select Name, State, Health, PrimaryServer |
ConvertTo-Html -Fragment -PreContent “Hyper-V Replica Status”

The script displays a table with the status of hyper-v replica of servers from clusters containing FS in the name.
My issue is that I am not able to display the status of servers which does not have replica enabled. How can I convert the text into a simple message in table ‘replication is not enabled’
Get-VMReplication : Replication is not enabled for virtual machine with name PCxxFS21.
At C:\Users\a\script2.PS1:53 char:128

  • … ike ‘FS’ } | Get-VMReplication|
  •                ~~~~~~~~~~~~~~~~~
    
    • CategoryInfo : ObjectNotFound: (PCxxFS21:String) [Get-VMReplication], VirtualizationOperationFailedException
    • FullyQualifiedErrorId : ObjectNotFound,Microsoft.HyperV.PowerShell.Commands.GetVMReplicationCommand

Can someone please help me with this?
Thank you a lot!

Well, there’s nothing to “convert,” per se, you’re getting an error. This is a little bit more complex than you might have realized.

You need to start by going through your computers one at a time, in a ForEach loop. That will allow each one to fail, and for you to capture that failure. So you also need to trap that error - see “The Big Book of PowerShell Error Handling” for a quick tutorial, or read about_try_catch_finally in the shell. The basic logic is this:

For each computer you want to query, run your command - with error handling enabled. If the command succeeds, the variable will have your output. If the command fails, you’ll end up in a Catch{} block. You’ll need to construct an object that looks just like successful output, having the same properties, with whatever failure text you want.

I’ll do a simpler example.

Function Get-MyServiceInfo {
 $computers = @('SERVER1','SERVER2') # could also read from a file, AD, or whatever
 ForEach ($computer in $computers) {
  Try {
    $services = Get-Service -computername $computer -ErrorAction Stop | Select-Object -Prop Name,Status
  } Catch { 
    New-Object -Type PSObject -Prop @{'Name'='No services found or failure to connect';'Status'=' '}
  }
 }
}

Get-MyServiceInfo | ConvertTo-HTML

You’ll notice that I’ve factored my code into a function, and then I call that function and pass its results to ConvertTo-HTML. This makes my job a lot easier, as I can just output stuff to the pipeline from my function, rather than having to accumulate it all in an array or something. Each major thing that’s happening - getting the info and converting to HTML - are self-contained in their own commands, this way.

You’ll see an expanded, similar approach in “Creating HTML Reports in PowerShell,” which I heartily recommend. Both are free ebooks - see the “ebooks” menu on this site.

I’ll also suggest “Learn PowerShell Toolmaking in a Month of Lunches,” which is not free. You’re starting to head down a bad path with your coding approach, and it’s probably a good time to get on board with PowerShell’s rather unique way of doing things.

Take a look at this. I tried to answer both of your posts. You should concentrate on generating you PSObject output before exporting it to HTML. This code is NOT tested, but should get you close to what you’re asking for:

function Get-HyperVReplicaStatus {
[CmdletBinding()]
param (
    [string[]]$Cluster
)
begin{}
process {
    $results = foreach ($Node in (Get-ClusterNode –Cluster $Cluster | Where-Object { $_.Name -like '*FS*' })) {
        Write-Verbose "Processing Nodes in Cluster {0}" -f $Cluster
        try {
            $VM = Get-VM –ComputerName $Node.Name
            try {
                $VM | 
                Get-VMReplication -ErrorAction Stop |
                Select Name, 
                       State, 
                       Health, 
                       PrimaryServer, 
                       @{Name="Cluster";Expression={$Cluster}}
            }
            catch {
                #Error occured with Get-VMReplication, so handle it
                #and generate a PSObject with the Name and other props
                #you want to return.  State is Disabled and the Health
                #would be Get-VMReplication : Replication is not enabled for virtual machine with name PCxxFS21.
                $VM |
                Select Name,
                       @{Name="State";Expression={"Disabled"}},
                       @{Name="Health";Expression={$_}}
                       @{Name="PrimaryServer";Expression={$NULL}}
                       @{Name="Cluster";Expression={$Cluster}}
            }
        }
        catch {
            "Unable to get VM {0}. {1}" -f $Node.Name, $_
        }
         
        
    }
}
end{$results}

} #Get-HyperVReplicaStatus


#Add some Styles to the Table
$head = @"
BODY{
    font-family:Arial;
    font-size: 12px;
    background-color:#FFFFFF;
}

TABLE{
    border-width: 1px;
    border-style: solid;
    border-collapse: collapse;
}

TH{
    border-width: 1px;
    border-color: #0070A8;
    background-color: #006699;
    color: #FFFFFF;
    font-size: 15px;
}

TD{
    border-width: 1px;
    border-style: solid;
    border-color: #0070A8;
    color: #00496B;
}
"@

$clusters = "Pxxclus4","Pxxc1","Lfdcl1"
#OR $clusters = Get-Content C:\Clusters.txt
$hyperVStatus = Get-HyperVReplicaStatus -Cluster $clusters
$hyperVStatusHTML = $hyperVStatus | ConvertTo-Html -PreContent "Hyper-V Replica Status"
$hyperVStatusHTML | Out-File "C:\Scripts\hyperVStatus.html"
Invoke-Item "C:\Scripts\hyperVStatus.html"

Thank you for the replies. Going to study harder, the scripts require corrections

Still not working :frowning:
Not able to succeed in display the hyper-v replica in a HTLM format for all VMs which have FS in their name

Maybe you could approach this solution in a different way.
Instead of working with output from a returned error, you could see if a machine is a replica by looking into the properties of the get-vm command. If you look into the error message then Get-VMReplication is expecting to be run against a machine that has replica settings enabled. If you use something like this:

$VMS = Get-VM –ComputerName (Get-ClusterNode –Cluster pcaxxclus204) | Where-Object { $.Name -like ‘FS’ }
$Replica=$VMS|where-object {$
.ReplicationState -ne “disabled”}
$NoReplica=$VMS |where-object {$_.ReplicationState -eq “disabled”}

From there you can work with the variables $Replica and $Noreplica, I did not test the code because I don’t have a cluster at this moment but I think you get the idea.

Thank you for this. I figured it out to display.

$VMS = Get-VM –ComputerName (Get-ClusterNode –Cluster pcaxxclus204) | Where-Object { $.Name -like ‘FS’ }
$NoReplica=$VMS |where-object {$
.ReplicationState -eq “disabled”} | Out-GridView

How can I get the results of the GridView into an email format?
I want to get the output grid sent in an email.

How can I get the result for pcaxxcl* clusters in the same format?
$VMS = Get-VM –ComputerName (Get-ClusterNode –Cluster pcaxxcl*) | Where-Object { $.Name -like ‘FS’ }
$Replica=$VMS|where-object {$
.ReplicationState -ne “disabled”}
$NoReplica=$VMS |where-object {$_.ReplicationState -eq “disabled”}
fails in an error

using $cluster and putting $cluster=cluser1, cluster2 not working

I would use a Select-Object to select the objects which should be included in the mail.
Then I would use the Send-MailMessage with a e-mail body something like this.
Note, that the Send-MailMessage need more parameters of course like -SmtpServer and -From -To etc.
Of course I have to see the results then in the e-mail to know if I need more formatting.

$NoReplica=$VMS |where-object {$_.ReplicationState -eq “disabled”} |select-object Name,ReplicationHealth,ReplicationMode,ReplicationState

$MessageBody = @"
Put text for mail here and place input from script here: $NoReplica
"@

Send-MailMessage -Body $MessageBody -Subject Hyper-V Replica

I guess that’s because Get-Cluster is not accepting multiple values string.
You could run the command in a for each loop and store your data in a Array and work from there.
Something like this:

$clusters= “cluser1”,“cluster2”

$VMS=@()
Foreach($cluster in $clusters)
{
$VMS += Get-VM –ComputerName (Get-ClusterNode –Cluster $cluster) |
Where-Object { $.Name -like ‘FS’ }
$Replica=$VMS|where-object {$
.ReplicationState -ne “disabled”}
$NoReplica=$VMS |where-object {$_.ReplicationState -eq “disabled”}
}

that’s great. Thank you!
How can it be sent to an email format? How to send the out-gridview display into an html format or in a text? the out-string is not displaying it properly.

Let’s say I get a list with the format
Name Host


t12FS161
lohjFS22
sdsR03FS21

I want under Host to have the HV node name on which the VM resides. How can I get this to display?

If that information is in the object you could use a format-list.

Format-List -Property name,host

Your welkom, have you tried using format-table or if you really want some fancy stuff you could use this cmdlet.

The information is not in the object, not sure how to get the name of the hyper-v node on which the VM resides

Depends which tools you are using, do you only work with Hyper-V or do you also use SCVMM?

You can get it from the registry.

(get-item "HKLM:\SOFTWARE\Microsoft\Virtual Machine\Guest\Parameters").GetValue("HostName")

I think when you use SCVMM you can get it with Get-SCVMHost and then add it to your object.

It seems to be not that easy to do it. Has someone done this already? Getting the list of clusters and finding out the FS which have hyper-v replica enabled/disabled?

I wrote a quick example for you, did you try something like this?

$clusters= "cluser1","cluster2"

$VMS=@()
 Foreach($cluster in $clusters)
 {
 $VMS += Get-VM –ComputerName (Get-ClusterNode –Cluster $cluster) |
 Where-Object { $_.Name -like '*FS*' }
 }
 
 $Replicas=$VMS|where-object {$_.ReplicationState -ne "disabled"}
 $NoReplica=$VMS |where-object {$_.ReplicationState -eq "disabled"}
 

 $VMSReplica=@()
 Foreach($Replica in $Replicas)
 {
 $VMRunsOnMachine =  invoke-command -ComputerName $Replica -ScriptBlock{(get-item "HKLM:\SOFTWARE\Microsoft\Virtual Machine\Guest\Parameters").GetValue("HostName")} 
 $VMSReplica+= $Replica|select *, @{Name="HostVMRunning";Expression={$VMRunsOnMachine}}

 }

 Write-output $VMSReplica

From there on you can work with the variable $VMSreplica.
I can not test it at the moment will run some tests tomorrow and get back to you.

Thank you! The output returns a lot of info. Something like this:
VMName : lDFS47
VMId : caed1244-4132-4bc8-9c50-16e6633c8960
Id : caed1244-4132-4bc8-9c50-16e6633c8960
Name : u0FS47
State : Off
IntegrationServicesState :
OperationalStatus : {Ok}
PrimaryOperationalStatus : Ok
SecondaryOperationalStatus :
StatusDescriptions : {Operating normally}
PrimaryStatusDescription : Operating normally
SecondaryStatusDescription :
Status : Operating normally
Heartbeat :
ReplicationState : WaitingForInitialReplication
ReplicationHealth : Warning
ReplicationMode : Replica
CPUUsage : 0
MemoryAssigned : 0
MemoryDemand : 0
MemoryStatus :
SmartPagingFileInUse : False
Uptime : 00:00:00
IntegrationServicesVersion :
ResourceMeteringEnabled : True
ConfigurationLocation : C:\ClusterStorage\PClus43_FS2\Hyper-V Replica
SnapshotFileLocation : C:\ClusterStorage\PClus43_FS2\Hyper-V Replica
AutomaticStartAction : Nothing
AutomaticStopAction : Save
AutomaticStartDelay : 0
SmartPagingFilePath : C:\ClusterStorage\PClus43_FS2\Hyper-V Replica
NumaAligned :
NumaNodesCount : 1
NumaSocketCount : 1
Key : Microsoft.HyperV.PowerShell.VirtualMachineObjectKey
IsDeleted : False
ComputerName : PCTHVOTH09
Version : 5.0
Notes : #CLUSTER-INVARIANT#:{ed308ee7-67cc-44c6-8271-342f8601c78f}
Generation : 2
Path : C:\ClusterStorage\PClus43_FS2\Hyper-V Replica
CreationTime : 7/25/2016 6:09:15 AM
IsClustered : True
SizeOfSystemFiles : 112730
ParentSnapshotId :
ParentSnapshotName :
MemoryStartup : 12884901888
DynamicMemoryEnabled : False
MemoryMinimum : 536870912
MemoryMaximum : 1099511627776
ProcessorCount : 4
RemoteFxAdapter :
NetworkAdapters : {Network Adapter}
FibreChannelHostBusAdapters : {}
ComPort1 : Microsoft.HyperV.PowerShell.VMComPort
ComPort2 : Microsoft.HyperV.PowerShell.VMComPort
FloppyDrive :
DVDDrives : {}
HardDrives : {Hard Drive on SCSI controller number 0 at location 0, Hard Drive on SCSI controller number 0 at location 6}
VMIntegrationService : {Time Synchronization, Heartbeat, Key-Value Pair Exchange, Shutdown…}
HostVMRunning :
HostVM running is empty field. Let’s see what we can get by editing it

Ok that’s good news so you have almost al your data to work with.
Did you already test just the invoke-command against one of your servers to see if you get anything back?
This one works in my environment.

invoke-command -ComputerName Computername -ScriptBlock{(get-item "HKLM:\SOFTWARE\Microsoft\Virtual Machine\Guest\Parameters").GetValue("HostName")}

On which OS is the VM running?