VMware Cloud Community
sspikent
Enthusiast
Enthusiast

PowerCli and SRM - capturing Errors.

I have a script that works very well auditing for unprotected VMs and then protecting them. However occasionally the SRM protect task fails for various reasons (e.g. CD/DVD drive Mounted).

Runs with:

$protectTask = $targetProtectionGroup.ProtectVms($protectionSpec)

If the task fails, I try to grab RESULT:

$result = $protectTask.GetResult()


but


$result[0].Error.LocalizedMessage


is always empty. In fact $result is always null. Who knows how do I get the error out of $result.?

Reply
0 Kudos
10 Replies
LucD
Leadership
Leadership

Does the error throw an exception?

Is there anything in $error[0] after the error?

Did you try to use a Try-Catch construct?


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

Reply
0 Kudos
sspikent
Enthusiast
Enthusiast

No there is no error exception. In fact I am using Try-Catch construct already.

I only noticed something was up when I checked the PG in SRM and noticed that although the task completed with no exception, some VMs remained unprotected.

    #Protect VM Task
    try {$protectTask = $targetProtectionGroup.ProtectVms($protectionSpec)}
    catch {$vm.Status = "Failed to protect"}
    while(-not $protectTask.IsComplete()) { sleep -Seconds 1 }

$result = $protectTask.GetResult()


Breaking the script at this point and browsing the variable $result in my variable browser shows:

result.JPG

But if I query it directly:

$result[0].Error.LocalizedMessage

Returns null.


$Error[0] has nothing also.


Reply
0 Kudos
sspikent
Enthusiast
Enthusiast

DOH! !!!

Found it.

$result[0] as per variable browser is misleading. I only needed


$result.Error.LocalizedMessage

Sorry for wasting time.

Reply
0 Kudos
LucD
Leadership
Leadership

No problem, glad you found it Smiley Happy


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

Reply
0 Kudos
PugazhendhiK
Contributor
Contributor

sspikent Am searching for a powercli script to audit my SRM environment were it will automatically protect the VM and report if any of them are not protected. It will be great if you could post the Script in this forum. Smiley Happy

Reply
0 Kudos
sspikent
Enthusiast
Enthusiast

HI,

The script was implemented into vRO, though there's no reason it couldn't be run manually. In which case you would probably want the script to either prompt for user credentials or pass them as script parameters.

The secure key file, if you wish to sue this method, is generated using knowledge I pulled from: http://www.adminarsenal.com/admin-arsenal-blog/secure-password-with-powershell-encrypting-credential...

Good luck

<#
    .SYNOPSIS
    Script to audit and protect all VMs in all SRM protection groups. It is written specifically for VRO.

    .DESCRIPTION
    Script connects to given vCenter server using specified credentials.
    It audits all vCenter local Protection Group, respective protected datastores
    and protected and non-protected VMs.
    Once scanned the script identifies VMs that no longer need protection (the source VM no longer exists) and
    new VMs that are not protected and need protection.
    This VM list is sent via email and the script will attempt to (un)protect them.

    Note - The script has been written specifically for use in vRO (the password paramater is in clear text).

    .PARAMETER vCenter
    Enter the vCenter server to query.
    .PARAMETER Username
    Provide a Username with required access to the vCenter server.
    .PARAMETER SecureKey
    Provide the required SecureKey to permit the use of the encrypted password file included with the script.
    .PARAMETER Cluster
    Provide vSphere CLUSTER name if the scan is to be run for a given Cluster name. ('*' Wildcard is valid.)
    .PARAMETER Update
    Add this paramater to indicate you wish to have the script Protect and Unprotect VMs in the SRM server. The absence of this parameter will cause the script to only run an audit.  

    .NOTES
    Version 1.0 Initial Release
    Version 1.1 Update to fix UnProtect of VMs. Script is now able to handle post failover state where VMs were being unprotected when they should not be.
