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 “per-machine” installation mode to make software available to all users. In MSIX, the equivalent user experience is achieved through provisioning.
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 ‘list of provisioned package families’ and ensures provisioned packages are automatically registered for all users.
NOTE: Deployment provisions package families, not specific packages. But ‘list of provisioned package families’ is a mouthful, so it’s often referred to as the ‘list of provisioned packages’, or simply ‘provisioned packages’. Likewise, ‘provisioned package family’ is wordy, thus often referred to as ‘provisioned package’. Regardless of the phrasing, package families are what’s provisioned.
User Experience
At logon before the desktop appears, Deployment compares the user’s registered packages against those staged on the machine. If a newer version is present, Deployment registers it for the user.
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’s a new user’s first logon or previously existing user’s Nth logon.
How does provisioning work?
At logon, Deployment determines what the user should have versus what they currently have, and updates accordingly. The logic amounts to:
Package[] todo = new Package[]
// Are there newer versions of packages the user has registered?
Package[] current = PackageManager.FindPackagesForUser("")
FOREACH (pcurrent IN current)
Package ptodo = null
// What packages in the family exist on the machine?
FOREACH (p IN PackageManager.FindPackages(p))
// Is this package newer than the registered packaged?
IF p.Version > pcurrent.Version
// Is this package a newer version than the previously found package (if any)
IF (ptodo == null) OR (p.Version > ptodo.Version)
// New 'better' fit
ptodo = p
ENDIF
ENDIF
// Did we find a newer package we should register?
IF (ptodo != null)
todo += ptodo
ENDIF
NEXT
// Any provisioned package families the user doesn't have registered?
Package[] provisioned = PackageManager.FindProvisionedPackages()
FOREACH (p IN provisioned)
IF (p.PackageFamilyName NOT IN current)
pmax = FindHighestVersionPackageInFamily(p.PackageFamilyName)
todo += pmax
ENDIF
NEXT
// Register the newer and provisioned packages the user should have but doesn't
FOREACH (p IN todo)
PackageDeploymentManager.RegisterPackage(p)
NEXT
Deployment determines the desired packages the user should have by comparing the provisioned list plus packages on the machine versus packages registered for the user:
- If a desired package is registered => Nothing to do
- If an older version is registered => Register the desired (newer) package (aka update)
- If no package in the family is registered => Register the desired package (aka add)
Deployment updates users to the latest version of provisioned packages during user logon, before the desktop appears. From the user’s perspective they’re automagically updated to the latest version of their installed software.
HOWTO Install Per-User vs All Users
Installation for all users differs from installation for the current user.
Per-User
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 There is no Install – it’s ‘Stage’ and ‘Register’ and Add vs Stage and Register.
var packageDeploymentManager = new Microsoft.Windows.Management.Deployment.PackageDeploymentManager();
var packageUri = new Uri("https://contoso.com/ContosoParts-v1.2.3.4-x64.msix");
var options = new Microsoft.Windows.Management.Deployment.AddPackageOptions();
var result = await packageManager.AddPackageByUriAsync(packageUri, options);
All Users
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 ProvisionPackageAsync():
var packageDeploymentManager = new Microsoft.Windows.Management.Deployment.PackageDeploymentManager();
var packageUri = new Uri("https://contoso.com/ContosoParts-v1.2.3.4-x64.msix");
var options = new Microsoft.Windows.Management.Deployment.StagePackageOptions();
var result = await packageDeploymentManager.StagePackageByUriAsync(packageUri, options);
string packageFamilyName = "Contoso.Parts_1234567890abc";
result = await packageDeploymentManager.ProvisionPackageAsync(packageFamilyName);
.ProvisionPackageAsync() will…
- Add the package family to the provisioned list
- Register the package for the current user, if necessary AND current user is not LocalSystem1
Deployment registers the newly provisioned package family for other users at their next logon.
HOWTO Uninstall Per-User vs All Users
Uninstall for all users vs current user is similar to installation.
Per-User
Uninstalling a package for a user is a simple Remove operation.
var packageDeploymentManager = new Microsoft.Windows.Management.Deployment.PackageDeploymentManager();
string packageFamilyName = "Contoso.Parts_1234567890abc";
var options = new Microsoft.Windows.Management.Deployment.RemovePackageOptions();
var result = await packageDeploymentManager.RemovePackageAsync(packageFamilyName, options);
RemovePackage*Async() has multiple method overloads. The simplest is to remove by package family name, as above.
Alternatively, one could specify a PackageFullName for a specific package to remove, but the package may have been updated since the initial install. RemovePackageAsync(pkgfullname,...) will fail if the exact package is not currently registered.
Remove operations implicitly include ForceTargetAppShutdown, causing running processes to quit, so the package can be cleanly deregistered.
In the simplest case there’s (now) no references to the package, so it will also be destaged.
All Users
To remove a package for all users, first remove the package family from the provisioned list via DeprovisionPackageAsync() AND deregistered for all users. The latter requires RemoveForAllUsers option.
var packageDeploymentManager = new Microsoft.Windows.Management.Deployment.PackageDeploymentManager();
string packageFamilyName = "Contoso.Parts_1234567890abc";
result = await packageDeploymentManager.DeprovisionPackageAsync(packageFamilyName);
var packageDeploymentManager = new Microsoft.Windows.Management.Deployment.PackageDeploymentManager();
var options = new Microsoft.Windows.Management.Deployment.RemovePackageOptions();
options.RemoveForAllUsers = true;
var result = await packageDeploymentManager.RemovePackageByFamilyNameAsync(packageFamilyName, options);
WARNING: .Deprovision...() should be called before .Remove...(). If not, new (unexpected) registrations could occur after .Remove...() but before .Deprovision...() removes the package family from the provisioned list.
Do I need to reprovision after staging a newer package?
If v1 is provisioned and I stage v2, do I need to reprovision?
No.
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.
Security
MSIX allows users to query and change their package inventory, but requires admin privilege to see or impact other users or machine-wide data.
Thus ProvisionPackageAsync(), DeprovisionPackageAsync() and RemoveForAllUsers require admin privilege.
Recap
The canonical operations are:
- Per-user: Add → Remove
- All users: Stage + Provision → Deprovision + RemoveForAllUsers
Provisioning is the MSIX mechanism that provides the traditional “install for all users” experience.
1 Deployment can register packages for any user except LocalSystem. This is a known limitation, and lifting this restriction is tracked in our backlog.
The post MSIX Per-User vs All Users: Install, Provision, and Uninstall Packages appeared first on Inside MSIX.