In this blog post, I will guide you through the process of building Confidential VM images using Azure Image Builder and deploying them as Azure Virtual Desktop session hosts. We’ll be using Azure Bicep for our infrastructure-as-code and Azure DevOps Pipelines to automate the entire workflow.
All source code including the Bicep modules, AIB templates, PowerShell scripts, and Azure DevOps pipelines is available on GitHub.
⭐ yannickdils/confidentialavd
Bicep modules, AIB templates, PowerShell scripts, and Azure DevOps pipelines
- What Confidential VMs actually protect that Trusted Launch doesn’t
- How to build CVM-compatible images with Azure Image Builder (the build VM trick)
- The Disk Encryption Set + Managed HSM configuration that makes it all work
- A three-pipeline Azure DevOps automation with smart AZ failover
- The real-world gotchas that cost me hours (so they don’t cost you hours)
Why Confidential Compute for AVD?
If you’re running Azure Virtual Desktop in a regulated industry (finance, healthcare, government), you’ve probably already enabled Trusted Launch on your session hosts. Secure Boot and vTPM are great, but Confidential VMs take things significantly further.
What does “Confidential” actually mean here?
Let me break this down, because the terminology can be confusing. In a traditional Azure VM, even one with Trusted Launch, the hypervisor has full access to your VM’s memory. Microsoft’s security practices mean they won’t look at your data, but the technical capability exists. For some regulatory frameworks, that’s not good enough.
With Confidential VMs, your session host memory is encrypted at the hardware level using AMD SEV-SNP (Secure Encrypted Virtualization, Secure Nested Paging). This is a feature of the physical CPU, not software. The encryption keys are managed by the processor itself, and the hypervisor is mathematically excluded from accessing the VM’s memory space.
Think of it this way:
Here’s a detailed comparison:
When should you use this?
You don’t need Confidential VMs for every AVD deployment. Here’s my rule of thumb:
- Use Trusted Launch when your organization doesn’t have regulatory requirements for data-in-use encryption, but you want best-practice security hygiene.
- Use Confidential VMs when you’re handling data subject to GDPR data sovereignty requirements, HIPAA, financial regulations, government classified data, or when your security team requires technical proof (not just contractual guarantees) that the cloud provider cannot access workload data.
The cost overhead is ~10-15%, so it’s not a decision to take lightly, but when you need it, nothing else will satisfy the requirement. And if you’re worried about the Managed HSM complexity, keep reading. There’s a simpler option using platform-managed keys that still gives you SEV-SNP memory encryption without the key management overhead.
Architecture Overview
Our architecture spans two Azure subscriptions. The images subscription (sub-avd-images-prd) hosts the Azure Compute Gallery, Image Definitions, and runs the Azure Image Builder process. The AVD subscription (sub-avd-prd) hosts the session hosts, host pools, Key Vaults, and the Disk Encryption Set backed by Managed HSM.