#>
#
    Param (
    [Parameter(Mandatory=$true)][String]$vCenter,
    [Parameter(Mandatory=$true)][String]$Username,
    [Parameter(Mandatory=$true)][String[]]$SecureKey,
    [Parameter(Mandatory=$false)][String]$Cluster,
    [switch]$Update,
    [Parameter(Mandatory=$true)][String[]]$emailTo
    )
#>
<#
    Key Variables:
    $VMs = List of all VMs on Protected Datastores
    $ProtectedVMs = All Protected VMs in Protection Groups

    Basic process to I.D unprotected VMS
    1. Grab list of protected VMs using ListProtectedVms()
    2. Grab list of protected datastores using ListProtectedDatastores()
    3. Grab list of VMs on protected datastores
    4. Difference of VMs in results of (c) but not in results of (a)
#>

#region Initialise
#Requires -Version 4 -Modules VMWare.VIMAutomation.Core

#Setup and flush logs
$logpath = "$($PSScriptRoot)\logs"
$CredFolder = $PSScriptRoot

if (!(Test-Path "$($logpath)")) {New-Item "$($logpath)" -ItemType directory}
$d = Get-Date -format yyyy-MM-dd
$t = Get-Date -Format HH-mm
$translog = "$d $t Output.log"
Start-Transcript "$($logpath)\$translog"
Write-Host "Using Log Folder: $($logpath)"
Write-Host "SRM Script started."
Write-Host "Using Credentials Folder: $($Credfile)" -Verbose
Write-Host "$(get-date -Format 'MM/dd HH:mm:ss:ms') Cleaning up transcript logs older than: 20 Days" -Verbose
Get-ChildItem -path "$($logpath)" | Sort-Object -Property lastwritetime | where {$_.LastWriteTime -gt (Get-Date).AddDays(20)} | Remove-Item -Confirm:$false | Out-Null

Write-Host "Initiialising script."
$SuccessHeader = "<style>"
$SuccessHeader = $SuccessHeader + "body{font-family:Tahoma;font-size:10pt}"
$SuccessHeader = $SuccessHeader + "TABLE{font-family:Tahoma;font-size:10pt;border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse; width:100%}"
$SuccessHeader = $SuccessHeader + "TH{border-width: 1px;padding: 0px;border-style: solid;border-color: black;color:white;background-color:darkgreen}"
$SuccessHeader = $SuccessHeader + "TD{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:white}"
$SuccessHeader = $SuccessHeader + "</style>"

$ErrorHeader = "<style>"
$ErrorHeader = $ErrorHeader + "body{font-family:Tahoma;font-size:10pt}"
$ErrorHeader = $ErrorHeader + "TABLE{font-family:Tahoma;font-size:10pt;border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse; width:100%}"
$ErrorHeader = $ErrorHeader + "TH{border-width: 1px;padding: 0px;border-style: solid;border-color: black;color:white;background-color:red}"
$ErrorHeader = $ErrorHeader + "TD{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:white}"
$ErrorHeader = $ErrorHeader + "</style>"

$smtpServer = "smtpmailserver.network.com@"
$emailFrom = "noreply@SRM-Report"

$VmDictionary =@{}
#endregion

#Create credential store for vCenter and SRM and connect
Write-Host "Connecting to vCenter $($vCenter) and SRM service."
#
#region Credentials Section
$PasswordFile = "$CredFolder\EncryptedPassword.pwd"
$SecPassword = Get-Content $PasswordFile | ConvertTo-SecureString -Key $SecureKey
$MyCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Username, $SecPassword
#endregion
#>

Set-PowerCLIConfiguration -DefaultVIServerMode multiple -Confirm:$false

if (!(Connect-VIServer $vCenter -Credential $MyCredential)) {
  Write-Error "Failed to connect to vCenter: $($vCenter))"
  exit}

$srmServers = Connect-SrmServer -RemoteCredential $MyCredential -Credential $MyCredential

  if (!($srmServers)) {
    Write-Error "Failed to connect to SRM Service on vCenter: $($vCenter))"
  exit}

$checkVMs = Get-View -ViewType VirtualMachine -Server $vCenter -Property Name
# Create Paired site VM dictionary
$checkVMs | %{$VmDictionary.Add($_.MoRef,$_.Name)}

