【VMware ESXi】如何在獨立的ESXi主機上配置vTPM並安裝Windows 11作業系統。

JUNIOR_MU發表於2024-03-07

根據微軟釋出的Windows 11作業系統要求,這個版本的系統需要硬體支援受信任的平臺模組 (TPM) 才能進行安裝和使用,不然就會提示你“這臺電腦無法執行Windows11。這臺電腦不符合安裝此版本的Windows所需的最低系統要求。有關詳細資訊,請訪問https://aka.ms/WindowsSysReq”錯誤。關於受信任的平臺模組(TPM)的功能和技術這裡不再概述,具體的官方文件說明可以看這裡

本文主要說明如何在獨立的ESXi主機上安裝Windows 11 23H2的操作過程,為什麼說是在獨立的ESXi主機呢?

通常情況下,如果我們的安裝環境是筆記本或者桌上型電腦,並且主機板帶有TPM 2.0晶片,那麼可以直接安裝系統,無需多言,或者我們想基於作業系統之上透過VMware Workstation、VirtualBox以及HyperV這種巢狀虛擬化的方式安裝Windows 11,只需要在配置虛擬機器硬體的時候對虛擬機器進行加密即可,比如官方文件或者這裡,當然如果使用低版本的客戶端程式可能預設不提供Windows 11選擇,只需要選擇Windows 10對虛擬機器加密並手動新增可信平臺模組(TPM)也可以,比如這裡。上述這些情況的前提條件是,你的硬體必須要支援TPM的情況下才能進行,如何確定你的硬體是否支援TPM呢?可以看這裡。但是,如果我的虛擬化客戶端既不支援Windows11,又不支援TPM,然後又想安裝Windows 11怎麼辦呢?可以參考這裡繞過TPM檢查。

如果是企業級VMware vSphere環境,可以透過在虛擬機器上配置虛擬TPM(vTPM)將TPM2.0用作虛擬裝置,對於vSphere 7.0 U2之前,需要單獨配置金鑰伺服器(KMS),對於vSphere 7.0 U2之後,在vCenter Server中內建了vSphere Native Key Provider功能,可以更加方便的為虛擬機器配置vTPM,具體可以看這裡。使用vTPM的一個主要好處是,底層ESXi主機中可以不需要物理TPM晶片,並且透過加密儲存金鑰的.nvram檔案來保護vTPM金鑰。用於加密vTPM的加密金鑰由金鑰提供程式置備,該提供程式可以是符合KMIP的外部標準金鑰提供程式(SKP),也可以使用vCenter Server的內建本機金鑰提供程式(NKP)。這些關鍵提供程式及其配置使用管理需要使用vCenter Server,從而在使用vTPM功能時提供集中式控制平面和無縫的使用者體驗。但是,這裡要使用vTPM的前提是,環境中必須要vCenter Server,雖然強烈建議使用vCenter Server以獲得更良好的vTPM使用者使用體驗,但從技術上講,vTPM執行並不需要它。

這也是寫本篇文章的主要原因,對於使用VMware ESXi虛擬化的HomeLab環境,可能只有一臺主機(工作站或伺服器)安裝了ESXi,並不需要vCenter Server進行管理,但是又想使用vTPM功能,比如本篇文章中安裝Windows 11需要vTPM,這時可以參考本篇文章中的方法,如何在獨立的ESXi主機上為虛擬機器新增vTPM。

  • 警告:ESXi主機最好支援TPM,並開啟金鑰永續性,若沒有TPM請看最後。
  • 注意:ESXi主機需要獲得許可證,除免費的ESXi之外的任何許可都沒問題。
  • 前提:客戶機連線需要準備PowerShell(系統自帶)和PowerCLI(安裝參考)。

由於預設的PowerCLI模組中不支援某些指令,比如ESXi主機上加密vSphere APIs,所以需要建立幾個PowerCLI指令,這些指令由PowerShell指令碼檔案(vTPMStandaloneESXiFunctions.ps1)建立,只需要在PowerShell裡執行這個指令碼檔案後,即可輕鬆地為不受vCenter Server管理的獨立ESXi主機配置vTPM。指令碼可以透過上面的連結地址進行下載,或者將下面的內容複製到記事本里,另存為vTPMStandaloneESXiFunctions.ps1即可。

vTPMStandaloneESXiFunctions.ps1
# Author: William Lam
# Description: PowerCLI functions to configure host encryption for a standanlone ESXi host to support vTPM without vCenter Server

Function New-256BitKey {
<#
    .NOTES
    ===========================================================================
    Created by:    William Lam
    Organization:  VMware
    Blog:          www.williamlam.com
    Twitter:       @lamw
    ===========================================================================
    .DESCRIPTION
        This function returns randomly generated 256 bit key encoded using base64
    .EXAMPLE
        New-256BitKey
#>
    # Generate 256 bit key
    # Thank you ChatGPT for this code
    $randomKey = [byte[]]::new(32)
    $rand = [System.Security.Cryptography.RandomNumberGenerator]::Create()
    $rand.GetBytes($randomKey)

    # Encode the key using Base64
    return [Convert]::ToBase64String($randomKey)
}

Function Prepare-VMHostForEncryption {
<#
    .NOTES
    ===========================================================================
    Created by:    William Lam
    Organization:  VMware
    Blog:          www.williamlam.com
    Twitter:       @lamw
    ===========================================================================
    .DESCRIPTION
        This function prepares the ESXi host for encryption
    .EXAMPLE
        Prepare-VMHostForEncryption
#>
    $cm = Get-View $global:DefaultVIServer.ExtensionData.Content.CryptoManager

    $cryptoState = (Get-VMHost).ExtensionData.Runtime.CryptoState

    if($cryptoState -eq "incapable") {
        Write-Host -ForegroundColor Yellow "`nPreparing ESXi Host for encryption ..."
        $cm.CryptoManagerHostPrepare()
        Write-Host -ForegroundColor Green "Successfully prepared ESXi Host for encryption ...`n"
    } else {
        Write-Host "`nESXi Host has already been prepared for encryption ...`n"
    }
}

Function New-InitialVMHostKey {
<#
    .NOTES
    ===========================================================================
    Created by:    William Lam
    Organization:  VMware
    Blog:          www.williamlam.com
    Twitter:       @lamw
    ===========================================================================
    .DESCRIPTION
        This function creates and/or ipmorts host key
    .PARAMETER Operation
        CREATE or IMPORT
    .PARAMETER KeyName
        Name of the VM Key
    .PARAMETER CSVTPMKeyFile
        Name of CSV file to save TPM keys (Default: tpm-keys.csv)
    .EXAMPLE
        # Request new VM Key
        New-InitialVMHostKey -Operation CREATE -KeyName "host-key-1"
    .EXAMPLE
        # Imports an existing VM Key
        New-InitialVMHostKey -Operation IMPORT -KeyName "host-key-1" -CSVTPMKeyFile tpm-keys.csv

#>
    param(
        [Parameter(Mandatory=$true)][ValidateSet("CREATE","IMPORT")][string]$Operation,
        [Parameter(Mandatory=$true)][String]$KeyName,
        [Parameter(Mandatory=$false)][String]$CSVTPMKeyFile="tpm-keys.csv"
    )

    $cryptoState = (Get-VMHost).ExtensionData.Runtime.CryptoState

    if($cryptoState -eq "safe") {
        Write-Host -ForegroundColor Red "`nESXi host has already been configured with initial host key ...`n"
        break
    }

    if($cryptoState -ne "prepared") {
        Write-Host -ForegroundColor Red "`nESXi host has not been prepared for encryption ...`n"
        break
    }

    $cm = Get-View $global:DefaultVIServer.ExtensionData.Content.CryptoManager

    # Create or import initial host key
    if($Operation -eq "CREATE") {
        Write-Host -ForegroundColor Yellow "Generating random 256 bit host key ..."
        $hostBase64Key = New-256BitKey
        $keyAlgorithim = "AES-256"
    } else {
        $csvfile = Import-Csv $CSVTPMKeyFile
        foreach ($line in $csvfile) {
            if($line.KEYID -eq $KeyName -and $line.TYPE -eq "HOST") {
                Write-Host -ForegroundColor Yellow "Importing existing host key from $CSVTPMKeyFile ..."
                $hostBase64Key = $line.DATA
                $keyAlgorithim = $line.ALGORITHIM
                break
            }
        }
    }

    if($hostBase64Key -eq $null) {
        Write-Host -ForegroundColor Red "Failed to find host key ${KeyName} ...`n"
        break
    }

    $hostKeyId = New-Object VMware.Vim.CryptoKeyId
    $hostKeyId.keyId = $KeyName

    $hostKeySpec = New-Object VMware.Vim.CryptoKeyPlain
    $hostKeySpec.KeyId = $hostKeyId
    $hostKeySpec.Algorithm = $keyAlgorithim
    $hostKeySpec.KeyData = $hostBase64Key

    Write-Host -ForegroundColor Yellow "Adding ESXi Host Key ${KeyName} ..."
    try {
        $cm.CryptoManagerHostEnable($hostKeySpec)
    } catch {
        Write-Host -ForegroundColor Red "Failed to add host key ${KeyName} ...`n"
        break
    }

    # Automatically backup host key to CSV file
    if($Operation -eq "CREATE") {
        if (Test-Path -Path $CSVTPMKeyFile -PathType Leaf) {
            Write-Host -ForegroundColor Yellow "ESXi TPM Keys file $CSVTPMKeyFile exists, please use import operation"
        } else {
            $newcsv = {} | Select "KEYID","ALGORITHIM","TYPE","DATA" | Export-Csv $CSVTPMKeyFile
            $csvfile = Import-Csv $CSVTPMKeyFile
            $csvfile.KEYID = $KeyName
            $csvfile.ALGORITHIM = $keyAlgorithim
            $csvfile.TYPE = "HOST"
            $csvfile.DATA = $hostBase64Key
            Write-Host -ForegroundColor Yellow "Exporting ${KeyName} to $CSVTPMKeyFile ..."
            $csvfile | Export-CSV -Path $CSVTPMKeyFile
        }
    }
    Write-Host -ForegroundColor Green "Successfully added initial host encryption key ${KeyName} ...`n"
}