Figure 1 – Dual-subscription architecture: image build (left) and AVD production (right)
Why two subscriptions?
This is a deliberate design choice, not just organizational preference. By keeping the image build infrastructure in a separate subscription, we achieve:
- Blast radius isolation: A misconfigured pipeline or RBAC change in the AVD subscription can’t accidentally delete or modify your golden images.
- Separate cost tracking: Image builds (especially with DC-series VMs) can be expensive. Having them in a dedicated subscription makes cost attribution clear.
- Independent lifecycle: You can update your image pipeline without touching production hosts, and vice versa.
- Least privilege RBAC: The AVD service principal only needs Reader access to the gallery subscription. It never needs to write to it.
Deployment Flow
At a high level, the deployment flow looks like this:
- Deploy the User-Assigned Managed Identity for Azure Image Builder
- Create the Azure Compute Gallery and Confidential VM Image Definition
- Deploy and run the AIB Image Template with a DC-series build VM
- The AIB build produces an image version replicated across target regions
- Deploy a Disk Encryption Set backed by Managed HSM
- Deploy Key Vault for admin credentials and domain join secrets
- Deploy Confidential VM session hosts using the gallery image and DES
Building Confidential VM Images with Azure Image Builder
This is where it gets interesting. One of the key insights I discovered while building this out is that the build VM itself does not need to be a Confidential VM. AIB uses Trusted Launch automatically when the target image definition supports it. The output image inherits CVM compatibility from the image definition’s SecurityType.
Many engineers assume you need a Confidential VM to build a Confidential VM image. You don’t. The image definition’s
SecurityType determines compatibility. The build process is separate from the deployment target. This saves significant complexity in your AIB pipeline.
Figure 2 – Azure Image Builder pipeline: from marketplace image to CVM-ready gallery version
Step 1: Create the Compute Gallery and Image Definition
The image definition must have SecurityType: TrustedLaunchAndConfidentialVmSupported. This dual-compatible setting allows the same image version to be used for both Trusted Launch and Confidential VM session hosts. This is valuable because you might run mixed environments: some host pools with CVM, some without.
You can review the key parts here, or view the full module on GitHub:
📄 Image/main.bicep
Compute Gallery image definition module
belgiumcentral as the image definition location because it has strong DC-series availability. Image versions can still be replicated to westeurope and northeurope for redundancy.Step 2: Configure the AIB Image Template
The AIB image template defines the source image, build VM configuration, customization steps, and distribution targets. For CVM images, the build VM uses a Standard_DC8as_v6 size, which is a DCasv6-series Confidential Compute capable VM.
📄 Image/Template/main.bicep
AIB image template with customization pipeline
Source Image
We start from a Microsoft marketplace image. For AVD with Microsoft 365 Apps, we use office-365 / win11-25h2-avd-m365.
Customization Pipeline
The AIB template supports a modular customization pipeline controlled by feature flags. Each step is conditionally included based on boolean parameters. This means you can use the same template across different image profiles (e.g. developer desktops vs. call center desktops) by simply toggling flags.
Here’s a breakdown of the customization steps:
Distribution
The image version is distributed to the Azure Compute Gallery and replicated across multiple regions for high availability:
excludeFromLatest = true is a safety net. It means newly built image versions won’t automatically become the “latest” version that new deployments pick up. You validate the image first, then explicitly promote it. This prevents a broken image from rolling out to all host pools.Deploying Confidential VM Session Hosts
With the golden image built, the next step is deploying the Confidential VM session hosts. But before we dive into the code, there’s an important decision to make about how your OS disk encryption will work.
Choose your encryption approach
Azure Confidential VMs support two disk encryption options. This choice affects the complexity of your deployment significantly:
Option A: Platform-managed keys (the simpler path)
If your regulatory requirements don’t mandate customer-managed keys, you can use platform-managed keys. This is significantly simpler to set up because you don’t need a Managed HSM, a Disk Encryption Set, or any key management infrastructure at all. Azure handles the encryption keys for you.
The VM guest state (vTPM secrets, boot measurements) is still encrypted, you just don’t control the key. The memory encryption via AMD SEV-SNP works identically in both options.
Here’s what the Bicep looks like for the simpler approach:
That’s it. No DES, no HSM, no RBAC for key access, no Crypto Service Encryption User role assignments. You still get Confidential VM with SEV-SNP memory encryption and secure boot. You just don’t bring your own key for the disk encryption.
Option B: Customer-managed keys (full control)
If your organization requires full control over encryption keys (common in financial services, healthcare, and government), you’ll need the Managed HSM + DES approach. This is what the rest of this section covers in detail.

