Automate Untrusted Guardian-based vTPM enablement across a Hyper-V cluster

Objective

Enable vTPM-enabled shielded VMs for all SCVMM 2022 templates and new VM deployments without deploying a full HGS fabric, using the Untrusted Guardian model.

What will happen when you run this script

Pre-requisites

$GuardianName         = "UntrustedGuardian"
$CertPasswordPlain    = "cert_password"
$CentralKeyPath       = "\\VMMServer\Keys\GuardianKeys"
$ClusterName          = "HVCluster01"
$VMMServerName        = "SCVMM2022"
$TemplateName         = "Windows2022-Base-Template"

$mypwd = ConvertTo-SecureString -String $CertPasswordPlain -Force -AsPlainText

Write-Host "Checking and creating local certificates..." -ForegroundColor Cyan

if (-not (Test-Path "Cert:\LocalMachine\Shielded VM Local Certificates")) {
    Write-Host "Shielded VM Local Certificates store not found. Creating Untrusted Guardian..."
    New-HgsGuardian -Name $GuardianName -GenerateCertificates -AllowUntrustedRoot | Out-Null
} else {
    Write-Host "Guardian certificate store exists. Skipping creation..."
}

$signingCert     = Get-ChildItem -Path "Cert:\LocalMachine\Shielded VM Local Certificates" | Where-Object { $_.Subject -match "Signing" }
$encryptionCert  = Get-ChildItem -Path "Cert:\LocalMachine\Shielded VM Local Certificates" | Where-Object { $_.Subject -match "Encryption" }

Write-Host "Exporting certificates to central share..." -ForegroundColor Cyan
Export-PfxCertificate -Cert $signingCert    -FilePath "$CentralKeyPath\SigningCertificate.pfx"    -Password $mypwd -Force
Export-PfxCertificate -Cert $encryptionCert -FilePath "$CentralKeyPath\EncryptionCertificate.pfx" -Password $mypwd -Force

Write-Host "Certificates exported successfully to $CentralKeyPath" -ForegroundColor Green

Write-Host "`Importing certificates on all cluster nodes..." -ForegroundColor Cyan

$ClusterNodes = (Get-ClusterNode -Cluster $ClusterName).Name
foreach ($Node in $ClusterNodes) {
    Write-Host "Processing $Node..." -ForegroundColor Yellow
    Invoke-Command -ComputerName $Node -ScriptBlock {
        param($CentralKeyPath, $mypwd)
        if (-not (Test-Path "Cert:\LocalMachine\Shielded VM Local Certificates")) {
            New-Item -Path "Cert:\LocalMachine\Shielded VM Local Certificates" -Force | Out-Null
        }
        Import-PfxCertificate -FilePath "$CentralKeyPath\SigningCertificate.pfx"    -Password $mypwd -CertStoreLocation "Cert:\LocalMachine\Shielded VM Local Certificates" | Out-Null
        Import-PfxCertificate -FilePath "$CentralKeyPath\EncryptionCertificate.pfx" -Password $mypwd -CertStoreLocation "Cert:\LocalMachine\Shielded VM Local Certificates" | Out-Null
        Write-Host "Imported signing/encryption certificates on $env:COMPUTERNAME"
    } -ArgumentList $CentralKeyPath, $mypwd
}

Write-Host "All cluster nodes now have identical Untrusted Guardian certificates." -ForegroundColor Green

Write-Host "Configuring SCVMM Template for vTPM..." -ForegroundColor Cyan

Get-SCVMMServer -ComputerName $VMMServerName | Out-Null

$Template = Get-SCVMTemplate -Name $TemplateName

Set-SCHardwareProfile -HardwareProfile $Template.HardwareProfile -EnableVirtualTPM $True

Write-Host "Template '$TemplateName' is now configured with vTPM (Untrusted Guardian mode)." -ForegroundColor Green

Write-Host "`Verifying setup..." -ForegroundColor Cyan

foreach ($Node in $ClusterNodes) {
    Invoke-Command -ComputerName $Node -ScriptBlock {
        Write-Host "Host: $env:COMPUTERNAME"
        Get-ChildItem -Path "Cert:\LocalMachine\Shielded VM Local Certificates" | Format-Table Subject, HasPrivateKey, Thumbprint
    }
}

Write-Host "Verification complete. All hosts ready for vTPM-enabled Shielded VM deployment via SCVMM." -ForegroundColor Green

Go to Library - Templates - select your Gen 2 template.

Stop-VM -Name "MyVM"
Set-VMKeyProtector -VMName "MyVM" -NewLocalKeyProtector
Enable-VMTPM -VMName "MyVM"
Start-VM -Name "MyVM"