VMware Cloud Community
piercj2
Enthusiast
Enthusiast
Jump to solution

SRM Reporting using PowerCLI

So, I'm back to this old Chesnutt of trying to get accurate SRM Reports using PowerCLI.

I've the following setup

  • Protection Group Name = MyTestPG
  • Protection Group Protection Status in Web Client = Not Configured (this is deliberate for testing, I've attached a .iso)
  • Recovery Plan Name = MyTestRP
  • Recovery Plan Recovery Status in Web Client = Ready

Using PowerCLI, I have the following

Connect-VIServer -Server $vcenter -Credential $cred | Out-Null
Connect-SrmServer -Credential $cred -RemoteCredential $cred -OutVariable srm

$srmApi = $srm.ExtensionData
$protectionGroups = $srmApi.Protection.ListProtectionGroups()

$testPgName = MyTestPG

# To Test 1 PG
$test = $protectionGroups | where {$_.getinfo().name -like $testPgName} | Get-Unique

foreach ($item in $test) {
      $item = $_
      $protectionGroupName = $item.GetInfo().name
     $protectionGroupState = $item.GetProtectionState()
      $recoveryPlanName = $item.ListRecoveryPlans().GetInfo().Name
      $recoveryPlanState = $item.ListRecoveryPlans().GetInfo().state
}

My issue is that at this point, $ProtectionGroupState returns a value of Ready.

This is not what's seen in the Web Client. Why does the Web Client show Not Configured but, the PowerCLI output shows Ready ?

Before continuing with the script,, including VM Names, VM Status, outputting results to either .csv / .html. I'd like to correct this issue first.

0 Kudos
1 Solution

Accepted Solutions
LucD
Leadership
Leadership
Jump to solution

Try replacing $RecoveryPlan.GetInfo().Name with $RecoveryPlan.GetInfo().Name -join ','

If it's an array, the code will join all array elements together, separated with a comma.

At least you'll be able to see why it is an array.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

View solution in original post

0 Kudos
20 Replies
LucD
Leadership
Leadership
Jump to solution

Aren't you looking at the Status of the individual VMs in the Protection Group in the script?

And at the ProtectionGroup Status in the Web Client?


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
piercj2
Enthusiast
Enthusiast
Jump to solution

I didn't think that at this point in the script I was looking at VM specifics.

when I do a Get-Member on the $test variable, I get

TypeName: VMware.VimAutomation.Srm.Views.SrmProtectionGroup

I will want to get VM specifics in the report, but i'm keeping it simple for now until I get this resolved first

0 Kudos
LucD
Leadership
Leadership
Jump to solution

Ok, got it.
I'm afraid I have no explanation for the difference in Status you see.
Can you perhaps try he question in the SRM community?


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

piercj2
Enthusiast
Enthusiast
Jump to solution

I'll try, thanks Luc

0 Kudos
piercj2
Enthusiast
Enthusiast
Jump to solution

HI ,

Moving this back from the SRM Community and into the Power CLI Community.

I've done some more investigations and am almost there.

Thanks to

Generate report of VMs not configured for SRM protection

and Ben Meadowcroft's reply, SRM-Cmdlets/SrmFunctions.ps1

I've come up with the following

$tempcred = Get-Credential 'SrmAccount'

Connect-VIServer -Server $vcenter -Credential $tempcred | Out-Null

Connect-SrmServer -Credential $tempcred -RemoteCredential $tempcred -OutVariable srmConnection

# Clear variables

$protectionGroups = $null

$TestProtectionGroup = $null

$protectionGroup = $null

$protectionGroupInfo = $null

$protectedVms = $null

$associatedVms = $null

$output = $null

$srmApi = $srmConnection.ExtensionData

$protectionGroups = $srmApi.Protection.ListProtectionGroups()

$TestProtectionGroup = $protectionGroups | where {$_.GetInfo().Name -eq "Test Program" }

# Ignore Protection Groups with a status of Shadowing, they only contain VM placeholders and as such, no actionable information

$TestProtectionGroup | where {$_.GetProtectionState() -ne "Shadowing" } | % {

#$protectionGroups | where {$_.GetProtectionState() -ne "Shadowing" } | % {

    $protectionGroup = $_

   

    $protectionGroupInfo = $protectionGroup.GetInfo()

   

    # The following command lists the virtual machines associated with a protection group

    $protectedVms = $protectionGroup.ListProtectedVms()

    # The result of the above call is an array of references to the virtual machines at the vSphere API

    # To populate the data from the vSphere connection, call the UpdateViewData method on each virtual machine view object

    $protectedVms | % { $_.Vm.UpdateViewData() }

   

    # the ListAssociatedVms() method only works with vSphere Replication, it errors for ABR - an alternative is needed, see below

    # Get the VM's in the Protected Datastore, this should include all Associated VM's

    # output the Associated VM's, less the Protected VM's and that should show which VM's need Configuration within a Protection Group

    $associatedVms = $protectionGroup.ListProtectedDatastores() | Get-VIObjectByVIView | Get-VM

    

   <#

         how to output the difference between $associatedVms and $protectedVms

   #>

    # After the data is populated, use it to generate a report

    $protectedVms | %{

        $output = "" | select PgName, ProtectedVmName, NeedsConfig, AssociatedVm

        $output.PgName = $protectionGroupInfo.Name

        $output.ProtectedVmName = $_.Vm.Name

        $output.NeedsConfig = $protectedVms.needsconfiguration | Get-Unique

        $output.AssociatedVm = $associatedVms.name | where {$_ -ne $output.ProtectedVmName }

        $output

    }

} | Format-Table @{Label="Protection group name"; Expression={$_.PgName} }, @{Label="VM Name"; Expression={$_.ProtectedVmName} }, @{Label="Needs Config"; Expression={$_.NeedsConfig} }, @{Label="Associated VM"; Expression={$_.AssociatedVm} }   | ft -AutoSize

My Protection Group "Test Program" contains 5 VM's

- 3 are fully configured within SRM

- 2 are deliberately not configured in SRM for testing purposes  (CD/DVD attached, Network mapping not configured, etc...

Looking forward to your input on this.

My ultimate objective is to have a daily report which highlights issues within SRM

Thanks

Jason

0 Kudos
LucD
Leadership
Leadership
Jump to solution

Would something along these lines do the trick?

$associatedVmNames = $associatedVms.Name

$ProtectedVmNames = $protectedVms.Name

$notProtectVmNames = $associatedVmNames | where{$ProtectedVmNames -notcontains $_}


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
piercj2
Enthusiast
Enthusiast
Jump to solution

Thanks Luc,

That was a great help.

I had an issue publishing the results by focusing on the "protected VM's" first and then adding the "unprotected VM's" to the array.

I changed direction, focusing on all the VM's in a Protection Group, as opposed to focusing on the "Protected VM's"

Below is what I came up with. It's working but I can't figure out how to include information for the $protectedVms.needsconfiguration property in the output

Also. if a "Protected VM" does need configuration, I only would like to see a single "True" result displayed

$tempcred = Get-Credential 'svc_srmhop@corp.emc.com'

Connect-VIServer -Server $vcenter -Credential $tempcred | Out-Null

Connect-SrmServer -Credential $tempcred -RemoteCredential $tempcred -OutVariable srmConnection

$srmApi = $srmConnection.ExtensionData

$protectionGroups = $srmApi.Protection.ListProtectionGroups()

# To run script on an individual Protection Group, uncomment two lines below (comment out line 33 if testing)

$TestProtectionGroup = $protectionGroups | where {$_.GetInfo().Name -eq "Test Program" }

$TestProtectionGroup | where {$_.GetProtectionState() -ne "Shadowing" } | % {

# To run script on all Protection Groups, uncomment below line

#$protectionGroups | where {$_.GetProtectionState() -ne "Shadowing" } | % {

    $protectionGroup = $_

    $protectionGroupInfo = $protectionGroup.GetInfo()

   

    # The following command lists the virtual machines protected by a Protection Group

    $protectedVms = $protectionGroup.ListProtectedVms()

    # The result of the above call is an array of references to the virtual machines at the vSphere API

    # To populate the data from the vSphere connection, call the UpdateViewData method on each virtual machine view object

    $protectedVms | % { $_.Vm.UpdateViewData() }

    $protectedVmNames = $protectedVms.vm.name | Sort-Object

   

    # To get all VM's in the Protected Datastore, this should be all Associated VM's

    $associatedVms = $protectionGroup.ListProtectedDatastores() | Get-VIObjectByVIView | Get-VM

    $associatedVmNames = $associatedVms.name | Sort-Object

   # re-order the output to place focus on Associated VM's (not protected VM's)

   # for each Associated VM, see if there is a Protection Status property

   # if no Protection Status Property, control output with error handling

  

   $srmDailyReport=@()

   foreach ($vm in $associatedVms) {

       $GeneralProp=[ordered]@{

           'Prod Cluster'=$vm.VMHost.Parent.Name

           'Portgroup Name' = $protectionGroupInfo.Name

           'Associated Vm Name' = $vm.name

       }

     #  $VmNeedsSrmConfig = $protectedVms | where {$protectedVms.name -eq $vm.Name}

     # $protectedVms.needsconfiguration property

     # output only if result = "True" (using error handling to keep output clean)

       $view = $vm | Get-View

            $GeneralProp.Add("Tools Recommendation", $view.Guest.ToolsVersionStatus)

       $srmDailyReport += New-Object -TypeName psobject -Property $GeneralProp

  

   }

   # Create the .csv file

    $srmDailyReport |

        Sort-Object -Property 'Associated Vm Name' |

        Export-Csv $outputfile -NoTypeInformation

    Invoke-Item $outputfile

}

0 Kudos
LucD
Leadership
Leadership
Jump to solution

Would something like this do the trick?

$VmNeedsSrmConfig = $protectedVms | where {$protectedVms.name -eq $vm.Name}

if($VmNeedsSrmConfig.needsconfiguration){

    $GeneralProp.Add('NeedsConfig','True')

}

else{

    $GeneralProp.Add('NeedsConfig','')

}


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
piercj2
Enthusiast
Enthusiast
Jump to solution

Hi Luc,

That worked perfectly and, I thought we were finished until I did a bit more testing.

If a Recovery Plan has been completed but, the Reprotect has not yet been completed, $associatedVms = $protectionGroup.ListProtectedDatastores() | Get-VIObjectByVIView | Get-VM contains no data.

As this variable is the source for populating the array, no data is entered for this Protection group.

In the below, I've added an if statement to say "Reprotect needed" if $protectionGroup.GetProtectionState() -eq "Recovered".

I've tried placing this in different parts of the script and using various methods to break out to the next Protection Group but, my efforts are failing.

would you mind taking one more look ?

$srmApi = $srmConnection.ExtensionData
$protectionGroups = $srmApi.Protection.ListProtectionGroups()

# To run script on an individual Protection Group, uncomment two lines below (comment out line 33 if testing)
$TestProtectionGroup = $protectionGroups | where {$_.GetInfo().Name -eq "Tes Program" }
$TestProtectionGroup | where {$_.GetProtectionState() -ne "Shadowing" } | % {

# To run script on all Protection Groups, uncomment below line
#$protectionGroups | where {$_.GetProtectionState() -ne "Shadowing" } | % {
    $protectionGroup = $_
    $protectionGroupInfo = $protectionGroup.GetInfo()
    $RecoveryPlan = $protectionGroup.ListRecoveryPlans()
    $srmDailyReport=@()

   
    # The following command lists the virtual machines protected by a Protection Group
    $protectedVms = $protectionGroup.ListProtectedVms()
    # The result of the above call is an array of references to the virtual machines at the vSphere API
    # To populate the data from the vSphere connection, call the UpdateViewData method on each virtual machine view object
    $protectedVms | % { $_.Vm.UpdateViewData() }
       
   
    foreach ($vm in $associatedVms) {
        $GeneralProp=[ordered]@{
           'Production Cluster'=$vm.VMHost.Parent.Name
           'Protection Group' = $protectionGroupInfo.Name
        }
      
    $GeneralProp.Add('Recovery Plan Name' , $RecoveryPlan.GetInfo().Name)   
          
    <#
    # If Protection group status -eq "Recovered", update "SRM Needs Config" field and break out to the next Protection group
    if ($protectionGroup.GetProtectionState() -eq "Recovered") {
        $GeneralProp.Add('Needs SRM Config','Recovery Needed')
        }
        else{
             $GeneralProp.Add('Needs SRM Config','')
        }

    # If Protection group status -ne "Recovered", continue quth the below
    #>

    # To get all VM's in the Protected Datastore, this should be all Associated VM's
    $associatedVms = $protectionGroup.ListProtectedDatastores() | Get-VIObjectByVIView | Get-VM
   

       $VmNeedsSrmConfig = $protectedVms | where {$protectedVms.vm.name -eq $vm.Name}
       if ($VmNeedsSrmConfig.needsconfiguration){
             $GeneralProp.Add('Needs SRM Config','')
       }
       else{
             $GeneralProp.Add('Needs SRM Config','True')
       }

       $GeneralProp.Add('Vm Name' , $vm.name)
       $GeneralProp.Add('Guest O/S' , $vm.GuestId)
      

       $view = $vm | Get-View
            $vmxfile = $view.Config.Files.VmPathName
            $vmxLocation = $vmxfile.split(" ")[0].TrimStart("[").TrimEnd("]")
            $GeneralProp.Add(".vmx Location", $vmxLocation)
            $GeneralProp.Add("Tools Recommendation", $view.Guest.ToolsVersionStatus)
       
      

       $srmDailyReport += New-Object -TypeName psobject -Property $GeneralProp
  
   }

   # Create the .csv file
    $srmDailyReport |
        Sort-Object -Property 'Vm Name' |
        Export-Csv $outputfile -NoTypeInformation

   
} Invoke-Item $outputfile

0 Kudos
LucD
Leadership
Leadership
Jump to solution

Would something like this work?

$srmApi = $srmConnection.ExtensionData

$protectionGroups = $srmApi.Protection.ListProtectionGroups()

# To run script on an individual Protection Group, uncomment two lines below (comment out line 33 if testing)

$TestProtectionGroup = $protectionGroups | where {$_.GetInfo().Name -eq "Tes Program" }

foreach($protectionGroup in ($protectionGroups | where {$_.GetProtectionState() -ne "Shadowing" })){

    if ($protectionGroup.GetProtectionState() -eq "Recovered") {

        $GeneralProp.Add('Needs SRM Config','Recovery Needed')

    }

    else{

        $GeneralProp.Add('Needs SRM Config','')

        $protectionGroupInfo = $protectionGroup.GetInfo()

        $RecoveryPlan = $protectionGroup.ListRecoveryPlans()

        $srmDailyReport=@()

       

        # The following command lists the virtual machines protected by a Protection Group

        $protectedVms = $protectionGroup.ListProtectedVms()

        # The result of the above call is an array of references to the virtual machines at the vSphere API

        # To populate the data from the vSphere connection, call the UpdateViewData method on each virtual machine view object

        $protectedVms | % { $_.Vm.UpdateViewData() }

           

       

        foreach ($vm in $associatedVms) {

            $GeneralProp=[ordered]@{

               'Production Cluster'=$vm.VMHost.Parent.Name

               'Protection Group' = $protectionGroupInfo.Name

            }

          

        $GeneralProp.Add('Recovery Plan Name' , $RecoveryPlan.GetInfo().Name)   

              

        # To get all VM's in the Protected Datastore, this should be all Associated VM's

        $associatedVms = $protectionGroup.ListProtectedDatastores() | Get-VIObjectByVIView | Get-VM

      

        $VmNeedsSrmConfig = $protectedVms | where {$protectedVms.vm.name -eq $vm.Name}

        $GeneralProp.Add('Vm Name' , $vm.name)

        $GeneralProp.Add('Guest O/S' , $vm.GuestId)

      

        $view = $vm | Get-View

        $vmxfile = $view.Config.Files.VmPathName

        $vmxLocation = $vmxfile.split(" ")[0].TrimStart("[").TrimEnd("]")

        $GeneralProp.Add(".vmx Location", $vmxLocation)

        $GeneralProp.Add("Tools Recommendation", $view.Guest.ToolsVersionStatus)

        

      

        $srmDailyReport += New-Object -TypeName psobject -Property $GeneralProp

      

   }

}

$outputfile = '.\report.csv'

# Create the .csv file

$srmDailyReport | Sort-Object -Property 'Vm Name' | Export-Csv $outputfile -NoTypeInformation

Invoke-Item $outputfile


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
piercj2
Enthusiast
Enthusiast
Jump to solution

Hi Luc,

The script is 99.99% there I think.

If I run the script on an individual Protection Group which I know to be in a ready state but with some unconfigured VM's, I get the expected results and no warnings from PowerCLI.

If I run the script against an individual Protection Group which I know to be Recovered but not Reprotected (so not in Ready State), I get the expected results and no warnings from PowerCLI.

If I run the script against all Protection Groups on the SRM Server, for the most part I do get the expected results but, I also get warnings from PowerCLI.

Some of the unexpected results are

  1. Recovery Plan Name is displayed as "System.Object[]"
    I've tested the script individually on the specific Protection Group giving this error and all variables are correct, even $RecoveryPlan.GetInfo().Name gives the correct result. I suspect the output in the array may have something to do with how the Recovery Plan Name is formatted within SRM itself
  2. another Protection Group returns "True" for $VmNeedsSrmConfig.needsconfiguration. I've gone over the Protection Group and Recovery Plan multiple times, I cannot see any issues. I've edited the plans, reset them, etc... but still get an unexpected "True" result. We're using ABR so I've asked the Storage Team to check from their side.

Below is an updated script

$outputfile = (Get-Date -Format yyyy-MMM-dd-HHmm) + ".csv"

# Clear variables

$outputfile = $null

$srmConnection = $null

$srmApi = $null

$protectionGroups = $null

$srmDailyReport = $null

$protectionGroup = $null

$protectionGroupInfo = $null

$RecoveryPlan = $null

$associatedVms = $null

$protectedVms = $null

$vm = $null

$VmNeedsSrmConfig = $null

$view = $null

$tempcred = Get-Credential 'UserAccount@Acme.Corp.com'

Connect-VIServer -Server $vcenter -Credential $tempcred | Out-Null

Connect-SrmServer -Credential $tempcred -OutVariable srmConnection

$srmApi = $srmConnection.ExtensionData

$protectionGroups = $srmApi.Protection.ListProtectionGroups()

$srmDailyReport=@()

# To run script on an individual Protection Group, uncomment two lines below (comment out line 32 if testing)

#$TestProtectionGroup = $protectionGroups | where {$_.GetInfo().Name -eq "AMZ Amazonite" }

#$TestProtectionGroup | where {$_.GetProtectionState() -ne "Shadowing" } | % {

# To run script on all Protection Groups per individual SRM Server

$protectionGroups | where {$_.GetProtectionState() -ne "Shadowing" } | % {

       

    $protectionGroup = $_

    $protectionGroupInfo = $protectionGroup.GetInfo()

    $RecoveryPlan = $protectionGroup.ListRecoveryPlans()

               

    if ($protectionGroup.GetProtectionState() -eq "Recovered") {

        $GeneralProp = [ordered]@{

            'Production Cluster'=""

            'Protection Group' = $protectionGroupInfo.Name

            'Recovery Plan Name' = $RecoveryPlan.GetInfo().Name

            'Needs SRM Config' = 'Reprotect Needed'

        }

        $srmDailyReport += New-Object -TypeName psobject -Property $GeneralProp

    }

    else{   

        # To get all VM's in the Protected Datastore, this should be all VM's associated with Protection Group

        $associatedVms = $protectionGroup.ListProtectedDatastores() | Get-VIObjectByVIView | Get-VM

        # The following command lists the virtual machines protected by a Protection Group

        $protectedVms = $protectionGroup.ListProtectedVms()

        # The result of the above call is an array of references to the virtual machines at the vSphere API

        # To populate the data from the vSphere connection, call the UpdateViewData method on each virtual machine view object

        $protectedVms | % { $_.Vm.UpdateViewData() }

                           

        foreach ($vm in $associatedVms) {

            $GeneralProp = [ordered]@{}

            $GeneralProp.Add('Production Cluster',$vm.VMHost.Parent.Name)

            $GeneralProp.Add('Protection Group',$protectionGroupInfo.Name)

            $GeneralProp.Add('Recovery Plan Name' , $RecoveryPlan.GetInfo().Name)   

             

            $VmNeedsSrmConfig = $protectedVms | where {$protectedVms.vm.name -eq $vm.Name}

            if ($VmNeedsSrmConfig.needsconfiguration){

                $GeneralProp.Add('Needs SRM Config','')

            }

            else{

                $GeneralProp.Add('Needs SRM Config','True')

            }

            $GeneralProp.Add('Vm Name' , $vm.name)

            $GeneralProp.Add('Guest O/S' , $vm.GuestId)

      

            $view = $vm | Get-View

                $vmxfile = $view.Config.Files.VmPathName

                $vmxLocation = $vmxfile.split(" ")[0].TrimStart("[").TrimEnd("]")

                $GeneralProp.Add(".vmx Location", $vmxLocation)

                $GeneralProp.Add("Tools Recommendation", $view.Guest.ToolsVersionStatus)

              

            $srmDailyReport += New-Object -TypeName psobject -Property $GeneralProp

  

        } # close "foreach ($vm in $associatedVms)"

    } # close else loop

    # Create the .csv file

    $srmDailyReport |

        Sort-Object -Property 'Protection Group', 'Vm Name' |

        Export-Csv $outputfile -NoTypeInformation

   

} # close "$protectionGroups | where {$_.GetProtectionState() -ne "Shadowing" }"

Invoke-Item $outputfile

0 Kudos
LucD
Leadership
Leadership
Jump to solution

Try replacing $RecoveryPlan.GetInfo().Name with $RecoveryPlan.GetInfo().Name -join ','

If it's an array, the code will join all array elements together, separated with a comma.

At least you'll be able to see why it is an array.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
PugazhendhiK
Contributor
Contributor
Jump to solution

issue
0 Kudos
PugazhendhiK
Contributor
Contributor
Jump to solution

am getting error while executing the above script. I have attached the screenshot
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Did you already stop/start your PowerShell/PowerCLI session?


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
PugazhendhiK
Contributor
Contributor
Jump to solution

issue
0 Kudos
PugazhendhiK
Contributor
Contributor
Jump to solution

tried restarting the powercli and powershell but still it showing same error.
0 Kudos
PugazhendhiK
Contributor
Contributor
Jump to solution

Code am trying with.
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Looks like you might have an SRM Datastore Protection group for a datastore that doesn't exist (anymore).


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos