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.?
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
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:
But if I query it directly:
$result[0].Error.LocalizedMessage
Returns null.
$Error[0] has nothing also.
DOH! !!!
Found it.
$result[0] as per variable browser is misleading. I only needed
$result.Error.LocalizedMessage
Sorry for wasting time.
No problem, glad you found it
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
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.
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 GroupsBasic 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 = $PSScriptRootif (!(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-NullWrite-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
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
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
Thank you. Do you want me to open separate mail thread ? I'll do. I need your help.
thanks
v
This below part of code is not fetching the exact names of the protected datastores and finally the Output is saying like
foreach ($pgDatastore in $pgDatastores){