# Azure Virtual Desktop Deep Dive

Build and validate a pooled Azure Virtual Desktop environment with Microsoft Entra-joined Windows 11 session hosts, FSLogix profiles on private Azure Files, power-management autoscale, and Azure Monitor.

The bundle uses PowerShell 7 as the orchestrator with Azure CLI and `az rest`. Secrets, object identifiers, registration tokens, passwords, consent identifiers, and raw captures are kept only in `work/azure-virtual-desktop-deep-dive` beside the bundle. Cleanup removes that private directory.

## What the lab creates

- Resource group `rg-avd-deepdive-we` in West Europe.
- VNet with separate session-host and private-endpoint subnets.
- Standard NAT Gateway outbound; the session hosts have no public IPs or inbound RDP rule.
- Standard_LRS StorageV2 account with a 100 GiB `fslogix` SMB share.
- Microsoft Entra Kerberos, default authenticated-identity share permission, private endpoint, private DNS, and creator-owner ACLs.
- Pooled, depth-first host pool `hp-avd-pooled-we`, desktop application group, and workspace with Entra SSO.
- Two Trusted Launch Windows 11 Enterprise multi-session 24H2 `Standard_D2s_v5` hosts.
- Log Analytics, Azure Monitor Agent, a DCR, AVD diagnostics, and queries for AVD Insights.
- A power-management scaling plan that maintains 50% availability and can deallocate one unused host.
- Optional temporary cloud-only member user with `usageLocation=DE` and an eligible unused license when the tenant already owns one.

## Important evidence mode

The tested tenant had no eligible unused Azure Virtual Desktop license. In that condition, the lab still deploys and validates the infrastructure, Entra join, private Azure Files, identical FSLogix settings, monitoring, and real autoscale deallocation. It records exactly:

`ProfilePersistenceStatus = NotTested-LicenseUnavailable`

The article must say that FSLogix was configured but profile persistence was not live-verified. Never publish a simulated VHDX, user session, connection, marker, or host-switch result.

## Prerequisites

- PowerShell 7 or later.
- Azure CLI signed in to the target subscription and tenant.
- Owner or User Access Administrator at subscription scope.
- Microsoft Entra permission to create a user and grant application consent.
- At least four available regional and DSv5-family vCPUs in West Europe.
- The Windows 11 Enterprise multi-session 24H2 image available to the subscription.
- No conflicting `rg-avd-deepdive-we` resource group or generated workshop identity.
- Optional: one eligible, unused Microsoft 365 or Windows per-user license for interactive AVD evidence.

The scripts never purchase a license, activate a trial, change Conditional Access, or disable tenant-wide MFA.

## Run the workshop

```powershell
$subscriptionId = az account show --query id --output tsv

./Start-AzureAvdWorkshop.ps1 `
  -SubscriptionId $subscriptionId `
  -Stage Preflight `
  -ProfileEvidenceMode AllowConfigurationOnly
```

`Preflight` is read-only. It writes private local state but performs no Azure or Microsoft Entra mutation.

Run the infrastructure stages in order:

```powershell
$stages = @(
  'Foundation',
  'Storage',
  'HostPool',
  'SessionHosts',
  'FSLogix',
  'Monitoring',
  'Scaling'
)

