{"id":188,"date":"2026-06-30T09:00:31","date_gmt":"2026-06-30T16:00:31","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/insidemsix\/?p=188"},"modified":"2026-06-28T21:46:29","modified_gmt":"2026-06-29T04:46:29","slug":"msix-per-user-vs-all-users","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/insidemsix\/msix-per-user-vs-all-users\/","title":{"rendered":"MSIX Per-User vs All Users: Install, Provision, and Uninstall Packages"},"content":{"rendered":"<p>MSIX supports making a package available to all users; the formal term is provisioning. Provisioning a package family makes it available to all users, whereas registration makes a package available to a single user.<\/p>\n<h2>Per-User vs All Users<\/h2>\n<p>Traditional installers often provide a &#8220;per-machine&#8221; installation mode to make software available to all users. In MSIX, the equivalent user experience is achieved through provisioning.<\/p>\n<p>To install a package for all users, first stage the package on the machine and then register it for each user. To facilitate this, Deployment manages a &#8216;list of provisioned package families&#8217; and ensures provisioned packages are automatically registered for all users.<\/p>\n<p><strong>NOTE:<\/strong> Deployment provisions package families, not specific packages. But &#8216;list of provisioned package families&#8217; is a mouthful, so it&#8217;s often referred to as the &#8216;list of provisioned packages&#8217;, or simply &#8216;provisioned packages&#8217;. Likewise, &#8216;provisioned package family&#8217; is wordy, thus often referred to as &#8216;provisioned package&#8217;. Regardless of the phrasing, package <strong>families<\/strong> are what&#8217;s provisioned.<\/p>\n<h2>User Experience<\/h2>\n<p>At logon before the desktop appears, Deployment compares the user&#8217;s registered packages against those staged on the machine. If a newer version is present, Deployment registers it for the user.<\/p>\n<p>The provisioned list is also checked at this time. Deployment registers the latest version in each provisioned family if the user has no packages in that family, i.e. add the package for the user. This ensures users have all provisioned packages registered and available for use, regardless of whether it&#8217;s a new user&#8217;s first logon or previously existing user&#8217;s Nth logon.<\/p>\n<h3>How does provisioning work?<\/h3>\n<p>At logon, Deployment determines what the user <em>should<\/em> have versus what they <em>currently<\/em> have, and updates accordingly. The logic amounts to:<\/p>\n<pre><code>Package[] todo = new Package[]\n\n\/\/ Are there newer versions of packages the user has registered?\nPackage[] current = PackageManager.FindPackagesForUser(\"\")\nFOREACH (pcurrent IN current)\n  Package ptodo = null\n\n  \/\/ What packages in the family exist on the machine?\n  FOREACH (p IN PackageManager.FindPackages(p))\n\n    \/\/ Is this package newer than the registered packaged?\n    IF p.Version &gt; pcurrent.Version\n\n      \/\/ Is this package a newer version than the previously found package (if any)\n      IF (ptodo == null) OR (p.Version &gt; ptodo.Version)\n        \/\/ New 'better' fit\n        ptodo = p\n    ENDIF\n  ENDIF\n\n  \/\/ Did we find a newer package we should register?\n  IF (ptodo != null)\n    todo += ptodo\n  ENDIF\nNEXT\n\n\/\/ Any provisioned package families the user doesn't have registered?\nPackage[] provisioned = PackageManager.FindProvisionedPackages()\nFOREACH (p IN provisioned)\n  IF (p.PackageFamilyName NOT IN current)\n    pmax = FindHighestVersionPackageInFamily(p.PackageFamilyName)\n    todo += pmax\n  ENDIF\nNEXT\n\n\/\/ Register the newer and provisioned packages the user should have but doesn't\nFOREACH (p IN todo)\n  PackageDeploymentManager.RegisterPackage(p)\nNEXT\n<\/code><\/pre>\n<p>Deployment determines the desired packages the user <em>should<\/em> have by comparing the provisioned list plus packages on the machine versus packages registered for the user:<\/p>\n<ul>\n<li>If a desired package is registered => Nothing to do<\/li>\n<li>If an older version is registered => Register the desired (newer) package (aka update)<\/li>\n<li>If no package in the family is registered => Register the desired package (aka add)<\/li>\n<\/ul>\n<p>Deployment updates users to the latest version of provisioned packages during user logon, before the desktop appears. From the user&#8217;s perspective they&#8217;re automagically updated to the latest version of their installed software.<\/p>\n<h2>HOWTO Install Per-User vs All Users<\/h2>\n<p>Installation for all users differs from installation for the current user.<\/p>\n<h3>Per-User<\/h3>\n<p>A package needs to be staged and registered to be accessed by a user. This can be simplified as an Add operation, as explained in <a href=\"https:\/\/devblogs.microsoft.com\/insidemsix\/there-is-no-install-its-stage-and-register\/\">There is no Install \u2013 it\u2019s &#8216;Stage&#8217; and &#8216;Register&#8217;<\/a> and <a href=\"https:\/\/devblogs.microsoft.com\/insidemsix\/add-vs-stage-and-register\/\">Add vs Stage and Register<\/a>.<\/p>\n<pre><code class=\"csharp\">var packageDeploymentManager = new Microsoft.Windows.Management.Deployment.PackageDeploymentManager();\nvar packageUri = new Uri(\"https:\/\/contoso.com\/ContosoParts-v1.2.3.4-x64.msix\");\nvar options = new Microsoft.Windows.Management.Deployment.AddPackageOptions();\nvar result = await packageManager.AddPackageByUriAsync(packageUri, options);\n<\/code><\/pre>\n<h3>All Users<\/h3>\n<p>You must stage a package on the system before you can provision it. For a new package family not yet present on a system this requires staging a package and then provisioning its package family via <a href=\"https:\/\/learn.microsoft.com\/windows\/windows-app-sdk\/api\/winrt\/microsoft.windows.management.deployment.packagedeploymentmanager.provisionpackageasync\">ProvisionPackageAsync()<\/a>:<\/p>\n<pre><code class=\"csharp\">var packageDeploymentManager = new Microsoft.Windows.Management.Deployment.PackageDeploymentManager();\nvar packageUri = new Uri(\"https:\/\/contoso.com\/ContosoParts-v1.2.3.4-x64.msix\");\nvar options = new Microsoft.Windows.Management.Deployment.StagePackageOptions();\nvar result = await packageDeploymentManager.StagePackageByUriAsync(packageUri, options);\n\nstring packageFamilyName = \"Contoso.Parts_1234567890abc\";\nresult = await packageDeploymentManager.ProvisionPackageAsync(packageFamilyName);\n<\/code><\/pre>\n<p><code>.ProvisionPackageAsync()<\/code> will&#8230;<\/p>\n<ol>\n<li>Add the package family to the provisioned list<\/li>\n<li>Register the package for the current user, if necessary AND current user is not LocalSystem<sup>1<\/sup><\/li>\n<\/ol>\n<p>Deployment registers the newly provisioned package family for other users at their next logon.<\/p>\n<h2>HOWTO Uninstall Per-User vs All Users<\/h2>\n<p>Uninstall for all users vs current user is similar to installation.<\/p>\n<h3>Per-User<\/h3>\n<p>Uninstalling a package for a user is a simple Remove operation.<\/p>\n<pre><code class=\"csharp\">var packageDeploymentManager = new Microsoft.Windows.Management.Deployment.PackageDeploymentManager();\nstring packageFamilyName = \"Contoso.Parts_1234567890abc\";\nvar options = new Microsoft.Windows.Management.Deployment.RemovePackageOptions();\nvar result = await packageDeploymentManager.RemovePackageAsync(packageFamilyName, options);\n<\/code><\/pre>\n<p><code>RemovePackage*Async()<\/code> has multiple method overloads. The simplest is to remove by package family name, as above.<\/p>\n<p>Alternatively, one could specify a PackageFullName for a specific package to remove, but the package may have been updated since the initial install. <code>RemovePackageAsync(pkgfullname,...)<\/code> will fail if the exact package is not currently registered.<\/p>\n<p>Remove operations implicitly include <a href=\"https:\/\/learn.microsoft.com\/windows\/windows-app-sdk\/api\/winrt\/microsoft.windows.management.deployment.addpackageoptions.forcetargetappshutdown\">ForceTargetAppShutdown<\/a>, causing running processes to quit, so the package can be cleanly deregistered.<\/p>\n<p>In the simplest case there&#8217;s (now) no references to the package, so it will also be destaged.<\/p>\n<h3>All Users<\/h3>\n<p>To remove a package for all users, first remove the package family from the provisioned list via <a href=\"https:\/\/learn.microsoft.com\/windows\/windows-app-sdk\/api\/winrt\/microsoft.windows.management.deployment.packagedeploymentmanager.deprovisionpackageasync\">DeprovisionPackageAsync()<\/a> AND deregistered for all users. The latter requires <a href=\"https:\/\/learn.microsoft.com\/windows\/windows-app-sdk\/api\/winrt\/microsoft.windows.management.deployment.removepackageoptions.removeforallusers\">RemoveForAllUsers<\/a> option.<\/p>\n<pre><code class=\"csharp\">var packageDeploymentManager = new Microsoft.Windows.Management.Deployment.PackageDeploymentManager();\nstring packageFamilyName = \"Contoso.Parts_1234567890abc\";\nresult = await packageDeploymentManager.DeprovisionPackageAsync(packageFamilyName);\n\nvar packageDeploymentManager = new Microsoft.Windows.Management.Deployment.PackageDeploymentManager();\nvar options = new Microsoft.Windows.Management.Deployment.RemovePackageOptions();\noptions.RemoveForAllUsers = true;\nvar result = await packageDeploymentManager.RemovePackageByFamilyNameAsync(packageFamilyName, options);\n<\/code><\/pre>\n<p><strong><em>WARNING<\/em><\/strong>: <code>.Deprovision...()<\/code> should be called before <code>.Remove...()<\/code>. If not, new (unexpected) registrations could occur after <code>.Remove...()<\/code> but before <code>.Deprovision...()<\/code> removes the package family from the provisioned list.<\/p>\n<h2>Do I need to reprovision after staging a newer package?<\/h2>\n<p>If v1 is provisioned and I stage v2, do I need to reprovision?<\/p>\n<p>No.<\/p>\n<p>Provisioning tracks package families, not individual packages. You do not provision a specific package version; you provision a package family. As a result, newer versions do not need to be reprovisioned. Simply stage the newer package. Because provisioning tracks package families rather than package versions, Deployment will register the highest available version in the family at user logon.<\/p>\n<h2>Security<\/h2>\n<p>MSIX allows users to query and change their package inventory, but requires admin privilege to see or impact other users or machine-wide data.<\/p>\n<p>Thus <a href=\"https:\/\/learn.microsoft.com\/windows\/windows-app-sdk\/api\/winrt\/microsoft.windows.management.deployment.packagedeploymentmanager.provisionpackageasync\">ProvisionPackageAsync()<\/a>, <a href=\"https:\/\/learn.microsoft.com\/windows\/windows-app-sdk\/api\/winrt\/microsoft.windows.management.deployment.packagedeploymentmanager.deprovisionpackageasync\">DeprovisionPackageAsync()<\/a> and <a href=\"https:\/\/learn.microsoft.com\/windows\/windows-app-sdk\/api\/winrt\/microsoft.windows.management.deployment.removepackageoptions.removeforallusers\">RemoveForAllUsers<\/a> require admin privilege.<\/p>\n<h2>Recap<\/h2>\n<p>The canonical operations are:<\/p>\n<ul>\n<li><strong>Per-user:<\/strong> Add \u2192 Remove<\/li>\n<li><strong>All users:<\/strong> Stage + Provision \u2192 Deprovision + RemoveForAllUsers<\/li>\n<\/ul>\n<p>Provisioning is the MSIX mechanism that provides the traditional &#8220;install for all users&#8221; experience.<\/p>\n<hr \/>\n<p><sup>1<\/sup> Deployment can register packages for any user except LocalSystem. This is a known limitation, and lifting this restriction is <a href=\"https:\/\/task.ms\/30901666\">tracked in our backlog<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>MSIX supports making a package available to all users; the formal term is provisioning. Provisioning a package family makes it available to all users, whereas registration makes a package available to a single user. Per-User vs All Users Traditional installers often provide a &#8220;per-machine&#8221; installation mode to make software available to all users. In MSIX, [&hellip;]<\/p>\n","protected":false},"author":911,"featured_media":101,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[9,3,16,2,14,5,15,4],"class_list":["post-188","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-msix","tag-code","tag-deployment","tag-deprovision","tag-msix","tag-provision","tag-register","tag-remove","tag-stage"],"acf":[],"blog_post_summary":"<p>MSIX supports making a package available to all users; the formal term is provisioning. Provisioning a package family makes it available to all users, whereas registration makes a package available to a single user. Per-User vs All Users Traditional installers often provide a &#8220;per-machine&#8221; installation mode to make software available to all users. In MSIX, [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/insidemsix\/wp-json\/wp\/v2\/posts\/188","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/insidemsix\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/insidemsix\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/insidemsix\/wp-json\/wp\/v2\/users\/911"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/insidemsix\/wp-json\/wp\/v2\/comments?post=188"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/insidemsix\/wp-json\/wp\/v2\/posts\/188\/revisions"}],"predecessor-version":[{"id":199,"href":"https:\/\/devblogs.microsoft.com\/insidemsix\/wp-json\/wp\/v2\/posts\/188\/revisions\/199"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/insidemsix\/wp-json\/wp\/v2\/media\/101"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/insidemsix\/wp-json\/wp\/v2\/media?parent=188"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/insidemsix\/wp-json\/wp\/v2\/categories?post=188"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/insidemsix\/wp-json\/wp\/v2\/tags?post=188"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}