10 Replies Latest reply on Feb 4, 2020 1:57 PM by PugazhendhiK

    PowerCli and SRM - capturing Errors.

    sspikent Novice

      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.?

        • 1. Re: PowerCli and SRM - capturing Errors.
          LucD Guru
          Community WarriorsUser ModeratorsvExpert

          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: http://lucd.info | Twitter: @LucD22 | PowerCLI Reference co-author: http://tinyurl.com/hkn4glz
          • 2. Re: PowerCli and SRM - capturing Errors.
            sspikent Novice

            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.


            • 3. Re: PowerCli and SRM - capturing Errors.
              sspikent Novice

              DOH! !!!

               

              Found it.

               

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


              $result.Error.LocalizedMessage

               

              Sorry for wasting time.

              • 4. Re: PowerCli and SRM - capturing Errors.
                LucD Guru
                vExpertCommunity WarriorsUser Moderators

                No problem, glad you found it

                Blog: http://lucd.info | Twitter: @LucD22 | PowerCLI Reference co-author: http://tinyurl.com/hkn4glz
                • 5. Re: PowerCli and SRM - capturing Errors.
                  PugazhendhiK Novice

                  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.

                  • 6. Re: PowerCli and SRM - capturing Errors.
                    sspikent Novice

                    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-credentials-part-2/

                     

                    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
                    
                    • 7. Re: PowerCli and SRM - capturing Errors.
                      vmk2014 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

                      • 8. Re: PowerCli and SRM - capturing Errors.
                        LucD Guru
                        User ModeratorsvExpertCommunity Warriors

                        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: http://lucd.info | Twitter: @LucD22 | PowerCLI Reference co-author: http://tinyurl.com/hkn4glz
                        • 9. Re: PowerCli and SRM - capturing Errors.
                          vmk2014 Expert

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

                           

                          thanks

                          v

                          • 10. Re: PowerCli and SRM - capturing Errors.
                            PugazhendhiK Novice

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