Function New-VMTPMKey {
<#
    .NOTES
    ===========================================================================
    Created by:    William Lam
    Organization:  VMware
    Blog:          www.williamlam.com
    Twitter:       @lamw
    ===========================================================================
    .DESCRIPTION
        This function creates and/or ipmorts Host key
    .PARAMETER Operation
        CREATE or IMPORT
    .PARAMETER KeyName
        Name of the VM Key
    .PARAMETER CSVTPMKeyFile
        Name of CSV file to save TPM keys (Default: tpm-keys.csv)
    .EXAMPLE
        # Request new Host Key
        New-VMTPMKey -Operation CREATE -KeyName "windows-11-key"
    .EXAMPLE
        # Imports an existing Host Key
        New-VMTPMKey -Operation IMPORT -KeyName "windows-11-key" -CSVTPMKeyFile tpm-keys.csv

#>
    param(
        [Parameter(Mandatory=$true)][ValidateSet("CREATE","IMPORT")][string]$Operation,
        [Parameter(Mandatory=$true)][String]$KeyName,
        [Parameter(Mandatory=$false)][String]$CSVTPMKeyFile="tpm-keys.csv"
    )

    $cm = Get-View $global:DefaultVIServer.ExtensionData.Content.CryptoManager

    # Ensure ESXi host encryption is enabled
    if($cm.Enabled) {
        # Create or import VM key
        if($Operation -eq "CREATE") {
            Write-Host -ForegroundColor Yellow "Generating random 256 bit VM key ..."
            $vmBase64Key = New-256BitKey
            $keyAlgorithim = "AES-256"
        } else {
            $csvfile = Import-Csv $CSVTPMKeyFile
            foreach ($line in $csvfile) {
                if($line.KEYID -eq $KeyName -and $line.TYPE -eq "VM") {
                    Write-Host -ForegroundColor Yellow "Importing existing VM key from $CSVTPMKeyFile ..."
                    $vmBase64Key = $line.DATA
                    $keyAlgorithim = $line.ALGORITHIM
                    break
                }
            }
        }

        if($vmBase64Key -eq $null) {
            Write-Host -ForegroundColor Red "Failed to find VM key ${KeyName} ...`n"
            break
        }

        $vmKeyId = New-Object VMware.Vim.CryptoKeyId
        $vmKeyId.keyId = $KeyName

        $vmKeySpec = New-Object VMware.Vim.CryptoKeyPlain
        $vmKeySpec.KeyId = $vmKeyId
        $vmKeySpec.Algorithm = $keyAlgorithim
        $vmKeySpec.KeyData = $vmBase64Key

        Write-Host -ForegroundColor Yellow "Adding VM key ${KeyName} ..."
        try {
            $cm.AddKey($vmKeySpec)
        } catch {
            Write-Host -ForegroundColor Red "Failed to add VM key ${KeyName} ...`n"
            break
        }

        # Automatically backup VM key to CSV file
        if($Operation -eq "CREATE") {
            if (Test-Path -Path $CSVTPMKeyFile -PathType Leaf) {
                $tmp = [PSCustomObject] [ordered]@{
                    KEYID = $KeyName;
                    ALGORITHIM = $keyAlgorithim;
                    TYPE = "VM";
                    DATA = $vmBase64Key
                }
                Write-Host -ForegroundColor Yellow "Exporting ${KeyName} to $CSVTPMKeyFile ..."
                $tmp | Export-CSV -Append -NoTypeInformation -Path $CSVTPMKeyFile
            } else {
                Write-Error "Unable to find $CSVTPMKeyFile ..."
            }
        }
        Write-Host -ForegroundColor Green "Successfully added VM encryption key ${KeyName} ...`n"
    } else {
        Write-Host -ForegroundColor Red "`nESXi host has not been prepared for encryption ...`n"
    }
}

Function Remove-VMTPMKey {
<#
    .NOTES
    ===========================================================================
    Created by:    William Lam
    Organization:  VMware
    Blog:          www.williamlam.com
    Twitter:       @lamw
    ===========================================================================
    .DESCRIPTION
        This function removes an existing VM key
    .PARAMETER KeyName
        Name of the VM Key
    .PARAMETER Force
        Force remove VM Key
    .EXAMPLE
        # Remove VM key
        Remove-VMTPMKey -KeyName "windows-11-key"
    .EXAMPLE
        # Forcefully remove VM key
        Remove-VMTPMKey -KeyName "windows-11-key" -Force $true
#>
    param(
        [Parameter(Mandatory=$true)][String]$KeyName,
        [Parameter(Mandatory=$false)][Boolean]$Force=$false
    )

    $cm = Get-View $global:DefaultVIServer.ExtensionData.Content.CryptoManager

    $key = $cm.ListKeys($null) | where {$_.KeyId -eq $KeyName}
    Write-Host -ForegroundColor Yellow "Removing VM key ${KeyName} ..."
    try {
        $cm.RemoveKey($key,$Force)
    } catch {
        Write-Host -ForegroundColor Red "Failed to remove VM key, maybe in use or use -Force option to forcefully remove ...`n"
        break
    }
    Write-Host -ForegroundColor Green "Successfully removed VM key ...`n"
}

