Skip to content

Instantly share code, notes, and snippets.

@tinuwalther
Last active June 8, 2022 14:53
Show Gist options
  • Save tinuwalther/c73888d98cab239362baca89743ff76a to your computer and use it in GitHub Desktop.
Save tinuwalther/c73888d98cab239362baca89743ff76a to your computer and use it in GitHub Desktop.
vSphere Auto Deloy Boot from SAN

Table of content

VMware vSphere Auto Deploy with Boot from SAN

You can use the first disk argument only to specify the search order. You cannot explicitly specify a disk. For example, you cannot specify a specific LUN on a SAN.

Arguments for first disk

When configuring a System Image Install disk, you have multiple options to define the device you want ESXi to be installed to and booted from. You can use the following arguments to define the disk for the installation:

  • esx – The first disk detected containing a valid installation of ESXi
  • local – The first local disk detected by ESXi after boot
  • device model
  • device vendor
  • vmkernel device driver name

There is the undocumented argument remote. In a Kickstart-script you can use the argument remote to install ESXi on a remote disk and boot from SAN. You can do the same way for vSphere Auto Deploy. The ESXi Host has an array of remote disks. If there are more than one remote disks, it write the installation to the first remote disk detected by ESXi. Make sure, that the ESXi Host has only one unformated and unused remote disk.

Configure the Host Profile for Stateful Installation under Advanced Configuration Settings > System Image Cache Configuration > System Image Cache Configuration.

  • In the System Image Cache Profile Settings drop-down menu, choose the policy option Enable stateful installs on the host.
  • Specify information about the disk to use Arguments for first disk and set it to remote.
  • Leave the Option Check to overwrite any VMFS volumes on the selected disk unchecked (thats because the Host Profile will write the installation to the first unused remote disk)

top

How it works

Three steps are recomended: Auto Deploy ESXi in a Cluster, Stateful Auto Deploy, and Post Auto Deploy. This steps can be executed with a PowerShell-Script (PCLI workflow).

Auto Deploy ESXi in a Cluster

Execute Auto Deploy. Boot the Host in to PXE and let Auto Deploy do the rest for you ... This step should only do the deployment in memory and adding the ESXi Host to a Cluster - nothing else.

sequenceDiagram
    participant PCLI workflow
    participant DHCP
    participant TFTP
    participant ESXi
    participant Auto Deploy
    participant Deploy Rule
    PCLI workflow->>ESXi: OneTimePXE boot
    ESXi->>DHCP: Give me my IP and TFTP options
    DHCP->>ESXi: Your IP and TFTP options are ...
    ESXi->>TFTP: Give me my Auto Deploy Server
    TFTP->>ESXi: Your Auto Deploy Server is ...
    ESXi->>Auto Deploy: Connect and start Installation
    Auto Deploy->>Deploy Rule: Give me my deployment rules
    Deploy Rule->>Auto Deploy: Image Profile ...
    Deploy Rule->>Auto Deploy: Cluster ...
    Auto Deploy->>ESXi: Install ESXi in memory
    Auto Deploy->>ESXi: Add Host to the Cluster

top

Stateful Auto Deploy

This step should do the Stateful installs. Its necessaire, that you make sure there is no unformated storage attached to the new Host before remediate Stateful Profile. If there are more than one unformated disks attached, it can be that ESXi detect the wrong disk an write the ESXi installation to this!

sequenceDiagram
    participant PCLI workflow
    participant ESXi
    participant Stateful Profile
    PCLI workflow->>ESXi: Check for unformated remote storage (needs to be coded)
        loop Format-SanStorage
        ESXi->>ESXi: Format unformated storage with VMFS (needs to be coded)
    end
    PCLI workflow->>ESXi: Attach Stateful Profile
    Stateful Profile->>ESXi: Enable stateful installs on the host: remote
    Stateful Profile->>ESXi: Remediate
    ESXi->>PCLI workflow: Return
    PCLI workflow->>ESXi: Restart Host

top

Post Auto Deploy

Make all of your post actions here. Switch the Stateful Host Profile to the final Host Profile and reboot the ESXi Host.

