So, I'm back to this old Chesnutt of trying to get accurate SRM Reports using PowerCLI.
I've the following setup
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.
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
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
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
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
I'll try, thanks Luc
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
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
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
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
}
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
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
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
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
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
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
Did you already stop/start your PowerShell/PowerCLI session?
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
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