Step by Step: Shielding existing VMs without VMM


Continuing on the topic of Shielded VMs from my last blog on creating shielded VMs, this blogpost will share my learnings from validating the scenario. This blogpost doesn’t dive deep in terminologies which are fully explained in the Shielded VM deployment guide.

A side note, System Center VMM has built-in functionality to support shielding existing VMs to make the process simpler, and it is recommended to use SCVMM to manage shielded VMs in production.

Process outline

  • Tenant admin creates the shielding data file, which defines the VM shielding policy and includes the certificates which restricts the VMs to run on allowed guarded fabrics
  • Tenant admin creates a helper VHDX that is used to shield the VMs
  • Fabric admin takes the shielding data file and helper VHDX, and shield an existing VM on a guarded host

Create Shielding Data (PDK) File for shielding existing VMs

The shielding data specifies the shielding policy and key material that identifies which fabrics can run shielded VMs. The tenant administrator creates the shielding data using the Shielding Data File Wizard (which is part of the Remote Server Administration Tools – Shielded VM Tools), on a machine which is not part of the guarded fabric. When running this tool, ensure to select the option for shielding existing VMs:

You can also use the following cmdlet to create the shielding data file:

New-ShieldingDataFile -ShieldingDataFilePath <PDK file path> -Owner $owner -Guardian $guardian -Policy <Shielded or EncryptionSupported>

Where $owner and $guardian are explained in this blogpost.

Tip: In testing, make sure you don’t run this tool/cmdlet on a guarded host.

Create a Helper VHDX

As part of the shielding process for existing VMs, a “helper VM” is created that is responsible for preparing and encrypting the existing VM’s VHDX. To make the Helper VHDX, you will need to create a fully functional Gen 2 VM (HelperVM) running Windows Server 2016 with either the desktop experience or Server Core installation option (Nano server is not supported for helper VHDXs). Make sure a user can log on to this VM before shutting it down. Once the VM is in the OFF state, make a copy of its VHDX file. This copy of VHDX file is referred to as a Helper VHDX. You can delete HelperVM and its configuration files if you want – they are not needed anymore.

Tips:

  • The Helper VHDX and VM to be shielded can’t come from the same VHDX file. This happens often in test labs, where the same VHDX files is copied multiple times to build HelperVM and other testing VMs. If the Helper VHDX and the VM to be shielded are originated from the same VHDX file, there will be disk signature conflicts which result in failure in the shielding process.
  • Do not use a differencing VHDX for the helper VHDX.

Run the following cmdlet to enable shielding feature in the Helper VHDX:

Initialize-VMShieldingHelperVHD -Path <Helper.vhdx>

Tips:

  • The cmdlet is part of the RSAT-> Feature Administration Tools -> Shielded VM tools. Make sure the Helper VHDX is on a Hyper-V host which is not the guarded host.
  • The VHDX file will be modified in place. If you want to keep a copy of the original VHDX, make sure you make a copy of the VHDX before running the cmdlet. 
  • After the Helper VHDX is initialized, do not attach it to any VM.
  • Each Helper VHDX can only be used once after it has been initialized. If you are planning to shield multiple existing VMs, make a copy of the initialized Helper VHDX first. The script in the next section will make a copy for you if you run it in its entirety.

Shield an existing VM

The VMs to be shielded (let’s call them TargetVMs) are running on guarded hosts. Before running the script below, make sure you first copy the PDK files and the initialized Helper VHDX file to the guarded host where the TargetVMs are running.

There are two steps to shield the VM:

  • Prepare VM: Using the Helper VHDX to configures the TargetVM and encrypt its disk.
  • Enable shielding: Shield the VM based on the policy defined in the Shielding Data File.

The script below will perform the shielding process described above. The script takes 3 parameters:

  • PDKFile: the path to the shielding data file created above.
  • HelperVHDX: the path to the Helper VHDX file created and initialized, and copied to the guarded host.
  • VMName: the name of the TargetVM

You can copy and save the script, and run it on the guarded host to shield the VM.

  <# 
  .SYNOPSIS 
  This script will shield an existing VM without VMM. 
  .DESCRIPTION 
  To run this script, you have created and initialized the Helper VHDX file, it is used to the shielding VM process. 
  .PARAMETER PDKFIle 
  Create a PDK file on the tenant host (local mode) which specifies the Grand fathering option. The PDK file should be copied to the guarded host before running this script. 
  .PARAMETER HelperVHDX 
  The path of the HelperVHDX file, which was created and initialized on another machine, and copied to the guarded host where the VM to be shielded is running.
  Prepare a helper.VHDX, which should be able to boot a VM to the logon screen. I.e. gone through OOBE
  .PARAMETER VMName
  The VM name which is to be shielded 
#> 

Param
(
    [Parameter (Mandatory=$true)][string] $PDKFile,
    [Parameter (Mandatory=$true)][string] $HelperVHDX,
    [Parameter (Mandatory=$true)][string] $VMName
)

