The series so far:
In Windows Server 2016, Microsoft implemented a strong security concept called Shielded VMs. Shielded VMs have been improved in the Windows Server 2019 release. In the second part of this series, Nicolas describes what Shielded Virtual Machines are and how to configure them using PowerShell. One key investment area that Microsoft improves in every Windows Server release is security. Whether you work for a small company or a multinational company, security is essential. But sometimes, it can be hard to secure your machines. If you work with physical machines, then you simply put these machines in a secure datacenter with padlocks and provide access only to authorized administrators. On top of that you could have security guards in front of the datacenter. With Virtual Machines, you don’t have this security perimeter because many administrators can access these VMs, such as: Hyper-V administrators, backup administrators, storage administrators, or maybe network administrators. These administrators can access your virtual machines because they need access to VMs in order to do their jobs! Thus, they must be able to access the VHD disks.What are the security risks?
The first risk that you can encounter is, for example, that your storage administrator with access to Hyper-V, will copy and paste one or many VHD disks to a USB device. The reason why Hyper-V presents such a huge risk has to do with Hyper-V anatomy. A virtual machine is a VHD disk which is nothing more than a file. Rather than booting the virtual machine, anyone could simply mount the VHD disk on a different Windows Server, thus giving the administrator access to the full contents of the VHD disk. In a better case, you will be notified that a copy has been made to an external drive; in the worst case you will have no awareness that your data has been compromised. Another risk is a malware attack. Imagine your Hyper-V host is compromised; all your VHD disks will be vulnerable and the malware will have full access to your Virtual Machines. To reduce such risks, Microsoft has introduced Shielded Virtual Machines.How Do Shielded Virtual Machines Work?
Shielded Virtual Machines are based on the following concepts:- Code integrity: Code integrity checks whether the Hyper-V hosts meet the criteria to run Shielded Virtual Machines.
- Virtual Trusted Platform Module (TPM v2): vTPM will allow BitLocker encryption of the VM’s disks. A vTPM device is encrypted with a transport key. Thanks to vTPM, the TPM technology can be used even if the hardware Hyper-V host does not have a physical TPM.
- Host Guardian Service (HGS): The Host Guardian Service exists in Windows Server 2016. The Host Guardian Service is a host level component that provides two services – attestation and key protection. When a Shielded Virtual Machine is turned on, HGS will check to see if the host is eligible to run the Shielded VM or not.
Shielded Virtual Machines in Practice
Let’s see how to implement Shielded VMs in a test environment. This is the environment used in the example explained in this article:- DC1: This VM is the Domain Controller for the following AD Forest: GET-CMD.local. The IP Address is 10.0.0.6
- HYPV1: This is the Hyper-V host that will become a Guarded Host. The IP Address is 10.0.0.5
- HGS01: This is a standalone HGS Server that will be unclustered because this is a test environment. The IP Address is 10.0.0.4. HGS Will create a standalone Active Directory Forest. In my case, the forest will be named: GET-CMD-HGS.local.
- Shielded-WinServer2019: This is the Shielded VM based on Windows Server 2019 that will be deployed on the HYPV1 host.
If you lack sufficient resources to test on your laptop or your server, you can use your Azure subscription. That’s what I did to create my lab environment, and, thanks to Nested Virtualization, it works like a charm. Just remember to use at least Standard Ds_v3 VM size for your Hyper-V host.
Deploying Active Directory and a Guarded Host
Begin this deployment by installing DC1 with an Active Directory domain called GET-CMD.local. I will not provide the PowerShell commands because that is not the goal of this article, but you can easily retrieve the commands on the Internet. Once your AD forest has been successfully created, you must install the Hyper-V role on your Hyper-V host. On HYPV1, run the following command:|
1 |
PS > Install-WindowsFeature Hyper-V -IncludeManagementTools –Restart |
Deploying HGS Server
Connect to your HGS Server called HGS1 and install the Host Guardian Server role:|
1 2 3 4 5 |
PS > Install-WindowsFeature HostGuardianServiceRole -IncludeManagementTools Success Restart Needed Exit Code Feature Result ------- -------------- --------- -------------- True No Success {Active Directory Domain Services, Failove... WARNING: You need to configure Host Guardian Service by calling Install-HgsServer & Initialize-HgsServer PowerShell commands. |
|
1 2 |
PS > $HGSPwd = ConvertTo-SecureString -AsPlainText 'xxxxxxxxxxx' -Force PS > Install-HgsServer -HgsDomainName 'get-cmd-hgs.local' -SafeModeAdministratorPassword $HGSPwd –Restart |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
PS > $CertPwd = ConvertTo-SecureString -AsPlainText 'xxxxxxxxx' -Force PS > $Cert = New-SelfSignedCertificate -DnsName "hgs.get-cmd-hgs.local" PS > Export-PfxCertificate -Cert $Cert -Password $CertPwd -FilePath 'C:\HGSCert.pfx' Directory: C:\ Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 3/30/2018 9:24 PM 2661 HGSCert.pfx PS > $encryptCert = New-SelfSignedCertificate -DnsName "hgs.get-cmd-hgs.local" PS > Export-PfxCertificate -Cert $encryptCert -Password $certPwd -FilePath 'C:\encryptCert.pfx' Directory: C:\ Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 3/30/2018 9:24 PM 2661 encryptCert.pfx |
- HGSCert.pfx: This is the certificate for signing
- encryptCert.pfx: This is the certificate for encryption
Initialize HGS Server
The next step is the initialization process. The Initialize-HGSServer cmdlet will check the status of the Active Directory domain controller and configure both certificates previously created:|
1 2 |
PS > Initialize-HgsServer -HgsServiceName 'HGS' -SigningCertificatePath 'C:\HGSCert.pfx' -SigningCertificatePassword $certPwd -EncryptionCertificatePath 'C:\encryptCert.pfx' -EncryptionCertificatePassword $certPwd -TrustTpm LogPath: C:\Windows\Logs\HgsServer\180330212859\HyperVShielded |
|
1 2 3 4 5 6 7 8 9 10 |
PS > Get-ADComputer -Identity HGS1 DistinguishedName : CN=HGS1,OU=Domain Controllers,DC=get-cmd-hgs,DC=local DNSHostName : HGS1.get-cmd-hgs.local Enabled : True Name : HGS1 ObjectClass : computer ObjectGUID : 031528cd-27ff-4680-aa3a-5b7304ccaf6b SamAccountName : HGS1$ SID : S-1-5-21-87811353-1222259967-1591882773-1000 UserPrincipalName : |
|
1 |
PS > Set-HgsServer –TrustActiveDirectory |
|
1 |
PS > Add-DnsServerConditionalForwarderZone -Name "get-cmd.local" -ReplicationScope "Forest" -MasterServers 10.0.0.6 |
|
1 2 |
PS > netdom trust get-cmd-hgs.local /domain:get-cmd.local /userD:get-cmd.local\Nicolas /passwordD:xxxxxxxx /add The command completed successfully. |
|
1 2 3 4 |
PS > Get-ADTrust -Filter * | select Name, Direction, Source, Target Name Direction Source Target ---- --------- ------ ------ get-cmd.local Outbound DC=get-cmd-hgs,DC=local get-cmd.local |
Configure the Guarded Host – Part 1
Connect to DC1 and create a group called GuardedHosts with Global scope and a Security type. Then add the guarded host server to the newly created group:|
1 |
PS > Add-ADGroupMember "GuardedHosts" –Members "HYPV1$" |
|
1 |
PS > Get-ADGroup ‘GuardedHosts’ | Select-Object SID |
Configure HGS Server – Part 1
Switch back to HGS1 and run the Add-HgsAttestationHostGroup cmdlet:|
1 2 |
PS > Add-HgsAttestationHostGroup -Name ‘GuardedHosts’ -Identifier "S-1-5-21-1700574040-1668240534-2077627731-1105" S-1-5-21-1700574040-1668240534-2077627731-1105:GuardedHosts |
|
1 2 3 4 |
PS > Get-HgsAttestationHostGroup Name Identifier ---- ---------- GuardedHosts S-1-5-21-1700574040-1668240534-2077627731-1105 |
Configure the Guarded Host – Part 2
You need to create the Code Integrity Policy. This policy will be applied to every machine with the same configuration and is used to prevent unauthorized software from running on the host. First, you need to scan a reference system to create an XML policy file. This should capture most files that Windows needs to boot and run. In my case, the Guarded Host is a fresh and clean install, so I will scan the whole system. Doing so may take a while. If you add or change something on the host, you must start a new scan to update the policy Then, you will convert the XML file into a .p7b file. If you just want to scan and restrict to a single directory, remember to add the –ScanPath parameter. This command will create the XML file that you will convert into a Device Guard Policy using the the ConvertFrom-CIPolicy cmdlet:|
1 2 3 4 |
PS > New-CIPolicy -Level FilePublisher -Fallback Hash -FilePath 'C:\CodeIntegrity.xml' "Scan completed successfully" PS > ConvertFrom-CIPolicy -XmlFilePath 'C:\CodeIntegrity.xml' -BinaryFilePath 'C:\CodeIntegrity.p7b' C:\CodeIntegrity.p7bPS C:\Users\nicolas> |
Configure HGS Server – Part2
On HGS1, run the following command to add the Code Integrity policy to the Attestation service, based on the specified .p7b file.|
1 2 3 4 |
PS > Add-HgsAttestationCIPolicy -Path C:\CodeIntegrity.p7b -Name "Hyper-V Enforced CI Policy" Name PolicyType State ---- ---------- ----- Hyper-V Enforced CI Policy {CiPolicy} Enabled |
|
1 2 3 4 5 6 |
PS > Get-HgsServer Name Value ---- ----- AttestationOperationMode AD AttestationUrl {http://hgs.get-cmd-hgs.local/Attestation} KeyProtectionUrl {http://hgs.get-cmd-hgs.local/KeyProtection} |
Configure the Guarded Host – Part3
Switch back to HYPV1 and configure it to use the Host Guardian Service. This command configures the URLs used by the attestation client and the key protection client:|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
PS > Get-HgsClientConfiguration IsHostGuarded : False Mode : Local KeyProtectionServerUrl : AttestationServerUrl : AttestationOperationMode : Unknown AttestationStatus : NotConfigured AttestationSubstatus : NoInformation PS > Set-HgsClientConfiguration -KeyProtectionServerUrl "http://hgs.get-cmd-hgs.local/KeyProtection" -AttestationServerUrl "http://hgs.get-cmd-hgs.local/Attestation" IsHostGuarded : True Mode : HostGuardianService KeyProtectionServerUrl : http://hgs.get-cmd-hgs.local/KeyProtection AttestationServerUrl : http://hgs.get-cmd-hgs.local/Attestation AttestationOperationMode : ActiveDirectory AttestationStatus : Passed AttestationSubstatus : NoInformation |
Deploy shielded VMs
To confirm that this works, deploy a simple Virtual Machine. Enable the Remote Desktop Protocol because after enabling Shielded Mode on this VM, you will not be able to access to this VM, except through RDP:|
1 2 3 4 5 6 7 8 9 10 11 |
PS > # Rhe guardian metadata is available at http://<HGSCLUSTERNAME>/KeyProtection/service/metadata/2014-07/metadata.xml PS > Invoke-WebRequest "http://hgs.get-cmd-hgs.local/KeyProtection/service/metadata/2014-07/metadata.xml" -OutFile "C:\Guard PS > # Import the HGS guardian for each fabric you want to run your shielded VM PS > Import-HgsGuardian -path "C:\GuardianMetadata.xml" -Name "Get-CMD" -AllowUntrustedRoot Name HasPrivateSigningKey Signing Certificate Subject ---- -------------------- --------------------------- Get-CMD False CN=hgs.get-cmd-hgs.local PS > # The certificate is stored at Cert:\LocalMachine\Shielded VM Local Certificates PS > $Owner = New-HgsGuardian -Name "Owner" -GenerateCertificates PS > $Guardian = Get-HgsGuardian -Name "Get-CMD" PS > $HGSKeyProtector = New-HgsKeyProtector -Owner $Owner -Guardian $Guardian –AllowUntrustedRoot |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
PS > Get-VM -Name Shielded-WinServer2019 Name State CPUUsage(%) MemoryAssigned(M) Uptime Status Version ---- ----- ----------- ----------------- ------ ------ ------- Shielded-WinServer2019 Running 0 546 00:48:15.5960000 Operating normally 8.0 PS > Get-VM -Name Shielded-WinServer2019 | Get-VMSecurity TpmEnabled : False KsdEnabled : False Shielded : False EncryptStateAndVmMigrationTraffic : False VirtualizationBasedSecurityOptOut : False CimSession : CimSession: . ComputerName : HGS IsDeleted : False |
|
1 2 3 4 5 6 |
PS > $VMName = "Shielded-WinServer2019" PS > Get-VM -Name $VMName | Stop-VM -Force PS > Set-VMKeyProtector -VMName $VMName -keyprotector $HGSKeyProtector.RawData PS > Set-VMSecurityPolicy -VMName $VMName -Shielded $true PS > Enable-VMTPM -VMName $VMName PS > Start-VM -Name $VMName |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
PS > Get-VM -Name Shielded-WinServer2019 Name State CPUUsage(%) MemoryAssigned(M) Uptime Status Version ---- ----- ----------- ----------------- ------ ------ ------- Shielded-WinServer2019 Running 0 512 00:01:53.3890000 Operating normally 8.0 PS C:\Users\nicolas> Get-VM -Name Shielded-WinServer2019 | Get-VMSecurity TpmEnabled : True KsdEnabled : False Shielded : True EncryptStateAndVmMigrationTraffic : True VirtualizationBasedSecurityOptOut : False CimSession : CimSession: . ComputerName : HGS IsDeleted : False |
Open the settings for this Virtual Machine and confirm that Enable Shielding is selected. At this step, you cannot modify the security settings because the VM is running in a guarded fabric.
What’s new in Windows Server 2019?
Microsoft states that the Shielded VMs concept in Windows Server 2016 was well received by customers, so in Windows Server 2019, Microsoft has extended the Shielded Virtual Machine concept to encompass Linux Virtual Machines. Windows Server 2019 also includes the ability to encrypt network segments. Microsoft has improved troubleshooting of Shielded VMs for Windows Server and Linux. You can for example use PowerShell Direct to connect to Shielded VMs even if you’ve lost network connectivity to these VMs.Conclusion
In my point of view, Shielded VMs are not very well documented. If you want to implement Shielded VMs in your environment, be sure that all the prerequisites are satisfied, and, if it’s possible, use a testing environment before implementing in production. Even though it is possible to run the Host Guardian Service on a standalone server, I should advise you to only use Shielded VMs within Hyper-V clusters. Otherwise, in case of a host level failure, the Shielded Virtual Machines could be permanently inaccessible. For those of you who want to test Shielded VMs, you can use nested Hyper-V for testing but not in production. To finish, remember that a shielded VM can exist only on a guarded host, and a shielded VM cannot run on a normal Hyper-V Host. But a normal Virtual Machine, without encryption, can coexist with Shielded VMs on guarded Hyper-V Hosts.FAQs
1. What is a shielded virtual machine in Hyper-V?
A shielded VM is a Hyper-V virtual machine protected by multiple security layers to prevent the fabric admin (Hyper-V host administrator, storage admin, backup operator) from accessing VM contents. Key protections: (1) virtual TPM (vTPM) enables BitLocker disk encryption within the guest OS, encrypting VM disk files at rest; (2) VM runtime state is encrypted so the host can’t dump guest memory; (3) console access from the Hyper-V Manager is disabled – you can start/stop/migrate but not see the VM screen; (4) the Host Guardian Service (HGS) attests that the Hyper-V host is trusted before allowing shielded VMs to boot. The combination prevents scenarios where a compromised or malicious host admin exfiltrates VM contents. Distinct from encryption-supported VMs (a lighter-weight protection where some features are optional) – shielded VMs enforce all protections. Introduced in Windows Server 2016, continues in 2019 and 2022.
2. What is the Host Guardian Service (HGS)?
HGS is a Windows Server role that acts as the trust anchor for Hyper-V guarded fabrics. Two primary services: (1) attestation – Hyper-V hosts contact HGS at boot and intermittently to prove they’re trusted (running approved code, correct security policies, etc.); HGS issues a health certificate if the host passes attestation. Two attestation modes: Admin-trusted (the host’s membership in a specific AD group is sufficient, simpler) or TPM-trusted (host’s measured boot is attested against known-good values via TPM, stronger). (2) Key Protection Service (KPS) – HGS holds the master keys needed to boot shielded VMs; it releases keys to hosts only if they have valid attestation health certificates. HGS should be deployed as a separate Active Directory forest (bastion forest) from the fabric being protected, on dedicated hardware, with its own backup and recovery plan – compromise of HGS compromises the entire guarded fabric. Typically 3+ HGS nodes for availability. Configure via Install-HgsServer and Initialize-HgsServer PowerShell cmdlets.
3. How do I create a shielded VM with PowerShell?
Five steps. (1) Install HGS and register Hyper-V hosts (Install-HgsServer, Initialize-HgsServer, Set-HgsClientConfiguration on each Hyper-V host pointing at HGS URL). (2) Create shielding data – a .PDK file containing the VM owner’s key, permitted signing certificates, and unattend.xml. Pattern: New-ShieldingDataAnswerFile, then Protect-ShieldingDataFile to produce the .PDK. (3) Use a signed VM template disk (VHDX) – must be signed with a certificate trusted by the shielding data file, typically prepared via Protect-TemplateDisk. (4) Create the shielded VM via New-VM and apply the shielding data via New-ShieldedVM or the modern Fabric Console patterns. Alternatively, convert an existing unshielded VM (if the guest OS supports conversion via Enable-VMTPM, BitLocker on disks, and the shielding process). (5) Validate – boot the VM and verify it’s shielded via Get-VMSecurity and confirm HGS attestation is succeeding. Requires Windows Server Datacenter edition on Hyper-V hosts; Standard edition can’t host shielded VMs.
This document contains proprietary information and is protected by copyright law.
Copyright © 2026 Red Gate Software Limited. All rights reserved
Load comments