Figure 3 – Security stack of a Confidential VM session host (customer-managed keys)
Understanding the encryption chain (Option B)
Before jumping into the code, it’s worth understanding how the pieces connect in the customer-managed key approach, because getting one link wrong will break the chain:
- Managed HSM holds your customer-managed key (CMK). This is the root of your encryption chain.
- The Disk Encryption Set (DES) references that key and acts as the bridge between Azure Compute and your HSM.
- The VM’s OS disk references the DES inside its
securityProfile, which tells Azure to encrypt the disk and VM guest state with your key. - AMD SEV-SNP handles the memory encryption at the hardware level. This part is automatic once
securityType: ConfidentialVMis set.
If any link is missing or misconfigured, the deployment fails. The good news is: once you get it right, it works reliably every time.
Creating the Managed HSM key
The first link in the chain is the encryption key itself. Confidential VMs using DiskWithVMGuestState encryption have specific key requirements that differ from standard Key Vault keys:
- The key must be stored in Managed HSM (not standard Key Vault)
- The key must be exportable (required for Secure Key Release)
- The key must have a Secure Key Release (SKR) policy attached at creation time. You cannot add this policy after the key is created.
- The Confidential VM Orchestrator service principal (App ID:
bf7b6499-ff71-4aa2-97a4-f372087be7f0) must have theManaged HSM Crypto Service Release Userrole on the key
Without all of these in place, the DES deployment fails with: "The target key has no key release policy". I wrote a PowerShell script that handles all of this in one shot.
The script runs through 7 steps:
Here’s how to run it:
📄 Scripts/CreateHSM_CMK.ps1
Managed HSM key creation with SKR policy and CVM Orchestrator role assignment
--default-cvm-policy flag is the simplest approach, but it may fail in newer Azure regions (like belgiumcentral) where the internal API version doesn’t recognise the region yet. When this happens, the script automatically falls back to an explicit policy file containing the official Microsoft CVM Secure Key Release policy. You don’t need to do anything differently. The script handles it.After the script completes, it outputs the key URL. You’ll need to paste this into your hostpool JSON config file (the managedHsmKeyUrl field) before running the deploy pipeline.
Disk Encryption Set
The DES is the bridge between the Confidential VM’s OS disk and the encryption key stored in Managed HSM. It must be configured with encryptionType: ConfidentialVmEncryptedWithCustomerKey.
You can review the key parts here, or view the full module on GitHub:
📄 DiskEncryptionSet/main.bicep
Disk Encryption Set backed by Managed HSM
securityProfile. Adding a top-level diskEncryptionSet triggers standard SSE-CMK, which is incompatible with ConfidentialVmEncryptedWithCustomerKey and will cause deployment failures. This one caught me off guard the first time.VM Security Profile
The session host VM’s security profile is where Confidential Compute is activated.
📄 SessionHost/avd-sessionhosts-cc.bicep
Confidential VM session host with PMK/CMK support
encryptionAtHost set to false? This confuses a lot of people. Encryption at host and Confidential VM disk encryption are two different mechanisms. They’re not additive, they’re mutually exclusive. When you use DiskWithVMGuestState, the DES handles all disk encryption. Setting encryptionAtHost: true at the same time creates a conflict and the deployment will fail.The OS disk’s managed disk section references the DES inside a nested securityProfile:
VM Extensions
Each Confidential VM session host receives a complete extension stack. The order matters, as some extensions depend on others completing first:
Pipeline Automation with Azure DevOps
The entire workflow is automated through three Azure DevOps pipelines that form a CI/CD chain for image lifecycle management.