Function GetPreparationJob ($job)
{
    $PercentComplete = 0;
    # Loop and ping the job  
    while ($PercentComplete -le 100)  
    {  
        Start-Sleep -s 20  
        $instance = Get-CimInstance $job.PreparationJob -ErrorAction Stop  
        $PercentComplete = $instance.PercentComplete
        $ElapsedTime = $instance.ElapsedTime

        Write-Host "Percent complete:$PercentComplete; ElapsedTime: $ElapsedTime"

        if ($instance.JobState -eq 10)  
        {  
            Write-Host "Error code: " $instance.ErrorCode  
            Write-Host "Error description: " $instance.ErrorDescription  
            throw "Preparation failed"  
        }  
        if ($instance.JobState -eq 7)  
        {  
            Write-Host "Preparation Succeeded"  
            break  
        }

    }  
}

Function GetProvioningJob ($job)
{
    $PercentComplete = 0;
    # Loop and ping the job  
    while ($PercentComplete -le 100)  
    {  
        Start-Sleep -s 20  
        $instance = Get-CimInstance $job.ProvisioningJob -ErrorAction Stop  
        $PercentComplete = $instance.PercentComplete
        $ElapsedTime = $instance.ElapsedTime

        Write-Host "Percent complete:$PercentComplete; ElapsedTime: $ElapsedTime"

        if ($instance.JobState -eq 10)  
        {  
            Write-Host "Error code: " $instance.ErrorCode  
            Write-Host "Error description: " $instance.ErrorDescription  
            throw "Preparation failed"  
        }  
        if ($instance.JobState -eq 7)  
        {  
            Write-Host "Preparation Succeeded"  
            break  
        }

    }  
}

#Checking if PDKfile is valid
$PDKFile = Get-Item ($PDKFile)

$importpdk = Invoke-CimMethod -ClassName  Msps_ProvisioningFileProcessor -Namespace root\msps -MethodName PopulateFromFile -Arguments @{FilePath=$PdkFile }
#Check if the pdk is for grandfathering
If ($importpdk.ProvisioningFile.VolumeIDFilters.Count -ne 0)
{
    Write-Error "Incorrect PDK used for the grandfathering process." -ErrorAction Stop
}

#validate VM
$VM = Get-VM -Name $VMName -ErrorAction Stop
#turn it off
Stop-VM $VMName

#Validate and make a copy of the HelperVHDX
$HelperFile = Get-Item $HelperVHDX -ErrorAction Stop

$CopyHelperVHDX = ($HelperFile.Directory).ToString() + '\CopyHelper.VHDX'
Copy-Item $HelperVHDX $CopyHelperVHDX

#Create a utility VM, in order to prepare the tenant VHD for provisioning process
$Job = Invoke-CimMethod -Namespace root\msps -ClassName Msps_ProvisioningService -MethodName PrepareSpecializedMachine -Arguments @{TemplateUtilityDiskPath=$CopyHelperVHDX;MachineID=$VM.VMID.Guid}
#wait for the job to compelte
GetPreparationJob $Job

#Extract and apply KP
$kp = Get-KeyProtectorFromShieldingDataFile -ShieldingDataFilePath $PdkFile
Set-VMKeyProtector -VMName $VMName -KeyProtector $kp

#Extract and apply security policy
$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}
$cimSerializer.Dispose()

Enable-VMTPM -VMName $VMName

$Job = Initialize-ShieldedVM -VM $vm -ShieldingDataFilePath $PdkFile
GetProvioningJob $Job 

Tips:

  • The TargetVM must not use a differencing VHDX. The provisioning job will fail immediately if it determines the TargetVM is running with differencing VHDX.
  • If an error occurred, you can also enable the “ShieldedVM-ProvionsioningService” debug event log channel to get more details on the error.
  • After the TargetVM is been prepared, the HelperVHDX file will be deleted. This is because the HelperVHDX can no longer be used to shield another VM. Make sure you make a copy of the HelperVHDX if you want to shield another VM.

Conclusion

This blogpost illustrated the steps to shield an existing VM without System Center Virtual Machine Manager(VMM). If you are using VMM to manage the fabric and it is recommended to use VMM in production, you can follow the deployment guide to learn the procedure.

I hope the script helps to simplify the process. As always, we’d love to hear your feedback. You can reach us by email at ShieldedVMFeedback@microsoft.com or submit and vote on requests through the User Voice web site


Comments (3)

  1. Thanks, Causally i used VMM in my environment.

  2. Diego says:

    Please help, I have a Laptop with Windows Server 2016 RTM, the Feature “Host Guardian Hyper-V Support” is not listed, I am trying to figure out why, anybody helps?

    1. @Diego, what edition of Windows Server 2016 are you running? HostGuardian feature is available in Datacenter edition.

Skip to main content