Author Posts

October 27, 2016 at 7:26 am

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!

October 27, 2016 at 1:46 pm

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.

October 27, 2016 at 1:48 pm

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"

October 28, 2016 at 5:37 am

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

October 30, 2016 at 8:55 pm

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

November 3, 2016 at 9:37 pm

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.

November 4, 2016 at 11:11 am

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.

November 4, 2016 at 1:49 pm

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

November 5, 2016 at 5:04 pm

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

November 5, 2016 at 5:13 pm

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"}
}

November 7, 2016 at 7:02 am

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.

November 7, 2016 at 7:27 am

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?

November 8, 2016 at 9:45 pm

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

Format-List -Property name,host

November 8, 2016 at 9:49 pm

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

Generating HTML Reports in PowerShell – Part 1

November 8, 2016 at 10:16 pm

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

November 9, 2016 at 8:40 pm

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.

November 15, 2016 at 4:24 pm

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?

November 15, 2016 at 9:47 pm

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.

November 16, 2016 at 7:08 am

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

November 17, 2016 at 9:14 pm

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?

November 21, 2016 at 6:51 am

most of the VMs are running under Windows Server 2012R2

November 22, 2016 at 8:16 pm

Did you test the invoke-command against a server 2012R2?