Function Get-VMHostTPMKeys {
<#
    .NOTES
    ===========================================================================
    Created by:    William Lam
    Organization:  VMware
    Blog:          www.williamlam.com
    Twitter:       @lamw
    ===========================================================================
    .DESCRIPTION
        This function returns all Host/VM keys
    .EXAMPLE
        Get-VMHostTPMKeys
#>

    $cm = Get-View $global:DefaultVIServer.ExtensionData.Content.CryptoManager

    if($cm.Enabled) {
        $cm.ListKeys($null)
    } else {
        Write-Host -ForegroundColor Red "`nESXi host has not been prepared for encryption or does not contain initial host key ...`n"
    }
}
Function Reconfigure-VMWithvTPM {
<#
    .NOTES
    ===========================================================================
    Created by:    William Lam
    Organization:  VMware
    Blog:          www.williamlam.com
    Twitter:       @lamw
    ===========================================================================
    .DESCRIPTION
        This function adds vTPM to existing VM and applies an existing VM key for encryption
    .PARAMETER KeyName
        Name of the VM Key
    .PARAMETER VMName
        Name of the VM to add vTPM
    .EXAMPLE
        Reconfigure-VMWithvTPM -KeyName "windows-11-key" -VMName "Windows-11"
#>
    param(
        [Parameter(Mandatory=$true)][String]$KeyName,
        [Parameter(Mandatory=$true)][String]$VMName
    )

    $vm = Get-VM $VMName

    $cm = Get-View $global:DefaultVIServer.ExtensionData.Content.CryptoManager

    # Retrieve VM key
    $cryptoSpec = New-Object VMware.Vim.CryptoSpecEncrypt
    $cryptoSpec.CryptoKeyId = $cm.ListKeys($null) | where {$_.KeyId -eq $KeyName}

    $spec = New-Object VMware.Vim.VirtualMachineConfigSpec

    # Set VM encryption key
    $spec.Crypto = $cryptoSpec

    # Add TPM device
    $spec.deviceChange = New-Object VMware.Vim.VirtualDeviceConfigSpec
    $spec.deviceChange[0].operation = 'add'
    $spec.deviceChange[0].device = New-Object VMware.Vim.VirtualTPM
    $spec.DeviceChange[0].Device.Key = 11000

    # Reconfigure VM
    Write-Host -ForegroundColor Yellow "Adding vTPM to ${VMName} using encryption key ${KeyName} ..."
    $task = $vm.ExtensionData.ReconfigVM_Task($spec)
    $task1 = Get-Task -Id ("Task-$($task.value)")
    $task1 | Wait-Task
}

1.在PowerShell裡執行vTPMStandaloneESXiFunctions.ps1指令碼檔案以獲取指令;

. .\Desktop\vTPMStandaloneESXiFunctions.psl  //指令碼存放的絕對位置

【VMware ESXi】如何在獨立的ESXi主機上配置vTPM並安裝Windows 11作業系統。

2.使用Connect-VIServer連線到獨立的ESXi主機;

Connect-VIServer 192.168.32.1 -User root -Password xxxxxx //ESXi使用者名稱和密碼

【VMware ESXi】如何在獨立的ESXi主機上配置vTPM並安裝Windows 11作業系統。

3. 執行Prepare-VMHostForEncryption命令,該命令將為ESXi主機進行加密做準備;

Prepare-VMHostForEncryption

【VMware ESXi】如何在獨立的ESXi主機上配置vTPM並安裝Windows 11作業系統。

4.執行New-InitialVMHostKey命令,為主機生成一個加密金鑰並取一個名字;

New-InitialVMHostKey -Operation CREATE -KeyName "host-key-1"

【VMware ESXi】如何在獨立的ESXi主機上配置vTPM並安裝Windows 11作業系統。

注:這個操作只需要在主機上執行一次就行。

5.執行New-VMTPMKey命令,為需要vTPM的虛擬機器建立一個加密金鑰並取一個名字;

New-VMTPMKey -Operation CREATE -KeyName "windows-11-key"

【VMware ESXi】如何在獨立的ESXi主機上配置vTPM並安裝Windows 11作業系統。

6.執行Get-VMHostTPMKeys命令,可以列出所有主機和虛擬機器的加密金鑰;

Get-VMHostTPMKeys

