How Atlas AVD Uses WinGet to Deploy, Update, and Audit Software on Every Session Host

Atlas AVD is built around the Windows Package Manager end to end: WinGet installs your apps when a session host is provisioned, a one-click Update button in the console upgrades a package on a live VM, and Atlas reads the WinGet-AutoUpdate logs back off each host so you can see exactly what installed, what failed, and why.

By David,

Deploying software to Azure Virtual Desktop has a reputation for being the tedious part, and honestly it earns it. You build a host. Someone remembers the accounting team also needs the PDF editor. A script gets copied from one host pool to the next until nobody is sure which version is the real one. Three months later you can't answer a question that should be simple: is Chrome patched on every session host?

Atlas AVD handles this differently. It treats the Windows Package Manager (WinGet) as the backbone of the whole software lifecycle instead of a one-off install step. WinGet puts your apps down when a session host is first built, upgrades them from a button in the console, and reads back the logs that every install and upgrade leaves behind, so the console can tell you what actually happened.

This article walks through all three parts: deployment, one-click updates, and log-aware reporting.

The short version: you pick your apps from the WinGet catalog once, Atlas installs them on every host it provisions, and after that you patch individual packages with a single click and see the results (right down to the exit code) without ever opening a remote session.


Why WinGet, specifically

WinGet is Microsoft's own package manager for Windows. It ships in the OS, it knows how to fetch and silently install thousands of mainstream apps, and it understands the difference between "install," "already installed," and "no newer version applies." Building on it means Atlas isn't reinventing an installer engine or asking you to repackage a pile of MSIs. It drives the same tool Microsoft ships, with the guardrails an AVD fleet needs wrapped around it. (WinGet is one of three ways an app can reach a session host; App delivery on AVD compares it with the golden image and App Attach, app type by app type.)

Atlas searches the public WinGet catalog directly, so when you go looking for an app you get live results by package ID (say, Google.Chrome or Microsoft.PowerShell). If the catalog is briefly unreachable, a built-in list of common business apps fills the gap.


1. Deployment: pick apps once, install everywhere

Atlas keeps track of which apps belong on your hosts as a layered set of assignments, so you configure the common stuff once and only get specific where you have to:

  • Global defaults. A baseline list that applies across your whole organization. The deployment wizard sets this up on first run.
  • Tenant software. The Application catalog for a specific Azure tenant, under Global Software (Winget) in the Configuration Matrix.
  • Per-host-pool software. The Application catalog section on a host pool, for apps only that pool needs.

When a new session host is provisioned, Atlas works out the effective set across all three layers (the more specific assignment wins), then installs each package during setup with WinGet running silent and exact. You define the list once, and every host the pool builds afterward comes up already carrying it. No golden-image surgery, no per-host checklist.

Installing on hosts that already exist

Provisioning takes care of new hosts, but you'll also need to push an app to machines that are already running. That's what the Install Apps action on a host pool's session-host list is for. The flow looks like this:

  1. Search the WinGet catalog and pick the apps you want. You can queue a batch across several hosts at once.
  2. Confirm, and Atlas runs the installs on the selected VMs through Azure VM Run Command. It's an agentless channel built into Azure, so there's nothing extra to install on the host to make it work.
  3. Watch the progress per host as it streams into the console.

Since the work runs server-side through Azure and not in your browser tab, a bulk install keeps going even if you close the console. Come back later and you'll see how it finished.

Every install is pinned to the exact package ID with --exact, which matters more than it looks. Without it, a request for "Git" could match a differently named package that just happens to start the same way. Atlas installs the package you actually chose, not a close cousin.


2. One-click updates from the console

Getting software installed is half the job. Keeping it current is the half that tends to slip, so that's where Atlas puts an update within reach. It lives on each VM's device page, in the Installed Software card.

Next to each app, the card shows a Latest column that compares the version reported as installed against the newest one published for that package. When there's a newer version, an Update button shows up beside it. Click it and Atlas does the following:

  • Powers the VM on first if it's deallocated, so you can patch a host that's currently scaled down without starting it by hand.
  • Runs winget upgrade for that exact package, silently, accepting the package and source agreements.
  • Falls back to a fresh install if the upgrade path doesn't apply, so a package WinGet can't upgrade in place still gets brought current instead of left behind.