sequenceDiagram
    participant PCLI workflow
    participant ESXi
    participant Final Profile
    PCLI workflow->>ESXi: Add License to Host
    PCLI workflow->>ESXi: Change Host Profile
    ESXi->>Final Profile: Attach Host Profile
    ESXi->>Final Profile: Attach Host Customization
    Final Profile->>ESXi: Remediate
    ESXi->>PCLI workflow: Return
    PCLI workflow->>ESXi: Restart Host

With this configuration, I hope all of my ESXi installations are going well.

top

ESXi Commands

Run the esxcli storage filesystem list command to generate a compact list of the LUNs currently connected to the ESXi host, including VMFS version.

Get ESXi Luns

function Get-IXESXiLuns {
    [CmdletBinding()]
    param(
        #region parameter, to add a new parameter, copy and paste the Parameter-region
        [Parameter(
            Mandatory=$true,
            ValueFromPipeline=$true,
            ValueFromPipelineByPropertyName=$true,
            Position = 0
        )]
        [Object] $InputObject
        #endregion
    )

    begin{
        #region Do not change this region
        $StartTime = Get-Date
        $function = $($MyInvocation.MyCommand.Name)
        Write-Verbose $('[', (Get-Date -f 'yyyy-MM-dd HH:mm:ss.fff'), ']', '[ Begin   ]', $function -Join ' ')
        #endregion
        $ret = $null # or @()
    }

    process{
        Write-Verbose $('[', (Get-Date -f 'yyyy-MM-dd HH:mm:ss.fff'), ']', '[ Process ]', $function -Join ' ')
        try{
            $ret = foreach($item in $InputObject.EsxiServer){
                #Esxi server information
                $esxi = Get-VMhost $item
    
                Write-Verbose "Rescan/Refresh Esxi Storage on $($item)"
                try{
                    $null = $esxi | Get-VMHostStorage -Refresh -RescanAllHba -ErrorAction Stop
                }catch{
                    Write-Warning $('ScriptName:', $($_.InvocationInfo.ScriptName), 'LineNumber:', $($_.InvocationInfo.ScriptLineNumber), 'Message:', $(($_.Exception.Message) -replace '\s+',' ') -Join ' ')
                    $Error.Clear()
                }

                Write-Verbose "Get HBA storage adapter information on $($item)"
                $hostHBA =  $esxi | Get-VMHostHba | Where-Object {$_.Status -match 'online'}

                #Get the list of Datastore
                $datastore = Get-Datastore

                Write-Verbose "Get available LUN storage devices list on $($item)"
                $lunList = $hostHBA | Get-ScsiLun

                #Get the list of LUN on datastores are created
                Write-Verbose "Get the list of LUN on datastores are created on $($item)"
                foreach ($lun in $lunList){
                    $DatastoreName = @{N='DatastoreName'; E={$datastore | Where-Object {$_.Extensiondata.Info.Vmfs.Extent.DiskName.Contains($lun.CanonicalName)} | Select-Object -ExpandProperty Name}}
                    $AttachedLun = $lun | Select-Object VMHost, CanonicalName, RuntimeName, CapacityGB, $DatastoreName
                    if([String]::IsNullOrEmpty($AttachedLun.DatastoreName)){
                        if($InputObject.Formatted -eq $false){
                            if($AttachedLun.CapacityGB -ne 100){
                                $CN = $AttachedLun.RuntimeName -split ':'
                                $LunID = $($CN[$CN.GetUpperBound(0)]).Trim('L')
                                Write-Verbose "$($AttachedLun.VMHost): $($AttachedLun.CanonicalName) $($AttachedLun.RuntimeName) is not VMFS-formated"
                                switch -Regex ($AttachedLun.VMHost){
                                    '\w\w\w\w60\d{2}' { $NewDatastoreName = "G_$($InputObject.ESxiCluster)_G_VMFS_$($LunID)" }
                                    '\w\w\w\w90\d{2}' { $NewDatastoreName = "S_$($InputObject.ESxiCluster)_S_VMFS_$($LunID)" }
                                }
                                $AttachedLun | Add-Member -MemberType NoteProperty -Name NewDatastoreName -Value $NewDatastoreName
                                $AttachedLun
                            }
                        }
                    }else{
                        if($InputObject.Formatted){
                            $AttachedLun
                        }
                    }
                }
            }
        }catch{
            Write-Warning $('ScriptName:', $($_.InvocationInfo.ScriptName), 'LineNumber:', $($_.InvocationInfo.ScriptLineNumber), 'Message:', $(($_.Exception.Message) -replace '\s+',' ') -Join ' ')
            $Error.Clear()
        }
    }

    end{
        #region Do not change this region
        Write-Verbose $('[', (Get-Date -f 'yyyy-MM-dd HH:mm:ss.fff'), ']', '[ End     ]', $function -Join ' ')
        $TimeSpan  = New-TimeSpan -Start $StartTime -End (Get-Date)
        $Formatted = $TimeSpan | ForEach-Object {
            '{1:0}h {2:0}m {3:0}s {4:000}ms' -f $_.Days, $_.Hours, $_.Minutes, $_.Seconds, $_.Milliseconds
        }
        Write-Verbose $('Finished in:', $Formatted -Join ' ')
        #endregion
        return $ret
    }
}
cls
Write-Host "List all formatted LUNS of MyHost.comapny.ch" -ForegroundColor Green
$ret   = Get-IXESXiLuns -InputObject @{EsxiServer = 'MyHost.comapny.ch'; Formatted = $true; ESxiCluster = 'MyCluster'}
$ret | Format-Table -AutoSize

