Pre-Upgrade Quality Check Script – VMware SDDC using VCF

I developed this script when updating/patching SDDC environments. Although SDDC environments should follow guidelines. Eg, no Host-Affinity Rules, no VMs on local storage… Let’s face it, in the real world not everyone follows the rules πŸ™

The further I dug into SDDC environments, the more anomalies I found that would prevent VCF from progressing with the SDDC updates. Here is a list of things (not to say I have caught everything, but at least for the ones I upgraded)

Things To Watch Out For

  • Clusters with 4 Hosts running RAID 5 vSAN Policy.
    I wanted to check for this as this will leave the Cluster in a vulnerable state whilst hosts are being updated. Eg, a host is rebooting, you sustain a failure on another host … bad things will happen! A risk not worth taking.
  • VMs with ISOs attached.
    Only an issue if the ISO is not on a shared datastore.

VCF Update Show-Stoppers

  • VMs with disks stored on Local Datastores (not vSAN)
  • VMs on Hardware Version < 11 with attached RDMs (these VMs will not vMotion)
  • DRS Anti-Affinity Rules whereby the rules would not allow one host to drop into maintenance mode (Eg. Keep 4 VMs apart in a 4 Host Cluster)
  • VMs with DRS Overrides
  • DRS Mode set to anything other than Fully Automated
  • VMs that have disks with MultiWrite enabled

So the script I wrote will give two outputs within a folder created on your Desktop…

FullInfo Folder

  • cluster-info.csv
  • DRSrules.csv
  • shared-RDMinfo.csv
  • VMinfo.csv

Issues Folder

This folder will write CSV files for any issues found as listed in the ‘Things To Watch Out For’ and ‘Show Stoppers’ section. If no CSV file is written, then you’re good to go… either that or a syntax issue in the script πŸ™‚

The Script

Pre-requisite: Update ‘vCenters’ List. Script can run against one or multiple vCenters.

# Author: shanemarsh.co.uk
# Description: Script created to do pre-upgrade checks for SDDC environments

if (!(get-module -name VMWare.VimAutomation.Core -ErrorAction SilentlyContinue) ) {
    cls
    Write-Host "`n`nLoading VMWare Core Module" -fore yellow
    Import-Module -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue
}

if (!(get-module -name VMware.VimAutomation.Storage -ErrorAction SilentlyContinue) ) {
    Write-Host "`n`nLoading VMWare Storage Module" -fore yellow
    Import-Module -Name VMware.VimAutomation.Storage -ErrorAction SilentlyContinue
}
Set-PowerCLIConfiguration -DisplayDeprecationWarnings $False -confirm:$False | Out-Null

cls

# Location for Script Outputs
$desktop = [Environment]::GetFolderPath("Desktop")
$script_dir = "$desktop\SDDC-Reports"

# vCenter Credentials
Write-Host -Fore Yellow "vCenter Credentials`n"
$vcUser = Read-Host "vCenter Username"
$SecureVcPassword = Read-Host -AsSecureString "vCenter Password for $($vcUser)"
$creds = Get-Credential -Credential (New-Object PSCredential $vcUser, $SecureVcPassword)

# vCenter Name

        $vCenters = @(
            "vcenter1",
            "vcenter2",
            "vcenter3"  
        )
    
# Create New Reports Folder
if (!(Test-Path $script_dir)) {
New-Item -ItemType Directory -Path "$script_dir" | Out-Null
}

###################### SCRIPT BEGIN ######################