The button narrates itself as it works, going from Update to Updating… to Updated, and it surfaces a clear error if something goes wrong so you can retry. It's a real one-click patch of a single app on a single host, with no remote desktop session involved.

When does the "Update" button show up? Only when Atlas can see a newer published version, has resolved the package's WinGet ID, and your role has permission to install software on that pool. If a package has no known WinGet identity, Atlas won't offer an update it can't reliably carry out.


3. Log-aware results: reading WinGet's own record

This is the part a lot of tooling skips. Running an installer is easy. Knowing whether it actually worked across a whole fleet is where visibility usually falls apart. Atlas closes that gap by paying attention to what WinGet reports, and it does that at two levels.

Reading exit codes properly, not just "non-zero means bad"

WinGet returns a non-zero exit code in two situations that aren't really failures:

  • The package is already installed and no newer version applies.
  • No applicable upgrade was found because what's installed is already current.

Treat both as errors and you train operators to ignore red that doesn't mean anything. Atlas knows these specific codes mean "already satisfied" and reports them as success, so re-running a deployment that already landed comes back clean instead of throwing noise. Anything outside that known-benign set is still a real failure and still reported as one, which is exactly why a flag from Atlas is worth reading.

Winget History: parsing the log files off each host

For a record that sticks around, Atlas reads the Winget-AutoUpdate (WAU) log files that pile up on a session host and turns them into a structured history you can actually browse. On the VM's device page, the Winget History tab collects those logs (again over Azure Run Command, agentlessly) and breaks them into individual update events.

Instead of dumping raw text on you, the parser reads WAU's log format line by line and pulls out, for each app update:

  • Status. Succeeded, Failed, Skipped, Already Current, or Warning (for instance, an update that worked but needs a reboot).
  • Exit code. Decoded from whichever form WinGet emits, hex HRESULT or decimal, so you see the real result code and not a mangled number.
  • Whether a reboot is required to finish the update.

The Winget History tab lays this out as a filterable table (with Exit Code and Reboot Required columns), charts of what's succeeding and failing over time, and a Raw Logs view for when you want the unfiltered source. A Refresh History button pulls the latest logs on demand. Anything the parser can't confidently classify is kept verbatim rather than quietly dropped, so nothing about a host's patch history gets hidden from you.

Why this matters: "we deployed it" and "it's actually installed and patched on all 40 hosts" are two very different claims. The second one is what auditors, security teams, and your own future self care about, and it's the one WinGet's logs can answer, as long as something bothers to read them.


Built for a multi-tenant, audited world

Atlas is built to run this across production estates, so the WinGet pipeline carries the controls that come with that:

  • Role-based permissions. Installing and updating software is its own permission, separate from just viewing a host pool, so you can let a service desk patch apps without handing over pool configuration.
  • Tenant isolation. Every software operation is scoped to the tenant you're actually working in. A request can't be aimed at a tenant you don't own.
  • A full audit trail. Every install, bulk install, and update writes an audit record of what was asked for and how it turned out. That's the kind of evidence a framework like CMMC expects for change and configuration management.
  • Injection-safe automation. Package IDs and names are validated before they ever land in a generated script, and installs are pinned with --exact, so the automation can't be nudged into running something you didn't ask for.

Where each capability lives

WinGet-based deployment (the provisioning-time installs, the host-pool and tenant software lists, and on-demand Install Apps) is available from the Starter plan up. The deeper Winget History view, with per-host patch history collected from each session host, is part of the Professional plan.

You can see the full breakdown on the pricing page and feature overview.


Try it on your own hosts

The quickest way to see the difference is to point Atlas at a host pool and watch it install, update, and then report back on what it did:

Software on AVD doesn't have to be the tedious part. Once the package manager, the update button, and the audit log are all the same system, "is every host patched?" stops being a question you dread and turns into one you can answer on the spot.