Format ESXi Luns

function Format-IXESXiLuns {
    [CmdletBinding()]
    param(
        #region parameter, to add a new parameter, copy and paste the Parameter-region
        [Parameter(
            Mandatory=$true,
            ValueFromPipeline=$true,
            ValueFromPipelineByPropertyName=$true,
            Position = 0
        )]
        [Object] $InputObject
        #endregion
    )

    begin{
        #region Do not change this region
        $StartTime = Get-Date
        $function = $($MyInvocation.MyCommand.Name)
        Write-Verbose $('[', (Get-Date -f 'yyyy-MM-dd HH:mm:ss.fff'), ']', '[ Begin   ]', $function -Join ' ')
        #endregion
        $ret = $null # or @()
    }

    process{
        Write-Verbose $('[', (Get-Date -f 'yyyy-MM-dd HH:mm:ss.fff'), ']', '[ Process ]', $function -Join ' ')
        try{
            Write-Verbose "Add new Datastore using RuntimeName of newly Added Lun"
            $ret = foreach($item in $InputObject){
                Write-Verbose "Datastore: $($item.NewDatastoreName), Path: $($item.CanonicalName)"
                New-Datastore -VMHost $item.VMHost -Name $item.NewDatastoreName -Path $item.CanonicalName -Vmfs -FileSystemVersion 6
            }
        }catch{
            Write-Warning $('ScriptName:', $($_.InvocationInfo.ScriptName), 'LineNumber:', $($_.InvocationInfo.ScriptLineNumber), 'Message:', $(($_.Exception.Message) -replace '\s+',' ') -Join ' ')
            $Error.Clear()
        }
    }

    end{
        #region Do not change this region
        Write-Verbose $('[', (Get-Date -f 'yyyy-MM-dd HH:mm:ss.fff'), ']', '[ End     ]', $function -Join ' ')
        $TimeSpan  = New-TimeSpan -Start $StartTime -End (Get-Date)
        $Formatted = $TimeSpan | ForEach-Object {
            '{1:0}h {2:0}m {3:0}s {4:000}ms' -f $_.Days, $_.Hours, $_.Minutes, $_.Seconds, $_.Milliseconds
        }
        Write-Verbose $('Finished in:', $Formatted -Join ' ')
        #endregion
        return $ret
    }
}
cls
Write-Host "Format all unformatted LUNS on ESXi Host MyHost.comapny.ch" -ForegroundColor Green
$ret = Format-IXESXiLuns -InputObject $ret -Verbose
$ret | Format-Table -AutoSize

top

References

VMware: Configure a Host Profile to Enable Stateful Installs

VMware: Installation and Upgrade Script Commands

VMware: Identifying disks when working with VMware ESXi

Virtual Geek: Add a VMFS datastore using VMware PowerCLI

Tags: #VMware, #ESXi, #AutoDeploy

top

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment