Skip to main content

Appendix C.1: AVD Deployment Runbook

This runbook covers the sequence of steps to deploy a production-ready Azure Virtual Desktop secure enclave in GCC High — from an empty subscription to 20 Entra-joined, Intune-enrolled session hosts. Steps are ordered to respect Azure resource dependencies.

For architecture context, firewall rules, and cost guidance, see Scenario: Azure Virtual Desktop. For the full 65-hour phased timeline, see AVD Deployment Timeline.


AVD Runbook Prerequisites

Complete these before starting. None of the steps below can proceed without them.

ItemWhere to Configure
Azure Government subscription active, engineer has Subscription OwnerAzure Government portal — portal.azure.us
GCC High Entra tenant active, engineer has Global AdministratorEntra admin center — entra.microsoft.us
M365 GCC High E3 or E5 licenses assigned to all usersMicrosoft 365 admin center
MDM auto-enrollment enabled for All usersEntra admin center > Mobility (MDM and MAM) > Microsoft Intune > MDM user scope: All (see Cloud-Only Phase 2 for the canonical procedure incl. the WIP=None trap)
"Users may join devices to Microsoft Entra ID" set to AllEntra admin center > Devices > Device settings
Conditional Access policies for AVD in place (phishing-resistant user policy + admin auth strength + the AVD-specific exclusions discussed in the scenario)Conditional Access chapter
Set MDM user scope to All before provisioning session hosts

If MDM user scope is set to a named group rather than All, the AADLoginForWindows extension will join the VM to Entra but Intune enrollment will silently fail. Verify this setting before deploying any session hosts.

Set the MDM enrollment URLs to .us before provisioning session hosts

A GCC High tenant ships the Mobility (MDM and MAM) > Microsoft Intune blade with the commercial (.com) MDM URLs by default. The portal shows a "should point to '.us' endpoints" banner but does not set them — and the Restore default MDM URLs link resets them to .com, so do not click it. If the URLs stay on .com, the AADLoginForWindows extension Entra-joins the VM correctly (device registration resolves to the sovereign enterpriseregistration.microsoftonline.us) but the Intune auto-enroll step fails: DeviceEnroller::EnrollToMdm() returns 0x80192efd (cannot connect to the commercial enrollment.manage.microsoft.com, which the firewall denies) and the half-join rolls back ("Device successfully unjoined").

In Entra admin center > Mobility (MDM and MAM) > Microsoft Intune, confirm all three MDM URLs are .us:

FieldGCC High value
MDM terms of use URLhttps://portal.manage.microsoft.us/TermsofUse.aspx
MDM discovery URLhttps://enrollment.manage.microsoft.us/enrollmentserver/discovery.svc
MDM compliance URLhttps://portal.manage.microsoft.us/?portalAction=Compliance

This is tenant-wide, so the fix unblocks every session host at once. For the on-device confirmation (dsregcmd /statusMdmUrl must be .us), see Cloud-Only Phase 3: the Critical Sovereign Check.


Tenant Device Settings

Configure these in the Entra admin center > Devices > Device settings before provisioning any session hosts. These are tenant-wide settings that apply to all Entra-joined devices.

SettingValueReason
Users may join devices to Microsoft Entra IDAllRequired for AVD provisioning — the AADLoginForWindows extension joins as SYSTEM, not as a named user. See compensating controls below.
Additional local administrators on all Microsoft Entra joined devices (Global Administrator role)DisabledGlobal Admin is a tenant-level role and must not carry local machine admin rights on session hosts. Use LAPS for local admin access instead.
Registering user is added as local administrator during Microsoft Entra joinDisabledAVD session hosts are joined by a system extension, not a user. Users must not have local admin rights on CUI session hosts — doing so would undermine AC, AU, and SI domain controls.
Enable Microsoft Entra Local Administrator Password Solution (LAPS)EnabledRotates the local admin password per device on a configurable schedule and stores it in Entra with a full access audit trail. Satisfies MA.L2-3.7.5 and IA.L2-3.5.3.
Restrict users from recovering the BitLocker key(s) for their owned devicesYesBitLocker recovery keys provide full disk decryption capability. In a CUI enclave, key retrieval must be an IT admin function only, with an audit trail. The Entra portal default is No (no restriction — users can self-recover); flip it to Yes to enforce admin-only recovery. Supports MP.L2-3.8.x.
After enabling LAPS here, configure the rotation policy in Intune

Enabling LAPS in Entra activates the feature but does not set rotation schedule or complexity. Create a Local Administrator Password Solution policy in Intune > Endpoint security > Account protection and target it to the EID AVD Devices group (the dynamic device group created in Step 9). Set backup directory to Azure Active Directory, rotation to every 30 days, and complexity to the maximum available setting.

"Users may join devices" set to All — compensating controls required

The AADLoginForWindows extension joins as SYSTEM, so this setting must be All. See Scenario: Azure Virtual Desktop § Entra Join Constraint for the architectural justification, the compensating-controls table (Intune enrollment restrictions, P006, B007), and the SSP narrative.


Step 1: Create the Resource Group

All AVD resources should live in a dedicated resource group for cost attribution and RBAC scoping.

Deploying more than one host pool? See the Multi-Pool Variant first

This step as written assumes a single host pool in a single resource group. If you are deploying two or more host pools — separate business units, projects, or OpCo segments — read Multi-Pool Variant below first. You will create one shared infrastructure RG here in Step 1 and one additional RG per host pool when you reach Step 7, with a subnet allocation decision made up-front in Step 3.

  1. Navigate to Azure Government portal > Resource groups > Create
  2. Set Subscription to your AVD subscription
  3. Set Resource group name — e.g., rg-avd-prod-usgovva (single-pool) or rg-avd-shared-usgovva (multi-pool, shared infrastructure RG per the variant)
  4. Set Region to US Gov Virginia
  5. Add tags as required by your tagging policy (e.g., environment: production, workload: avd)
  6. Select Review + Create > Create

Multi-Pool Variant

The 15 steps that follow are written for a single host pool in a single resource group. Most CMMC AVD deployments past their first quarter need more than one host pool — a second business unit comes online, a privileged-admin enclave gets carved out, or a ParentCo running Shared Services Option B provisions a host pool per OpCo it serves. This variant categorizes the runbook so a reader doing 7 pools does not re-do 7 firewalls.

Resource group decomposition

  • One shared infrastructure RG (rg-avd-shared-usgovva) holds the resources that are physically one thing serving every pool — the firewall, the virtual network, the LogAnalytics workspace, and the backup vault.
  • One RG per host pool (rg-avd-<pool>-usgovva, where <pool> is the business unit name or pool identifier) holds the per-pool resources — the host pool, its application groups, the session-hosts, and the per-pool RBAC assignments to the pool's mail-enabled security group.