foreach ($stage in $stages) {
  ./Start-AzureAvdWorkshop.ps1 `
    -SubscriptionId $subscriptionId `
    -Stage $stage `
    -ProfileEvidenceMode AllowConfigurationOnly
}
```

Immediately before identity creation, obtain explicit action-time confirmation. Then run:

```powershell
./Start-AzureAvdWorkshop.ps1 `
  -SubscriptionId $subscriptionId `
  -Stage Identity `
  -CreateTestUser `
  -ProfileEvidenceMode AllowConfigurationOnly
```

The stage creates only the uniquely generated workshop user. If an eligible unused license exists, it assigns one and records the exact SKU privately. If no eligible license exists, it does not purchase or activate anything.

Run the deliberate tests and validation:

```powershell
./Start-AzureAvdWorkshop.ps1 -SubscriptionId $subscriptionId -Stage TestProfile -ProfileEvidenceMode AllowConfigurationOnly
./Start-AzureAvdWorkshop.ps1 -SubscriptionId $subscriptionId -Stage TestScaling -ProfileEvidenceMode AllowConfigurationOnly
./Start-AzureAvdWorkshop.ps1 -SubscriptionId $subscriptionId -Stage Validate -ProfileEvidenceMode AllowConfigurationOnly
```

## Interactive FSLogix proof

This path runs only when an eligible license is assigned.

1. Set host 2 to drain mode and allow new sessions on host 1.
2. Sign in through the [Azure Virtual Desktop web client](https://client.wvd.microsoft.com/arm/webclient/index.html).
3. Confirm the user-context startup script wrote the marker into the FSLogix-backed profile.
4. Log the user off, drain host 1, and allow host 2.
5. Reconnect and verify the marker reports a different current host while preserving the first host.
6. Confirm exactly one real VHDX container exists.

Do not run or describe these steps as successful without the actual session, marker, host change, and VHDX.

## Autoscale proof

`TestScaling` starts both hosts, verifies the host pool is empty, temporarily moves the ramp-down boundary into a short proof window, and waits for Azure Virtual Desktop autoscale. Passing requires exactly one `VM running` and one `VM deallocated` state plus the related Azure operation. A `finally` block restores the production schedule even if the test fails.

## Collect sanitized evidence

```powershell
./Get-AzureAvdEvidence.ps1 -SubscriptionId $subscriptionId
```

The public JSON contains counts, booleans, non-sensitive statuses, and the explicit profile-evidence mode. It rejects UPNs, GUIDs, tenant/subscription paths, IP addresses, registration tokens, and identity-bearing filenames.

## Cleanup

```powershell
./Remove-AzureAvdWorkshop.ps1 `
  -SubscriptionId $subscriptionId `
  -ConfirmCleanup
```

Cleanup validates the ownership tags, removes only the workshop user role assignments, returns the assigned license if there was one, soft-deletes only the exact temporary user, removes the workshop-created subscription autoscale role assignment, removes the workshop-created storage consent/application, deletes only `rg-avd-deepdive-we`, verifies the original license-consumption baseline, and deletes the private runtime directory.

## Files

- `Start-AzureAvdWorkshop.ps1` — staged orchestrator.
- `Get-AzureAvdEvidence.ps1` — live sanitized evidence collector.
- `Remove-AzureAvdWorkshop.ps1` — ownership-bounded cleanup and baseline restoration.
- `AvdWorkshop.Common.ps1` — Azure CLI, REST, Graph, state, wait, and sanitizer helpers.
- `session-host/Configure-AvdSessionHost.ps1` — FSLogix, Kerberos client, networking, ACL, and marker setup.
- `session-host/Write-FSLogixProfileMarker.ps1` — real user-context persistence marker.
- `kql/avd-workshop-queries.kql` — heartbeat, registration, connection, FSLogix, autoscale, and performance queries.
- `workshop-article.md` — publishable Workshopz article.
- `publishing-brief.md` — Wix metadata, media, and Workshops-page instructions.
- `live-test-report.md`, `screenshots/`, and `evidence/` — sanitized proof artifacts.

## Microsoft references

- [Deploy Microsoft Entra-joined virtual machines in Azure Virtual Desktop](https://learn.microsoft.com/en-us/azure/virtual-desktop/deploy-azure-ad-joined-vm)
- [Store FSLogix profile containers on Azure Files using Microsoft Entra ID](https://learn.microsoft.com/en-us/fslogix/how-to-configure-profile-container-entra-id-hybrid)
- [Enable Microsoft Entra Kerberos authentication for Azure Files](https://learn.microsoft.com/en-us/azure/storage/files/storage-files-identity-auth-hybrid-identities-enable)
- [Assign share-level permissions for Azure Files](https://learn.microsoft.com/en-us/azure/storage/files/storage-files-identity-assign-share-level-permissions)
- [Create and assign an autoscale scaling plan](https://learn.microsoft.com/en-us/azure/virtual-desktop/autoscale-create-assign-scaling-plan)
- [Use Insights to monitor Azure Virtual Desktop](https://learn.microsoft.com/en-us/azure/virtual-desktop/insights)
- [Set up a data collection rule for Azure Monitor Agent](https://learn.microsoft.com/en-us/azure/azure-monitor/vm/data-collection)
