根據微軟發佈的Windows 11操作系統要求,這個版本的系統需要硬體支持受信任的平臺模塊 (TPM) 才能進行安裝和使用,不然就會提示你“這臺電腦無法運行Windows11。這臺電腦不符合安裝此版本的Windows所需的最低系統要求。有關詳細信息,請訪問https://aka.ms/Windows ...
根據微軟發佈的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 //腳本存放的絕對位置
2.使用Connect-VIServer連接到獨立的ESXi主機;
Connect-VIServer 192.168.32.1 -User root -Password xxxxxx //ESXi用戶名和密碼
3. 運行Prepare-VMHostForEncryption命令,該命令將為ESXi主機進行加密做準備;
Prepare-VMHostForEncryption
4.運行New-InitialVMHostKey命令,為主機生成一個加密密鑰並取一個名字;
New-InitialVMHostKey -Operation CREATE -KeyName "host-key-1"
註:這個操作只需要在主機上執行一次就行。
5.運行New-VMTPMKey命令,為需要vTPM的虛擬機創建一個加密密鑰並取一個名字;
New-VMTPMKey -Operation CREATE -KeyName "windows-11-key"
6.運行Get-VMHostTPMKeys命令,可以列出所有主機和虛擬機的加密密鑰;
Get-VMHostTPMKeys
7.最後,運行Reconfigure-VMWithvTPM命令,將剛創建的虛擬機加密密鑰附加給需要vTPM的虛擬機;
Reconfigure-VMWithvTPM -KeyName "windows-11-key" -VMName "win11"
註:-VMName後面的名字為環境中實際創建的虛擬機名字。
如果一切順利,這時你登陸VMware Host Client,找到剛剛完成加密的虛擬機(win11),右擊編輯設置,可以看到“可信平臺模塊”已被添加。現在,你應該可以打開虛擬機電源,正常安裝Windows 11操作系統了。
Windows 11 23H2首次啟動時可使用Shift + F10或Shift + Fn + F10調出CMD運行oobe\BypassNRO.cmd命令跳過微軟賬戶強制登錄。
進入系統後,打開Win + R運行視窗運行tpm.msc進入TPM管理,可以看到虛擬機的TPM製造商為VMW發佈的vTPM,規範版本為2.0。
如果需要刪除虛擬機的加密密鑰,運行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?