foreach ($vc in $vCenters) {

    Connect-VIServer -Server $vc -Credential $creds

    # Looping through each cluster within the vCenter

    $clusters = Get-Cluster
    $hyps = Get-VMHost
    $vms = Get-VM

    foreach ($cluster in $clusters) {

        $antiAffinityIssue = @()
        $drsMode = @()
        $drsrules = $cluster | Get-DrsRule
        $hypCount = $hyps.count
        $clusterName = $cluster.Name

        # Pull Cluster Info into Hash Table

        Write-Host "Pulling Cluster information for $cluster..." -ForegroundColor Yellow

        $clusterinfo = [pscustomobject][ordered]@{
            vCenter            = $vc
            Cluster            = $cluster.name
            HAEnabled          = $cluster.haenabled
            HAFailoverLevel    = $cluster.hafailoverlevel
            DrsEnabled         = $cluster.drsenabled
            DrsMode            = $cluster.DrsMode
            DrsAutomationLevel = $cluster.drsautomationlevel;
            HostCount          = $hypCount
        }

        # Report on Clusters that are do not have DRS set to Fully Automated
        if (($cluster.DrsMode -ne "FullyAutomated") -or ($cluster.DrsAutomationLevel -ne "FullyAutomated")) {
            Write-Host "Cluster DRS Mode/Automation Level not set to Fully Automated" -ForegroundColor Red
            $drsMode += $clusterinfo
        }

        # Pull info on DRS Rules
        $alldrsrules = $drsrules | Select Name, Enabled, Type, @{Name = "VM"; Expression = { $iTemp = @(); $_.VMIds | % { $iTemp += (Get-VM -Id $_).Name }; [string]::Join(";", $iTemp) }}

        # Loop through DRS rules
        foreach ($rule in $alldrsrules){
            $drsRuleVMcount = $rule.vm.Split(";").count

            # Check to see if DRS Anti-Affinity will prevent vMotions and log to array
            if (($rule.Type -match "VMAntiAffinity") -and ($hypCount -le $drsRuleVMcount)) {
                Write-Host "An Anti-Affinity DRS Rule has more/equal VMs to Hosts. Please Amend." -ForegroundColor Red
                
                $ruleresult = "" | select Name, Enabled, hypCount, vmCount, VMs
                $ruleresult.Name = $rule.Name
                $ruleresult.Enabled = $rule.Enabled
                $ruleresult.hypCount = $hypCount
                $ruleresult.vmCount = $drsRuleVMcount
                $ruleresult.VMs = $rule.VM
                $antiAffinityIssue += $ruleresult
            }
        }

        $VMinfo = @()
        $localVMs = @()
        $4hypRaid5VMs = @()
        $rdmOverview = @()
        $rdmIssues = @()
        $isoAttached = @()
        $vmDRSoverrides = @()
        $MultiWrite = @()

        $GetVM = Get-VM

        # Loop through each VM in Cluster
        foreach ($vm in $GetVM) {

            $vmStoragePolicy = $vm | Get-SpbmEntityConfiguration | Select StoragePolicy
            $vmVersionNum = $($vm.Version -Split("v") )[1]
            $isoMounted = $vm | Get-CDDrive | select @{N="VM";E="Parent"},IsoPath
                 
            Write-Host "Pulling VM Hardware information for $vm..." -ForegroundColor Yellow

            # Loop through each vDisk within the VM
            foreach ($vmHardDisk in $vm | Get-HardDisk) {
                $scsiHD = $vmHardDisk | Get-ScsiController
                $HDMultiWriter = $vmHardDisk.ExtensionData.Backing.Sharing

                $result = "" | select Cluster, vmName, vmVersion, NumCpu, MemoryGB, HardDiskName, HardDiskCapacityGB, DiskLocation, SCSIcontroller, SCSIBusSharing, MultiWriter, StoragePolicy, MountedISO, DrsAutomationLevel
                $result.Cluster = $cluster
                $result.vmName = $vm.Name
                $result.vmVersion = $vm.Version 
                $result.NumCpu = $vm.NumCpu
                $result.MemoryGB = $vm.MemoryGB
                $result.HardDiskName = $vmHardDisk.Name
                $result.HardDiskCapacityGB = [System.Math]::Round($vmHardDisk.CapacityGB, 0)
                $result.DiskLocation = $vmHardDisk.Filename
                $result.SCSIcontroller = $scsiHD.Name
                $result.SCSIBusSharing = $scsiHD.BusSharingMode
                $result.MultiWriter = $HDMultiWriter
                $result.StoragePolicy = $vmStoragePolicy.StoragePolicy.Name
                $result.MountedIso = $isoMounted.IsoPath
                $result.DrsAutomationLevel = $vm.DrsAutomationLevel
                $VMinfo += $result

                                # Checking for Multi Writer
                                if ($HDMultiWriter -match "sharingMultiWriter") {
                                    Write-Host "$vm has a vDisk with MultiWrite Enabled" -ForegroundColor Red
                                    $MultiWrite += $result
                                }
            }

                # Checking for 4 Node Cluster with RAID 5 Storage Policy
                if (($hypCount -eq "4") -and ($result.StoragePolicy -match "RAID5" -eq $True)) {
                    Write-Host "$vm running on RAID 5 Storage Policy in 4 Node Cluster" -ForegroundColor Red
                    $4hypRaid5VMs += $result
                }

                # Checking for vDisks on Local Datastores
                if ($result.DiskLocation -match '\d{3,7}-local-') {
                    Write-Host "$vm running on Local Datastore" -Foreground Red
                    $localVMs += $result
                }   

                # Checking for ISOs mounted to VM
                if (($isoMounted.IsoPath -ne $null) -and ($isoMounted.IsoPath -ne "[]")) {
                    Write-Host "$vm has ISO attached" -Foreground Red
                    $isoAttached += $result
                }

                # Checking for VM DRS Overrides
                if (($vm.DrsAutomationLevel -notmatch "AsSpecifiedByCluster") -and ($vm.DrsAutomationLevel -notmatch "FullyAutomated") -and ($vm.DrsAutomationLevel -notmatch $null)){
                    Write-Host "$vm is set to DRS mode:" $vm.DrsAutomationLevel -Foreground Red 
                    $vmDRSoverrides += $result
                }

                            
        
            # Get RDM Info
            $vmView = $vm | Get-View
            
            # Looping through each RDM and pulling information
            foreach($dev in $vmView.Config.Hardware.Device){
                if(($dev.gettype()).Name -eq "VirtualDisk"){
                    if(($dev.Backing.CompatibilityMode -eq "physicalMode") -or
                    ($dev.Backing.CompatibilityMode -eq "virtualMode")){
                       
                        Write-Host "Pulling RDM information for $vm" -ForegroundColor Magenta
                        $row = "" | select vmName, vmVersion, HDDeviceName, HDFileName, HDMode, HDsize, HDDisplayName
                        $row.vmName = $vmView.Name
                        $row.vmVersion = $vm.Version
                        $row.HDDeviceName = $dev.Backing.DeviceName
                        $row.HDFileName = $dev.Backing.FileName
                        $row.HDMode = $dev.Backing.CompatibilityMode
                        $row.HDSize = $dev.CapacityInKB
                        $esx = Get-View $vmView.Runtime.Host
                        $row.HDDisplayName = ($esx.Config.StorageDevice.ScsiLun | where {$_.Uuid -eq $dev.Backing.LunUuid}).DisplayName
                        $rdmOverview += $row

                        # Report if VM version less than 11
                        if ($vmVersionNum -lt 11) {
                            Write-Host "VM Version is less than 11, which is required to migrate RDMs" -ForegroundColor Red
                            $rdmIssues += $row
                        }
                        else  {
                            Write-Host "VM Version is" $vm.Version -ForegroundColor Green
                        }
                    }
                }
            }
        }
                 

        # Create Directory Folders
        New-Item -ItemType Directory -Path "$script_dir\$vc" -Force | Out-Null
        New-Item -ItemType Directory -Path "$script_dir\$vc\Clus-$clusterName" -Force | Out-Null
        New-Item -ItemType Directory -Path "$script_dir\$vc\Clus-$clusterName\FullInfo" -Force | Out-Null
        New-Item -ItemType Directory -Path "$script_dir\$vc\Clus-$clusterName\Issues" -Force | Out-Null
       
        # Overview Output to CSV
        $clusterinfo | Export-csv "$script_dir\$vc\Clus-$clusterName\FullInfo\cluster-info.csv" -NoTypeInformation -Force
        $alldrsrules | Export-csv "$script_dir\$vc\Clus-$clusterName\FullInfo\DRSrules.csv" -NoTypeInformation -Force
        $VMinfo | Export-csv "$script_dir\$vc\Clus-$clusterName\FullInfo\VMinfo.csv" -NoTypeInformation -Force
        $rdmOverview | Export-csv "$script_dir\$vc\Clus-$clusterName\FullInfo\shared-RDMinfo.csv" -NoTypeInformation -Force
      
        # Issues Output to CSV
        if ($localVMs -ne "0") {
            $localVMs | Export-csv "$script_dir\$vc\Clus-$clusterName\Issues\LocalVMs.csv" -NoTypeInformation -Force
        }
        if ($4hypRaid5VMs -ne "0") {
            $4hypRaid5VMs | Export-csv "$script_dir\$vc\Clus-$clusterName\Issues\4Host-RAID5-VMs.csv" -NoTypeInformation -Force
        }
        if ($rdmIssues -ne "0") {
            $rdmIssues | Export-csv "$script_dir\$vc\Clus-$clusterName\Issues\RDMs-on-old-VM-version.csv" -NoTypeInformation -Force
        }
        if ($isoAttached -ne "0") {
            $isoAttached | Export-csv "$script_dir\$vc\Clus-$clusterName\Issues\ISO-Attached.csv" -NoTypeInformation -Force
        }
        if ($antiAffinityIssue -ne "0") {
            $antiAffinityIssue | Export-csv "$script_dir\$vc\Clus-$clusterName\Issues\AntiAffinityCount.csv" -NoTypeInformation -Force
        }
        if ($vmDRSoverrides -ne "0") {
            $vmDRSoverrides | Export-csv "$script_dir\$vc\Clus-$clusterName\Issues\vmDRSoverrrides.csv" -NoTypeInformation -Force
        }
        if ($drsMode -ne "0") {
            $drsMode | Export-csv "$script_dir\$vc\Clus-$clusterName\Issues\drsMode.csv" -NoTypeInformation -Force
        }
        if ($MultiWrite -ne "0") {
            $MultiWrite | Export-csv "$script_dir\$vc\Clus-$clusterName\Issues\MultiWrite.csv" -NoTypeInformation -Force
        }
    }

    # Disconnect from looped vCenter Server
    Disconnect-VIServer -Server $vc -Confirm:$false

}

# End of script

Download Script

If you have any concerns or questions about this process, feel free to drop a comment below and I’ll gladly get back to you ASAP.

Leave a Reply

Your email address will not be published. Required fields are marked *