Step by step – Creating Shielded VMs without VMM

Hi, I’m Jane, one of the newest members of the Windows Server Security Product Team. My very first hands-on experience is to deploy Shielded VMs with the minimum amount of hardware. It was fun and a great learning experience. I followed the comprehensive TP5 deployment guide on Shielded VM and Guarded Fabric guide with one deviation: I deployed it without VMM. If you are just like me, trying out the scenario end to end, this blog post will provide you the step by step instructions to use just the PowerShell cmdlets to create shielded VMs.

This blog post doesn’t cover any of the gory details or the building blocks for this scenario, etc.  For that, I highly recommend that you first read through the deployment guide which also covers terminology descriptions, scenario overview and architectural designs.


Before you start, you should have completed the deployment of both the guarded host and the Host Guardian Service (HGS) server.  For your reference, you can follow the deploying HGS server blog to setup the HGS server as well as deploying guarded host guide to configure the guarded hosts.

As a way to quickly check and verify your deployment, run the following cmdlet on each guarded host:


The output of the cmdlet should display that IsHostGuarded is True, and AttestationStatus is Passed.

There are 2 ways to create shielded VMs:

  • Shielding an existing VM (a.k.a grandfathering)
  • Provisioning a shielded VM from a template

I’ll illustrate both approaches in this blog post.

Shielding an existing VM

Let’s start with the simpler approach. This requires you to have a running VM on a host which is not the guarded host. This is important to distinguish, because you are simulating the scenario where a tenant wants to take an existing, unprotected VM and shield it before moving it to a guarded host. For clarity, the host machine which is not the guarded host will be referred as the tenant host below. A shielded VM can only run on a trusted guarded host. The trust is established by the adding the HGS guardian (retrieved from the HGS server) to the Key Protector which is used to shield the VM. That way, the shielded VM can only be started after the guarded host successfully attest against the HGS server.

In this example, the running VM is named SVM. This VM must be generation 2 and have a supported OS installed with remote desktop enabled. You should verify the VM can be connected through RDP first, as it will almost certainly be the primary way to access the VM once it is shielded (unless you have installed other remoting capabilities).

Retrieve the HGS guardian

As explained above, the first step is to get the HGS guardian metadata from the HGS server, and use it to create the Key protector. To do this, run the following PowerShell command on a guarded host or any machine that can reach the HGS server:

Invoke-WebRequest http://<HGSServer">FQDN>/KeyProtection/service/metadata/2014-07/metadata.xml -OutFile C:\HGSGuardian.xml

Copy the file C:\HGSGuardian.xml to the tenant host.

Shield the VM

Each shielded VM has a Key Protector which contains one owner guardian, and one or more HGS guardians. The steps below illustrate the process of getting the guardians, create the Key Protector in order to shield the VM.

Run the following cmdlets on a tenant host:

# SVM is the VM name which to be shielded
$VMName = 'SVM'

# Turn off the VM first. You can only shield a VM when it is powered off
Stop-VM –VMName $VMName

# Create an owner self-signed certificate
$Owner = New-HgsGuardian –Name 'Owner' –GenerateCertificates

# Import the HGS guardian
$Guardian = Import-HgsGuardian -Path 'C:\HGSGuardian.xml' -Name 'TestFabric' –AllowUntrustedRoot

# Create a Key Protector, which defines which fabric is allowed to run this shielded VM
$KP = New-HgsKeyProtector -Owner $Owner -Guardian $Guardian -AllowUntrustedRoot

# Enable shielding on the VM
Set-VMKeyProtector –VMName $VMName –KeyProtector $KP.RawData

# Set the security policy of the VM to be shielded
Set-VMSecurityPolicy -VMName $VMName -Shielded $true

# Enable vTPM on the VM
Enable-VMTPM -VMName $VMName

Typically, you would logon to the VM and enable BitLocker before moving it to the guarded host. Since this is only for testing purposes, we’ll skip this step here.

To complete the process, perform the following steps:

  • shut down the shielded VM
  • export the VM from the tenant host
  • copy the exported file to the guarded host and import them
  • start the VM on the guarded host

As shown above, this approach is quite manual and easy to make mistakes (such as not running BitLocker to encrypt data). Therefore, it is not recommended in production. For that, you should use the second approach illustrated in the following section: Provisioning Shielded VMs using the template disk.

Provisioning Shielded VMs using shielded templates

