<# .SYNOPSIS Configure ESXi hosts to work with attached RDM's .DESCRIPTION Able to set the following. 1) Initialize a New ESXi Host. 2) Set a given LUN ID to Perennially Reserved. 3) Scan a Host and set all or select LUNs to Perennially Reserved. 5) Set the SATP ALUA default policy for HP3Par. 6) Set the LUN Multipath Policy to RoundRobin, Fixed or MostRecentlyUsed. PowerCLI v6.5+ Required .INPUTS None. .OUTPUTS None. .NOTES NAME: Set-VMWareRDM.psm1 AUTHOR: Derek Bannard, Kristoph Minchau CREATED: 2017-06-08 LASTEDIT: 2017-06-19 VERSION: 1.20 KEYWORDS: VMWare, RDM, LUN, HP 3PAR REVISION LOG: [2017-06-08] - db - v1.00 ** Created ** [2017-06-14] - km - v1.10 * Made Restart optional in New-3ParSatpAluaRule, and other fixes [2017-06-19] - km - v1.20 * Configured Module Manafest to export commands. .EXAMPLE Import-Module Set-VMWareRDM Initialize-NewHost -ESXiHost ESX01 #> <# .SYNOPSIS Connect to the vCenter server .DESCRIPTION Connect to the vCenter server .PARAMETER vCenter vCenter Server Name. E.g. vCServer01 .INPUTS N/A .OUTPUTS N/A .EXAMPLE Connect-VCenterServer -vCenter vCServer01 #> Function Connect-VCenterServer { [CmdletBinding(SupportsShouldProcess=$true)] Param( [Parameter(Position = 0, Mandatory=$true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName=$true)] [AllowEmptyString()] [string]$vCenter ) #end param Process { Try { if(!$vCenter -or $vCenter -eq $null) { $vCenter = Read-Host "Please Enter in vCenter Server Name" } #Ensure that the VMware modules are loaded Import-Module VMware.VimAutomation.Core -Force -ErrorAction SilentlyContinue #Check if running PowerShell as Administrator, if true, we can disable the Invalid Certificate warning if( ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { #Suppress Invalid Certificate Warning Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -ErrorAction SilentlyContinue -Confirm:$false } #Connect to VIServer - Will prompt for credentials if needed. Connect-VIServer -Server $vCenter -WarningAction SilentlyContinue } Catch { Write-Error "Connect-VCenterServer - Error Connecting." } } #end Process }#end Connect-VCenterServer <# .SYNOPSIS Disconnect to the vCenter server .DESCRIPTION Disconnect to the vCenter server .INPUTS N/A .OUTPUTS N/A .EXAMPLE Disconnect-VCenterServer #> Function Disconnect-VCenterServer { [CmdletBinding(SupportsShouldProcess=$true)] Param() Process { Try { #Disconnect to VIServer - Will prompt for credentials if needed. Disconnect-VIServer -WarningAction SilentlyContinue -Force -Confirm:$false } Catch { Write-Error "Disconnect-VCenterServer - Error Disconnecting." } } #end Process }#end Disconnect-VCenterServer <# .SYNOPSIS Confirm connection to the vCenter server .DESCRIPTION Confirm connection to the vCenter server .PARAMETER vCenter vCenter Server Name. E.g. vCServer01 .INPUTS N/A .OUTPUTS EsxCliImpl .EXAMPLE Confirm-VCenterServer -vCenter vCServer01 #> Function Confirm-VCenterServer { [CmdletBinding(SupportsShouldProcess=$true)] Param( [Parameter(Position = 0, Mandatory=$false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName=$true)] [AllowEmptyString()] [string]$vCenter ) #end param Process { Try { #Ensure that the VMware modules are loaded #Check to see if we are connected to any servers if($Global:DefaultVIServers.Count -gt 0) { #Check to see if we are connected to more than one vCenter if($Global:DefaultVIServers.Count -ne 1) { #Currently connected to more than one vCenter. Disconnect and re-connect Disconnect-VCenterServer Connect-VCenterServer -vCenter $vCenter } else { #We have 1 vCenter Connected. Check to see if we specified $vCenter to connect to. if(!$vCenter -or $vCenter -eq $null) { #$vCenter not specified. Assume we are already connected to the right server } else { #$vCenter specified, Check to make sure that we are connected to the right server if($Global:DefaultVIServers.Name -ne $vCenter) { #We are not conencted to the right server. Disconnect and re-connect Disconnect-VCenterServer Connect-VCenterServer -vCenter $vCenter } } } } else { #Connect to vCenter Connect-VCenterServer -vCenter $vCenter } } Catch { Write-Error "Confirm-VCenterServer - Error Confirming." } } #end Process }#end Confirm-VCenterServer <# .SYNOPSIS Get the ESX CLI object .DESCRIPTION Get the ESX CLI object .PARAMETER ESXiHostName ESXi Host Server Name. E.g. ESXi01 .INPUTS N/A .OUTPUTS EsxCliImpl .EXAMPLE Get-CLI -ESXiHostName ESXi01 #> Function Get-CLI { [CmdletBinding(SupportsShouldProcess=$true)] Param( [Parameter(Position = 0, Mandatory=$false, ValueFromPipelineByPropertyName=$true)] [AllowEmptyString()] [string]$vCenter = $Global:DefaultVIServers[0].Name, [Parameter(Position = 1, Mandatory=$true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName=$true)] [string]$ESXiHostName ) #end param Process { Try { #Confirm Connection to VCenter Confirm-VCenterServer -vCenter $vCenter $ESXiHost = Get-VMHost "$ESXiHostName*" Write-Output (Get-EsxCli -VMHost $ESXiHost -V2) } Catch { Write-Error "Get-CLI - Host Not Found." } } #end Process }#end Get-CLI <# .SYNOPSIS New 3PARdata ALUA Rule .DESCRIPTION New 3PARdata ALUA Rule. For 3PAR setting device specific ALUA Rule. .PARAMETER ESXiHost ESXi Host Server Name .INPUTS N/A .OUTPUTS N/A .EXAMPLE New-3ParSatpAluaRule -ESXiHost ESXi01 #> Function New-3ParSatpAluaRule { [CmdletBinding(SupportsShouldProcess=$true)] Param( [Parameter(Position = 0, Mandatory=$true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName=$true)] [string]$ESXiHost, [Parameter(Position = 1, Mandatory=$false)] [switch]$Force ) #end param Process { Try { $esxcli = Get-CLI -ESXiHostName $ESXiHost if(!($esxcli.storage.nmp.satp.rule.list.Invoke() | where {$_.vendor -eq "3PARdata"}) -or $Force) { #Rule Does not exist, add it Write-Host "3ParSatpAluaRule Rule Does not exist. Adding rule..." -ForegroundColor Yellow # Set HP SATP rules $args = $esxcli.storage.nmp.satp.rule.add.CreateArgs() $args.claimoption = "tpgs_on" $args.psp = "VMW_PSP_RR" $args.description = "HP 3PAR Custom iSCSI/FC/FCoE ALUA Rule" $args.model = "VV" $args.satp = "VMW_SATP_ALUA" $args.pspoption = "iops=1" $args.vendor = "3PARdata" # Create A new satp rule for 3PAR $esxcli.storage.nmp.satp.rule.add.Invoke($args) #Load and re-claim rules $esxcli.storage.core.claimrule.load.Invoke() } } Catch { Write-Error $Error[0] } } #end Process }#end New-3ParSatpAuaRule <# .SYNOPSIS Set Multi Pathing Policy .DESCRIPTION Set Multi Pathing Policy. E.g. Round Robin .PARAMETER ESXiHost ESXi Host Server Name .PARAMETER LUN [ScsiLunImpl] LUN .PARAMETER MultipathPolicy Mulitpath Policy Valid Values: "RoundRobin","Fixed","MostRecentlyUsed" .PARAMETER queueFullSampleSize Default = 32 .PARAMETER queueFullThreshold Default = 4 .INPUTS N/A .OUTPUTS N/A .EXAMPLE #Set RR for New Host for All LUN's $ESXiHost = "ESX01" $VMHost = Get-VMHost $ESXiHost $AllLUNs = Get-ScsiLun -LunType disk -VmHost $VMhost $AllLUNs | Set-MPPolicy -ESXiHost $ESXiHost -MultipathPolicy "RoundRobin" .EXAMPLE #Set RR for new LUN on all Hosts $LUN = Get-VMHost | Get-ScsiLun -LunType disk | Where {$_.CanonicalName -eq "naa.40000bb000000000000000b600007847"} Get-VMHost | Select name | Set-MPPolicy -LUN $LUN -MultipathPolicy "RoundRobin" #> Function Set-MPPolicy { [CmdletBinding(SupportsShouldProcess=$true)] Param( [Parameter(Position = 0, Mandatory=$true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName=$true)] $LUN, [Parameter(Position = 1, Mandatory=$true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName=$true)] [string]$ESXiHost, [Parameter(Mandatory=$true)] [ValidateSet("RoundRobin","Fixed","MostRecentlyUsed")] [string]$MultipathPolicy, [Parameter(Mandatory=$false)] [int]$queueFullSampleSize = 32, [Parameter(Mandatory=$false)] [int]$queueFullThreshold = 4 ) #end param Process { Try { # Get CLI $esxcli = Get-CLI -ESXiHostName $ESXiHost $Device = $esxcli.storage.core.device.list.Invoke(@{device=$LUN.CanonicalName}) #Set Queue Sample Size if($Device.Vendor -eq "3PARdata") { # Set Multipath Policy if($LUN.MultipathPolicy -ne $MultipathPolicy) { Write-Host "Setting Multi Pathing Policy for $($LUN.CanonicalName) $MultipathPolicy" -ForegroundColor Yellow $LUN | Set-ScsiLun -MultipathPolicy $MultipathPolicy } else { Write-Verbose "Multipath policy for $($LUN.CanonicalName) already set" } #Set QueueFullSampleSize and QueueFullThreshold if($Device.QueueFullSampleSize -ne $queueFullSampleSize -or $Device.QueueFullThreshold -ne $queueFullThreshold) { Write-Host "Setting Multi Pathing Policy for $($LUN.CanonicalName) to queuefullsamplesize=$queueFullSampleSize;queuefullthreshold=$queueFullThreshold" -ForegroundColor Yellow $esxcli.storage.core.device.set.Invoke(@{device=$LUN.CanonicalName;queuefullsamplesize=$queueFullSampleSize;queuefullthreshold=$queueFullThreshold}) } } } Catch { Write-Error $Error[0] } } #end Process }#end Set-MPPolicy <# .SYNOPSIS Returns a list of RDM Devices in an environment .DESCRIPTION Get-RDMDevice returns a list of raw device mapping devices that are currently mapped to a virtual machine This can be used to generate a list of devices that should have a perennial reservation set to true. .PARAMETER Location Cluster Location i.e. "Production" .INPUTS N/A .OUTPUTS All ScsiCanonicalName's of RDM devices .EXAMPLE #This will return all RDM devices connected to virtual machines in the environment. Get-RDMDevice .EXAMPLE #Return all RDM's In Production cluster Get-RDMDevice -Location Production #> function Get-RDMDevice { [CmdletBinding()] Param ( #Enter location in vCenter to search under [Parameter(Mandatory=$false, ValueFromPipelineByPropertyName=$true, Position=0)] $Location ) Process { #Confirm Connection to VCenter Confirm-VCenterServer -vCenter $vCenter #If the location is specified, get all VMs in the specified location that has a RDM attached if ($Location) { $RDMDevices = Get-VM -Location $Location | Get-HardDisk -DiskType "RawPhysical","RawVirtual" } else { #If the location is not specified, get all VMs in the environment that have a RDM attached $RDMDevices = Get-VM | Get-HardDisk -DiskType "RawPhysical","RawVirtual" } Write-Output $RDMDevices } } <# .SYNOPSIS Sets a perennial reservation on a device connected to ESXi host(s) to true or false .DESCRIPTION Set-DevicePerennialReservation sets the perennially reserved settings of device(s) connected to ESXi host(s) to either true or false. .PARAMETER ScsiCanonicalName ScsiCanonicalName of Device .PARAMETER ESXiHost ESXiHost Name .PARAMETER PerenniallyReserved Sets PerenniallyReserved to true/false .INPUTS N/A .OUTPUTS N/A .EXAMPLE #Sets the device naa.40000bb000000000000000b600007847 on ESXi host ESXi01 to perenially reserved Set-DevicePerennialReservation -ScsiCanonicalName naa.40000bb000000000000000b600007847 -ESXiHost ESXi01 -PerenniallyReserved:$true .EXAMPLE #Sets the device naa.40000bb000000000000000b600007847 on all ESXi Hosts to perenially reserved Get-VMHost | ForEach-Object { Set-DevicePerennialReservation -ScsiCanonicalName naa.40000bb000000000000000b600007847 -ESXiHost $_.Name -PerenniallyReserved:$true } .EXAMPLE #Uses the Get-RDMDevice function to get all RDM devices in the environment, and then pipes that to Set-DevicePerennialReservation to set all of the devices to perenially reserved on all ESXi hosts within the location Cluster1. Get-RDMDevice | Set-DevicePerennialReservation -ESXiHost ESXi01 -PerenniallyReserved:$true #> function Set-DevicePerennialReservation { [CmdletBinding()] Param ( [Parameter(Mandatory=$false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName=$true, Position=0)] [String]$ScsiCanonicalName, [Parameter(Mandatory=$true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName=$true)] $ESXiHost, [Parameter(Mandatory=$true)] [Bool]$PerenniallyReserved ) Process { if($ScsiCanonicalName) { # Get CLI $esxcli = Get-CLI -ESXiHostName $ESXiHost # Get device $Device = $esxcli.storage.core.device.list.Invoke(@{device=$ScsiCanonicalName}) #Set PerenniallyReserved if($Device.IsPerenniallyReserved -ne $PerenniallyReserved) { Write-Host "Setting Perennially Reserved for $ScsiCanonicalName to $PerenniallyReserved" -ForegroundColor Yellow $esxcli.storage.core.device.setconfig.Invoke(@{device=$ScsiCanonicalName;perenniallyreserved=$PerenniallyReserved}) } } } } <# .SYNOPSIS Initializes new Host .DESCRIPTION Initializes new Host. Adds ALUA Rule, sets RR for all LUNS and Adds Perennial Reservations .PARAMETER ESXiHost ESXiHost Name .INPUTS N/A .OUTPUTS N/A .EXAMPLE Initialize-NewHost -ESXiHost ESXi01 #> function Initialize-NewHost { [CmdletBinding()] Param ( [Parameter(Mandatory=$false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName=$true, Position=0)] [String]$ESXiHost ) Process { Confirm-VCenterServer #Confirm 3PARdata ALUA Rule New-3ParSatpAluaRule -ESXiHost $ESXiHost #Set RR for New Host for All LUN's $VMHost = Get-VMHost $ESXiHost $AllLUNs = Get-ScsiLun -LunType disk -VmHost $VMhost $AllLUNs | Set-MPPolicy -ESXiHost $ESXiHost -MultipathPolicy "RoundRobin" #Set Perennial Reservation on all RDM's for the host Get-RDMDevice | Set-DevicePerennialReservation -ESXiHost $ESXiHost -PerenniallyReserved:$true $Success = Read-Host "$ESXiHost Initialized Successfully, reboot? y/n" if($Success -eq "y") { if($esxcli.system.maintenanceMode.get.Invoke() -eq "Disabled") { Write-Host "Entering Maintenace Mode..." Set-VMHost -VMHost $ESXiHost -State "Maintenance" } Write-Host "Rebooting Server..." Restart-VMHost $ESXiHost -Force Read-Host "Please Hit Enter after server has finished rebooting..." } else { Write-Host "Please remember to Reboot When Finished" -ForegroundColor Magenta Read-Host "Press Enter to continue." } } } <# .SYNOPSIS Initializes new RDM .DESCRIPTION Initializes new RDM and add to all hosts .PARAMETER ScsiCanonicalName ScsiCanonicalName Name of the RDM .INPUTS N/A .OUTPUTS N/A .EXAMPLE Initialize-NewRDM -ScsiCanonicalName naa.40000bb000000000000000b600007847 .EXAMPLE # Source VMWare $RDM = Get-RDMDevice $RDM | Export-Csv .\RDMList.csv # Destination Vmware $RDM = Import-Csv .\RDMList.csv $RDM | Initialize-NewRDM -ErrorAction SilentlyContinue #> function Initialize-NewRDM { [CmdletBinding()] Param ( [Parameter(Mandatory=$false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName=$true, Position=0)] [String]$ScsiCanonicalName ) Process { Confirm-VCenterServer #Loop through all VMHosts Get-VMHost | ForEach-Object { #Set RR for New Host for LUN $VMHost = Get-VMHost $_.Name $LUN = Get-ScsiLun -CanonicalName $ScsiCanonicalName -VmHost $VMhost $LUN | Set-MPPolicy -ESXiHost $_.Name -MultipathPolicy "RoundRobin" #Sets the ScsiCanonicalName on all ESXi Hosts to perenially reserved Set-DevicePerennialReservation -ScsiCanonicalName $ScsiCanonicalName -ESXiHost $_.Name -PerenniallyReserved:$true } Write-Host "RDM $ScsiCanonicalName Initialized Successfully" } } #Only present desired functions Export-ModuleMember -Function Connect-VCenterServer, Disconnect-VCenterServer, Confirm-VCenterServer, Get-CLI, New-3ParSatpAluaRule, Set-MPPolicy, Get-RDMDevice, Set-DevicePerennialReservation, Initialize-NewHost, Initialize-NewRDM