How to Configure Hyper-V Shielded VMs on Windows Server 2025
In a traditional virtualisation environment, a rogue Hyper-V host administrator can mount a VM’s virtual hard disk, extract credentials from the offline OS, or attach debugging tools to a running guest — all without authentication. Shielded Virtual Machines address this threat at the hypervisor layer. A Shielded VM is protected by a combination of BitLocker encryption on the VHDX, a virtual Trusted Platform Module (vTPM) that holds BitLocker keys in hardware-bound storage, and a Host Guardian Service (HGS) that performs cryptographic attestation of every host before releasing VM secrets. If a host cannot prove it is in a known-good, compliant state, HGS refuses to release the key material and the VM simply will not start. On Windows Server 2025, shielded VM support is a built-in Hyper-V capability that requires no third-party software. This tutorial walks through deploying HGS, configuring guarded hosts, and provisioning shielded VMs from template images.
Prerequisites
- At least two Windows Server 2025 servers: one (or a cluster) for the HGS role and one or more Hyper-V hosts to become guarded hosts
- HGS servers should be domain-joined to a dedicated, isolated HGS domain (separate from the production forest for maximum security)
- For TPM-trusted attestation: UEFI firmware with Secure Boot and a TPM 2.0 chip on each guarded host
- For Admin-trusted attestation (test/dev only): a security group approach — no TPM required
- Fabric administrator and HGS administrator accounts
- A code-signing certificate for the Shielding Data File (.pdk)
Step 1: Deploy the Host Guardian Service
HGS runs on its own Windows Server instances, ideally in a minimal-footprint Server Core configuration in a separate, highly secured forest. Install the HGS role and initialise the HGS cluster:
# On the first HGS server — install the Host Guardian Service role
Install-WindowsFeature -Name HostGuardianServiceRole -IncludeManagementTools -Restart
# After reboot, configure HGS with TPM-trusted attestation mode
# This creates a new single-node Active Directory forest for HGS
# Replace with a short DNS label (e.g. "hgs")
Install-HgsServer `
-HgsDomainName "hgs.contoso.local" `
-SafeModeAdministratorPassword (ConvertTo-SecureString "P@ssw0rd!HGS2025" -AsPlainText -Force)
# Wait for reboot, then initialize the HGS service (TPM-trusted attestation)
Initialize-HgsServer `
-HgsServiceName "hgs" `
-SigningCertificateThumbprint "" `
-EncryptionCertificateThumbprint "" `
-TrustTPM
# Verify HGS service URLs (used by guarded hosts)
Get-HgsServer
# Expected output includes:
# AttestationOperationModeName : TpmTrusted
# AttestationUrl : http://hgs.hgs.contoso.local/Attestation
# KeyProtectionUrl : http://hgs.hgs.contoso.local/KeyProtection
For high availability in production, add a second HGS node and join it to the HGS cluster using Install-HgsServer -HgsDomainName hgs.contoso.local -HgsServerIPAddress <primary-HGS-IP>.
Step 2: Configure TPM-Trusted Attestation Policies
With TPM-trusted attestation, HGS verifies that the guarded host’s firmware and boot state match a known-good baseline recorded as a TPM endorsement key certificate and a code integrity policy.
# On a representative guarded host — capture its TPM endorsement key certificate
$ekCert = Get-PlatformIdentifier -Name "HVHOST01"
Add-HgsAttestationTpmHost -Path $ekCert -Name "HVHOST01" -Force
# Capture the host's secure boot baseline (Unified Extensible Firmware Interface variables)
$tpmBaseline = Get-HgsAttestationBaselinePolicy -SkipValidation
Add-HgsAttestationTpmPolicy -Name "WS2025-Baseline" -Path $tpmBaseline
# Create a code integrity policy that allows only Microsoft-signed and WHQL-signed drivers
New-CIPolicy -FilePath "C:HGSBaseCI.xml" -Level Publisher -Fallback Hash -UserPEs:$false
ConvertFrom-CIPolicy "C:HGSBaseCI.xml" "C:HGSBaseCI.p7b"
Add-HgsAttestationCIPolicy -Name "WS2025-CIPolicy" -Path "C:HGSBaseCI.p7b"
# Verify all HGS attestation policies
Get-HgsAttestationPolicy | Format-Table Name, PolicyType, IsEnabled, Version -AutoSize
Step 3: Configure Guarded Hosts
Each Hyper-V host that will run Shielded VMs must be configured as a guarded host by pointing it at the HGS attestation and key protection URLs. Run the following on each Hyper-V host:
# On each Hyper-V guarded host
# Set HGS client URLs (use HTTPS in production; shown with HTTP for lab clarity)
Set-HgsClientConfiguration `
-AttestationServerUrl "http://hgs.hgs.contoso.local/Attestation" `
-KeyProtectionServerUrl "http://hgs.hgs.contoso.local/KeyProtection"
# Test attestation immediately — the host must pass before it can run Shielded VMs
Get-HgsClientConfiguration
# Expected: AttestationStatus = Passed
# If the status is Rejected, check the EventLog on the HGS server:
Get-WinEvent -ComputerName hgs01 -LogName "Microsoft-Windows-HostGuardianService-Attestation/Operational" |
Where-Object { $_.Level -le 3 } | Select-Object TimeCreated, Message | Format-List
If attestation fails, common causes include a mismatched code integrity policy (the host has an unsigned driver not covered by the CI policy), outdated UEFI firmware, or Secure Boot disabled in the BIOS. Resolve each failure by either updating the CI policy or fixing the host configuration — never weaken the attestation policy to bypass failures in production.
Step 4: Create a Shielding Data File (.pdk)
The Shielding Data File (PDK) is a binary blob that contains the secrets and policies for a specific Shielded VM: the administrator password, domain join credentials, authorized HGS guardians, and the list of which HGS-attested hosts are permitted to run the VM. The fabric (Hyper-V admin) never sees inside the PDK.
# On the VM tenant's workstation (not on the Hyper-V host — the tenant creates this)
# Import the Shielding Data cmdlets
Import-Module ShieldingDataFileCommands
# Get the guardian metadata from HGS (the "guardian" represents the HGS-protected hosting fabric)
Invoke-WebRequest -Uri "http://hgs.hgs.contoso.local/KeyProtection/service/metadata/2014-07/metadata.xml" `
-OutFile "C:PDKHgsGuardian.xml"
$guardian = Import-HgsGuardian -Path "C:PDKHgsGuardian.xml" -Name "ProductionFabric" -AllowExpired
# Create the owner guardian (tenant keeps this certificate — it allows the tenant to decrypt the VM)
$ownerCert = New-SelfSignedCertificate -Subject "CN=VMTenantOwner" -CertStoreLocation Cert:LocalMachineMy
$ownerGuardian = New-HgsGuardian -Name "TenantOwner" -GenerateCertificates
# Build the shielding data file
$adminCred = New-Object System.Management.Automation.PSCredential(".Administrator",
(ConvertTo-SecureString "VMAdminP@ss2025!" -AsPlainText -Force))
New-ShieldingDataFile `
-ShieldingDataFilePath "C:PDKWebServer01.pdk" `
-Owner $ownerGuardian `
-Guardian $guardian `
-VolumeIDQualifier (New-VolumeIDQualifier -VolumeTag "WebServer2025Template") `
-WindowsUnattendFile "C:PDKunattend.xml" `
-Policy Shielded
Step 5: Convert an Existing VM to Shielded
You can mark an existing Generation 2 VM as requiring shielding, which enables the vTPM, enforces Secure Boot with Microsoft UEFI certificate authority, and prevents the VM from starting on a host that fails attestation:
# On the Hyper-V host — convert a Generation 2 VM to shielded
$vmName = "WebServer01"
# Enable vTPM (required for shielded VMs)
Set-VMKeyProtector -VMName $vmName -NewLocalKeyProtector
Enable-VMTPM -VMName $vmName
# Enforce the shielded VM security policy
Set-VMSecurityPolicy -VMName $vmName -Shielded $true
# Verify the security profile
Get-VMSecurity -VMName $vmName | Format-List *
# Expected output includes:
# TpmEnabled : True
# KsdEnabled : True (Key Storage Device)
# Shielded : True
# EncryptStateAndVmMigrationTraffic : True
Step 6: Provision a New Shielded VM from a Template
# On the Hyper-V/VMM fabric — provision a shielded VM from a signed template VHDX
# The template was previously prepared with the VMM template wizard or New-ShieldedVMSpecializationDataFile
# Use the shielding data file created by the tenant
$pdkPath = "\FILESERVERPDKStoreWebServer01.pdk"
$templateVHDX = "\FILESERVERTemplatesWS2025-Core-Template.vhdx"
# Create the VM (VMM or manual)
$newVHDX = "C:ClusterStorageVolume1WebServer01OSDisk.vhdx"
Copy-Item -Path $templateVHDX -Destination $newVHDX
$vm = New-VM -Name "WebServer01" -Generation 2 -MemoryStartupBytes 4GB `
-VHDPath $newVHDX -SwitchName "External"
# Apply the shielding data to provision the VM
Initialize-ShieldedVM -VM $vm -ShieldingDataFilePath $pdkPath -Verbose
Start-VM -Name "WebServer01"
Step 7: Troubleshooting Attestation Failures
# On the guarded host — check current attestation health
Get-HgsClientConfiguration | Select-Object AttestationStatus, AttestationSubstatus, KeyProtectionClientUrl
# Force a fresh attestation attempt
Invoke-WebRequest -Uri "$((Get-HgsClientConfiguration).AttestationServerUrl)/v2/api/health" -UseBasicParsing
# View attestation events on the HGS server
Get-WinEvent -LogName "Microsoft-Windows-HostGuardianService-Attestation/Operational" |
Select-Object TimeCreated, LevelDisplayName, Message |
Where-Object { $_.LevelDisplayName -ne "Information" } |
Format-List
# View guarded host events locally
Get-WinEvent -LogName "Microsoft-Windows-Hyper-V-VMMS-Admin" |
Where-Object { $_.Message -like "*shielded*" -or $_.Message -like "*guardian*" } |
Select-Object TimeCreated, Message | Format-List
Hyper-V Shielded VMs on Windows Server 2025 provide a robust defence against malicious or compromised fabric administrators by binding VM secrets to cryptographically attested host states. The combination of HGS attestation, vTPM-backed BitLocker encryption, and the Shielding Data File model ensures that even a physical attacker who steals a VHDX file cannot read the data it contains. Deploy HGS on at minimum two nodes for high availability, use TPM-trusted attestation in all production environments, and treat the PDK and owner guardian certificate with the same care as a root CA private key — they are the only artefacts that can unlock your shielded VM outside the authorised fabric.