In production, you would typically use a fabric manager (e.g. VMM) to deploy shielded VMs. However, the steps illustrated below allow you to deploy and validate the entire scenario without a fabric manager.

In a nutshell, you will create a template disk, a shielding data file, a Windows unattend.xml file and other security artifacts on the tenant host, then copy these files to a guarded host and provision shielded VMs.


In order to create the template disk and the shielding data file, you will need to install the “Shielded VM Tools” feature under the Remote Server Administration Tools -> Feature Administration Tools on the tenant host machine. You can do this either in Server Manager or by running the following cmdlet:
Install-WindowsFeature RSAT-Shielded-VM-Tools
Next, you will also need a VHDX file with a fully installed and sysprepped OS—we’ll call it ServerOS.vhdx.

Create a signed template disk

In order to create the signed template disk, you will need to have a certificate to sign it. For simplicity, let’s create a self-signed certificate for this purpose.  Run the following PowerShell commands on the tenant host:

$cert = New-SelfSignedCertificate -DnsName ''

Now, we sign the disk:

Protect-TemplateDisk -Path ‘C:\ServerOS.vhdx’ -TemplateName "ServerOSTemplate" -Version -Certificate $cert

Create Shielding Data (PDK) file

Shielding Data is created/owned by tenants/VM owners and contains secrets that must be protected from the fabric admin and that is needed to create shielded VMs, e.g. the shielded VM’s administrator password.  Shielding Data is contained in a PDK file which is also encrypted.  Once created by the tenant/VM owner, the resulting PDK file must be copied to the guarded fabric. The deployment guide has much more detailed explanation of shielding data and why it is necessary.

In addition, you will also need a Windows unattend.xml file; a working sample is also included in the deployment guide.

During deployment, you will need to import the HGS guardian, you can follow the steps described in the section above titled Retrieve the HGS guardian—it will be referenced as ‘C:\HGSGuardian.xml’ below.

Run the following cmdlets on the tenant host to create the PDK:

#Create a VolumeSignatureCatalog file for the template disk, to ensure the template disk is not being tampered by anyone at the deployment time
Save-VolumeSignatureCatalog -TemplateDiskPath "ServerOS.vhdx" –VolumeSignatureCatalogPath "C:\ServerOSTemplate.vsc"

# Create owner certificate
$Owner = New-HgsGuardian –Name 'Owner' –GenerateCertificates

# Import the HGS guardian
$Guardian = Import-HgsGuardian -Path C:\HGSGuardian.xml -Name 'TestFabric' –AllowUntrustedRoot

# Create the PDK file on Server running post TP5 build
New-ShieldingDataFile -ShieldingDataFilePath 'C:\Contoso.pdk' -Owner $Owner –Guardian $guardian –VolumeIDQualifier (New-VolumeIDQualifier -VolumeSignatureCatalogFilePath 'C:\ServerOSTemplate.vsc' -VersionRule Equals) -WindowsUnattendFile 'C:\unattend.xml'

Provision shielded VM on a guarded host

Copy the template disk file (ServerOS.vhdx) and the PDK file (contoso.pdk) to the guarded host, to get ready for deployment.

The Windows unattend.xml file has some placeholder settings that need to be substituted during deployment to ensure the resulting VM is unique and fully configured, e.g. it contains a computer name and a timezone. These unique values are specified in a FSK (Fabric Specialization Key) file. To create one, run the following cmdlet on the guarded host to create the FSK file, and provision a shielded VM using the signed template disk:

This script will provisioin a new shielded VM from existing disk template and a PDK file.

You will need a PDK and associated disk template file prior for shielded VM provisioning

The name of the VM to be created

The path for the pdk file

.PARAMETER TemplateDiskPath
The path for the disk template

The path for VM location.
    [Parameter (Mandatory=$true)][string] $VMName,
    [Parameter (Mandatory=$true)][string] $PdkFile,
    [Parameter (Mandatory=$true)][string] $TemplateDiskPath,
    [Parameter (Mandatory=$true)][string] $VMPath,
    [string] $switch = 'External',
    [Int64] $VMMemSize = 1GB

$VmVhdPath = $VMPath + '\' + $VMName + '.vhdx'
$fskFile = $VMPath + '\' + $VMName + '.fsk'

#check if the VMPath exist
#create the folder
If ((Test-Path $VMPath) -eq $false) 
    New-Item $VMPath -Type Directory