This split gives per-pool cost attribution at the RG level, per-pool RBAC scoping (a pool admin cannot accidentally touch a sibling pool's VMs), per-pool lifecycle (decommission a pool by deleting its RG), and a clean audit story (the C3PAO reads the resource list and sees one shared-services RG plus one RG per business unit).

Subnet allocation — do this up-front

Azure binds a VM NIC to a subnet at creation time and the binding is immutable. "Move the host pool to its own subnet later" actually means rebuilding every VM in the pool — restorable from backup per Step 13, but lost user-installed applications and AppData between rebuild and restore. Allocate per-pool subnets in Step 3 when the VNet is first created.

Size each subnet to the pool that will live in it. Azure reserves 5 addresses per subnet (network, default gateway, two for DNS mapping, broadcast) regardless of subnet size, so usable counts trail the total by 5:

MaskTotal addressesUsable (Azure)Fits
/273227Pools up to ≈25 VMs with growth room
/266459Pools up to ≈55 VMs
/25128123Pools up to ≈115 VMs

Mix sizes within the same 10.0.2.0/24 block — a /26 takes the space of two /27s, so you can pack two large pools plus four small pools into one /24. Example for a 6-pool deployment with two large business units (≈34 and ≈31 users) and four standard pools (≤25 users each), in a 10.0.0.0/16 VNet with AzureFirewallSubnet at 10.0.1.0/26:

SubnetCIDRUsablePurpose
default10.0.0.0/24manyOptional: first pool's session hosts, or reserved for legacy / single-pool deployments
AzureFirewallSubnet10.0.1.0/26(firewall)Bound to Azure Firewall (required name)
subnet-avd-<largeA>10.0.2.0/2659Large pool A
subnet-avd-<largeB>10.0.2.64/2659Large pool B
subnet-avd-<pool3>10.0.2.128/2727Standard pool
subnet-avd-<pool4>10.0.2.160/2727Standard pool
subnet-avd-<pool5>10.0.2.192/2727Standard pool
subnet-avd-<pool6>10.0.2.224/2727Standard pool

All per-pool subnets associate to the same UDR (udr-avd-sessionhosts) — one route table can attach to many subnets, so adding pools later is just a new subnet plus a UDR association, no new route entries. Use business-unit names in the subnet names where possible (subnet-avd-aero, subnet-avd-defense, etc.) so an auditor reading the resource list can map the topology to the org chart.

Pick the bigger size if there's any doubt

A /26 with 25 spare addresses costs nothing while a /27 that fills up forces a migration. Subnet expansion via the Azure portal is limited and frequently requires NIC recreation (which means rebuilding the VMs in the subnet per the warning at the top of this section). When a pool's eventual headcount is uncertain, allocate /26 by default; the 10.0.0.0/16 VNet has more than enough room to extend into 10.0.3.0/24 and beyond for additional /26s without renumbering anything already allocated.

Step categorization — once vs. per-pool

StepRun once (shared infrastructure RG)Repeat per pool (per-pool RG)Notes
1 — RG✓ shared RG✓ one RG per poolFirst Step 1 creates the shared RG; loop back to Step 1 once per pool when ready to provision its VMs
2 — Firewall PolicyOne policy serves all pools; per-tenant SharePoint FQDNs go in clientTenantFqdns once
3 — VNet + FirewallCreate the VNet once with all per-pool subnets allocated up-front per the table above
4 — UDROne UDR, associated to all per-pool subnets
5 — Log AnalyticsOne workspace ingests for all pools
6 — AVD WorkspaceOne workspace (shared) — see the workspace strategy note below
7 — Host PoolThe Desktop Application Group auto-created with each pool gets its own name (hp-avd-<pool>-DAG)
8 — Users + RBACPer-pool MESG, per-pool Desktop Virtualization User assignment, per-pool Virtual Machine User Login at the per-pool RG scope
9a — Dynamic device groupEither one device group per pool (-startsWith "avd-<pool>-") or one shared group with a broader prefix — per-pool gives finer policy targeting
9b — SP SSO config✓ (one-time per tenant)Add each per-pool device group as a target the first time the pool is provisioned
10 — Session HostsVMs land in the per-pool RG, on the per-pool subnet, with the per-pool VM name prefix
11 — Pin Users
12 — NoClose PolicyOne Settings Catalog policy, assigned to all per-pool device groups
13 — Backup✓ vault + policy✓ protected items per poolOne shared vault, one shared policy; per-pool VMs enrolled into the policy as they come online
14 — ValidateRun end-to-end checks against each pool individually
15 — Power SchedulingdependsdependsOne scaling plan can target multiple host pools, or use per-pool plans for different shift patterns

Workspace and application group strategy

For OpCos with multiple pools that serve the same overall user community (different business units of one company), one shared workspace with one application group per pool is the simpler and more defensible layout. Users see one named tile in their Windows App client ("Safran USA AVD") and one desktop entry per pool they are entitled to. The C3PAO reads the architecture as a single AVD environment partitioned by business unit — which matches reality.

For OpCos that should not see each other's workspace tiles in the Windows App client — common under Shared Services Option B where ParentCo serves multiple subsidiaries from one AVD fleet but each subsidiary expects to see only its own desktop tile — provision one workspace per OpCo with that OpCo's host pools attached. This costs more workspaces to maintain but gives each OpCo a clean per-organization UX.

Cost effect

Sharing the firewall is the single largest cost lever. Azure Firewall Standard is a fixed ≈$912/month regardless of how many pools route through it. Sharing one firewall across 7 pools versus deploying one per pool saves ≈$5,472/month in pure infrastructure overhead, before any compute. The Log Analytics + Sentinel stack and the Recovery Services Vault share similarly — one shared workspace and one shared vault are typically cheaper to operate than per-pool equivalents, with cost driven by per-GB ingestion and per-VM protected-instance fees respectively rather than per-resource fixed costs.

Cross-references


Step 2: Deploy the Firewall Policy

The Azure Firewall Policy is a standalone resource that holds all the rule collections — network rules, application rules, and the deny-all logging rule. Deploying the policy before the firewall (rather than creating the firewall first and attaching the policy after) has three benefits:

  • The VNet wizard's Firewall policy dropdown shows the existing policy, so the firewall is bound to it at creation time.
  • Rules are live the instant the firewall comes up — no window where the firewall is up, the UDR points at it, and a session host could egress through unfiltered defaults.
  • The "one-time associate" command in load-firewall.azcli is no longer required for new deployments.

Deploy the policy from the Bicep template (source) and a tenant-specific parameter file (example):

az cloud set --name AzureUSGovernment
az login
az account set --subscription <subscription-guid>

az deployment group create \
--resource-group rg-avd-prod-usgovva \
--template-file static/scripts/avd/avd-firewall.bicep \
--parameters tenant.bicepparam

The template is generic — it contains no tenant SharePoint or OneDrive hostnames. Those live in a small Bicep parameter file, conventionally tenant.bicepparam, that pairs with the template at deployment time. Start from avd-firewall.example.bicepparam, replace the placeholder hostnames with your tenant's, and keep the file outside the template's directory tree (it is deployment data, not source). The pattern, the rule collection it generates, and the full parameter reference are in AVD Firewall Reference § Tenant-Specific Endpoints.

This creates:

ResourceNameNotes
Microsoft.Network/firewallPoliciesfwp-avd-prod-usgovvaStandard tier, no parent firewall yet
Network rule collection groupDefaultNetworkRuleCollectionGroupEssential ports, Teams media, Azure services
Application rule collection groupDefaultApplicationRuleCollectionGroupM365, AVD control plane, Defender, Windows Update, etc.

Verify in the Azure portal > Firewall Policies blade that fwp-avd-prod-usgovva shows the expected rule counts before continuing to Step 3. For the complete rule set and the source avd-firewall.bicep template, see AVD Firewall Reference.

Re-running this deployment is the rule-update workflow

Every time the rule set changes, redeploy the same Bicep with the same command. Bicep is idempotent — it diffs against the live policy and updates only what changed. There is no "edit rules in the portal" step in this runbook.


Step 3: Deploy the Virtual Network

AVD session hosts require a VNet with two subnets: one dedicated to Azure Firewall (required size /26) and one for session hosts. The Azure portal Create virtual network wizard organizes settings across four tabs — fill them in the order below to match the portal flow.

Navigate to Virtual networks > Create.

Basics tab

FieldValue
SubscriptionYour AVD subscription
Resource grouprg-avd-prod-usgovva
Virtual network namevnet-avd-usgovva
RegionUS Gov Virginia

Security tab

SettingValueReason
Virtual network encryptionUncheckedAVD traffic is already TLS-encrypted at the application layer. No CMMC requirement is unmet without this.
Enable Azure BastionUncheckedAVD users never connect through Bastion — they reach session hosts via reverse-connect over HTTPS to the AVD service, so Bastion only affects admin access, which here uses the AVD client and Azure portal Run Command. Trade-off: with no public IPs and Bastion off, you can't open an interactive RDP desktop to a specific session host for deep troubleshooting (Run Command is non-interactive) — add Bastion or a jumpbox if that need arises. Bastion is also separately billed (≈$140–170/mo, billed hourly from deployment — verify current GCC High pricing).
Azure FirewallCheckedAll session host outbound traffic is forced through the firewall via UDR in Step 4.

When Azure Firewall is checked, the wizard reveals these sub-fields:

FieldValueReason
Azure Firewall namefw-avd-prod-usgovvaMatches the Cloud Adoption Framework naming convention (fw- for firewall) and shares the avd-prod-usgovva base with the policy fwp-avd-prod-usgovva deployed in Step 2.
Subnet nameAzureFirewallSubnet (read-only)Azure Firewall binds only to a subnet with this exact name; the wizard fills it for you.
TierStandardBasic (≈$295/mo deployment + ≈$0.065/GB processed) is Microsoft-positioned for SMB workloads, capped at 250 Mbps, with threat-intelligence in alert-only mode (cannot deny), no network-level FQDN filtering (only application-level, SNI-based), and no DNS proxy — gaps that matter for a CMMC L2 CUI enclave.
Premium (≈$1,400/mo) adds TLS inspection, IDPS, and URL filtering — none of which apply here: Microsoft explicitly documents that TLS inspection on M365 traffic breaks the service and is unsupported.
Standard (≈$870/mo) is the right tier — it adds threat-intel blocking, network-level FQDN filtering, and DNS proxy on top of Basic, and matches avd-firewall.bicep (tier: 'Standard'). All prices are approximate commercial USD; GCC High pricing is similar — verify with the Azure pricing calculator for your region.
Firewall policyfwp-avd-prod-usgovva (existing)The Standard-tier policy deployed in Step 2 appears in this dropdown — selecting it binds the firewall to all rule collections at creation time. The other dropdown options ("None — use classic rules", or creating a new Basic/Premium policy) do not apply: classic rules are deprecated; Basic and Premium policies require a tier change that the Bicep template does not support.
Azure Firewall public IP addressNew: pip-fw-avd-prod-usgovvaThe firewall needs exactly one public IP for SNAT egress. CAF naming (pip-fw-...) makes the IP's role unambiguous in the resource list.

The Security tab also has an Azure DDoS Network Protection subsection:

SettingValueReason
Enable Azure DDoS Network ProtectionUncheckedThe enclave has one public IP (firewall egress only) and no inbound internet traffic. The default platform-level DDoS protection is sufficient. DDoS Standard (~$2,944/month) is warranted only for public-facing workloads.

IP addresses tab

The portal defaults to 10.0.0.0/16 for the VNet address space. You may use any RFC 1918 range that does not conflict with your on-premises or peered networks.

The wizard creates a default subnet automatically at 10.0.0.0/24 and — because Azure Firewall was checked on the Security tab — also creates AzureFirewallSubnet at 10.0.1.0/26. The default subnet may be renamed for clarity (no functional impact); AzureFirewallSubnet must keep its exact name.

SubnetCIDRPurpose
default (or your chosen name)10.0.0.0/24AVD session host NICs
AzureFirewallSubnet10.0.1.0/26 (auto-created, fixed name)Bound to Azure Firewall — /26 is the required minimum size

Review + create

Select Review + create > Create.

Firewall deployment takes 5–10 minutes

The VNet and firewall provision together. Do not proceed to Step 4 until both show as succeeded. Note the firewall private IP address from the firewall's Overview blade — you will need it for the UDR in Step 4.

Selecting the policy in the Firewall policy dropdown binds all application and network rules at creation time — no separate rule-loading step. Verify this binding before continuing — it is the most common silent failure in this runbook. If the wizard's Firewall policy field is left at "None — use classic rules" (or the firewall is created by another path), the firewall comes up in classic-rules mode and every rule deployed in Step 2 is ignored, with no error.

Verify the firewall is in policy mode

On the firewall's Overview blade, confirm the Essentials panel shows Firewall policy: fwp-avd-prod-usgovva.

If instead you see a Rules (classic) item in the left menu and a Migrate to firewall policy button on the toolbar — and no Firewall policy field in Essentials — the firewall is in classic-rules mode and the policy is not in effect. Remediate before continuing:

  1. Open Firewall Manager > Virtual networks (or the policy's Settings > Secured virtual networks blade).
  2. Select vnet-avd-usgovva and associate fwp-avd-prod-usgovva. Associating a policy with an existing firewall replaces its classic rules with no downtime — the firewall keeps processing traffic during the swap. (Convert virtual networks)
  3. Re-check the Overview blade; Firewall policy should now read fwp-avd-prod-usgovva.

To inspect the rule set, navigate to Firewall Policies > fwp-avd-prod-usgovva > Rule collections, or see AVD Firewall Reference for the complete catalog.


Step 4: Create the User Defined Route

The UDR forces all session host outbound traffic through the Azure Firewall, enforcing the deny-all-by-default posture.

  1. Navigate to Route tables > Create
  2. Set Resource group to rg-avd-prod-usgovva
  3. Set Name — e.g., udr-avd-sessionhosts
  4. Set Region to US Gov Virginia
  5. Set Propagate gateway routes to No
  6. Select Review + Create > Create

After creation, add the default route:

  1. Open udr-avd-sessionhosts > Routes > Add
  2. Set Route name: route-all-to-firewall
  3. Set Destination: 0.0.0.0/0
  4. Set Next hop type: Virtual appliance
  5. Set Next hop address: the Azure Firewall private IP from Step 3
  6. Select Add

Associate the UDR with the session host subnet:

  1. Open udr-avd-sessionhosts > Subnets > Associate
  2. Select your VNet and the session host subnet (default unless renamed — not AzureFirewallSubnet)
  3. Select OK

Step 5: Create or Reuse the Log Analytics Workspace

Required for AVD Insights, firewall logs, and Defender for Endpoint telemetry.

Reuse an existing tenant SIEM workspace if you have one

This step creates a dedicated law-avd-usgovva because the runbook assumes a greenfield "empty subscription → AVD" deployment. If the tenant already has a Log Analytics workspace backing its SIEM/Sentinel (e.g., law-<company> collecting Entra, M365, and Defender telemetry), point the AVD diagnostic settings at that workspace instead of creating a new one — skip the create sub-step below and substitute the existing workspace everywhere this runbook says law-avd-usgovva.

Reuse is the better default for a CMMC environment:

  • Unified correlation. AVD session diagnostics, firewall logs, and AVD Insights land alongside Entra sign-ins, MDE alerts, and M365 audit — one query surface for incident response (AU.L2-3.3.1, SI.L2-3.14.6) instead of stitching across workspaces.
  • One Sentinel instance. A separate workspace means a second Sentinel instance and cross-workspace queries to correlate; consolidation avoids both.
  • One retention / RBAC / cost surface to manage.

Two things to confirm before reusing: (1) the existing workspace is in the same region as the AVD VNet (US Gov Virginia) — diagnostic settings and AVD Insights data collection rules want same-region; (2) if the workspace is Sentinel-enabled, the AVD firewall and session telemetry adds to per-GB Sentinel ingestion (firewall logs are chatty — budget a few GB/month). Create a dedicated AVD workspace only when you need separate billing attribution, separate retention, or RBAC isolation from the rest of the tenant's security data — none of which typically applies to a single-client CMMC enclave.

  1. Navigate to Log Analytics workspaces > Create
  2. Set Resource group to rg-avd-prod-usgovva
  3. Set Name — e.g., law-avd-usgovva
  4. Set Region to US Gov Virginia
  5. Select Review + Create > Create

Once the workspace exists (created here or reused), enable structured firewall logging by adding a diagnostic setting:

  1. Open the firewall resource > Diagnostic settings > Add diagnostic setting

  2. Name it diag-afw-avd

  3. Under Logs > Categories, check exactly these four — they are the security-relevant audit events (AU.L2-3.3.1) at low-to-moderate volume:

    Checkbox label (portal)TableWhy
    Azure Firewall Network RuleAZFWNetworkRuleNetwork-layer allow/deny audit
    Azure Firewall Application RuleAZFWApplicationRuleApplication-layer (FQDN) allow/deny — the table the troubleshooting KQL queries
    Azure Firewall Threat IntelligenceAZFWThreatIntelThreat-intel feed matches (Standard tier defaults to alert mode)
    Azure Firewall FQDN Resolution FailureAZFWFqdnResolveFailureCheap, low-volume; surfaces DNS-resolution failures fast during troubleshooting
  4. Leave these unchecked — they produce nothing on a Standard-tier AVD firewall without inbound DNAT or DNS proxy:

    Checkbox label (portal)Why it's empty
    Azure Firewall IDPS SignatureIDPS is a Premium-tier feature; never populated on Standard
    Azure Firewall DNS queryRequires DNS proxy enabled; the Bicep does not configure DNS proxy
    Azure Firewall DNS Flow Trace LogRequires DNS proxy; also high-volume
    Azure Firewall Nat RuleLogs inbound DNAT; the enclave has no public ingress
  5. Leave these unchecked for steady state — high-volume diagnostic data that drives up Sentinel ingestion cost. Enable temporarily only while actively debugging, then turn back off:

    Checkbox label (portal)Why off by default
    Azure Firewall Flow Trace LogTCP flag-level flow tracing (SYN / SYN-ACK / FIN) — very high volume
    Azure Firewall Fat Flow LogHigh-throughput flow records; performance diagnostics, not audit
  6. Leave these unchecked unless using Azure Firewall Policy Analytics — they feed that separate opt-in (and separately-billed) feature:

    Checkbox label (portal)
    Azure Firewall Network Rule Aggregation (Policy Analytics)
    Azure Firewall Application Rule Aggregation (Policy Analytics)
    Azure Firewall Nat Rule Aggregation (Policy Analytics)
  7. Leave the three (Legacy Azure Diagnostics) entries (Azure Firewall Application Rule, Azure Firewall Network Rule, Azure Firewall DNS Proxy) unchecked — they write to the consolidated AzureDiagnostics table regardless of the destination-table setting, defeating the resource-specific tables the troubleshooting KQL relies on (per Microsoft's structured-logs reference).

  8. Check Send to Log Analytics workspace and select law-avd-usgovva (or your reused tenant SIEM workspace).

  9. Set Destination table to Resource specific.

  10. Leave Archive to a storage account, Stream to an event hub, and Send to partner solution unchecked.

  11. Select Save.

Firewall metrics need a separate diagnostic setting

The setting above captures logs only. Per Microsoft's Monitor Azure Firewall guidance, firewall metrics can only flow to Log Analytics via the legacy Azure Diagnostics destination. If you want metrics in law-avd-usgovva (e.g., for the Azure Firewall Workbook), add a second diagnostic setting with destination Azure Diagnostics and only the AllMetrics checkbox enabled. Most operational alerting in this enclave is log-based, so this is optional.

This workspace is also the Sentinel ingestion target

Microsoft Sentinel is enabled on a Log Analytics workspace, not deployed separately — onboarding law-avd-usgovva to Sentinel and wiring the M365 / Entra / Defender / Azure connectors is documented in the SIEM Strategy chapter. Run that workstream after this step (or in parallel — it does not block the rest of the AVD build).


Step 6: Create the AVD Workspace

  1. Navigate to Azure Virtual Desktop > Workspaces > Create
  2. Set Resource group to rg-avd-prod-usgovva
  3. Set Workspace name — e.g., avdws-prod
  4. Set Location to US Gov Virginia
  5. Skip the Application groups tab — the application group will be created with the host pool in Step 7
  6. On the Advanced tab, select law-avd-usgovva as the Log Analytics workspace. Leave the storage account and event hub checkboxes unchecked.
  7. Select Review + Create > Create

After creation, verify the diagnostic settings — and complete only what's missing:

  1. Open avdws-prod > Diagnostic settings.
  2. If a setting already exists (the creation flow or AVD Insights may have created one) that sends Checkpoint, Error, Management, and Feed to your Log Analytics workspace as Resource specific — you're done. Skip to Step 7.
  3. If no setting exists, or it's missing categories, add or edit one:
    1. Select Add diagnostic setting (or Edit setting on the existing one).
    2. Name it diag-avdws-prod.
    3. Check the four log categories: Checkpoint, Error, Management, Feed.
    4. Check Send to Log Analytics workspace and select law-avd-usgovva (or your reused tenant SIEM workspace).
    5. Set Destination table to Resource specific.
    6. Select Save.
Don't create a second setting with categories that are already captured

A given log category can be sent to a given workspace by only one diagnostic setting. If a category like Feed is already being sent to your workspace by an existing setting, adding a second setting with the same category errors on save ("category is already enabled in another diagnostic setting"). Always check what already exists before adding — edit the existing setting to fill gaps rather than creating a duplicate. Per Microsoft's AVD diagnostics guidance.


Step 7: Create the AVD Host Pool

  1. Navigate to Azure Virtual Desktop > Host pools > Create
  2. Basics tab:
SettingValue
Resource grouprg-avd-prod-usgovva
Host pool namehp-avd-prod
LocationUS Gov Virginia
Preferred app group typeDesktop
Host pool typePersonal
Assignment typeAutomatic
  1. Virtual machines tab: Leave empty — session hosts are added in Step 10
  2. Workspace tab: Select avdws-prod
  3. Diagnostics tab: Select law-avd-usgovva (or your reused tenant SIEM workspace) as the Log Analytics workspace and check all available log categories. Leave archive to storage account and stream to event hub unchecked. Because this happens during creation, it produces the host pool's first diagnostic setting — there's no duplicate-category conflict to worry about here (unlike the workspace in Step 6, where a setting may already exist).
  4. Select Review + Create > Create
Personal pool: one VM per user, always

With Automatic assignment, AVD permanently assigns each user to a specific VM on their first connection — no admin action required. Every subsequent connection returns that user to the same VM — their desktop, files, and application state are exactly as they left them. Users cannot land on a different VM.

Assignments do not release automatically. If a user leaves or needs to free their VM for someone else, an admin must remove the assignment manually: Host pool > Session hosts > select the VM > Remove user assignment. Until cleared, the VM remains reserved for that user even if they never connect again. Direct assignment requires the same admin step to unassign, but also requires admin action to assign in the first place — making Automatic the better choice for most deployments.

Capacity must equal or exceed user count

With Automatic assignment, AVD assigns one VM per user. If all session hosts are assigned and a new user attempts to connect, AVD will find no available VM and deny the connection — there is no queue. The number of session hosts in the pool must always equal or exceed the number of users who need access. To add capacity, open the host pool > Session hosts > Add and provision additional VMs.

After creation, configure RDP properties:

  1. Open hp-avd-prod > RDP properties
  2. Set the following under Device redirection:
PropertyValueReason
Clipboard redirectionDisabledPrevent CUI from leaving the session
Drive redirectionDisabledPrevent local drive mounting
Camera redirectionDisabled
Printer redirectionDisabled (or selectively enabled)
WebAuthn redirectionEnabledAllows YubiKeys and Windows Hello to authenticate inside the session — extends phishing-resistant auth to resources accessed within AVD
USB redirectionDisabledGeneric USB redirection includes storage devices — a direct CUI exfiltration path. WebAuthn uses a separate channel and is unaffected by this setting.
Smart card redirectionEnabledAllows CAC/PIV cards to authenticate inside the session. No data exfiltration risk.
  1. Under Advanced → Custom RDP properties, enter each property on its own line, then select Save. This is the tested set for a CUI session-host posture:
watermarking:i:1;
screen capture protection:i:2;
targetisaadjoined:i:1;
drivestoredirect:s:;
usbdevicestoredirect:s:;
redirectclipboard:i:0;
redirectprinters:i:0;
audiomode:i:0;
videoplaybackmode:i:1;
devicestoredirect:s:;
redirectcomports:i:0;
redirectsmartcards:i:1;
enablecredsspsupport:i:1;
redirectwebauthn:i:1;
use multimon:i:1;
enablerdsaadauth:i:1;

The security-relevant properties in that set:

PropertyWhy it matters
enablerdsaadauth:i:1Enables SSO via Entra ID — prevents a second authentication prompt at the VM
targetisaadjoined:i:1Tells the client the VM is Entra-joined; required for SSO to work
screen capture protection:i:2Blocks screen capture on both client and server — prevents screenshots of CUI from inside the session
watermarking:i:1Overlays a QR-code watermark that ties a photographed screen back to the session for traceability
redirectclipboard:i:0Disables clipboard redirection — prevents CUI copy-out
drivestoredirect:s:Disables drive redirection — prevents local drive mounting
usbdevicestoredirect:s:Disables USB storage redirection — closes a direct CUI exfiltration path
redirectwebauthn:i:1Allows FIDO2 / Windows Hello inside the session — extends phishing-resistant auth
redirectsmartcards:i:1Allows CAC/PIV cards to authenticate inside the session

The clipboard, drive, and USB entries above set the same controls as the Device redirection toggles in step 2 — the portal writes both into the same underlying customRdpProperty string, so they agree rather than conflict. Pasting this complete block is sufficient on its own; it also carries the SSO, screen-capture, and watermarking properties that have no Device-redirection toggle.

Enable Start VM on Connect:

  1. Open hp-avd-prod > Properties
  2. Set Start VM on connect to Yes
  3. Select Save
Start VM on Connect requires a role assignment

The Azure Virtual Desktop service principal (AppId 9cdead84-a844-4324-93f2-b2e6bb768d07) must have the Desktop Virtualization Power On Off Contributor role on the subscription or resource group. Assign this before session hosts are added.

Navigate to the resource group > Access control (IAM) > Add role assignment > Desktop Virtualization Power On Off Contributor > Assign access to: User, group, or service principal > Search "Azure Virtual Desktop".


Step 8: Assign Users to the Application Group and Grant VM Login Rights

Azure automatically creates a Desktop Application Group named hp-avd-prod-DAG when the host pool is created and associates it with the workspace. No manual creation is needed.

A user needs three things to reach a session host successfully — and a single mail-enabled security group (MESG) can carry all three. Use one group rather than per-user assignments so onboarding/offboarding is a single group-membership change:

#RequirementWithout it…
1Membership in a group assigned to the Desktop Application GroupThe workspace tile does not appear in the Windows App client
2Virtual Machine User Login Azure RBAC role on the resource groupEntra auth succeeds, but the Windows sign-in inside the VM returns "Access Denied"
3(CUI enclaves only) Membership in the group named in the sensitivity label's Encryption → Assign permissions scopeLabeled CUI files are encrypted but the user cannot decrypt them inside the session

A plain Entra security group satisfies #1 and #2 but not #3 — sensitivity-label encryption requires a mail-enabled security group, distribution list, or Microsoft 365 group. Naming the group *-MESG makes the requirement self-documenting (this matches the AVD-Enclave-FCI-Users-MESG pattern in the Enclave chapter).

Step 8.1 — Create the mail-enabled security group (Exchange admin center)

Mail-enabled security groups cannot be created from the Entra portal — the Entra "New group" flow has no mail-enabled toggle. Use the Exchange admin center instead:

  1. Open the Exchange admin center (GCC High URL — note .us)
  2. Navigate to Recipients > Groups > Add a group
  3. Group type: Mail-enabled security
  4. Name: AVD-Users-MESG (or AVD-Enclave-FCI-Users-MESG if this deployment is a CUI enclave per Chapter 11-7)
  5. Email alias: AVD-Users (or AVD-Enclave-FCI-Users for the enclave variant). The full address becomes <alias>@<tenant>.onmicrosoft.us — drop the -MESG suffix from the email itself so the alias stays short; the display name carries the type indicator. Use the tenant's .onmicrosoft.us domain unless you intend to receive external mail to this group — typically you do not.
  6. Under Settings, check Require owner approval to join the group. This prevents self-join — without it, any user could grant themselves AVD entitlement, VM login authority, and (for the enclave variant) decryption authority on CUI content. Membership becomes gated by a named owner, typically the IT lead or a delegated approver from an RMAU.
  7. Add the AVD users as members
  8. Select Create

The group propagates to Entra within a few minutes and becomes usable for Azure RBAC and AVD assignment.

Step 8.2 — Assign the MESG to the Application Group

The AVD Assignments blade writes the Azure RBAC role Desktop Virtualization User at the application group scope — the same role visible from the application group's Access control (IAM) view or via az role assignment list --scope <app-group-id>. The AVD-native blade is preferred because it filters to compatible group types and surfaces the assignment in the AVD experience, but the underlying mechanism is standard Azure RBAC.

  1. Navigate to Azure Virtual Desktop > Application groups
  2. Open hp-avd-prod-DAG > Assignments > Add
  3. Search for the MESG, select it, and click Select

Step 8.3 — Grant Virtual Machine User Login at the resource-group scope

Assigning at the resource group scope (rather than per-VM) covers all current and future session hosts in this pool — and means future capacity additions need no additional RBAC work.

  1. Navigate to the resource group rg-avd-prod-usgovva > Access control (IAM) > Add role assignment
  2. Role: Virtual Machine User Login
  3. Members: the MESG
  4. Select Review + assign
CUI enclave: same MESG drives the sensitivity-label scope

If this AVD deployment is a CUI enclave with Purview sensitivity labels and RMS encryption, the same MESG should appear as the principal in the label's Encryption > Assign permissions scope. This binds CUI decryption authority to the same group that controls AVD access — a member-removal in one place revokes both the session host login AND the ability to open CUI content in the existing session. See Scenario: AVD Secure Enclave § Data for the full label and DLP configuration.


Step 9: Configure Entra SSO for AVD

Entra SSO eliminates the second authentication prompt users would otherwise see when connecting to a session host. It requires enabling RDP on two AVD service principals and scoping that permission to a device group containing your session hosts.

Create the AVD device group:

  1. Navigate to Entra admin center > Groups > New group
  2. Set Group type to Security
  3. Set Membership type to Dynamic Device
  4. Set Group name — e.g., EID AVD Devices
  5. Under Dynamic device members, add the following rule:
(device.displayName -startsWith "avd-")
  1. Select Create
Dynamic group processing takes a few minutes

The group will not contain devices immediately. Once session hosts are provisioned in Step 10, they will be added automatically within 5–15 minutes based on the naming rule. If your session hosts use a different name prefix, adjust the rule accordingly.

Run the SSO configuration script:

The following script enables RDP on the two Entra service principals that back AVD SSO and scopes the permission to your device group. It requires the Microsoft.Graph.Authentication and Microsoft.Graph.Applications PowerShell modules and must be run by a Global Administrator.

  1. Replace AVD DEVICE GROUP HERE in the script with the object ID of the EID AVD Devices group created above
  2. Run the script:
Import-Module Microsoft.Graph.Authentication
Import-Module Microsoft.Graph.Applications

function Set-RdpEnabledForServicePrincipal {
param (
[Parameter(Mandatory = $true)]
[string] $ServicePrincipalId
)
$config = Get-MgServicePrincipalRemoteDesktopSecurityConfiguration -ServicePrincipalId $ServicePrincipalId
if (-not $config -or $config.IsRemoteDesktopProtocolEnabled -ne $true) {
Update-MgServicePrincipalRemoteDesktopSecurityConfiguration -ServicePrincipalId $ServicePrincipalId -BodyParameter @{ isRemoteDesktopProtocolEnabled = $true }
Write-Host "RDP enabled for service principal $ServicePrincipalId"
} else {
Write-Host "RDP already enabled for service principal $ServicePrincipalId"
}
}

Connect-MgGraph -Environment USGov -Scopes "Application.Read.All","Application-RemoteDesktopConfig.ReadWrite.All"

$MSRDspId = (Get-MgServicePrincipal -Filter "AppId eq 'a4a365df-50f1-4397-bc59-1a1564b8bb9c'").Id
$WCLspId = (Get-MgServicePrincipal -Filter "AppId eq '270efc09-cd0d-444b-a71f-39af4910ec45'").Id

Set-RdpEnabledForServicePrincipal -ServicePrincipalId $MSRDspId
Set-RdpEnabledForServicePrincipal -ServicePrincipalId $WCLspId

$tdg = New-Object -TypeName Microsoft.Graph.PowerShell.Models.MicrosoftGraphTargetDeviceGroup
$tdg.Id = "<Object ID of EID AVD Devices group>"
$tdg.DisplayName = "EID AVD Devices"

New-MgServicePrincipalRemoteDesktopSecurityConfigurationTargetDeviceGroup -ServicePrincipalId $MSRDspId -BodyParameter $tdg
New-MgServicePrincipalRemoteDesktopSecurityConfigurationTargetDeviceGroup -ServicePrincipalId $WCLspId -BodyParameter $tdg
  1. Verify output confirms RDP is enabled on both service principals

Enable SSO on the host pool:

  1. Open hp-avd-prod > RDP properties > Connections
  2. Set Microsoft Entra single sign-on to Enabled
  3. Select Save

Step 10: Add Session Hosts

Session hosts are Entra-joined and Intune-enrolled automatically during provisioning — no user interaction, no MFA challenge. See Entra Join and Intune Enrollment: No User Interaction Required for details.

  1. Open hp-avd-prod > Session hosts > Add
  2. Virtual machines tab:
SettingValue
Resource grouprg-avd-prod-usgovva
Name prefixavd (Azure appends -0, -1, etc. — dashes are not allowed in the prefix)
Virtual machine typeAzure virtual machine
Availability optionsNo infrastructure redundancy required
VM sizeStandard_D8as_v6 (or D16as_v6 for power users)
Number of VMs20
Security typeTrusted Launch
Secure BootEnabled
vTPMEnabled
Integrity monitoringEnabled
OS disk typePremium SSD LRS
OS disk size128 GB (default)
Boot diagnosticsEnabled with managed storage account
  1. Image: Select Windows 11 Enterprise (single-session) from the gallery. Personal pools use single-session — one user per VM, with persistent desktop and user data.

  2. Network tab:

SettingValue
Virtual networkvnet-avd-usgovva
Subnetdefault (or your session host subnet name)
Network security groupNone
Public inbound portsNone
NSG not required with Azure Firewall

Session hosts have no public IPs and no inbound internet path. The Azure Firewall and UDR provide the network perimeter. An NSG on the session host subnet would be redundant for outbound control and adds no meaningful inbound protection in this topology.

  1. Domain to join tab:
SettingValue
Select which directory you would like to joinMicrosoft Entra ID
Enroll VM with IntuneYes
Add VM to a single groupOptional — specify a device group if targeting scoped Intune policies
  1. Virtual machine administrator account: Set a local admin username and password (stored in the VM, not synced to Entra)

  2. Select Review + Create > Add

Provisioning time

20 VMs typically take 25–45 minutes to fully provision. Monitor progress under Host pool > Session hosts. Each VM transitions through: Provisioning → Available. Intune enrollment completes within 5–15 minutes of the VM reaching Available state.

Host joins Entra but never enrolls in Intune? Check the MDM URLs

If a session host appears in the host pool but the AADLoginForWindows extension fails — DeviceEnroller::EnrollToMdm() with 0x80192efd in the User Device Registration / Admin event log — and the device never shows up in Intune, the tenant is handing out the commercial (.com) MDM enrollment URL. This is a tenant-wide prerequisite fix, not a per-host one: correct the three MDM URLs to .us per Set the MDM enrollment URLs to .us above, then redeploy the affected hosts.

Defender for Endpoint onboarding is the natural next step

Once session hosts are Intune-enrolled, onboard them to Microsoft Defender for Endpoint by assigning the EDR configuration profile to the EID AVD Devices group. The full GCC High onboarding flow (Defender for Cloud connector prerequisite, EDR profile, sensor health verification) is in Appendix B § Defender for Endpoint. MDE telemetry then flows to the same Log Analytics workspace created in Step 5.


Step 11: Pin Users to Session Hosts

The host pool's Assignment type is Automatic (Step 7) — first sign-in to an unassigned VM auto-pins that user, and the VM is theirs from then on. For CUI enclaves where each user is a known cleared individual, pre-assign users to specific VMs before they first sign in. Two reasons it's worth the extra five minutes:

  1. Predictable user-to-VM mapping for audit and forensics. A C3PAO asking "who used avd-05 on March 14?" gets a deterministic answer rather than "whoever logged in first."
  2. Avoids the first-login race. When 20 users in a freshly-provisioned host pool connect within the same hour, Automatic assigns whoever's TLS handshake completes first. Pre-pinning eliminates the race.

Manual assignments take precedence over Automatic. Once Alice is pinned to avd-05, she always goes to avd-05. If avd-05 is offline she gets an error rather than landing on a different VM — the desired behavior for a per-user persistent desktop.

Portal path

  1. Azure Virtual Desktop > Host pools > hp-avd-prod > Session hosts
  2. Select the VM > Assign user in the toolbar
  3. Search for the user's UPN > Save

PowerShell path (for bulk assignment)

Connect-AzAccount -Environment AzureUSGovernment

Update-AzWvdSessionHost `
-ResourceGroupName "rg-avd-prod-usgovva" `
-HostPoolName "hp-avd-prod" `
-Name "avd-05.<full-fqdn-from-portal>" `
-AssignedUser "alice@yourdomain.us"

The -Name parameter takes the full FQDN as displayed in the Session hosts list (typically <vmname>.<random-suffix> for Entra-joined VMs). Copy it directly from the portal rather than guessing.

Removing or transferring an assignment

To clear or transfer, use the same blade: Session hosts > [VM] > Assign user > clear field > Save. Or via PowerShell: Update-AzWvdSessionHost -AssignedUser "". Then assign the new user via the same path.

When to skip

If your operational model prefers users self-assign on first login (smaller deployments, transient operators, no per-VM forensic requirement), skip this step entirely. The Automatic assignment type from Step 7 handles it on first login.


Step 12: Lock Down User Power Options on Session Hosts

Personal AVD session hosts auto-start via "Start VM on Connect" when a user re-connects, but only if the VM is in a deallocated state. If a user shuts the VM down from the Windows Start menu, they can't start it back up themselves — they're locked out until an admin restarts the VM from the Azure portal. The fix is to hide the shutdown options from the Start menu while preserving sign-out.

Create the policy

  1. Intune admin center → Devices → Configuration → Create → New policy
  2. Platform: Windows 10 and later; Profile type: Settings catalog; click Create.
  3. Basics tab:
    • Name: Win - Custom - SC - AVD - User Power Lockdown
    • Description: Hides Shut Down, Restart, Sleep, and Hibernate commands from the Start menu on AVD session hosts. Sign out remains available. Admin-side shutdown via portal/PowerShell unaffected.
  4. Configuration settings tab → + Add settings. In the picker, search for Remove and prevent access to the Shut Down, Restart, Sleep, and Hibernate commands (User Configuration → Administrative Templates → Start Menu and Taskbar). Toggle to Enabled.
  5. Scope tags — accept Default unless you have RBAC scope tags in use.
  6. Assignments — target the EID AVD Devices group (the dynamic device group created in Step 9).
  7. Review + create.

What this does (and doesn't do)

  • Hides Shut Down / Restart / Sleep / Hibernate from the Start menu, Ctrl+Alt+Del menu, and lock-screen power button.
  • Leaves Sign Out visible so users can end their session normally.
  • Does not block administrative shutdown via shutdown.exe, PowerShell Stop-Computer, or the Azure portal — admins can still maintenance-cycle the VM.
  • Takes effect on next sign-in to the session host (or gpupdate /force from an existing session).

CSP / registry mechanism: HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\NoClose = 1. ADMX policy name: NoClose. Microsoft documents the underlying behavior at Hide the shutdown options on Windows.

Defense-in-depth note

This policy is a UI-level restriction — it hides the Start menu options. A user who can open a command prompt or PowerShell window can still type shutdown /s to initiate a shutdown. The defense for that is the Shut down the system user right in Local Security Policies, which is restricted to Administrators by default on Windows 11. The OIB Local Security Policies baseline (Appendix B § Local Security Policies) preserves this default, so non-admin users on properly-baselined session hosts cannot shut down the VM via the command line either. Verify the user-rights setting hasn't been overridden in your tenant before relying on this defense.


Step 13: Create the Recovery Services Vault and Configure Backup

Personal AVD session hosts accumulate per-user state that nothing in the deployment process captures — application configuration in %AppData%, browser profiles, signing certificates, installed line-of-business clients, registry tweaks. A failed VM is a failed user: rebuilding from the stock Windows 11 Enterprise gallery image plus the Intune baseline (re-run of Step 10) returns a clean OS but discards everything the user has installed or configured since their VM was first provisioned. Daily backup with 30-day retention preserves this provisioning investment, restores in 20–40 minutes versus 2–4 hours to provision and baseline a replacement VM, and provides the disk-state evidence required for incident response under CMMC IR.L2-3.6.1 / 3.6.2.

Backup in this architecture is explicitly not a CUI-custody mechanism — CUI lives in governed SharePoint sites per the Data Storage Policy, and any CUI found on a restored disk would be a policy violation in either the original or the restored copy. The full architectural rationale for backing up an ostensibly near-ephemeral session host pool is in Scenario: AVD Secure Enclave § Session Host Backup.

Create the Recovery Services Vault

  1. Navigate to Azure Government portal > Recovery Services vaults > Create
  2. Set Subscription to your AVD subscription
  3. Set Resource group to rg-avd-prod-usgovva
  4. Set Vault name — e.g., rsv-avd-prod-usgovva
  5. Set Region to US Gov Virginia — must match the region of the VMs being protected
  6. On the Redundancy tab, set Backup Storage Redundancy to Locally-redundant (half the price)
  7. On the Encryption tab, leave Encryption type at Use Microsoft-managed key (default)
  8. On the Vault properties tab, check Enable immutability (ransomware-defense: attacker cannot destroy the backup)
  9. On the Networking tab, leave Allow public access from all networks (the default) — see the trade-off discussion below
  10. Select Review + Create > Create
Choose Customer-managed key only if the client or contractual flow down requires it.

Microsoft-managed key (the default selected above) is FIPS 140-2/3 validated within the FedRAMP High boundary and satisfies SC.L2-3.13.10 and SC.L2-3.13.11 — CMMC L2 does not require customer-managed keys for backup encryption.

Public network access stays enabled (default)

The Allow public access from all networks default on the Networking tab is correct for this architecture. With Azure Firewall filtering all session-host egress (Step 4 forward) and the AzureBackup rule collection explicitly allowing *.backup.windowsazure.us and the Azure Backup queue backend (*.queue.core.usgovcloudapi.net), the backup data path is already controlled — it crosses public networking but only along the documented Microsoft-managed endpoints, and only from inside the AVD VNet through the firewall.

Private-endpoint alternative. For organizations that require backup traffic stay entirely inside the VNet (no public IP traversal at all), deploy a private endpoint for the Recovery Services Vault inside the AVD VNet. Backup traffic then routes over a private IP, the firewall is no longer in the data path, and the AzureBackup application rule collection in avd-firewall.bicep silently no-ops on the private path — the rule still exists but is never matched.

The trade-off is operational complexity. Private endpoints require per-zone private DNS configuration in the AVD VNet — the vault's *.privatelink.<region>.backup.windowsazure.us zone and a storage-account zone for the backup blob/queue namespaces — plus a second control plane to maintain alongside the public-egress firewall rules. Most CMMC AVD deployments leave public access enabled and rely on the firewall plus the vault's own security settings (Soft Delete + immutability, configured in the next subsection) for protection. Switch to private endpoints only when a specific data-path-confinement requirement in the SSP forces it; the firewall-controlled public path satisfies SC.L2-3.13.1 (boundary protection) and SC.L2-3.13.5 (publicly accessible content separation) on its own.

Configure security settings

Recovery Services Vault has two relevant protection mechanisms at Properties > Security Settings: Soft Delete and Multi-User Authorization (MUA). For this architecture, set Soft Delete retention to the maximum and skip MUA unless the client has a dedicated security team holding rights on an Azure Resource Guard. Both are free.

Soft Delete

Soft delete protects against accidental or malicious deletion of backup items by retaining deleted recovery points before permanent purge.

  1. Open rsv-avd-prod-usgovva > Properties > Security Settings > Soft Delete and security settings > Update
  2. Enable soft delete for cloud workloads: confirm the checkbox is selected (default; current portals do not allow unchecking)
  3. Soft delete retention period (in days): 180
  4. Click Update

Multi-User Authorization (MUA) — optional, usually skip

MUA requires destructive backup operations (disable backup, delete vault, reduce retention, modify policy) to be approved by an identity holding rights on a separately-owned Azure Resource Guard. Appropriate for organizations where backup administration and security approval are separate teams; overkill for a small DIB shop where one engineer wears all the hats — the approval loop adds friction without a meaningful threat model. Full procedure is out of scope for this runbook — see Microsoft's MUA documentation.

Configure the backup policy

The vault ships with three pre-created policies — HourlyLogBackup (SQL Server in Azure VM, streaming backup), DefaultPolicy (Azure VM, Standard sub-type), and EnhancedPolicy (Azure VM, Enhanced sub-type). Don't use any of them directly. Create a new policy named for its intent so the purpose is self-documenting and the vault-default templates stay untouched.

  1. Open rsv-avd-prod-usgovva > Backup policies > Add
  2. Policy type: Azure Virtual Machine
  3. Policy sub-type: Enhanced — first-class Trusted Launch VM support (Step 10 provisioned session hosts with Secure Boot + vTPM enabled), Microsoft's go-forward policy for new Azure VM backups, and forward-compatible if a future client wants sub-daily RPO. Standard works too but caps at one snapshot per day with no in-place upgrade path.
  4. Policy name: bp-avd-daily-30day
  5. Backup schedule: Daily, at a low-utilization time (e.g., 02:00 local time)
  6. Instant restore: 7 days (default)
  7. Retention range:
RetentionValue
Retention of daily backup point30 Days
Retention of weekly backup pointDisabled — enable only if longer-term retention is required by your data retention policy
Retention of monthly backup pointDisabled
Retention of yearly backup pointDisabled
  1. Select Create
Enhanced configured for daily behaves identically to Standard

The "Enhanced" sub-type unlocks hourly scheduling but does not require it. Configured for Daily at a fixed time with daily-only retention (above), it produces the same once-per-day snapshot pattern as Standard. The benefit is forward compatibility — if you ever need to tighten RPO, you adjust the existing policy in place rather than tearing it down and re-enrolling protected items.

Enable backup on the session hosts

  1. Open rsv-avd-prod-usgovva > Backup > + Backup
  2. Where is your workload running? Azure
  3. What do you want to back up? Virtual machines
  4. Select Backup
  5. Backup policy: bp-avd-daily-30day
  6. Virtual Machines: click Add and select all session hosts in rg-avd-prod-usgovva — the wizard filters by vault region, so only VMs in US Gov Virginia appear
  7. Select Enable Backup
Per-operation UI cap of 20 VMs

The portal's + Backup wizard caps VM selection at 20 per operation, even when more VMs are eligible. For pools larger than 20 session hosts, enroll 20 in the first wizard pass, then re-run + Backup with the remaining VMs — each run is an independent operation and the cap resets between them. The Azure CLI alternative az backup protection enable-for-vm has no per-operation cap if you'd rather script bulk enrollment in a single loop. The documented per-day limits (250 VM registrations, 1000 configure-protection operations) are not a constraint at AVD-pool scale.

Trusted Launch VMs are supported

Azure Backup supports Trusted Launch VMs (Secure Boot + vTPM, enabled in Step 10) with no special configuration — Azure Backup detects Trusted Launch automatically and uses the appropriate snapshot mechanism. This was a gap when Trusted Launch first shipped and is sometimes still cited in older guidance; it has since been resolved.

Monitor the first backup

Enrolling a VM in the policy does not run a backup — it only schedules one for the next configured window (02:00 with our policy). Until the first backup completes, the per-VM Last backup status shows Warning (initial backup pending). Trigger the first backup immediately rather than waiting for the schedule:

  1. Open rsv-avd-prod-usgovva > Backup items > Azure Virtual Machine > select the VM
  2. Click Backup now in the toolbar
  3. Retain Until: accept the default (30 days, matches policy retention)
  4. Click OK

The job appears under Backup jobs within about 30 seconds. Repeat per VM, or script via az backup protection backup-now if you want to fire all 20+ off in a loop.

Where to watch progress

The main view is vault > Backup jobs in the left navigation:

  1. Filter Operation: Backup, Status: In Progress, From/To: today
  2. Each row is one VM's backup attempt — start time, duration, current status
  3. Click into a row for the per-stage breakdown:
StageTypical duration
Taking snapshotseconds to ≈1 minute
Transferring data to vaultthe long part on a first backup — 30–60 minutes for a 128GB session host
Updating recovery pointsseconds (final commit)

The percentage indicator advances as data transfers — this is the closest thing to a real progress bar Azure Backup exposes.

For a single host, Backup items > Azure Virtual Machine > [VM] shows the current job status and the latest restore point inline — faster than navigating to Backup jobs if you only care about one VM. Subsequent incremental backups take 2–10 minutes (only changed blocks).

When a job appears stuck

If a job shows In Progress for over an hour with no percentage advance, the most common causes in rough frequency order:

  • Network rule blocking outbound backup traffic. Verify the Azure-Backup firewall collection is deployed and the session host subnet appears in sourceAddresses. Run Query 1 of the firewall troubleshooting KQL filtered to the session host's private IP — denies on *.backup.windowsazure.us or *.queue.core.usgovcloudapi.net indicate the rules are missing or misscoped.
  • Backup extension installation failed. Azure Backup pushes the VMSnapshot extension to the VM via the VM agent on first protection. Check the VM's Extensions + applications blade — provisioning state should be Succeeded. Re-trigger if it failed.
  • VM offline for extended period. Backup runs even on stopped VMs (crash-consistent), but a deallocated VM that has never been started since enrollment may delay extension installation. Start the VM once to let the agent run.
A vault with policy assigned but no successful run is not a backed-up VM

Confirm Last backup status: Successful for every session host before signing the deployment off. The per-VM status is the authoritative signal — a vault dashboard showing 22 protected items can include items whose first backup never completed.

Adding new session hosts

When capacity is added (Step 10 re-run, more VMs provisioned), repeat the Enable backup on the session hosts sub-step to associate the new VMs with the existing policy. There is no automatic discovery — newly-provisioned VMs do not enroll into backup until explicitly added.


Step 14: Validate

Entra Join:

  • Entra admin center > Devices > All devices — each session host should appear as Join type: Microsoft Entra joined, MDM: Microsoft Intune

Intune Enrollment:

  • Intune admin center > Devices > All devices — each session host should appear with Enrollment type: AzureAD, Compliance: Compliant (once compliance policies have been applied and evaluated)

AVD connectivity (legacy Remote Desktop client + Subscribe with URL):

The new "Windows App" client has incomplete sovereign-cloud feed support. Use the classic Remote Desktop client (the AVD-specific build, distinct from the built-in Windows Remote Desktop Connection and from the new "Windows App") for GCC High.

  1. Download the classic client from Microsoft: https://go.microsoft.com/fwlink/?linkid=2068602
  2. Run the .msi and complete the standard install.
  3. Open Remote Desktop from the Start menu — the icon is a red circle with white arrows, not the blue/teal "Windows App" icon.
  4. On the main screen, click Subscribe with URL.
  5. Paste the GCC High AVD feed URL: https://rdweb.wvd.azure.us/api/arm/feeddiscovery
  6. Click Next and sign in with a test Entra user.
  7. The user's assigned desktop tile appears in the workspace.
  8. Launch the desktop and verify:
    • Session connects within ~30 seconds
    • SSO completes without a second authentication prompt (Entra Kerberos was configured in Step 9)
    • Clipboard and drive redirection are blocked per the host pool's RDP property settings

Firewall logging:

  • In Log Analytics, run: AZFWApplicationRule | limit 50
  • Verify traffic is flowing through the firewall and expected FQDNs are being allowed

Start VM on Connect:

  • Power off a session host from the Azure portal
  • Sign in as the assigned user via the AVD client
  • Verify the VM starts automatically and the session connects within 2–3 minutes

Step 15: Configure Power Scheduling

Step 7 enabled Start VM on Connect, which handles on-demand startup when a user reconnects to a deallocated VM. That solves the start side but not the stop side — left alone, every VM stays running until manually deallocated, and the per-VM compute cost lands on the bill regardless of whether anyone signed in.

Pick a power-off mechanism. Three patterns are commonly used in this architecture, compared with cost detail and failure-mode discussion in Scenario: AVD § Nerdio Manager for AVD:

  • Azure Automation Runbook — free; cheapest to operate; least resilient to runbook author / permission drift.
  • Native AVD Scaling Plans — first-party; supports personal pools; no third-party dependency.
  • Nerdio Manager for AVD — best operational UX and right-sizing data; adds a SaaS dependency and license cost. The Resilience and Failsafe Configuration subsection of the scenario chapter is required reading if you pick this path.

Regardless of mechanism, configure Azure Cost Management budget alerts on the AVD resource group — they fire independently of whichever scheduler is in use and are the primary financial safety net when a scheduler fails silently. Set thresholds at 80% (warning) and 100% (critical) of the expected monthly compute cost.

📩 Don't Miss the Next Solution

Join the list to see the real-time solutions I'm delivering to my GCC High clients.