Cody.PureStorage.FlashArray.VMware.psm1
write-warning -Message "This PowerShell module has been deprecated. Please uninstall this one and install the new one PureStorage.FlashArray.VMware" function get-faVolumeNameFromVvolUuid{ <# .SYNOPSIS Connects to vCenter and FlashArray to return the FA volume that is a VVol virtual disk. .DESCRIPTION Takes in a VVol UUID to identify what volume it is on the FlashArray. If a VVol UUID is not specified it will ask you for a VM and then a VMDK and will find the UUID for you. .INPUTS FlashArray connection and VVol UUID. .OUTPUTS Returns volume name. .NOTES Version: 1.1 Author: Cody Hosterman https://codyhosterman.com Creation Date: 01/31/2019 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> #Import PowerCLI. Requires PowerCLI version 6.3 or later. Will fail here if PowerCLI cannot be installed #Will try to install PowerCLI with PowerShellGet if PowerCLI is not present. [CmdletBinding()] Param( [Parameter(Position=0)] [string]$purevip, [Parameter(Position=1,ValueFromPipeline=$True)] [Microsoft.PowerShell.Commands.WebRequestSession]$faSession, [Parameter(Position=2,mandatory=$true)] [string]$vvolUUID, [Parameter(Position=3,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray ) if ($vvolUUID -eq "") { throw "You must enter a VVol UUID" } if ($purevip -ne "") { Write-Warning -Message "purevip will be deprecated in a future version. Please use new-pfaarray and pass in only the flasharray parameter." } if ($null -ne $faSession) { Write-Warning -Message "faSession will be deprecated in a future version. Please use new-pfaarray and pass in only the flasharray parameter." } if (($null -eq $flasharray) -and ($purevip -eq "")) { throw "Please use new-pfaarray and pass in a FlashArray connection." } $ErrorActionPreference = "stop" if ($null -eq $faSession) { if ($null -eq $flasharray) { if ($global:faRestSession -ne "") { $faSession = $global:faRestSession } else { throw "Please pass in a FlashArray connection using new-pfaarray" } } else { $faSession = new-pureflasharrayRestSession -flasharray $flasharray $purevip = $flasharray.EndPoint } } else { if ($null -ne $flasharray) { $purevip = $flasharray.EndPoint } } #Pull tags that match the volume with that UUID add-type @" using System.Net; using System.Security.Cryptography.X509Certificates; public class TrustAllCertsPolicy : ICertificatePolicy { public bool CheckValidationResult( ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) { return true; } } "@ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy $volumeTags = Invoke-RestMethod -Method Get -Uri "https://$($purevip)/api/1.14/volume?tags=true&filter=value='${vvolUUID}'" -WebSession $faSession -ErrorAction Stop $volumeName = $volumeTags |where-object {$_.key -eq "PURE_VVOL_ID"} if ($null -ne $volumeName) { write-host return $volumeName.name } else { throw "The VVol was not found on this FlashArray" } } function new-pureflasharrayRestSession { <# .SYNOPSIS Connects to FlashArray and creates a REST connection. .DESCRIPTION For operations that are in the FlashArray REST, but not in the Pure Storage PowerShell SDK yet, this provides a connection for invoke-restmethod to use. .INPUTS FlashArray connection or FlashArray IP/FQDN and credentials .OUTPUTS Returns REST session .NOTES Version: 1.1 Author: Cody Hosterman https://codyhosterman.com Creation Date: 01/31/2019 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0)] [string]$purevip, [Parameter(Position=1,ValueFromPipeline=$True)] [System.Management.Automation.PSCredential]$faCreds, [Parameter(Position=2,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray ) if ($purevip -ne "") { Write-Warning -Message "purevip will be deprecated in a future version. Please pass in only the flasharray parameter." } if ($null -ne $faCreds) { Write-Warning -Message "faCreds will be deprecated in a future version. Please use new-pfaarray and pass in only the flasharray parameter." } if ($null -eq $flasharray) { if (($purevip -eq "") -or ($null -eq $faCreds)) { throw "Please use new-pfaarray and pass in the flasharray parameter. " } } #Connect to FlashArray add-type @" using System.Net; using System.Security.Cryptography.X509Certificates; public class TrustAllCertsPolicy : ICertificatePolicy { public bool CheckValidationResult( ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) { return true; } } "@ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy #Create FA session to obtain token if ($null -eq $flasharray) { $flasharray = New-PfaArray -EndPoint $purevip -Credentials $faCreds -IgnoreCertificateError } #Create FA REST session $SessionAction = @{ api_token = $flasharray.ApiToken } Invoke-RestMethod -Method Post -Uri "https://$($flasharray.Endpoint)/api/$($flasharray.apiversion)/auth/session" -Body $SessionAction -SessionVariable Session -ErrorAction Stop |Out-Null $global:faRestSession = $Session return $global:faRestSession } function remove-pureflasharrayRestSession { <# .SYNOPSIS Disconnects a FlashArray REST session .DESCRIPTION Takes in a FlashArray Connection or session and disconnects on the FlashArray. .INPUTS FlashArray connection or session .OUTPUTS Returns success or failure. .NOTES Version: 1.1 Author: Cody Hosterman https://codyhosterman.com Creation Date: 01/31/2019 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0)] [string]$purevip, [Parameter(Position=1,ValueFromPipeline=$True)] [Microsoft.PowerShell.Commands.WebRequestSession]$faSession, [Parameter(Position=2,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray ) if ($purevip -ne "") { Write-Warning -Message "purevip will be deprecated in a future version. Please pass in only the flasharray parameter." } if (($purevip -eq "") -and ($null -eq $flasharray)) { throw "Please pass in the flasharray parameter." } if ($null -ne $flasharray) { $purevip = $flasharray.endpoint $apiVersion = $flasharray.ApiVersion } else { $apiVersion = 1.12 } if ($null -eq $faSession) { $faSession = $global:faRestSession if ($null -eq $faSession) { throw "No REST session found. You must enter in a REST session to disconnect." } } #Delete FA session Invoke-RestMethod -Method Delete -Uri "https://${purevip}/api/${apiVersion}/auth/session" -WebSession $faSession -ErrorAction Stop |Out-Null } function get-vmdkFromWindowsDisk { <# .SYNOPSIS Returns the VM disk object that corresponds to a given Windows file system .DESCRIPTION Takes in a drive letter and a VM object and returns a matching VMDK object .INPUTS VM, Drive Letter .OUTPUTS Returns VMDK object .NOTES Version: 1.0 Author: Cody Hosterman https://codyhosterman.com Creation Date: 08/24/2018 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$false,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine]$vm, [Parameter(Position=1,mandatory=$false)] [string]$driveLetter ) if ($null -eq $global:defaultviserver) { throw "There is no PowerCLI connection to a vCenter, please connect first with connect-viserver." } if ($null -eq $vm) { try { $vmName = Read-Host "Please enter in the name of your VM" $vm = get-vm -name $vmName -ErrorAction Stop } catch { throw $Global:Error[0] } } try { $guest = $vm |Get-VMGuest } catch { throw $Error[0] } if ($guest.State -ne "running") { throw "This VM does not have VM tools running" } if ($guest.GuestFamily -ne "windowsGuest") { throw "This is not a Windows VM--it is $($guest.OSFullName)" } try { $advSetting = Get-AdvancedSetting -Entity $vm -Name Disk.EnableUUID -ErrorAction Stop } catch { throw $Error[0] } if ($advSetting.value -eq "FALSE") { throw "The VM $($vm.name) has the advanced setting Disk.EnableUUID set to FALSE. This must be set to TRUE for this cmdlet to work." } if (($null -eq $driveLetter) -or ($driveLetter -eq "")) { try { $driveLetter = Read-Host "Please enter in a drive letter" if (($null -eq $driveLetter) -or ($driveLetter -eq "")) { throw "No drive letter entered" } } catch { throw $Global:Error[0] } } try { $VMdiskSerialNumber = $vm |Invoke-VMScript -ScriptText "get-partition -driveletter $($driveLetter) | get-disk | ConvertTo-CSV -NoTypeInformation" -WarningAction silentlyContinue -ErrorAction Stop |ConvertFrom-Csv } catch { throw $Error[0] } if (![bool]($VMDiskSerialNumber.PSobject.Properties.name -match "serialnumber")) { throw ($VMdiskSerialNumber |Out-String) } try { $vmDisk = $vm | Get-HardDisk |Where-Object {$_.ExtensionData.backing.uuid.replace("-","") -eq $VMdiskSerialNumber.SerialNumber} } catch { throw $Global:Error[0] } if ($null -ne $vmDisk) { return $vmDisk } else { throw "Could not match the VM disk to a VMware virtual disk" } } function new-faHostFromVmHost { <# .SYNOPSIS Create a FlashArray host from an ESXi vmhost object .DESCRIPTION Takes in a vCenter ESXi host and creates a FlashArray host .INPUTS FlashArray connection, a vCenter ESXi vmHost, and iSCSI/FC option .OUTPUTS Returns new FlashArray host object. .NOTES Version: 1.0 Author: Cody Hosterman https://codyhosterman.com Creation Date: 09/09/2018 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.Inventory.VMHost]$esxi, [Parameter(Position=1,mandatory=$true)] [string]$protocolType, [Parameter(Position=2,mandatory=$true,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray ) if (($protocolType -ne "FC") -and ($protocolType -ne "iSCSI")) { throw 'No valid protocol entered. Please make sure $protocolType is set to either "FC" or "iSCSI"' } if ($null -eq $global:defaultviserver) { throw "There is no PowerCLI connection to a vCenter, please connect first with connect-viserver." } if ($protocolType -eq "iSCSI") { $iscsiadapter = $esxi | Get-VMHostHBA -Type iscsi | Where-Object {$_.Model -eq "iSCSI Software Adapter"} if ($null -eq $iscsiadapter) { throw "No Software iSCSI adapter found on host $($esxi.NetworkInfo.HostName)." } else { $iqn = $iscsiadapter.ExtensionData.IScsiName } try { $newFaHost = New-PfaHost -Array $flasharray -Name $esxi.NetworkInfo.HostName -IqnList $iqn -ErrorAction stop return $newFaHost } catch { Write-Error $Global:Error[0] return } } if ($protocolType -eq "FC") { $wwns = $esxi | Get-VMHostHBA -Type FibreChannel | Select-Object VMHost,Device,@{N="WWN";E={"{0:X}" -f $_.PortWorldWideName}} | Format-table -Property WWN -HideTableHeaders |out-string $wwns = (($wwns.Replace("`n","")).Replace("`r","")).Replace(" ","") $wwns = &{for ($i = 0;$i -lt $wwns.length;$i += 16) { $wwns.substring($i,16) }} try { $newFaHost = New-PfaHost -Array $flasharray -Name $esxi.NetworkInfo.HostName -WwnList $wwns -ErrorAction stop return $newFaHost } catch { Write-Error $Global:Error[0] return } } } function get-faHostFromVmHost { <# .SYNOPSIS Gets a FlashArray host object from a ESXi vmhost object .DESCRIPTION Takes in a vmhost and returns a matching FA host if found .INPUTS FlashArray connection and a vCenter ESXi host .OUTPUTS Returns FA host if matching one is found. .NOTES Version: 1.0 Author: Cody Hosterman https://codyhosterman.com Creation Date: 09/09/2018 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.Inventory.VMHost]$esxi, [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray ) if ($null -eq $global:defaultviserver) { throw "There is no PowerCLI connection to a vCenter, please connect first with connect-viserver." } $iscsiadapter = $esxi | Get-VMHostHBA -Type iscsi | Where-Object {$_.Model -eq "iSCSI Software Adapter"} $wwns = $esxi | Get-VMHostHBA -Type FibreChannel | Select-Object VMHost,Device,@{N="WWN";E={"{0:X}" -f $_.PortWorldWideName}} | Format-table -Property WWN -HideTableHeaders |out-string $wwns = (($wwns.Replace("`n","")).Replace("`r","")).Replace(" ","") $wwns = &{for ($i = 0;$i -lt $wwns.length;$i += 16) { $wwns.substring($i,16) }} $fahosts = Get-PFAHosts -array $flasharray -ErrorAction Stop if ($null -ne $iscsiadapter) { $iqn = $iscsiadapter.ExtensionData.IScsiName foreach ($fahost in $fahosts) { if ($fahost.iqn.count -ge 1) { foreach ($fahostiqn in $fahost.iqn) { if ($iqn.ToLower() -eq $fahostiqn.ToLower()) { return $fahost } } } } } if ($null -ne $wwns) { foreach ($wwn in $wwns) { foreach ($fahost in $fahosts) { if ($fahost.wwn.count -ge 1) { foreach($fahostwwn in $fahost.wwn) { if ($wwn.ToLower() -eq $fahostwwn.ToLower()) { return $fahost } } } } } } else { throw "No matching host could be found on the FlashArray" } } function get-faHostGroupfromVcCluster { <# .SYNOPSIS Retrieves a FA host group from an ESXi cluster .DESCRIPTION Takes in a vCenter Cluster and retrieves corresonding host group .INPUTS FlashArray connection and a vCenter cluster .OUTPUTS Returns success or failure. .NOTES Version: 1.0 Author: Cody Hosterman https://codyhosterman.com Creation Date: 09/09/2018 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster]$cluster, [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray ) if ($null -eq $global:defaultviserver) { throw "There is no PowerCLI connection to a vCenter, please connect first with connect-viserver." } $esxiHosts = $cluster |Get-VMHost $faHostGroups = @() $faHostGroupNames = @() foreach ($esxiHost in $esxiHosts) { try { $faHost = $esxiHost | get-faHostFromVmHost -flasharray $flasharray if ($null -ne $faHost.hgroup) { if ($faHostGroupNames.contains($faHost.hgroup)) { continue } else { $faHostGroupNames += $faHost.hgroup $faHostGroup = Get-PfaHostGroup -Array $flasharray -Name $faHost.hgroup $faHostGroups += $faHostGroup } } } catch{ continue } } if ($null -eq $faHostGroup) { throw "No host group found for this cluster" } if ($faHostGroups.count -gt 1) { Write-Warning -Message "This cluster spans more than one host group. The recommendation is to have only one host group per cluster" } return $faHostGroups } function new-faHostGroupfromVcCluster { <# .SYNOPSIS Create a host group from an ESXi cluster .DESCRIPTION Takes in a vCenter Cluster and creates hosts (if needed) and host group .INPUTS FlashArray connection, a vCenter cluster, and iSCSI/FC option .OUTPUTS Returns success or failure. .NOTES Version: 1.0 Author: Cody Hosterman https://codyhosterman.com Creation Date: 08/06/2018 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster]$cluster, [Parameter(Position=1,mandatory=$true)] [string]$protocolType, [Parameter(Position=2,mandatory=$true,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray ) if ($null -eq $global:defaultviserver) { throw "There is no PowerCLI connection to a vCenter, please connect first with connect-viserver." } if (($protocolType -ne "FC") -and ($protocolType -ne "iSCSI")) { throw 'No valid protocol entered. Please make sure $protocolType is set to either "FC" or "iSCSI"' } $hostGroup = $cluster |get-faHostGroupfromVcCluster -flasharray $flasharray -ErrorAction SilentlyContinue if ($hostGroup.count -gt 1) { throw "The cluster already is configured on the FlashArray and spans more than one host group. This cmdlet does not support a multi-hostgroup configuration." } if ($hostGroup.count -eq 1) { $clustername = $hostGroup.name } $esxiHosts = $cluster |Get-VMHost $faHosts = @() foreach ($esxiHost in $esxiHosts) { $faHost = $null try { $faHost = $esxiHost |get-faHostFromVmHost -flasharray $flasharray } catch {} if ($null -eq $faHost) { try { $faHost = $esxiHost |new-faHostFromVmHost -flasharray $flasharray -protocolType $protocolType -ErrorAction Stop $faHosts += $faHost } catch { Write-Error $Global:Error[0] throw "Could not create host. Cannot create host group." } } else { $faHosts += $faHost } } #FlashArray only supports Alphanumeric or the dash - character in host group names. Checking for VMware cluster name compliance and removing invalid characters. if ($null -eq $hostGroup) { if ($cluster.Name -match "^[a-zA-Z0-9\-]+$") { $clustername = $cluster.Name } else { $clustername = $cluster.Name -replace "[^\w\-]", "" $clustername = $clustername -replace "[_]", "" $clustername = $clustername -replace " ", "" } $hg = Get-PfaHostGroup -Array $flasharray -Name $clustername -ErrorAction SilentlyContinue if ($null -ne $hg) { if ($hg.hosts.count -ne 0) { #if host group name is already in use and has only unexpected hosts i will create a new one with a random number at the end $nameRandom = get-random -Minimum 1000 -Maximum 9999 $hostGroup = New-PfaHostGroup -Array $flasharray -Name "$($clustername)-$($nameRandom)" -ErrorAction stop $clustername = "$($clustername)-$($nameRandom)" } } else { #if there is no host group, it will be created $hostGroup = New-PfaHostGroup -Array $flasharray -Name $clustername -ErrorAction stop } } $faHostNames = @() foreach ($faHost in $faHosts) { if ($null -eq $faHost.hgroup) { $faHostNames += $faHost.name } } #any hosts that are not already in the host group will be added Add-PfaHosts -Array $flasharray -Name $clustername -HostsToAdd $faHostNames -ErrorAction Stop |Out-Null $fahostGroup = Get-PfaHostGroup -Array $flasharray -Name $clustername return $fahostGroup } function set-vmHostPureFaiSCSI{ <# .SYNOPSIS Configure FlashArray iSCSI target information on ESXi host .DESCRIPTION Takes in an ESXi hosts and configures FlashArray iSCSI target info .INPUTS FlashArray connection and an ESXi host .OUTPUTS Returns ESXi iSCSI targets. .NOTES Version: 1.0 Author: Cody Hosterman https://codyhosterman.com Creation Date: 09/09/2018 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.Inventory.VMHost]$esxi, [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray ) if ($esxi.ExtensionData.Runtime.ConnectionState -ne "connected") { Write-Warning "Host $($esxi.NetworkInfo.HostName) is not in a connected state and cannot be configured." return } $ESXitargets = @() $faiSCSItargets = Get-PfaNetworkInterfaces -Array $flasharray |Where-Object {$_.services -eq "iscsi"} if ($null -eq $faiSCSItargets) { throw "The target FlashArray does not currently have any iSCSI targets configured." } $iscsi = $esxi |Get-VMHostStorage if ($iscsi.SoftwareIScsiEnabled -ne $true) { $esxi | get-vmhoststorage |Set-VMHostStorage -SoftwareIScsiEnabled $True |out-null } foreach ($faiSCSItarget in $faiSCSItargets) { $iscsiadapter = $esxi | Get-VMHostHba -Type iScsi | Where-Object {$_.Model -eq "iSCSI Software Adapter"} if (!(Get-IScsiHbaTarget -IScsiHba $iscsiadapter -Type Send -ErrorAction stop | Where-Object {$_.Address -cmatch $faiSCSItarget.address})) { New-IScsiHbaTarget -IScsiHba $iscsiadapter -Address $faiSCSItarget.address -ErrorAction stop } $esxcli = $esxi |Get-esxcli -v2 $iscsiargs = $esxcli.iscsi.adapter.discovery.sendtarget.param.get.CreateArgs() $iscsiargs.adapter = $iscsiadapter.Device $iscsiargs.address = $faiSCSItarget.address $delayedAck = $esxcli.iscsi.adapter.discovery.sendtarget.param.get.invoke($iscsiargs) |where-object {$_.name -eq "DelayedAck"} $loginTimeout = $esxcli.iscsi.adapter.discovery.sendtarget.param.get.invoke($iscsiargs) |where-object {$_.name -eq "LoginTimeout"} if ($delayedAck.Current -eq "true") { $iscsiargs = $esxcli.iscsi.adapter.discovery.sendtarget.param.set.CreateArgs() $iscsiargs.adapter = $iscsiadapter.Device $iscsiargs.address = $faiSCSItarget.address $iscsiargs.value = "false" $iscsiargs.key = "DelayedAck" $esxcli.iscsi.adapter.discovery.sendtarget.param.set.invoke($iscsiargs) |out-null } if ($loginTimeout.Current -ne "30") { $iscsiargs = $esxcli.iscsi.adapter.discovery.sendtarget.param.set.CreateArgs() $iscsiargs.adapter = $iscsiadapter.Device $iscsiargs.address = $faiSCSItarget.address $iscsiargs.value = "30" $iscsiargs.key = "LoginTimeout" $esxcli.iscsi.adapter.discovery.sendtarget.param.set.invoke($iscsiargs) |out-null } $ESXitargets += Get-IScsiHbaTarget -IScsiHba $iscsiadapter -Type Send -ErrorAction stop | Where-Object {$_.Address -cmatch $faiSCSItarget.address} } return $ESXitargets } function set-clusterPureFAiSCSI { <# .SYNOPSIS Configure an ESXi cluster with FlashArray iSCSI information .DESCRIPTION Takes in a vCenter Cluster and configures iSCSI on each host. .INPUTS FlashArray connection and a vCenter cluster. .OUTPUTS Returns iSCSI targets. .NOTES Version: 1.0 Author: Cody Hosterman https://codyhosterman.com Creation Date: 09/09/2018 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster]$cluster, [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray ) $esxihosts = $cluster |Get-VMHost $esxiiSCSItargets = @() $hostCount = 0 foreach ($esxihost in $esxihosts) { if ($hostCount -eq 0) { Write-Progress -Activity "Configuring iSCSI" -status "Host: $esxihost" -percentComplete 0 } else { Write-Progress -Activity "Configuring iSCSI" -status "Host: $esxihost" -percentComplete (($hostCount / $esxihosts.count) *100) } $esxiiSCSItargets += $esxihost | set-vmHostPureFaiSCSI -flasharray $flasharray $hostCount++ } return $esxiiSCSItargets } function get-faVolfromVMFS { <# .SYNOPSIS Retrieves the FlashArray volume that hosts a VMFS datastore. .DESCRIPTION Takes in a VMFS datastore and one or more FlashArrays and returns the volume if found. .INPUTS FlashArray connection(s) and a VMFS datastore. .OUTPUTS Returns FlashArray volume or null if not found. .NOTES Version: 1.0 Author: Cody Hosterman https://codyhosterman.com Creation Date: 09/10/2018 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.Datastore]$datastore, [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray ) if ($datastore.Type -ne 'VMFS') { throw "This is not a VMFS datastore." } $lun = $datastore.ExtensionData.Info.Vmfs.Extent.DiskName |select-object -unique if ($lun -like 'naa.624a9370*') { $pureVolumes = Get-PfaVolumes -Array $flasharray $volserial = ($lun.ToUpper()).substring(12) $purevol = $purevolumes | where-object { $_.serial -eq $volserial } if ($null -ne $purevol.name) { return $purevol } else { return $null } } else { throw "This VMFS is not hosted on FlashArray storage." } } function new-faVolVmfs { <# .SYNOPSIS Create a new VMFS on a new FlashArray volume .DESCRIPTION Creates a new FlashArray-based VMFS and presents it to a cluster. .INPUTS FlashArray connection, a vCenter cluster, a volume size, and name. .OUTPUTS Returns a VMFS object. .NOTES Version: 1.0 Author: Cody Hosterman https://codyhosterman.com Creation Date: 09/10/2018 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster]$cluster, [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray, [Parameter(Position=2,mandatory=$true)] [string]$volName, [Parameter(Position=3)] [int]$sizeInGB, [Parameter(Position=4)] [int]$sizeInTB ) if (($sizeInGB -eq 0) -and ($sizeInTB -eq 0)) { throw "Please enter a size in GB or TB" } elseif (($sizeInGB -ne 0) -and ($sizeInTB -ne 0)) { throw "Please only enter a size in TB or GB, not both." } elseif ($sizeInGB -ne 0) { $volSize = $sizeInGB * 1024 *1024 *1024 } else { $volSize = $sizeInTB * 1024 *1024 *1024 * 1024 } try { $hostGroup = $cluster | get-faHostGroupfromVcCluster -flasharray $flasharray -ErrorAction Stop } catch { throw $Global:Error[0] } $newVol = New-PfaVolume -Array $flasharray -Size $volSize -VolumeName $volName -ErrorAction Stop New-PfaHostGroupVolumeConnection -Array $flasharray -VolumeName $newVol.name -HostGroupName $hostGroup.name |Out-Null $esxi = $cluster | get-vmhost | where-object {($_.version -like '5.5.*') -or ($_.version -like '6.*')}| where-object {($_.ConnectionState -eq 'Connected')} |Select-Object -last 1 $cluster| Get-VMHost | Get-VMHostStorage -RescanAllHba |Out-Null $newNAA = "naa.624a9370" + $newVol.serial.toLower() $ESXiApiVersion = $esxi.ExtensionData.Summary.Config.Product.ApiVersion try { if (($ESXiApiVersion -eq "5.5") -or ($ESXiApiVersion -eq "6.0") -or ($ESXiApiVersion -eq "5.1")) { $newVMFS = $esxi |new-datastore -name $newVol.name -vmfs -Path $newNAA -FileSystemVersion 5 -ErrorAction Stop } else { $newVMFS = $esxi |new-datastore -name $newVol.name -vmfs -Path $newNAA -FileSystemVersion 6 -ErrorAction Stop } return $newVMFS } catch { Write-Error $Global:Error[0] Remove-PfaHostGroupVolumeConnection -Array $flasharray -VolumeName $newVol.name -HostGroupName $hostGroup.name Remove-PfaVolumeOrSnapshot -Array $flasharray -Name $newVol.name Remove-PfaVolumeOrSnapshot -Array $flasharray -Name $newVol.name -Eradicate return $null } } function add-faVolVmfsToCluster { <# .SYNOPSIS Add an existing FlashArray-based VMFS to another VMware cluster. .DESCRIPTION Takes in a vCenter Cluster and a datastore and the corresponding FlashArray .INPUTS FlashArray connection, a vCenter cluster, and a datastore .OUTPUTS Returns the FlashArray host group connection. .NOTES Version: 1.0 Author: Cody Hosterman https://codyhosterman.com Creation Date: 09/10/2018 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster]$cluster, [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray, [Parameter(Position=2,mandatory=$true,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.Datastore]$datastore ) try { $pureVol = $datastore | get-faVolfromVMFS -flasharray $flasharray -ErrorAction Stop $hostGroup = $cluster |get-faHostGroupfromVcCluster -flasharray $flasharray -ErrorAction Stop $faConnection = New-PfaHostGroupVolumeConnection -Array $flasharray -VolumeName $pureVol.name -HostGroupName $hostGroup.name -ErrorAction Stop } catch { Write-Error $Global:Error return $null } try { $cluster| Get-VMHost | Get-VMHostStorage -RescanAllHba -RescanVmfs -ErrorAction Stop |Out-Null } catch { Write-Error $Global:Error[0] Remove-PfaHostGroupVolumeConnection -Array $flasharray -VolumeName $pureVol.name -HostGroupName $hostGroup.name |Out-Null return $null } return $faConnection } function set-faVolVmfsCapacity { <# .SYNOPSIS Increase the size of a FlashArray-based VMFS datastore. .DESCRIPTION Takes in a datastore, the corresponding FlashArray, and a new size. Both the volume and the VMFS will be grown. .INPUTS FlashArray connection, a size, and a datastore .OUTPUTS Returns the datastore. .NOTES Version: 1.0 Author: Cody Hosterman https://codyhosterman.com Creation Date: 09/11/2018 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray, [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.Datastore]$datastore, [Parameter(Position=2)] [int]$sizeInGB, [Parameter(Position=3)] [int]$sizeInTB ) if (($sizeInGB -eq 0) -and ($sizeInTB -eq 0)) { throw "Please enter a size in GB or TB" } elseif (($sizeInGB -ne 0) -and ($sizeInTB -ne 0)) { throw "Please only enter a size in TB or GB, not both." } elseif ($sizeInGB -ne 0) { $volSize = $sizeInGB * 1024 *1024 *1024 } else { $volSize = $sizeInTB * 1024 *1024 *1024 * 1024 } if ($volSize -le $pureVol.size) { throw "The new size cannot be smaller than the existing size. VMFS volumes cannot be shrunk." return $null } try { $pureVol = $datastore | get-faVolfromVMFS -flasharray $flasharray -ErrorAction Stop Resize-PfaVolume -Array $flasharray -VolumeName $pureVol.name -NewSize $volSize |Out-Null } catch { Write-Error $Global:Error return $null } try { $datastore| Get-VMHost | Get-VMHostStorage -RescanAllHba -RescanVmfs -ErrorAction Stop |Out-Null $esxiView = Get-View -Id ($Datastore.ExtensionData.Host |Select-Object -last 1 | Select-Object -ExpandProperty Key) $datastoreSystem = Get-View -Id $esxiView.ConfigManager.DatastoreSystem $expandOptions = $datastoreSystem.QueryVmfsDatastoreExpandOptions($datastore.ExtensionData.MoRef) $expandedDS = $datastoreSystem.ExpandVmfsDatastore($datastore.ExtensionData.MoRef,$expandOptions[0].spec) } catch { Write-Error $Global:Error[0] return $null } return $expandedDS } function get-faVolVmfsSnapshots { <# .SYNOPSIS Retrieve all of the FlashArray snapshots of a given VMFS volume .DESCRIPTION Takes in a datastore and the corresponding FlashArray and returns any available snapshots. .INPUTS FlashArray connection and a datastore .OUTPUTS Returns any snapshots. .NOTES Version: 1.0 Author: Cody Hosterman https://codyhosterman.com Creation Date: 09/17/2018 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray, [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.Datastore]$datastore ) try { $pureVol = $datastore | get-faVolfromVMFS -flasharray $flasharray -ErrorAction Stop $volSnapshots = Get-PfaVolumeSnapshots -Array $flasharray -VolumeName $pureVol.name } catch { Write-Error $Global:Error return $null } return $volSnapshots } function new-faVolVmfsSnapshot { <# .SYNOPSIS Creates a new FlashArray snapshot of a given VMFS volume .DESCRIPTION Takes in a datastore and the corresponding FlashArray and creates a snapshot. .INPUTS FlashArray connection and a datastore .OUTPUTS Returns created snapshot. .NOTES Version: 1.0 Author: Cody Hosterman https://codyhosterman.com Creation Date: 09/17/2018 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray, [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.Datastore]$datastore, [Parameter(Position=2)] [string]$SnapName ) try { $pureVol = $datastore | get-faVolfromVMFS -flasharray $flasharray -ErrorAction Stop $NewSnapshot = New-PfaVolumeSnapshots -Array $flasharray -Sources $pureVol.name -Suffix $SnapName } catch { throw $Global:Error return $null } return $NewSnapshot } function new-faVolVmfsFromSnapshot { <# .SYNOPSIS Mounts a copy of a VMFS datastore to a VMware cluster from a FlashArray snapshot. .DESCRIPTION Takes in a snapshot name, the corresponding FlashArray, and a cluster. The VMFS copy will be resignatured and mounted. .INPUTS FlashArray connection, a snapshotName, and a cluster. .OUTPUTS Returns the new datastore. .NOTES Version: 1.0 Author: Cody Hosterman https://codyhosterman.com Creation Date: 10/24/2018 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster]$cluster, [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray, [Parameter(Position=2,mandatory=$true)] [string]$snapName ) try { $volumeName = $snapName.split(".")[0] + "-snap-" + (Get-Random -Minimum 1000 -Maximum 9999) $newVol =New-PfaVolume -Array $flasharray -Source $snapName -VolumeName $volumeName -ErrorAction Stop } catch { return $null } $hostGroup = $flasharray |get-faHostGroupfromVcCluster -cluster $cluster New-PfaHostGroupVolumeConnection -Array $flasharray -VolumeName $newVol.name -HostGroupName $hostGroup.name |Out-Null $esxi = $cluster | Get-VMHost| where-object {($_.ConnectionState -eq 'Connected')} |Select-Object -last 1 $esxi | Get-VMHostStorage -RescanAllHba -RescanVMFS -ErrorAction stop |Out-Null $hostStorage = get-view -ID $esxi.ExtensionData.ConfigManager.StorageSystem $resigVolumes= $hostStorage.QueryUnresolvedVmfsVolume() $newNAA = "naa.624a9370" + $newVol.serial.toLower() $deleteVol = $false foreach ($resigVolume in $resigVolumes) { if ($deleteVol -eq $true) { break } foreach ($resigExtent in $resigVolume.Extent) { if ($resigExtent.Device.DiskName -eq $newNAA) { if ($resigVolume.ResolveStatus.Resolvable -eq $false) { if ($resigVolume.ResolveStatus.MultipleCopies -eq $true) { write-host "The volume cannot be resignatured as more than one unresignatured copy is present. Deleting and ending." -BackgroundColor Red write-host "The following volume(s) are presented and need to be removed/resignatured first:" $resigVolume.Extent.Device.DiskName |where-object {$_ -ne $newNAA} } $deleteVol = $true break } else { $volToResignature = $resigVolume break } } } } if (($null -eq $volToResignature) -and ($deleteVol -eq $false)) { write-host "No unresolved volume found on the created volume. Deleting and ending." -BackgroundColor Red $deleteVol = $true } if ($deleteVol -eq $true) { Remove-PfaHostGroupVolumeConnection -Array $flasharray -VolumeName $newVol.name -HostGroupName $hostGroup.name |Out-Null Remove-PfaVolumeOrSnapshot -Array $flasharray -Name $newVol.name |Out-Null Remove-PfaVolumeOrSnapshot -Array $flasharray -Name $newVol.name -Eradicate |Out-Null return $null } $esxcli=get-esxcli -VMHost $esxi -v2 -ErrorAction stop $resigOp = $esxcli.storage.vmfs.snapshot.resignature.createargs() $resigOp.volumelabel = $volToResignature.VmfsLabel $esxcli.storage.vmfs.snapshot.resignature.invoke($resigOp) |out-null Start-sleep -s 5 $esxi | Get-VMHostStorage -RescanVMFS -ErrorAction stop |Out-Null $datastores = $esxi| Get-Datastore -ErrorAction stop foreach ($ds in $datastores) { $naa = $ds.ExtensionData.Info.Vmfs.Extent.DiskName if ($naa -eq $newNAA) { $resigds = $ds | Set-Datastore -Name $newVol.name -ErrorAction stop return $resigds } } } function update-faVvolVmVolumeGroup { <# .SYNOPSIS Updates the volume group on a FlashArray for a VVol-based VM. .DESCRIPTION Takes in a VM and a FlashArray connection. A volume group will be created if it does not exist, if it does, the name will be updated if inaccurate. Any volumes for the given VM will be put into that group. .INPUTS FlashArray connection, a virtual machine. .OUTPUTS Returns the FlashArray volume names of the input VM. .NOTES Version: 1.1 Author: Cody Hosterman https://codyhosterman.com Creation Date: 02/01/2019 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine[]]$vm, [Parameter(Position=1)] [string]$purevip, [Parameter(Position=2)] [System.Management.Automation.PSCredential]$faCreds, [Parameter(Position=3,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.Datastore]$datastore, [Parameter(Position=4,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray ) Begin{ if ($purevip -ne "") { Write-Warning -Message "purevip will be deprecated in a future version. Please pass in only the flasharray parameter." } if ($null -ne $faCreds) { Write-Warning -Message "faCreds will be deprecated in a future version. Please use new-pfaarray and pass in only the flasharray parameter." } $ErrorActionPreference = "stop" } Process{ if ($null -eq $flasharray) { if (($global:faRestSession -ne "") -and ($null -ne $global:faRestSession)) { $faSession = $global:faRestSession } if (($purevip -eq "") -or ($null -eq $faCreds)) { throw "Please use new-pfaarray and pass in the flasharray parameter. " } $flasharray = New-PfaArray -EndPoint $purevip -Credentials $faCreds -IgnoreCertificateError } else { $faSession = new-pureflasharrayRestSession -flasharray $flasharray } $volumeFinalNames = @() if ($null -ne $datastore) { if ($datastore.Type -ne "VVOL") { throw "This is not a VVol datastore" } if ($datastore.ExtensionData.Info.VvolDS.StorageArray[0].VendorId -ne "PURE") { throw "This is not a Pure Storage VVol datastore" } $vms = $datastore |get-vm } elseif ($null -ne $vm) { $vms = $vm } else{ throw "You must enter in either a VM object or a VVol datastore" } foreach ($vm in $vms) { $configUUID = $vm.ExtensionData.Config.VmStorageObjectId if ($null -eq $configUUID) { write-warning -message "The input VM $($vm.name) is not a VVol-based virtual machine. Skipping" continue } add-type @" using System.Net; using System.Security.Cryptography.X509Certificates; public class TrustAllCertsPolicy : ICertificatePolicy { public bool CheckValidationResult( ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) { return true; } } "@ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy $volumeConfig = Invoke-RestMethod -Method Get -Uri "https://$($flasharray.Endpoint)/api/$($flasharray.apiversion)/volume?tags=true&filter=value='${configUUID}'" -WebSession $faSession -ErrorAction Stop $configVVolName = ($volumeConfig |where-object {$_.key -eq "PURE_VVOL_ID"}).name if ($null -eq $configVVolName) { write-warning -message "The VM $($vm.name) was not found on this FlashArray. Skipping." continue } if ($vm.Name -match "^[a-zA-Z0-9\-]+$") { $vmName = $vm.Name } else { $vmName = $vm.Name -replace "[^\w\-]", "" $vmName = $vmName -replace "[_]", "" $vmName = $vmName -replace " ", "" } $vGroupRand = '{0:X}' -f (get-random -Minimum 286331153 -max 4294967295) $newName = "vvol-$($vmName)-$($vGroupRand)-vg" if ([Regex]::Matches($configVVolName, "/").Count -eq 0) { $vGroup = New-PfaVolumeGroup -Array $flasharray -Name $newName } else { $vGroup = $configVVolName.split('/')[0] $vGroup = Invoke-RestMethod -Method Put -Uri "https://$($flasharray.Endpoint)/api/$($flasharray.apiversion)/vgroup/${vGroup}?name=${newName}" -WebSession $faSession -ErrorAction Stop } $vmId = $vm.ExtensionData.Config.InstanceUuid $volumesVmId = Invoke-RestMethod -Method Get -Uri "https://$($flasharray.Endpoint)/api/$($flasharray.apiversion)/volume?tags=true&filter=value='${vmId}'" -WebSession $faSession -ErrorAction Stop $volumeNames = $volumesVmId |where-object {$_.key -eq "VMW_VmID"} foreach ($volumeName in $volumeNames) { if ([Regex]::Matches($volumeName.name, "/").Count -eq 1) { if ($newName -ne $volumeName.name.split('/')[0]) { $volName= $volumeName.name.split('/')[1] Add-PfaVolumeToContainer -Array $flasharray -Container $newName -Name $volName |Out-Null } } else { $volName= $volumeName.name Add-PfaVolumeToContainer -Array $flasharray -Container $newName -Name $volName |Out-Null } } $volumesVmId = Invoke-RestMethod -Method Get -Uri "https://$($flasharray.Endpoint)/api/$($flasharray.apiversion)/volume?tags=true&filter=value='${vmId}'" -WebSession $faSession -ErrorAction Stop $volumeFinalNames += $volumesVmId |where-object {$_.key -eq "VMW_VmID"} } } End{ remove-pureflasharrayRestSession -flasharray $flasharray -faSession $faSession |Out-Null return $volumeFinalNames.name } } function get-vvolUuidFromHardDisk { <# .SYNOPSIS Gets the VVol UUID of a virtual disk .DESCRIPTION Takes in a virtual disk object .INPUTS Virtual disk object (get-harddisk). .OUTPUTS Returns the VVol UUID. .NOTES Version: 1.1 Author: Cody Hosterman https://codyhosterman.com Creation Date: 02/01/2019 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$True,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$vmdk ) Begin { } Process { if ($vmdk.ExtensionData.Backing.backingObjectId -eq "") { throw "This is not a VVol-based hard disk." } if ((($vmdk |Get-Datastore).ExtensionData.Info.vvolDS.storageArray.vendorId) -ne "PURE") { throw "This is not a Pure Storage FlashArray VVol disk" } $uuid = $vmdk.ExtensionData.Backing.backingObjectId } End { return $uuid } } function get-faSnapshotsFromVvolHardDisk { <# .SYNOPSIS Returns all of the FlashArray snapshot names of a given hard disk .DESCRIPTION Takes in a virtual disk object .INPUTS Virtual disk object (get-harddisk). .OUTPUTS Returns all specified snapshot names. .NOTES Version: 1.1 Author: Cody Hosterman https://codyhosterman.com Creation Date: 02/01/2019 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0)] [string]$purevip, [Parameter(Position=1,ValueFromPipeline=$True)] [System.Management.Automation.PSCredential]$faCreds, [Parameter(Position=2,ValueFromPipeline=$True)] [Microsoft.PowerShell.Commands.WebRequestSession]$faSession, [Parameter(Position=3,mandatory=$True,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$vmdk, [Parameter(Position=4,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray ) Begin{ if ($purevip -ne "") { Write-Warning -Message "purevip will be deprecated in a future version. Please pass in only the flasharray parameter." } if ($null -ne $faCreds) { Write-Warning -Message "faCreds will be deprecated in a future version. Please use new-pfaarray and pass in only the flasharray parameter." } if ($null -eq $flasharray) { if (($purevip -eq "") -or ($null -eq $faCreds)) { throw "Please use new-pfaarray and pass in the flasharray parameter. " } } if ($null -eq $faSession) { if ($null -eq $flasharray) { if ($global:faRestSession -ne "") { $faSession = $global:faRestSession } else { throw "Please pass in a FlashArray connection using new-pfaarray" } } else { $faSession = new-pureflasharrayRestSession -flasharray $flasharray $purevip = $flasharray.EndPoint } } else { if ($null -ne $flasharray) { $purevip = $flasharray.EndPoint } } } Process { $vvolUuid = get-vvolUuidFromHardDisk -vmdk $vmdk if ($null -eq $flasharray) { $faVolume = get-faVolumeNameFromVvolUuid -faSession $faSession -purevip $purevip -vvolUUID $vvolUuid $volumeSnaps = Invoke-RestMethod -Method Get -Uri "https://${purevip}/api/1.13/volume/${faVolume}?snap=true" -WebSession $faSession -ErrorAction Stop } else { $faVolume = get-faVolumeNameFromVvolUuid -flasharray $flasharray -vvolUUID $vvolUuid $volumeSnaps = Invoke-RestMethod -Method Get -Uri "https://${purevip}/api/$($flasharray.apiversion)/volume/${faVolume}?snap=true" -WebSession $faSession -ErrorAction Stop } $snapNames = @() foreach ($volumeSnap in $volumeSnaps) { $snapNames += $volumeSnap.name } } End { return $snapNames } } function copy-faVvolVmdkToNewVvolVmdk { <# .SYNOPSIS Takes an existing VVol-based virtual disk and creates a new VVol virtual disk from it. .DESCRIPTION Takes in a hard disk and creates a copy of it to a certain VM. .INPUTS FlashArray connection information, a virtual machine, and a virtual disk. .OUTPUTS Returns the new hard disk. .NOTES Version: 1.1 Author: Cody Hosterman https://codyhosterman.com Creation Date: 02/01/2019 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$True,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine]$targetVm, [Parameter(Position=1,mandatory=$True,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$vmdk, [Parameter(Position=2)] [string]$purevip, [Parameter(Position=3,ValueFromPipeline=$True)] [System.Management.Automation.PSCredential]$faCreds, [Parameter(Position=4,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray ) Begin { if ($purevip -ne "") { Write-Warning -Message "purevip will be deprecated in a future version. Please pass in only the flasharray parameter." } if ($null -ne $faCreds) { Write-Warning -Message "faCreds will be deprecated in a future version. Please use new-pfaarray and pass in only the flasharray parameter." } if ($null -eq $flasharray) { if (($purevip -eq "") -or ($null -eq $faCreds)) { throw "Please use new-pfaarray and pass in the flasharray parameter. " } } $ErrorActionPreference = "stop" } Process { if ($null -eq $flasharray) { $flasharray = New-PfaArray -EndPoint $purevip -Credentials $faCreds -IgnoreCertificateError } $vvolUuid = get-vvolUuidFromHardDisk -vmdk $vmdk $faVolume = get-faVolumeNameFromVvolUuid -flasharray $flasharray -vvolUUID $vvolUuid $datastore = $vmdk | Get-Datastore $newHardDisk = New-HardDisk -Datastore $datastore -CapacityGB $vmdk.CapacityGB -VM $targetVm $newVvolUuid = get-vvolUuidFromHardDisk -vmdk $newHardDisk $newFaVolume = get-faVolumeNameFromVvolUuid -flasharray $flasharray -vvolUUID $newVvolUuid New-PfaVolume -Array $flasharray -Source $faVolume -Overwrite -VolumeName $newFaVolume |Out-Null } End { return $newHardDisk } } function copy-faSnapshotToExistingVvolVmdk { <# .SYNOPSIS Takes an snapshot and creates a new VVol virtual disk from it. .DESCRIPTION Takes in a hard disk and creates a copy of it to a certain VM. .INPUTS FlashArray connection information, a virtual machine, and a virtual disk. .OUTPUTS Returns the new hard disk. .NOTES Version: 1.1 Author: Cody Hosterman https://codyhosterman.com Creation Date: 02/01/2019 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$true)] [string]$snapshotName, [Parameter(Position=1,mandatory=$True,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$vmdk, [Parameter(Position=2)] [string]$purevip, [Parameter(Position=3,ValueFromPipeline=$True)] [System.Management.Automation.PSCredential]$faCreds, [Parameter(Position=4,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray ) Begin { if ($purevip -ne "") { Write-Warning -Message "purevip will be deprecated in a future version. Please pass in only the flasharray parameter." } if ($null -ne $faCreds) { Write-Warning -Message "faCreds will be deprecated in a future version. Please use new-pfaarray and pass in only the flasharray parameter." } if ($null -eq $flasharray) { if (($purevip -eq "") -or ($null -eq $faCreds)) { throw "Please use new-pfaarray and pass in the flasharray parameter. " } } $ErrorActionPreference = "stop" } Process { if ($null -eq $flasharray) { $flasharray = New-PfaArray -EndPoint $purevip -Credentials $faCreds -IgnoreCertificateError } $vvolUuid = get-vvolUuidFromHardDisk -vmdk $vmdk $faVolume = get-faVolumeNameFromVvolUuid -flasharray $flasharray -vvolUUID $vvolUuid $datastore = $vmdk | Get-Datastore $arrayID = (Get-PfaArrayAttributes -Array $flasharray).id if ($datastore.ExtensionData.info.vvolDS.storageArray[0].uuid.substring(16) -eq $arrayID) { $snapshotSize = Get-PfaSnapshotSpaceMetrics -Array $flasharray -Name $snapshotName if ($vmdk.ExtensionData.capacityinBytes -eq $snapshotSize.size) { New-PfaVolume -Array $flasharray -Source $snapshotName -Overwrite -VolumeName $faVolume |Out-Null } elseif ($vmdk.ExtensionData.capacityinBytes -lt $snapshotSize.size) { $vmdk = Set-HardDisk -HardDisk $vmdk -CapacityKB ($snapshotSize.size / 1024) -Confirm:$false $vmdk = New-PfaVolume -Array $flasharray -Source $snapshotName -Overwrite -VolumeName $faVolume } else { throw "The target VVol hard disk is larger than the snapshot size and VMware does not allow hard disk shrinking." } } else { throw "The snapshot and target VVol VMDK are not on the same array." } } End { return $vmdk } } function copy-faSnapshotToNewVvolVmdk { <# .SYNOPSIS Takes an snapshot and overwrites an existing VVol virtual disk from it. .DESCRIPTION Takes an snapshot and overwrites an existing VVol virtual disk from it. .INPUTS FlashArray connection information, a source snapshot, and a virtual disk. .OUTPUTS Returns the hard disk. .NOTES Version: 1.1 Author: Cody Hosterman https://codyhosterman.com Creation Date: 02/01/2019 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$true)] [string]$snapshotName, [Parameter(Position=1,mandatory=$True,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine]$targetVm, [Parameter(Position=2)] [string]$purevip, [Parameter(Position=3,ValueFromPipeline=$True)] [System.Management.Automation.PSCredential]$faCreds, [Parameter(Position=4,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray ) Begin { if ($purevip -ne "") { Write-Warning -Message "purevip will be deprecated in a future version. Please pass in only the flasharray parameter." } if ($null -ne $faCreds) { Write-Warning -Message "faCreds will be deprecated in a future version. Please use new-pfaarray and pass in only the flasharray parameter." } if ($null -eq $flasharray) { if (($purevip -eq "") -or ($null -eq $faCreds)) { throw "Please use new-pfaarray and pass in the flasharray parameter. " } } $ErrorActionPreference = "stop" } Process{ if ($null -eq $flasharray) { $flasharray = New-PfaArray -EndPoint $purevip -Credentials $faCreds -IgnoreCertificateError } $arrayID = (Get-PfaArrayAttributes -Array $flasharray).id $datastore = $targetVm| Get-VMHost | Get-Datastore |where-object {$_.Type -eq "VVOL"} |Where-Object {$_.ExtensionData.info.vvolDS.storageArray[0].uuid.substring(16) -eq $arrayID} |Select-Object -First 1 $snapshotSize = Get-PfaSnapshotSpaceMetrics -Array $flasharray -Name $snapshotName $newHardDisk = New-HardDisk -Datastore $datastore -CapacityKB ($snapshotSize.size / 1024 ) -VM $targetVm $newVvolUuid = get-vvolUuidFromHardDisk -vmdk $newHardDisk $newFaVolume = get-faVolumeNameFromVvolUuid -flasharray $flasharray -vvolUUID $newVvolUuid New-PfaVolume -Array $flasharray -Source $snapshotName -Overwrite -VolumeName $newFaVolume |Out-Null } End{ return $newHardDisk } } function copy-faVvolVmdkToExistingVvolVmdk { <# .SYNOPSIS Takes an virtual disk and refreshes an existing VVol virtual disk from it. .DESCRIPTION Takes an virtual disk and refreshes an existing VVol virtual disk from it. .INPUTS FlashArray connection information, a source and target virtual disk. .OUTPUTS Returns the new hard disk. .NOTES Version: 1.1 Author: Cody Hosterman https://codyhosterman.com Creation Date: 02/01/2019 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$True,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$sourceVmdk, [Parameter(Position=1,mandatory=$True,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$targetVmdk, [Parameter(Position=2)] [string]$purevip, [Parameter(Position=3,ValueFromPipeline=$True)] [System.Management.Automation.PSCredential]$faCreds, [Parameter(Position=4,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray ) Begin { if ($purevip -ne "") { Write-Warning -Message "purevip will be deprecated in a future version. Please pass in only the flasharray parameter." } if ($null -ne $faCreds) { Write-Warning -Message "faCreds will be deprecated in a future version. Please use new-pfaarray and pass in only the flasharray parameter." } if ($null -eq $flasharray) { if (($purevip -eq "") -or ($null -eq $faCreds)) { throw "Please use new-pfaarray and pass in the flasharray parameter. " } } $ErrorActionPreference = "stop" } Process { $targetDatastore = $targetVmdk | Get-Datastore $sourceDatastore = $sourceVmdk | Get-Datastore if ($sourceDatastore.ExtensionData.info.vvolDS.storageArray[0].uuid -eq $targetDatastore.ExtensionData.info.vvolDS.storageArray[0].uuid) { if ($null -eq $flasharray) { $flasharray = New-PfaArray -EndPoint $purevip -Credentials $faCreds -IgnoreCertificateError } $vvolUuid = get-vvolUuidFromHardDisk -vmdk $sourceVmdk $sourceFaVolume = get-faVolumeNameFromVvolUuid -flasharray $flasharray -vvolUUID $vvolUuid $vvolUuid = get-vvolUuidFromHardDisk -vmdk $targetVmdk $targetFaVolume = get-faVolumeNameFromVvolUuid -flasharray $flasharray -vvolUUID $vvolUuid if ($targetVmdk.CapacityKB -eq $sourceVmdk.CapacityKB) { New-PfaVolume -Array $flasharray -Source $sourceFaVolume -Overwrite -VolumeName $targetFaVolume |Out-Null } elseif ($targetVmdk.CapacityKB -lt $sourceVmdk.CapacityKB) { $targetVmdk = Set-HardDisk -HardDisk $targetVmdk -CapacityKB $sourceVmdk.CapacityKB -Confirm:$false New-PfaVolume -Array $flasharray -Source $sourceFaVolume -Overwrite -VolumeName $targetFaVolume |Out-Null } else { throw "The target VVol hard disk is larger than the snapshot size and VMware does not allow hard disk shrinking." } } else { throw "The snapshot and target VVol VMDK are not on the same array." } } End{ return $targetVmdk } } function new-faSnapshotOfVvolVmdk { <# .SYNOPSIS Takes a VVol virtual disk and creates a FlashArray snapshot. .DESCRIPTION Takes a VVol virtual disk and creates a snapshot of it. .INPUTS FlashArray connection information and a virtual disk. .OUTPUTS Returns the snapshot name. .NOTES Version: 1.1 Author: Cody Hosterman https://codyhosterman.com Creation Date: 02/01/2019 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$True,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$vmdk, [Parameter(Position=1)] [string]$purevip, [Parameter(Position=2,ValueFromPipeline=$True)] [System.Management.Automation.PSCredential]$faCreds, [Parameter(Position=3,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray ) Begin { if ($purevip -ne "") { Write-Warning -Message "purevip will be deprecated in a future version. Please pass in only the flasharray parameter." } if ($null -ne $faCreds) { Write-Warning -Message "faCreds will be deprecated in a future version. Please use new-pfaarray and pass in only the flasharray parameter." } if ($null -eq $flasharray) { if (($purevip -eq "") -or ($null -eq $faCreds)) { throw "Please use new-pfaarray and pass in the flasharray parameter. " } } $ErrorActionPreference = "stop" } Process { if ($null -eq $flasharray) { $flasharray = New-PfaArray -EndPoint $purevip -Credentials $faCreds -IgnoreCertificateError } $vvolUuid = get-vvolUuidFromHardDisk -vmdk $vmdk -ErrorAction Stop $faVolume = get-faVolumeNameFromVvolUuid -flasharray $flasharray -vvolUUID $vvolUuid -ErrorAction Stop $snapshot = New-PfaVolumeSnapshots -Array $flasharray -Sources $faVolume -ErrorAction Stop } End { return $snapshot.name } } function new-faVolRdm { <# .SYNOPSIS Creates a new Raw Device Mapping for a VM .DESCRIPTION Creates a new volume on a FlashArray and presents it to a VM as a RDM. Optionally can also be created from a snapshot .INPUTS FlashArray connection, volume name, capacity, virtual machine, SCSI adapter. .OUTPUTS FlashArray volume name .NOTES Version: 1.0 Author: Cody Hosterman https://codyhosterman.com Creation Date: 02/27/2019 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray, [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine]$vm, [Parameter(Position=2)] [string]$volName, [Parameter(Position=3)] [int]$sizeInGB, [Parameter(Position=4)] [int]$sizeInTB, [Parameter(Position=5,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.Datastore]$datastore, [Parameter(Position=6,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.ScsiController]$scsiController, [Parameter(Position=7)] [string]$snapshotName ) Begin{ if ($snapshotName -eq "") { if (($sizeInGB -eq 0) -and ($sizeInTB -eq 0)) { throw "Please enter a size in GB or TB" } elseif (($sizeInGB -ne 0) -and ($sizeInTB -ne 0)) { throw "Please only enter a size in TB or GB, not both." } elseif ($sizeInGB -ne 0) { $volSize = $sizeInGB * 1024 *1024 *1024 } else { $volSize = $sizeInTB * 1024 *1024 *1024 * 1024 } } elseif (($snapshotName -ne "") -and (($sizeInGB -ne 0) -or ($sizeInTB -ne 0))) { throw "Please enter a snapshot or a size, not both." } } Process{ $ErrorActionPreference = "stop" if ($volName -eq "") { $rand = get-random -Maximum 9999 -Minimum 1000 $volName = "$($vm.Name)-rdm$($rand)" if ($volName -notmatch "^[a-zA-Z0-9\-]+$") { $volName = $volName -replace "[^\w\-]", "" $volName = $volName -replace "[_]", "" $volName = $volName -replace " ", "" } } if ($null -eq $datastore) { $vmDatastore = Get-Datastore -vm $vm if ($vmDatastore.count -gt 1) { $datastore = get-datastore (($vm.ExtensionData.Layoutex.file |where-object {$_.name -like "*.vmx*"}).name.split("]")[0].substring(1)) } else { $datastore = $vmDatastore[0] } } if ($datastore.type -eq "VVOL") { throw "The datastore cannot be of type VVols as RDM pointer files are not supported on that type of datastore. Please specify a VMFS or NFS datastore" } $cluster = $vm | get-cluster if ($null -eq $cluster) { throw "This VM is not on a host in a cluster. Non-clustered hosts are not supported by this script." } $hostGroup = $cluster | get-faHostGroupfromVcCluster -flasharray $flasharray -ErrorAction Stop if ($snapshotName -ne "") { $newVol = New-PfaVolume -Array $flasharray -VolumeName $volName -Source $snapshotName -ErrorAction Stop } else { $newVol = New-PfaVolume -Array $flasharray -Size $volSize -VolumeName $volName -ErrorAction Stop } New-PfaHostGroupVolumeConnection -Array $flasharray -VolumeName $newVol.name -HostGroupName $hostGroup.name |Out-Null $esxiHosts = $cluster| Get-VMHost foreach ($esxiHost in $esxiHosts) { $esxi = $esxiHost.ExtensionData $storageSystem = Get-View -Id $esxi.ConfigManager.StorageSystem $hbas = ($esxihost |Get-VMHostHba |where-object {$_.Type -eq "FibreChannel" -or $_.Type -eq "iSCSI"}).device foreach ($hba in $hbas) { $storageSystem.rescanHba($hba) } } $newNAA = "naa.624a9370" + $newVol.serial.toLower() if($null -eq $scsiController) { $controller = $vm |Get-ScsiController if ($controller.count -gt 1) { $pvSCSIs = $vm |Get-ScsiController |Where-Object {$_.Type -eq "ParaVirtual"} if ($pvSCSIs.count -gt 1) { $pvDisksHigh = 1000 foreach ($pvSCSI in $pvSCSIs) { $pvDisks = ($vm | Get-HardDisk | Where-Object {$_.ExtensionData.ControllerKey -eq $pvSCSI.key}).count if ($pvDisksHigh -ge $pvDisks) { $pvDisksHigh = $pvDisks $controller = $pvSCSI } } } elseif ($pvSCSIs.count -eq 1) { $controller = $pvSCSIs } else { $lsiSCSIs = $vm |Get-ScsiController if ($lsiSCSIs.count -gt 1) { $lsiDisksHigh = 1000 foreach ($lsiSCSI in $lsiSCSIs) { $lsiDisks = ($vm | Get-HardDisk | Where-Object {$_.ExtensionData.ControllerKey -eq $lsiSCSI.key}).count if ($lsiDisksHigh -ge $lsiDisks) { $lsiDisksHigh = $lsiDisks $controller = $lsiSCSI } } } else { $controller = $lsiSCSIs } } } } else { $controller = $scsiController } try { $newRDM = $vm | new-harddisk -DeviceName "/vmfs/devices/disks/$($newNAA)" -DiskType RawPhysical -Controller $controller -Datastore $datastore -ErrorAction stop } catch { Remove-PfaHostGroupVolumeConnection -Array $flasharray -VolumeName $newvol.name -HostGroupName $hostGroup.name|Out-Null Remove-PfaVolumeOrSnapshot -Array $flasharray -Name $newvol.name |Out-Null Remove-PfaVolumeOrSnapshot -Array $flasharray -Eradicate:$true -Name $newvol.name |Out-Null throw $PSItem } } End{ return $newRDM } } function get-faVolfromRDM { <# .SYNOPSIS Retrieves the FlashArray volume that hosts a RDM disk. .DESCRIPTION Takes in a RDM virtual disk and a FlashArray and returns the volume if found. .INPUTS FlashArray connection and a virtual disk. .OUTPUTS Returns FlashArray volume or error if not found. .NOTES Version: 1.0 Author: Cody Hosterman https://codyhosterman.com Creation Date: 02/27/2019 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$True,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$rdm, [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray ) Begin { #nothing to do here } Process { if ($rdm.DiskType -ne 'RawPhysical') { throw "This is not a physical mode RDM disk. Detected configuration is $($rdm.DiskType)" } $lun = ("naa." + $rdm.ExtensionData.Backing.LunUuid.substring(10).substring(0,32)) if ($lun -like 'naa.624a9370*') { $volSerial = ($lun.ToUpper()).substring(12) $purevol = Get-PfaVolumes -Array $flasharray -Filter "serial='$volSerial'" } else { throw "Specified RDM is not hosted on FlashArray storage." } } End { if ($null -ne $purevol) { return $purevol } else { throw "Specified RDM was not found on this FlashArray." } } } function new-faVolRdmSnapshot { <# .SYNOPSIS Creates a new FlashArray snapshot of a given RDM .DESCRIPTION Takes in a RDM disk and the corresponding FlashArray and creates a snapshot. .INPUTS FlashArray connection and a RDM disk .OUTPUTS Returns created snapshot. .NOTES Version: 1.0 Author: Cody Hosterman https://codyhosterman.com Creation Date: 02/27/2019 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray, [Parameter(Position=1,mandatory=$True,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$rdm, [Parameter(Position=2)] [string]$snapshot ) Begin { #nothing to do here } Process { $pureVol = $rdm | get-faVolfromRDM -flasharray $flasharray -ErrorAction Stop if ($snapshot -eq "") { $newSnapshot = New-PfaVolumeSnapshots -Array $flasharray -Sources $pureVol.name } else { $newSnapshot = New-PfaVolumeSnapshots -Array $flasharray -Sources $pureVol.name -Suffix $snapshot } } End { return $newSnapshot } } function get-faVolRDMSnapshots { <# .SYNOPSIS Retrieves snapshots of a FlashArray-based RDM .DESCRIPTION Pass in a RDM disk and this will returns all of the FlashArray snapshots .INPUTS FlashArray connection and a RDM-based disk. .OUTPUTS Returns FlashArray snapshot(s). .NOTES Version: 1.0 Author: Cody Hosterman https://codyhosterman.com Creation Date: 02/27/2019 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$True,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$rdm, [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray ) Begin { #nothing to do here } Process { $pureVol = $rdm | get-faVolfromRDM -flasharray $flasharray $snapshots = Get-PfaVolumeSnapshots -Array $flasharray -VolumeName $pureVol.name } End { return $snapshots } } function copy-faSnapshotToRDM { <# .SYNOPSIS Input a FlashArray RDM and a snapshot to refresh the RDM .DESCRIPTION Pass in a RDM disk and a snapshot and it will copy the snapshot to the RDM FlashArray volume .INPUTS FlashArray connection, a snapshot, and a RDM-based disk. .OUTPUTS Returns the RDM disk .NOTES Version: 1.0 Author: Cody Hosterman https://codyhosterman.com Creation Date: 02/27/2019 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$True,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$rdm, [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray, [Parameter(Position=2)] [string]$snapshot, [Parameter(Position=3)] [switch]$offlineConfirm ) Begin { if ($snapshot -eq "") { throw "You must enter a snapshot source" } } Process { $sourceVol = $rdm | get-faVolfromRDM -flasharray $flasharray $vm = $rdm.Parent $controller = $rdm |Get-ScsiController $datastore = $rdm |Get-Datastore Remove-HardDisk $rdm -DeletePermanently -Confirm:$false $refreshedVol = New-PfaVolume -Array $flasharray -VolumeName $sourceVol.name -Source $snapshot -Overwrite -ErrorAction Stop $snapshotObj = Get-PfaVolumeSnapshot -SnapshotName $snapshot -Array $flasharray if ($snapshotObj.size -ne $sourceVol.size) { if ($offlineConfirm -ne $true) { throw "The snapshot and target are different sizes. FlashArray volumes can be resized online, but VMware does not permit it with RDMs. Please confirm you will allow the RDM to go offline temporarily for the resize operation with the -offlineConfirm parameter." } $esxiHosts = $rdm.Parent|get-cluster| Get-VMHost foreach ($esxiHost in $esxiHosts) { $esxi = $esxiHost.ExtensionData $storageSystem = Get-View -Id $esxi.ConfigManager.StorageSystem $hbas = ($esxihost |Get-VMHostHba |where-object {$_.Type -eq "FibreChannel" -or $_.Type -eq "iSCSI"}).device foreach ($hba in $hbas) { $storageSystem.rescanHba($hba) } $storageSystem.RefreshStorageSystem() } $newNAA = "naa.624a9370" + $refreshedVol.serial.toLower() $updatedRDM = $vm | new-harddisk -DeviceName "/vmfs/devices/disks/$($newNAA)" -DiskType RawPhysical -Controller $controller -Datastore $datastore -ErrorAction stop } } End { return $updatedRDM } } function set-faVolRDMCapacity { <# .SYNOPSIS Resizes the RDM volume .DESCRIPTION Takes in a new size and resizes the underlying volume and rescans the VMware environment .INPUTS Takes in a RDM virtual disk, a FlashArray, and a new size. .OUTPUTS Returns RDM disk. .NOTES Version: 1.0 Author: Cody Hosterman https://codyhosterman.com Creation Date: 03/1/2019 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$True,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$rdm, [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray, [Parameter(Position=2)] [int]$sizeInGB, [Parameter(Position=3)] [int]$sizeInTB, [Parameter(Position=4)] [switch]$truncate, [Parameter(Position=5)] [switch]$offlineConfirm ) Begin { if ($offlineConfirm -ne $true) { throw "FlashArray volumes can be resized online, but VMware does not permit it with RDMs. Please confirm you will allow the RDM to go offline temporarily for the resize operation with the -offlineConfirm parameter." } if (($sizeInGB -eq 0) -and ($sizeInTB -eq 0)) { throw "Please enter a size in GB or TB" } elseif (($sizeInGB -ne 0) -and ($sizeInTB -ne 0)) { throw "Please only enter a size in TB or GB, not both." } elseif ($sizeInGB -ne 0) { $volSize = $sizeInGB * 1024 *1024 *1024 } else { $volSize = $sizeInTB * 1024 *1024 *1024 * 1024 } } Process { $pureVol = $rdm | get-faVolfromRDM -flasharray $flasharray $vm = $rdm.Parent $controller = $rdm |Get-ScsiController $datastore = $rdm |Get-Datastore if (($truncate -ne $true) -and ($pureVol.size -gt $volSize)) { throw "This operation will shrink the target RDM--please ensure this is expected and if so, please rerun the operation with the -truncate parameter." } Remove-HardDisk $rdm -DeletePermanently -Confirm:$false if ($truncate -eq $true) { $expandedVol = Resize-PfaVolume -Array $flasharray -VolumeName $pureVol.name -NewSize $volSize -Truncate } else { $expandedVol = Resize-PfaVolume -Array $flasharray -VolumeName $pureVol.name -NewSize $volSize } $esxiHosts = $rdm.Parent|get-cluster| Get-VMHost foreach ($esxiHost in $esxiHosts) { $esxi = $esxiHost.ExtensionData $storageSystem = Get-View -Id $esxi.ConfigManager.StorageSystem $hbas = ($esxihost |Get-VMHostHba |where-object {$_.Type -eq "FibreChannel" -or $_.Type -eq "iSCSI"}).device foreach ($hba in $hbas) { $storageSystem.rescanHba($hba) } $storageSystem.RefreshStorageSystem() } $expandedVol = Get-PfaVolume -Name $expandedVol.name -Array $flasharray $newNAA = "naa.624a9370" + $expandedVol.serial.toLower() $updatedRDM = $vm | new-harddisk -DeviceName "/vmfs/devices/disks/$($newNAA)" -DiskType RawPhysical -Controller $controller -Datastore $datastore -ErrorAction stop } End { return $updatedRDM } } function remove-faVolRDM { <# .SYNOPSIS Removes an RDM volume .DESCRIPTION Deletes the virtual disk pointer to the RDM and deletes the volume on the FlashArray. Volume will be deleted permanently in 24 hours .INPUTS Takes in a RDM virtual disk and a FlashArray connection. .OUTPUTS Returns destroyed FA volume. .NOTES Version: 1.0 Author: Cody Hosterman https://codyhosterman.com Creation Date: 02/28/2019 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$True,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$rdm, [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray ) Begin { #nothing to do } Process { $pureVol = $rdm | get-faVolfromRDM -flasharray $flasharray $esxiHosts = $rdm.Parent|get-cluster| Get-VMHost Remove-HardDisk $rdm -DeletePermanently -Confirm:$false $hostConnections = Get-PfaVolumeHostConnections -Array $flasharray -VolumeName $pureVol.name if ($hostConnections.count -gt 0) { foreach ($hostConnection in $hostConnections) { Remove-PfaHostVolumeConnection -Array $flasharray -VolumeName $pureVol.name -HostName $hostConnection.host |Out-Null } } $hostGroupConnections = Get-PfaVolumeHostGroupConnections -Array $flasharray -VolumeName $pureVol.name if ($hostGroupConnections.count -gt 0) { $hostGroupConnections = $hostGroupConnections.hgroup |Select-Object -unique foreach ($hostGroupConnection in $hostGroupConnections) { Remove-PfaHostGroupVolumeConnection -Array $flasharray -VolumeName $pureVol.name -HostGroupName $hostGroupConnection |Out-Null } } foreach ($esxiHost in $esxiHosts) { $esxi = $esxiHost.ExtensionData $storageSystem = Get-View -Id $esxi.ConfigManager.StorageSystem $hbas = ($esxihost |Get-VMHostHba |where-object {$_.Type -eq "FibreChannel" -or $_.Type -eq "iSCSI"}).device foreach ($hba in $hbas) { $storageSystem.rescanHba($hba) } } $destroyedVol = Remove-PfaVolumeOrSnapshot -Array $flasharray -Name $pureVol.name } End { return $destroyedVol } } function convert-faVolRDMtoVvol { <# .SYNOPSIS Converts a RDM to a VVol .DESCRIPTION Removes the RDM from the virtual machine and copies it to a new VVol and destroys the old RDM. .INPUTS Takes in a RDM virtual disk, a FlashArray connection, and optionally a VVol datastore. .OUTPUTS Returns the new VVol virtual disk. .NOTES Version: 1.0 Author: Cody Hosterman https://codyhosterman.com Creation Date: 03/01/2019 Purpose/Change: Initial script development *******Disclaimer:****************************************************** This scripts are offered "as is" with no warranty. While this scripts is tested and working in my environment, it is recommended that you test this script in a test lab before using in a production environment. Everyone can use the scripts/commands provided here without any written permission but I will not be liable for any damage or loss to the system. ************************************************************************ #> [CmdletBinding()] Param( [Parameter(Position=0,mandatory=$True,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$rdm, [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)] [PurePowerShell.PureArray]$flasharray, [Parameter(Position=3,ValueFromPipeline=$True)] [VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.Datastore]$datastore, [Parameter(Position=3)] [switch]$offlineConfirm ) Begin { #nothing to do } Process { $vm = $rdm.Parent if (($vm.PowerState -ne "PoweredOff") -and ($offlineConfirm -ne $true)) { throw "The RDM to VVol migration is an offline process--please either shut down the VM or confirm this downtime with the -offlineConfirm parameter" } $sourceVol = $rdm|get-faVolfromRDM -flasharray $flasharray -ErrorAction Stop $arraySerial = (Get-PfaArrayAttributes -array $fa).id if ($null -eq $datastore) { $datastores = $vm |get-vmhost |Get-Datastore |Where-Object {$_.Type -eq "VVOL"} foreach ($checkDatastore in $datastores) { if ($arraySerial -eq $checkDatastore.ExtensionData.Info.VvolDS.StorageArray[0].uuid.Substring(16)) { #finding first VVol datastore on host running VM on same FlashArray $datastore = $checkDatastore break } } if ($null -eq $datastore) { throw "No VVol datastore found on ESXi host for input array. Please ensure one is mounted." } } else { if ($arraySerial -ne $datastore.ExtensionData.Info.VvolDS.StorageArray[0].uuid.Substring(16)) { throw "The input datastore is not on the same array as the input FlashArray connection." } } $controller = $rdm |Get-ScsiController $volSize = $rdm.CapacityGB remove-faVolRDM -rdm $rdm -flasharray $flasharray -ErrorAction Stop |Out-Null $vvolVmdk = $vm | new-harddisk -CapacityGB $volSize -Controller $controller -Datastore $datastore -ErrorAction stop $vvolUuid = $vvolVmdk |get-vvolUuidFromHardDisk $targetVol = get-faVolumeNameFromVvolUuid -flasharray $flasharray -vvolUUID $vvolUuid Restore-PfaDestroyedVolume -Array $flasharray -Name $sourceVol.name |Out-Null New-PfaVolume -Array $flasharray -VolumeName $targetVol -Source $sourceVol.name -Overwrite -ErrorAction Stop |Out-Null Remove-PfaVolumeOrSnapshot -Array $flasharray -Name $sourceVol.name |Out-Null } End { return $vvolVmdk } } |