#create fsk file
New-ShieldedVMSpecializationDataFile -ShieldedVMSpecializationDataFilePath $fskfile -SpecializationDataPairs @{ '@ComputerName@' = "$VMName"; '@TimeZone@' = 'Pacific Standard Time' }

#Make a copy of the template
Copy-Item -Path $TemplateDiskPath -Destination $VmVhdPath

#create VM
$vm = New-VM -Name $VMName -Generation 2 -VHDPath $VmVhdPath -MemoryStartupBytes $VMMemSize -Path $VMPath -SwitchName $switch -erroraction Stop
$kp = Get-KeyProtectorFromShieldingDataFile -ShieldingDataFilePath $PdkFile
Set-VMkeyProtector -VM $vm -KeyProtector $kp

#Get PDK security policy
$importpdk = Invoke-CimMethod -ClassName  Msps_ProvisioningFileProcessor -Namespace root\msps -MethodName PopulateFromFile -Arguments @{FilePath=$PdkFile }
$cimvm = Get-CimInstance  -Namespace root\virtualization\v2 -Class Msvm_ComputerSystem -Filter "ElementName = '$VMName'"

$vsd = Get-CimAssociatedInstance -InputObject $cimvm -ResultClassName "Msvm_VirtualSystemSettingData"
$vmms = gcim -Namespace root\virtualization\v2 -ClassName Msvm_VirtualSystemManagementService
$ssd = Get-CimAssociatedInstance -InputObject $vsd -ResultClassName "Msvm_SecuritySettingData"
$ss = Get-CimAssociatedInstance -InputObject $cimvm -ResultClassName "Msvm_SecuritySErvice"
$cimSerializer = [Microsoft.Management.Infrastructure.Serialization.CimSerializer]::Create()
$ssdString = [System.Text.Encoding]::Unicode.GetString($cimSerializer.Serialize($ssd, [Microsoft.Management.Infrastructure.Serialization.InstanceSerializationOptions]::None))
$result = Invoke-CimMethod -InputObject $ss -MethodName SetSecurityPolicy -Arguments @{"SecuritySettingData"=$ssdString;"SecurityPolicy"=$importPdk.ProvisioningFile.PolicyData}

Enable-VMTPM -vm $vm
Initialize-ShieldedVM -VM $vm -ShieldingDataFilePath $PdkFile -ShieldedVMSpecializationDataFilePath $fskfile

While the shielded VM is being provisioned, you can use the following cmdlet to check the progress:

Get-ShieldedVMProvisioningStatus -VMName $vmname

Once it’s completed, make sure the shielded VM has the correct Network Adapter configured so it can be accessed through RDP.

Running Shielded VMs on a Hyper-V cluster

If you are trying to deploy shielded VMs on clustered guarded hosts (using a Windows Failover Cluster), you can configure the shielded VM to be highly available using the following cmdlet:

Add-ClusterVirtualMachineRole -VMName 'contososvm1' -Cluster <guarded host cluster name>

The shielded VM can now be live migrated within the cluster.


In summary, this blog post walked you through the steps to create shielded VMs without a fabric manager such as VMM. This enables you to deploy and validate the scenario with a simpler topology (at the expense of a more complex administration experience).

This scenario is new in Windows Server 2016—we’d love to hear your feedback. You can reach us, or submit and vote on request through the User Voice web site.

Comments (6)

  1. PTG says:

    Is there any other simple method to shield a Hyper-V VM? The process looks pretty difficult.

    1. Hi PTG, This blog post focus on creating a Shielded VM without Virtual Machine Manager, using PowerShell only. The goal here is to use native Windows Server tools to create a Shielded VM. To simplify the process of managing Shielded VMs, you can always use a management tool, such as VMM for that. Another option for tenants of VM owners is to use Windows Azure Pack that was modified to support Shielded VMs and the process to create Shielded VMs was described here:


  2. Ahmed El-Fiky says:

    I’ve been asked by a Hoster. he have a customer and he decided to migrate to another SP and he needs his VHD back decrypted or by any other mean.

    1. @Ahmed, for shielded VMs, you can get the VM back, with the presence of the owner private key (defined in the PDK file), you will be able to remove shielding policy associated with the VM.

  3. Zawar says:

    I get an unrecognised cmdlet error for protect-ShieldingDataFile. Is it something that changed after preview 5? If so, What is the replacement command?

    1. Protect-ShieldedDataFile is the cmdlet for TP5. It has changed to New-ShieldedDataFile in RTM

Skip to main content