Figure 4 – Three-pipeline CI/CD chain with zone failover and host replacement
AVD-GalleryInfrastructure Pipeline
Deploys the foundational gallery infrastructure: the User-Assigned Managed Identity, Azure Compute Gallery, and all image definitions. This pipeline uses a Bicep orchestrator that chains the component library modules together.
📄 AVD-GalleryInfrastructure.yml
Gallery, UAMI, and image definition deployment
AVD-ImageBuild Pipeline
This one handles the full image build lifecycle. It supports two build methods: Azure Image Builder (automated, recommended) and VM Capture (manual, for specialized scenarios). The pipeline auto-increments the image version number, deploys the AIB template, monitors the build with live log tailing from the staging storage account, and verifies the resulting image version across all replication regions.
📄 AVD-ImageBuild.yml
AIB template deployment, build monitoring, and replication verification
AVD-DeployAdditionalHosts Pipeline
This is the most complex pipeline, supporting both add and replace operations for session hosts. When the confidentialCompute toggle is enabled, it automatically uses the CC Bicep template, retrieves or deploys the Disk Encryption Set, and selects the CC-capable VM size from the JSON configuration.
📄 AVD-DeployAdditionalHosts.yml
Session host add/replace with CC toggle and zone failover
Smart Availability Zone Handling
DC-series VMs are not always available in every availability zone. Rather than failing the entire deployment, our pipeline implements automatic zone failover:
- Attempt all VMs in Zone 1
- If a capacity error is detected (
SkuNotAvailable,ZonalAllocationFailed), clean up the partial deployment - Retry the entire batch in Zone 2, then Zone 3
The key design decision here is that we deploy all VMs in a single zone rather than spreading them across zones. This sounds counter-intuitive for availability, but for session hosts it makes management much simpler. You know exactly where your hosts are, and you avoid the complexity of cross-zone networking and load balancing.
Host Replacement Workflow
For replace operations, the pipeline executes a graceful workflow designed to minimize user disruption:
- Drain: Disable new logons on target hosts
- Warn: Send user notifications with a configurable delay
- Evict: Force-logoff remaining sessions after the delay
- Deallocate: Shutdown and deallocate old VMs
- Optimize: Downgrade old OS disks to Standard HDD (saves cost while you keep them for rollback)
- Deploy: Provision new session hosts with the updated image
- Cleanup: Schedule old host deletion after a configurable retention period
Prerequisites and Common Pitfalls
Before you start building this out, there are a few things to be aware of. I learned most of these the hard way.
Feature Flag Registration
The DCasv6 Confidential Compute VM sizes require the feature flag Microsoft.Compute/DCav6Series to be registered on the subscription. This flag cannot be self-registered and requires a Microsoft support ticket. Plan for 1–3 business days for approval. Don’t let this one surprise you the day before a deadline.
Managed HSM Key Requirements
The encryption key must be stored in Azure Managed HSM (not standard Key Vault) and the DES’s User-Assigned Managed Identity must have the Crypto Service Encryption User role on the HSM key. The DES must be co-located in the same region as the Managed HSM.
Common Deployment Errors
Here’s an overview of errors I’ve encountered and how to resolve them:
Conclusion
Confidential Computing for Azure Virtual Desktop is a significant step forward in protecting end-user desktop workloads. By combining AMD SEV-SNP hardware encryption with Azure Image Builder automation and Infrastructure-as-Code practices, we can build and deploy secure virtual desktops at scale without compromising on operational efficiency.
Key takeaways
- You don’t need a CVM to build a CVM image. The image definition’s SecurityType determines compatibility, not the build VM.
- Start with platform-managed keys if you can.
VMGuestStateOnlygives you SEV-SNP memory encryption without any key management infrastructure. Add CMK later if needed. - If using CMK, the DES goes inside
securityProfile, not at the top level. This is the #1 deployment error I’ve seen. encryptionAtHostmust be false. It conflicts with bothDiskWithVMGuestStateandVMGuestStateOnly. They’re different encryption mechanisms.- Register the DCav6Series feature flag early. It requires a support ticket and takes days.
- Use separate subscriptions for images and production. The blast radius isolation is worth the extra setup.
What’s next?
If you’re planning to implement this in your environment, I recommend this approach:
- Start with the feature flag. Submit the support ticket for
Microsoft.Compute/DCav6Seriestoday. It takes days and you can’t do anything without it. - Decide: platform-managed or customer-managed keys? If you don’t have a regulatory CMK requirement, start with
VMGuestStateOnly. You can skip the Managed HSM and DES entirely. You can always upgrade later. - If using CMK, set up Managed HSM. If your organization doesn’t have one yet, this involves provisioning and security domain configuration. Budget a few hours.
- Build a non-CVM image first. Get your AIB pipeline working with Trusted Launch before adding the Confidential VM layer. This isolates problems.
- Add CVM to one host pool. Don’t roll it out everywhere at once. Pick one host pool, validate end-to-end, then expand.
- Monitor Guest Attestation. Once running, use the Guest Attestation extension data to verify your VMs are actually running in a TEE. Don’t just assume.
All source code is available on GitHub. Feel free to star the repo, fork it, or open an issue. If you run into a problem not covered in this blog, open an issue on the repo and I’ll do my best to help.
References
- yannickdils/confidentialavd – Full source code on GitHub
- Azure Confidential VMs Overview
- Azure Image Builder Documentation
- Disk Encryption with Confidential VMs
- Azure Bicep Documentation
- Virtual Desktop Optimization Tool (VDOT)
Community reading: Azure Confidential Computing deep dives by Thomas Van Laere
- Azure Confidential Computing: Confidential VMs – A closer look at AMD SEV-SNP VM SKUs
- Azure Confidential Computing: IaaS – The trust ladder and confidential compute VM options
- Azure Confidential Computing: Microsoft Azure Attestation – Remote attestation and TEE integrity verification
- Azure Confidential Computing: Secure Key Release – Releasing Key Vault keys to attested CVMs
- Azure Confidential Computing: Confidential Temp Disk Encryption – SKR and Azure Disk Encryption combined
- Azure Confidential Computing: Confidential GPUs and AI – NVIDIA H100 confidential GPU workloads
2 Comments on “How to build and deploy confidential AVD images with Azure Image Builder”