【VMware ESXi】如何在獨立的ESXi主機上配置vTPM並安裝Windows 11作業系統。

7.最後,執行Reconfigure-VMWithvTPM命令,將剛建立的虛擬機器加密金鑰附加給需要vTPM的虛擬機器;

Reconfigure-VMWithvTPM -KeyName "windows-11-key" -VMName "win11"

【VMware ESXi】如何在獨立的ESXi主機上配置vTPM並安裝Windows 11作業系統。

注:-VMName後面的名字為環境中實際建立的虛擬機器名字。

如果一切順利,這時你登陸VMware Host Client,找到剛剛完成加密的虛擬機器(win11),右擊編輯設定,可以看到“可信平臺模組”已被新增。現在,你應該可以開啟虛擬機器電源,正常安裝Windows 11作業系統了。

【VMware ESXi】如何在獨立的ESXi主機上配置vTPM並安裝Windows 11作業系統。

Windows 11 23H2首次啟動時可使用Shift + F10或Shift + Fn + F10調出CMD執行oobe\BypassNRO.cmd命令跳過微軟賬戶強制登入。

進入系統後,開啟Win + R執行視窗執行tpm.msc進入TPM管理,可以看到虛擬機器的TPM製造商為VMW釋出的vTPM,規範版本為2.0。

【VMware ESXi】如何在獨立的ESXi主機上配置vTPM並安裝Windows 11作業系統。

如果需要刪除虛擬機器的加密金鑰,執行Remove-VMTPMKey命令即可進行刪除,可以新增-Force $true選項進行強制刪除。

Remove-VMTPMKey -KeyName "windows-11-key" -Force $true

預設情況下,ESXi不會在重新引導時儲存或儲存任何加密金鑰!您需要重新新增已分配給虛擬機器的所有主機和任何虛擬機器加密金鑰,否則將無法開啟虛擬機器電源。這是vCenter Server透過管理由SKP或NKP置備到相應ESXi主機的加密金鑰以確保其可用而提供的主要優勢和功能之一。

這就是擁有物理TPM非常有用的地方!如果您具有符合AND標準的物理TPM 2.0晶片(需要FIFO而不是CRB協議),則可以按文章開頭的警告說明在ESXi中啟用金鑰永續性功能,然後已新增到ESXi主機的所有加密金鑰將會自動保留。

如果您沒有符合物理且相容TPM 2.0的晶片,作為解決方法,在上面的指令碼(vTPMStandaloneESXiFunctions.psl)裡實現了每次使用PowerCLI函式生成主機或虛擬機器加密金鑰時的自動加密金鑰備份。預設情況下,加密金鑰會自動儲存到名為tpm-keys.csv的CSV檔案中(你可以在你指令碼所在的目錄找到它)。與上面ESXi主機建立加密金鑰類似的過程,使用加密金鑰備份檔案,我們可以輕鬆地將所有加密金鑰重新匯入到ESXi主機上。這樣就不用重新生成新的主機或虛擬機器加密金鑰了。

同上面的方式一樣,執行指令碼,連線到ESXi主機,使用IMPORT操作從給定的CSV檔案裡匯入指定名稱的加密金鑰:

. .\vTPMStandaloneESXiFunctions.psl
Connect-VIServer xx.xx.xx.xx -User root -Password xxxxxx
Prepare-VMHostForEncryption
New-InitialVMHostKey -Operation IMPORT -KeyName "host-key-1" -CSVTPMKeyFile tpm-keys.csv
New-VMTPMKey -Operation IMPORT -KeyName "windows-11-key" -CSVTPMKeyFile tpm-keys.csv
Get-VMHostTPMKeys

如果金鑰已成功匯入,您現在應該能夠開啟已分配了生成的虛擬機器加密金鑰的任何虛擬機器的電源!

按理來說,如果沒錯,上述方法應該同樣也適用於不受vCenter Server管理的獨立ESXi-Arm主機。

參考:Support for Virtual Trusted Platform Module (vTPM) on ESXi without vCenter Server?

相關文章