foreach ($srm in $srmServers) {
  $vms = @()
  $ProtectedVMs = @()
  $report = @()
  $notconfigured = @()
  $srmPgDs = @()
  $protectionGroups = @()
  $SrmName = (([System.Net.Dns]::GetHostEntry($srm.Name)).HostName).Split('.')[0]
  # Validate Cluster parameter
  if ($Cluster) {
    if (!(Get-Cluster $Cluster -ErrorAction silentlycontinue)) {
      Write-Error "Cluster $($Cluster) not found in vCenter: $($vCenter))"
    exit}
  }

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

  # Get list of all (not PlaceHolders) VMs from Protected site.
  Write-Host "Getting list of VMs in vCenter."

  #Filter $PGs to those that are protected only - Where ProtectionState = READY
  $PGs = $PGs | ?{$_.GetProtectionState() -eq "Ready"}

  #Create ProtectionGroup Array
  Write-Host "Getting Protection Groups."
  foreach ($item in $PGs) {
    $info = $item.GetInfo()
    $MyObj = New-Object PSObject -Property @{
      PG = $item
      Name = $info.Name
    Description = $info.Description}
    $protectionGroups += $MyObj
  }
  $dsCount = 0
  #If CLUSTER parameter has been specified, filter ProtectionGroups for Cluster name
  if ($Cluster) {$protectionGroups = $protectionGroups | Where-Object {$_.Name -match $Cluster}}

  if (!$protectionGroups) {
    if ($Cluster) {Write-Host "No Protection Groups found for $($Cluster) in SRM Server $($SrmName) on vCenter: $($vCenter)"}
    else {Write-Host "No Protection Groups found in SRM Server $($SrmName) on vCenter: $($vCenter)"}
  continue}
  Write-Host "Protection Groups found in SRM Server $($SrmName) on vCenter: $($vCenter)"
  Write-Host "`nGetting protected VMs in Protection Groups."
  foreach ($protectionGroup in $protectionGroups) {
    $dsCount++
    Write-Host "`nProcessing ProtectionGroup $($dsCount) of $($protectionGroups.count)   $($protectionGroup.Name)"
    ## Grab Protected VMs on protection group only for this vCenter.
    $ProtectedVMs += $protectionGroup.PG.ListProtectedVms() | Select-Object -Property @{N="MoRef";E={$_.VM.MoRef}},
    @{N="ProtectionGroup";E={$protectionGroup.PG}},
    @{N="ProtectionGroupName";E={$protectionGroup.Name}},
    @{N="ProtectionGroupDescription";E={$protectionGroup.Description}},
    @{N="Status";E={"Successfully UnProtected"}}
    #Get Datastores in ProtectionGroup
    Write-Host "Getting Protected Datastores."
    $pgDatastores = ($protectionGroup.PG.listprotecteddatastores() | Select-Object -Property MoRef).MoRef
    $i = 0
    #Grab list of all VMs on protected DataStores. If there are unprotected VMs then the number of VMs in this
    #list will be greater than the VMs in $ProtectedVMs above.
    foreach ($pgDatastore in $pgDatastores){
      $i++
      Write-Host "Processing datastore $($i) of $($pgDatastores.count):    $($pgDatastore.Value)"
      $capture = get-view $pgDatastore -Property Vm -ErrorAction silentlycontinue
      foreach ($Vm in $capture.Vm){
        $MyObj = New-Object -TypeName PSObject -Property ([ordered]@{
            Name = $VmDictionary[$Vm]
            MoRef = $Vm
            Datastore = $capture.MoRef
        })
        $Vms += $MyObj
      }
    }
    #Create a reference array for ProtectGroupName to Protected Datastores
    $srmItem = New-Object PSObject -Property @{
      PGName = $protectionGroup.Name
    Datastore = $pgDatastores}
    $srmPgDs += $srmItem
  }

  #$VMs = List of all VMs on Protected Datastores (regardless if protected or not)
  #$ProtectedVMs = All Protected VMs in Protection Groups
  #Calculate UnProtected VMs and add to array $notconfigured
  Write-Host "Processing VMs..."
  #If there are VMs and none are protected:
  if ($ProtectedVMs.count -eq 0 -and $Vms.count -gt 0) {
    $notconfigured = $Vms | select Name,MoRef,
    @{N="DataStore";E={$_.Datastore.Value}},
    @{N="Status";E={"Successfully Protected"}}
    }
  else {
    foreach ($Vm in $Vms) {
      if (@($ProtectedVMs.MoRef).IndexOf($Vm.MoRef) -eq -1) {
        $notconfigured += $Vm | select Name,MoRef,
        @{N="DataStore";E={$_.Datastore.Value}},
        @{N="Status";E={"Successfully Protected"}}
      }
    }
  }

  # Calculate Protected VMs that no longer exist and need to be unprotected.
  # $checkVMs - list of all VMs that do exist in Protected Site
  # $cleanupvms - $checkvms = list of vms that no longer exist.

  # Calculate list of VMs to be unprotected ($cleanupVMs) - 1st remove known protected VMs ($VMs)
  $cleanupVMs = $ProtectedVMs | where {($Vms).MoRef -notcontains $_.MoRef}
  # Calculate list of VMs to be unprotected ($cleanupVMs) - 2nd remove known all other VMs ($checkVMs)
  $cleanupVMs = $cleanupVMs | where {($checkVMs).MoRef -notcontains $_.MoRef}
  if ($Cluster) {$cleanupVMs = $cleanupVMs | Where-Object {$_.ProtectionGroupName -match $Cluster}} # Filter only where cluster is involved

  #Region Un/Protect VMs in SRM
  if ($Update) {  #Only run this section if the -UPDATE script parameter is present
    if (@($cleanupVMs).Count -gt 0) {
      #Remove (UnProtect) deleted VMs from SRM
      foreach ($pvm in @($cleanupVMs)) {
        Write-Host "UnProtecting VM: $($pvm.moref)  on Protection Group: $($pvm.ProtectionGroupName)..."
        $ProtectionGroup = $pvm.ProtectionGroup
        #UnProtect VM Task
        try { $UnprotectTask = $ProtectionGroup.UnprotectVms($pvm.MoRef) }
        catch {$pvm.Status = "Failed to remove"}
        while(-not $UnprotectTask.IsComplete()) { sleep -Seconds 1 }
      }
    }
    if (@($notconfigured).Count -gt 0) {
      #Protect VMs in $notconfigured array
      Write-Host "Protecting unprotected VMs..."
      foreach ($Vm in @($notconfigured)) {
        #Get VM's Protection Group and create protection spec
        $tgtPGName = ($srmPgDs | Where-Object {$_.datastore.value  -eq $Vm.DataStore}).PGName
        Write-Host "Protecting VM: $($vm.Name)   on Protection Group: $($tgtPGName)"
        $targetProtectionGroup = ($protectionGroups | where {$_.Name -eq $tgtPGName}).PG
        $protectionSpec = New-Object VMware.VimAutomation.Srm.Views.SrmProtectionGroupVmProtectionSpec
        $protectionSpec.Vm = $Vm.MoRef
        #Protect VM Task
        try {$protectTask = $targetProtectionGroup.ProtectVms($protectionSpec)}
        catch {$Vm.Status = "Failed to protect"}
        while(-not $protectTask.IsComplete()) { sleep -Seconds 1 }
        $err = ($protectTask.GetResult()).Error.LocalizedMessage
        if ($err) {$Vm.status = "Error $err"}
      }
    }
  }
  #endregion

  # Change report text to suit script action.
  if ($Update) {
    $ReportProt = "Protected "
    $ReportDel = "Deleted "
  }
  else {
    $notconfigured | foreach {$_.Status = "No Protection"}
    $cleanupVMs | foreach {$_.Status = "Needs Un-Protecting"}
    }

  #region Send email report
  #Success - $notconfigured | ?{$_.Status -like "Success*"}
  $reportBody = "<H3>$($ReportProt)$(($notconfigured | ?{$_.Status -like "Success*"}).count) unprotected VMs in SRM</H3>"
  $Protectreport = $notconfigured | ?{$_.Status -like "Success*"} | select Name, @{N="ProtectionGroup";E={($srmPgDs[($srmPgDs.datastore.value.indexof($_.DataStore))].PGName)}},Status
  $Protectreport = [string] ($Protectreport | ConvertTo-Html -head $SuccessHeader -Body $reportBody)

  #Error - $notconfigured | ?{$_.Status -like "Error*"}
  $reportBody = "<H3>VMware Support Team please investigate.<br>Failed to $($ReportProt)$(($notconfigured | ?{$_.Status -like "Error*"}).count) unprotected VMs in SRM</H3>"
  $ErrorProtectreport = $notconfigured | ?{$_.Status -like "Error*"} | select Name, @{N="ProtectionGroup";E={($srmPgDs[($srmPgDs.datastore.value.indexof($_.DataStore))].PGName)}},Status
  $ErrorProtectreport = [string] ($ErrorProtectreport | ConvertTo-Html -head $ErrorHeader -Body $reportBody)

  $reportBody = "<H3>$($ReportDel)$($cleanupVMs.count) superfluous placeholder VMs in SRM</H3>"
  $UnProtectreport = $cleanupVMs | select MoRef, ProtectionGroupName,ProtectionGroupDescription,Status | Sort ProtectionGroupName
  $UnProtectreport = [string]($UnProtectreport | ConvertTo-Html -Head $SuccessHeader -Body $reportBody)

  if (($notconfigured | ?{$_.Status -like "Error*"}).count -gt 0) {
  $mailMsg = $ErrorProtectreport+$Protectreport+$UnProtectreport}
  else {$mailMsg = $Protectreport+$UnProtectreport}
  Send-MailMessage -to $emailTo -From $emailFrom -SmtpServer $smtpServer -Subject "$($SrmName.toupper()) SRM Automation Report" -BodyAsHtml $mailMsg
  #endregion

}

Disconnect-SRMServer * -Force -Confirm:$false

Disconnect-VIServer * -Force -Confirm:$false

Reply
0 Kudos
vmk2014
Expert
Expert

HI LucD,

Does this script can used for auditing first for unprotected VM's  in a SRM environment, then send mail, based on that then it will protect the VM's missing from protection group ? i mean after reviewing the report.

Thanks

v

Reply
0 Kudos
LucD
Leadership
Leadership

I haven't studied the script in detail, but from a quick read it looks as if points 1-3 (of) generate the list of unprotected VMs.

You would need to stop there and send an email with that report.

Step 4 which makes corrections could be run as a separate script.


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

Reply
0 Kudos
vmk2014
Expert
Expert

Thank you. Do you want me to open separate mail thread ? I'll do.   I need your help.

thanks

v

Reply
0 Kudos
PugazhendhiK
Contributor
Contributor

This below part of code is not fetching the exact names of the protected datastores and finally the Output is saying like

0 unprotected VMs in SRM

foreach ($pgDatastore in $pgDatastores){ 

  1.       $i++ 
  2.       Write-Host "Processing datastore $($i) of $($pgDatastores.count):    $($pgDatastore.Value)" 
  3.       $capture = get-view $pgDatastore -Property Vm -ErrorAction silentlycontinue  
  4.       foreach ($Vm in $capture.Vm){ 
  5.         $MyObj = New-Object -TypeName PSObject -Property ([ordered]@{ 
  6.             Name = $VmDictionary[$Vm] 
  7.             MoRef = $Vm 
  8.             Datastore = $capture.MoRef 
  9.         }) 
  10.         $Vms += $MyObj 
  11.       } 
  12.     } 
  13.     #Create a reference array for ProtectGroupName to Protected Datastores 
  14.     $srmItem = New-Object PSObject -Property @{ 
  15.       PGName = $protectionGroup.Name 
  16.     Datastore = $pgDatastores}  
  17.     $srmPgDs += $srmItem 
  18.   } 
Reply
0 Kudos