diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj
index 7e9f524056..449671bb30 100644
--- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj
+++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj
@@ -1105,4 +1105,4 @@
-
\ No newline at end of file
+
diff --git a/src/AppInstallerCLITests/InstallFlow.cpp b/src/AppInstallerCLITests/InstallFlow.cpp
index c87be7b62c..0e6b4d3fda 100644
--- a/src/AppInstallerCLITests/InstallFlow.cpp
+++ b/src/AppInstallerCLITests/InstallFlow.cpp
@@ -3,7 +3,10 @@
#include "pch.h"
#include "WorkflowCommon.h"
#include "TestHooks.h"
+#include "AppInstallerRuntime.h"
#include
+#include
+#include
#include
#include
#include
@@ -590,6 +593,41 @@ TEST_CASE("MSStoreInstallFlowWithTestManifest", "[InstallFlow][workflow]")
REQUIRE(installResultStr.find("9WZDNCRFJ364") != std::string::npos);
}
+TEST_CASE("MSStoreInstallFlow_MachineScopeProvision", "[InstallFlow][MSStore]")
+{
+ if (!AppInstaller::Runtime::IsRunningAsAdmin() || AppInstaller::Runtime::IsRunningAsSystem())
+ {
+ WARN("Test requires running as admin but not SYSTEM. Skipped.");
+ return;
+ }
+
+ TestHook::SetForceProvisionAfterInstall_Override forceProvisionOverride(true);
+
+ AppInstaller::ProgressCallback progress;
+ AppInstaller::MSStore::MSStoreOperation installOperation(
+ AppInstaller::MSStore::MSStoreOperationType::Install,
+ L"9NVTPZWRC6KQ",
+ AppInstaller::Manifest::ScopeEnum::User,
+ true,
+ false);
+
+ HRESULT hr = installOperation.StartAndWaitForOperation(progress);
+ REQUIRE(SUCCEEDED(hr));
+
+ // Verify the package is now provisioned.
+ winrt::Windows::Management::Deployment::PackageManager packageManager;
+ bool isProvisioned = false;
+ for (auto const& pkg : packageManager.FindProvisionedPackages())
+ {
+ if (pkg.Id().FamilyName() == L"Microsoft.DesiredStateConfiguration_8wekyb3d8bbwe")
+ {
+ isProvisioned = true;
+ break;
+ }
+ }
+ REQUIRE(isProvisioned);
+}
+
TEST_CASE("MsixInstallFlow_DownloadFlow", "[InstallFlow][workflow]")
{
TestCommon::TempFile installResultPath("TestMsixInstalled.txt");
diff --git a/src/AppInstallerCLITests/TestHooks.h b/src/AppInstallerCLITests/TestHooks.h
index 44c5b2304a..eac856bd48 100644
--- a/src/AppInstallerCLITests/TestHooks.h
+++ b/src/AppInstallerCLITests/TestHooks.h
@@ -100,6 +100,8 @@ namespace AppInstaller
void SetSfsClientAppContents_Override(std::function(std::string_view)>* value);
void SetLicensingHttpPipelineStage_Override(std::shared_ptr value);
+
+ void TestHook_SetProvisionAfterInstall(bool* value);
}
namespace Utility::TestHooks
@@ -320,6 +322,22 @@ namespace TestHook
}
};
+ struct SetForceProvisionAfterInstall_Override
+ {
+ SetForceProvisionAfterInstall_Override(bool value) : m_value(value)
+ {
+ AppInstaller::MSStore::TestHooks::TestHook_SetProvisionAfterInstall(&m_value);
+ }
+
+ ~SetForceProvisionAfterInstall_Override()
+ {
+ AppInstaller::MSStore::TestHooks::TestHook_SetProvisionAfterInstall(nullptr);
+ }
+
+ private:
+ bool m_value;
+ };
+
struct SetDownloadResult_Function_Override
{
SetDownloadResult_Function_Override(std::function& deployOperation,
+ IProgressCallback& callback,
+ bool throwOnError)
+ {
+ return WaitForDeployment(deployOperation, GetDeploymentOperationId(), callback, throwOnError);
+ }
+
void AddPackage(
const winrt::Windows::Foundation::Uri& uri,
const Options& options,
diff --git a/src/AppInstallerCommonCore/MSStore.cpp b/src/AppInstallerCommonCore/MSStore.cpp
index 6b8f458072..29e0d161b5 100644
--- a/src/AppInstallerCommonCore/MSStore.cpp
+++ b/src/AppInstallerCommonCore/MSStore.cpp
@@ -5,6 +5,7 @@
#include
#include
#include
+#include
#include
#include
@@ -14,6 +15,7 @@ namespace AppInstaller::MSStore
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Foundation::Collections;
using namespace winrt::Windows::ApplicationModel::Store::Preview::InstallControl;
+ using namespace winrt::Windows::Management::Deployment;
namespace
{
@@ -213,6 +215,50 @@ namespace AppInstaller::MSStore
std::atomic_bool m_isUpdating = false;
};
+ // After a successful machine-scope Store install, the package may not be provisioned on older
+ // Windows versions (fixed in Windows 11 26100). This helper ensures the package is provisioned
+ // as a best-effort mitigation.
+ void EnsureProvisionedForMachineScope(const IVectorView& installItems, std::wstring_view productId, IProgressCallback& progress) try
+ {
+ PackageManager packageManager;
+ auto provisionedPackages = packageManager.FindProvisionedPackages();
+
+ for (auto const& installItem : installItems)
+ {
+ if (Utility::CaseInsensitiveEquals(installItem.ProductId(), productId))
+ {
+ std::wstring familyName{ installItem.PackageFamilyName() };
+ if (familyName.empty())
+ {
+ continue;
+ }
+
+ bool isProvisioned = false;
+ for (auto const& provisionedPkg : provisionedPackages)
+ {
+ if (provisionedPkg.Id().FamilyName() == familyName)
+ {
+ isProvisioned = true;
+ break;
+ }
+ }
+
+ if (!isProvisioned)
+ {
+ AICLI_LOG(Core, Info, << "Package not provisioned after machine scope install, provisioning: " << Utility::ConvertToUTF8(familyName));
+ try
+ {
+ auto operation = packageManager.ProvisionPackageForAllUsersAsync(familyName);
+ Deployment::WaitForDeployment(operation, progress);
+ AICLI_LOG(Core, Info, << "Successfully provisioned package: " << Utility::ConvertToUTF8(familyName));
+ }
+ CATCH_LOG();
+ }
+ }
+ }
+ }
+ CATCH_LOG();
+
HRESULT WaitForOperation(const std::wstring& productId, bool isSilentMode, IVectorView& installItems, IProgressCallback& progress, const PackageUpdateMonitor& monitor)
{
auto cancelIfOperationFailed = wil::scope_exit(
@@ -321,6 +367,18 @@ namespace AppInstaller::MSStore
}
}
+#ifndef AICLI_DISABLE_TEST_HOOKS
+ namespace TestHooks
+ {
+ static bool* s_ProvisionAfterInstall = nullptr;
+
+ void TestHook_SetProvisionAfterInstall(bool* value)
+ {
+ s_ProvisionAfterInstall = value;
+ }
+ }
+#endif
+
HRESULT MSStoreOperation::InstallPackage(IProgressCallback& progress)
{
PackageUpdateMonitor monitor;
@@ -378,7 +436,25 @@ namespace AppInstaller::MSStore
installOptions).get();
}
- return WaitForOperation(m_productId, m_isSilentMode, installItems, progress, monitor);
+ HRESULT hr = WaitForOperation(m_productId, m_isSilentMode, installItems, progress, monitor);
+
+ bool shouldProvision = m_scope == Manifest::ScopeEnum::Machine;
+#ifndef AICLI_DISABLE_TEST_HOOKS
+ if (TestHooks::s_ProvisionAfterInstall)
+ {
+ shouldProvision = *TestHooks::s_ProvisionAfterInstall;
+ }
+#endif
+
+ if (SUCCEEDED(hr) && shouldProvision)
+ {
+ // Mitigation: on older OS versions (fixed in Windows 11 26100) the Store install service
+ // may not provision the package even when InstallForAllUsers was set.
+ // Explicitly provision if not already provisioned.
+ EnsureProvisionedForMachineScope(installItems, m_productId, progress);
+ }
+
+ return hr;
}
HRESULT MSStoreOperation::UpdatePackage(IProgressCallback& progress)
diff --git a/src/AppInstallerCommonCore/Public/AppInstallerDeployment.h b/src/AppInstallerCommonCore/Public/AppInstallerDeployment.h
index 68f16b80e8..2216abf224 100644
--- a/src/AppInstallerCommonCore/Public/AppInstallerDeployment.h
+++ b/src/AppInstallerCommonCore/Public/AppInstallerDeployment.h
@@ -20,6 +20,12 @@ namespace AppInstaller::Deployment
std::vector> ExpectedDigests;
};
+ // Waits for a deployment operation.
+ HRESULT WaitForDeployment(
+ winrt::Windows::Foundation::IAsyncOperationWithProgress& deployOperation,
+ IProgressCallback& callback,
+ bool throwOnError = true);
+
// Calls winrt::Windows::Management::Deployment::PackageManager::AddPackageAsync if skipSmartScreen is true,
// Otherwise, calls winrt::Windows::Management::Deployment::PackageManager::RequestAddPackageAsync
void AddPackage(