You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Installing Python install manager MSIX for all users #119
There's a lack of clear information online about how best to install an MSIX for all users of a machine, and a number of approaches that seem to half work.
This issue is to attract discussion until we can figure out the best ways and document them.
So far I believe (but haven't tested at scale) that the best way to do this is to create a Scheduled Task that runs for all users at login that launches:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -Command Add-AppxPackage <path to MSIX>
Or alternatively (without needing a pre-downloaded MSIX):
It unfortunately pops up a command window briefly, but otherwise it works best for ensuring all the aliases are set correctly and it's ready for the user to use. It doesn't seem to have any problems running again if it's already installed, and won't downgrade (though it will fail in this case).
A close second approach so far is the Add-AppxProvisionedPackage PowerShell command, which when run as admin with an MSIX file, will provision the package and enable auto-install for all users next time they log in:
Unfortunately it seems to have a bug right now where it doesn't correctly replace existing python.exe and python3.exe aliases, even though it reports that they've been updated. Users don't get their first-run experience in this model (unless they do py install --configure) and so won't find out. The other aliases seem to be okay.
Any other approaches? Has anyone tried using any of the management/deployment tools?
MSIX supports installing a package for all users; the formal term is you 'provision' the package. This makes the package available to all users, compared to registering a package for a user. Here's the definitive way to install + uninstall a package 'per user' and 'for all users'. The simple explanation is...
For a more detailed explanation keep reading, but remember, you asked :P If your coffee's low I suggest a refill before proceeding...
Here's some code snippets with the key APIs. I'l use C# syntax as I'm not as familiar with pywinrt's projections but that's just syntax. It's the APIs that matter:
The APIs have multiple overloads for behavioral variations but this is representative enough.
Explanation
It helps to understand 'install' isn't actually a proper noun for MSIX. To oversimplify...
Stage = Lay down the package content on the disk appropriately. Suuuuper oversimplistically: MKDIR pkgdir & UNZIP package.msix to pkgdir & ACL pkgdir
Register = Wire up the user profile for the package (create tiles on StartMenu, define file type associations, yadda yadda)
Stage is done once per machine, and when complete the files are readable but only the Deployment pipeline has write access. This lets us share the files across all users (no need for unique copies of files).
OTOH Register is a per-user action. To "install" a package for 5 users, ultimately, the package is staged once and registered 5x.
"Add" is a common operation that does both "Stage this package if necessary + Register it for the user".
This requires no special permissions, other than the API is called by a Medium IL process (or an AppContainer with the packageManagement capability).
To 'uninstall' you Remove the package which will Deregister it for the current user and, if there's no other references on the package, it's Destaged (i.e. removed from the system).
Package Lifetime and References
MSIX uses a GC style model for package lifetime. A package can be removed only if there are no 'references' to the package. There's several ways to define a reference e.g. Register'ing a package for a user. If a package is registered for 2 users and one Removes the package it's no longer Registered for that user but can't be removed from disk without breaking the other Registered user. When the 2nd user Removes the package its Deregistered and if that's the last reference on the package it's also automagically removed from disk.
There's several ways a 'reference' can be defined on a package. Register is one. Being listed in the Provisioned list of packages is another. There's a few other ways but this is beyond the scope of this discussion. Just know MSIX uses GC style semantics so packages will be removed from disk when the last reference is removed.
AllUser Explanation
Windows has a list of provision packages, alterable with De/Provision APIs. This list affects more than just the current user so altering it requires admin privilege (aka elevation).
Notice the order of operations. To install we must Stage then Provision because Provision takes a package family name for a package known to Windows at this point. Windows knows about packages once Staged so you can't (successfully) Provision a package before it's Staged (or Added).
Provisioned packages are registered for users at the next opportunity, usually their next login. At login Windows compares what a user has registered vs what the should have e.g. the Provisioned package list has the Foo package but when Bob logs in he doesn't have Foo registered, so Windows registers it for him during his login. This is why Calculator and other provisioned packages show up on everyone's StartMenu despite never having done anything to specifically request installation.
On the flipside, during uninstall we Deprovision the package first, to make sure no new user gets it Registered (i.e. avoid timing/race conditions). Deprovisioning the package removes it from the Provisioned list. That's all. There's no change to user Registration, it's simply no longer in the list so new users don't get it offered to them.
Then once Deprovisioned we call Remove with the RemoveForAllUsers option. This deregisters the package for the current user, and other users logged in. IF a package is registered for a user and they're not logged in, the remove request is tracked for that user and processed when they next login.*
* Registering a package requires a user be logged in because various resources and APIs used during registration are only available and functional while the user's logged in. And for those thinking of HKCU yes that's one reason, but the simplest one. There's other more complex interactions which can't be done without the user being logged in.
Powershell Cmdlets
You can do this via the command line using Powershell cmdlets
The *-AppxProvisionedPackage cmdlets require admin privilege e.g. run from a command run you launched as an admin prompt.
P.S. The cmdlets also support offline operation against a Windows image. You can also un/install packages via DISM.EXE, various other tools or (for the adventurous) use the WinRT API (above) from Powershell v5 via WinRT invocation. In the end it largely boils down to the same thing as these are functional equivalents to the PackageManager API (and in the online cases, are often literally thin wrappers over the API).
This is the canonical and recommended solution to make a package available for all users.
Unfortunately it seems to have a bug right now where it doesn't correctly replace existing python.exe and python3.exe aliases, even though it reports that they've been updated.
You misunderstand - that's a feature, not a bug.
AppExecutionAliases follow FirstWriterWins semantics. When you install e.g. Python 3.12 as the first Python on the machine the AppExecutionAlias python.exe is created for the 3.12 package and, since it's not already defined for the user, it's enabled. Now install Python 3.13 and a python.exe alias is created for the 3.13 package but, since there's already a 'python.exe' alias the new one is NOT enabled.
Same is true of python3.exe - first installed for a user is enabled, 2nd+ are defined but user must explicitly enable.
You can go into Settings > Apps > Advanced app settings > App execution aliases to see which aliases are defined, which are active, disable one if you like, and if there's multiple identical aliases you can choose which one is enabled. That's why you can have 7 versions of Python install with 7 instances of python.exe but of course python.exe will only launch one of them (or non, if they're all disabled).
If you desire unique aliases for each version of Python you'll need unique aliases e.g. Python 3.12 can define python312.exe, Python 3.13 python313.exe etc (in addition to the shared python.exe and python3.exe)
AppExecutionAliases follow FirstWriterWins semantics. When you install e.g. Python 3.12 as the first Python on the machine the AppExecutionAlias python.exe is created for the 3.12 package and, since it's not already defined for the user, it's enabled. Now install Python 3.13 and a python.exe alias is created for the 3.13 package but, since there's already a 'python.exe' alias the new one is NOT enabled.
There's an additional feature known as uap8:AllowOverride that is set on the redirector's aliases (we added it to the OS specifically for this scenario), and it disables the semantics you describe. That flag is being ignored for a system-wide provision, but it works great for a per-user install. The inconsistency there is the bug.
the best way to do this is to create a Scheduled Task that runs for all users at login that launches:
Ick! No, you don't need to do that.
If your only hammer is Group Policy, what's the better option than this? (I know it's not the only hammer, but I keep encountering "IT" "Professionals" who believe it is, and there's only so much arguing I can do with each of them...)
There's an additional feature known as uap8:AllowOverride that is set on the redirector's aliases (we added it to the OS specifically for this scenario), and it disables the semantics you describe.
Ahhh. You learn something new every day :-)
That flag is being ignored for a system-wide provision, but it works great for a per-user install. The inconsistency there is the bug.
There's a lack of clear information online about how best to install an MSIX for all users of a machine, and a number of approaches that seem to half work.
This issue is to attract discussion until we can figure out the best ways and document them.
So far I believe (but haven't tested at scale) that the best way to do this is to create a Scheduled Task that runs for all users at login that launches:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -Command Add-AppxPackage <path to MSIX>
...
It unfortunately pops up a command window briefly...
Here's a way around this. AddAppXPackage.js:
var Args = WScript.Arguments;
if ( Args.Unnamed.Count == 0 ) {
WScript.Echo("Usage: AddAppXPackage.js <MsixFileName>");
WScript.Quit();
}
function scriptPath() {
return WScript.ScriptFullName.substring(0, WScript.ScriptFullName.length -
WScript.ScriptName.length);
}
var FSO = new ActiveXObject("Scripting.FileSystemObject");
var WshShell = new ActiveXObject("WScript.Shell");
var MsixFileName = Args.Unnamed.Item(0);
// Assume script's directory if not specified
if ( MsixFileName.indexOf("\\") == -1 ) {
MsixFileName = FSO.BuildPath(scriptPath(), MsixFileName);
}
if ( ! FSO.FileExists(MsixFileName) ) {
WScript.Echo("File not found: " + MsixFileName);
WScript.Quit(2); // ERROR_FILE_NOT_FOUND
}
// FSO.GetSpecialFolder(1) is System32 directory
var PowerShell = FSO.BuildPath(FSO.GetSpecialFolder(1),
"WindowsPowerShell\\v1.0\\powershell.exe");
var CommandLine = PowerShell + " " +
"-NoProfile -NonInteractive -Command Add-AppxPackage " +
"'" + MsixFileName + "'";
// 0 = run hidden; false = run asynchronously
WshShell.Run(CommandLine, 0, false);
Run this using wscript.exe and the MSIX filename as a parameter. You can omit the path of the MSIX file (specify just its name) if the MSIX file sits in the same directory as AddAppxPackage.js.
Running this script with wscript.exe means there's no initial console/terminal window, and the script itself executes powershell.exe in a hidden window.
Is there some way us non-Microsoft folks can see the status of this issue?
Afraid not, you just need to poke someone with access and ask them to check. But it makes it easier to connect public issues with the internal work so they don't get lost so easily.
Activity
zooba commentedon May 21, 2025
So far I believe (but haven't tested at scale) that the best way to do this is to create a Scheduled Task that runs for all users at login that launches:
Or alternatively (without needing a pre-downloaded MSIX):
It unfortunately pops up a command window briefly, but otherwise it works best for ensuring all the aliases are set correctly and it's ready for the user to use. It doesn't seem to have any problems running again if it's already installed, and won't downgrade (though it will fail in this case).
A close second approach so far is the
Add-AppxProvisionedPackagePowerShell command, which when run as admin with an MSIX file, will provision the package and enable auto-install for all users next time they log in:Unfortunately it seems to have a bug right now where it doesn't correctly replace existing
python.exeandpython3.exealiases, even though it reports that they've been updated. Users don't get their first-run experience in this model (unless they dopy install --configure) and so won't find out. The other aliases seem to be okay.Any other approaches? Has anyone tried using any of the management/deployment tools?
davidldennison commentedon Jul 19, 2025
Why would they do this to us 😂 😂 😂
DrusTheAxe commentedon Jul 21, 2025
MSIX supports installing a package for all users; the formal term is you 'provision' the package. This makes the package available to all users, compared to registering a package for a user. Here's the definitive way to install + uninstall a package 'per user' and 'for all users'. The simple explanation is...
For a more detailed explanation keep reading, but remember, you asked :P If your coffee's low I suggest a refill before proceeding...
Here's some code snippets with the key APIs. I'l use C# syntax as I'm not as familiar with pywinrt's projections but that's just syntax. It's the APIs that matter:
Per-User
Install
Uninstall
All Users
Install
Uninstall
The APIs have multiple overloads for behavioral variations but this is representative enough.
Explanation
It helps to understand 'install' isn't actually a proper noun for MSIX. To oversimplify...
MKDIR pkgdir & UNZIP package.msix to pkgdir & ACL pkgdirStage is done once per machine, and when complete the files are readable but only the Deployment pipeline has write access. This lets us share the files across all users (no need for unique copies of files).
OTOH Register is a per-user action. To "install" a package for 5 users, ultimately, the package is staged once and registered 5x.
"Add" is a common operation that does both "Stage this package if necessary + Register it for the user".
This requires no special permissions, other than the API is called by a Medium IL process (or an AppContainer with the packageManagement capability).
To 'uninstall' you Remove the package which will Deregister it for the current user and, if there's no other references on the package, it's Destaged (i.e. removed from the system).
Package Lifetime and References
MSIX uses a GC style model for package lifetime. A package can be removed only if there are no 'references' to the package. There's several ways to define a reference e.g. Register'ing a package for a user. If a package is registered for 2 users and one Removes the package it's no longer Registered for that user but can't be removed from disk without breaking the other Registered user. When the 2nd user Removes the package its Deregistered and if that's the last reference on the package it's also automagically removed from disk.
There's several ways a 'reference' can be defined on a package. Register is one. Being listed in the Provisioned list of packages is another. There's a few other ways but this is beyond the scope of this discussion. Just know MSIX uses GC style semantics so packages will be removed from disk when the last reference is removed.
AllUser Explanation
Windows has a list of provision packages, alterable with De/Provision APIs. This list affects more than just the current user so altering it requires admin privilege (aka elevation).
Notice the order of operations. To install we must Stage then Provision because Provision takes a package family name for a package known to Windows at this point. Windows knows about packages once Staged so you can't (successfully) Provision a package before it's Staged (or Added).
Provisioned packages are registered for users at the next opportunity, usually their next login. At login Windows compares what a user has registered vs what the should have e.g. the Provisioned package list has the
Foopackage but when Bob logs in he doesn't have Foo registered, so Windows registers it for him during his login. This is why Calculator and other provisioned packages show up on everyone's StartMenu despite never having done anything to specifically request installation.On the flipside, during uninstall we Deprovision the package first, to make sure no new user gets it Registered (i.e. avoid timing/race conditions). Deprovisioning the package removes it from the Provisioned list. That's all. There's no change to user Registration, it's simply no longer in the list so new users don't get it offered to them.
Then once Deprovisioned we call Remove with the RemoveForAllUsers option. This deregisters the package for the current user, and other users logged in. IF a package is registered for a user and they're not logged in, the remove request is tracked for that user and processed when they next login.*
* Registering a package requires a user be logged in because various resources and APIs used during registration are only available and functional while the user's logged in. And for those thinking of HKCU yes that's one reason, but the simplest one. There's other more complex interactions which can't be done without the user being logged in.
Powershell Cmdlets
You can do this via the command line using Powershell cmdlets
Per-User
Install
Add-AppxPackage c:\blah\foo.msixUninstall
All Users
Install
Uninstall
The
*-AppxProvisionedPackagecmdlets require admin privilege e.g. run from a command run you launched as an admin prompt.P.S. The cmdlets also support offline operation against a Windows image. You can also un/install packages via DISM.EXE, various other tools or (for the adventurous) use the WinRT API (above) from Powershell v5 via WinRT invocation. In the end it largely boils down to the same thing as these are functional equivalents to the PackageManager API (and in the online cases, are often literally thin wrappers over the API).
DrusTheAxe commentedon Jul 21, 2025
Ick! No, you don't need to do that.
This is the canonical and recommended solution to make a package available for all users.
You misunderstand - that's a feature, not a bug.
AppExecutionAliases follow FirstWriterWins semantics. When you install e.g. Python 3.12 as the first Python on the machine the AppExecutionAlias
python.exeis created for the 3.12 package and, since it's not already defined for the user, it's enabled. Now install Python 3.13 and apython.exealias is created for the 3.13 package but, since there's already a 'python.exe' alias the new one is NOT enabled.Same is true of
python3.exe- first installed for a user is enabled, 2nd+ are defined but user must explicitly enable.You can go into Settings > Apps > Advanced app settings > App execution aliases to see which aliases are defined, which are active, disable one if you like, and if there's multiple identical aliases you can choose which one is enabled. That's why you can have 7 versions of Python install with 7 instances of
python.exebut of coursepython.exewill only launch one of them (or non, if they're all disabled).If you desire unique aliases for each version of Python you'll need unique aliases e.g. Python 3.12 can define
python312.exe, Python 3.13python313.exeetc (in addition to the sharedpython.exeandpython3.exe)zooba commentedon Jul 28, 2025
There's an additional feature known as
uap8:AllowOverridethat is set on the redirector's aliases (we added it to the OS specifically for this scenario), and it disables the semantics you describe. That flag is being ignored for a system-wide provision, but it works great for a per-user install. The inconsistency there is the bug.If your only hammer is Group Policy, what's the better option than this? (I know it's not the only hammer, but I keep encountering "IT" "Professionals" who believe it is, and there's only so much arguing I can do with each of them...)
DrusTheAxe commentedon Jul 28, 2025
Ahhh. You learn something new every day :-)
:-(
Known issue
https://task.ms/57641304
monster739 commentedon Jun 6, 2026
Bill-Stewart commentedon Jun 16, 2026
It seems that this link is not available to users that don't have an account (or aren't an external user) in Microsoft's Azure tenant.
Is there some way us non-Microsoft folks can see the status of this issue?
Bill-Stewart commentedon Jun 16, 2026
Here's a way around this. AddAppXPackage.js:
Run this using
wscript.exeand the MSIX filename as a parameter. You can omit the path of the MSIX file (specify just its name) if the MSIX file sits in the same directory as AddAppxPackage.js.Running this script with
wscript.exemeans there's no initial console/terminal window, and the script itself executespowershell.exein a hidden window.zooba commentedon Jun 18, 2026
Afraid not, you just need to poke someone with access and ask them to check. But it makes it easier to connect public issues with the internal work so they don't get lost so easily.