diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj
index 5c6730fd72..30b40e6999 100644
--- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj
+++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj
@@ -302,6 +302,7 @@
+
@@ -390,6 +391,7 @@
+
diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters
index 29ce6a719a..ce771bdb4e 100644
--- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters
+++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters
@@ -290,6 +290,9 @@
Commands\Configuration
+
+ Commands\Configuration
+
@@ -547,6 +550,9 @@
Commands\Configuration
+
+ Commands\Configuration
+
diff --git a/src/AppInstallerCLICore/Commands/DscAdminSettingsResource.cpp b/src/AppInstallerCLICore/Commands/DscAdminSettingsResource.cpp
new file mode 100644
index 0000000000..761ee8d523
--- /dev/null
+++ b/src/AppInstallerCLICore/Commands/DscAdminSettingsResource.cpp
@@ -0,0 +1,294 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+#include "pch.h"
+#include "DscAdminSettingsResource.h"
+#include "DscComposableObject.h"
+#include "Resources.h"
+#include
+
+using namespace AppInstaller::Utility::literals;
+using namespace AppInstaller::Repository;
+
+namespace AppInstaller::CLI
+{
+ namespace AdminSettingsDetails
+ {
+ // The base for admin settings.
+ struct IAdminSetting
+ {
+ virtual ~IAdminSetting() = default;
+
+ // Gets the name of the setting.
+ virtual Utility::LocIndView SettingName() const = 0;
+
+ // Tests the value.
+ // Returns true if in the desired state; false if not.
+ virtual bool Test() const = 0;
+
+ // Sets the value.
+ // Returns true if the value could be set; false if not.
+ virtual bool Set() const = 0;
+ };
+
+ // A boolean based admin setting
+ struct AdminSetting_Bool : public IAdminSetting
+ {
+ AdminSetting_Bool(Settings::BoolAdminSetting setting, bool value) : m_setting(setting), m_value(value) {}
+
+ Utility::LocIndView SettingName() const override
+ {
+ return Settings::AdminSettingToString(m_setting);
+ }
+
+ bool Test() const override
+ {
+ return Settings::IsAdminSettingEnabled(m_setting) == m_value;
+ }
+
+ bool Set() const override
+ {
+ return m_value ? Settings::EnableAdminSetting(m_setting) : Settings::DisableAdminSetting(m_setting);
+ }
+
+ private:
+ Settings::BoolAdminSetting m_setting;
+ bool m_value;
+ };
+
+ // A string based admin setting
+ struct AdminSetting_String : public IAdminSetting
+ {
+ AdminSetting_String(Settings::StringAdminSetting setting, std::optional value) : m_setting(setting), m_value(std::move(value)) {}
+
+ Utility::LocIndView SettingName() const override
+ {
+ return Settings::AdminSettingToString(m_setting);
+ }
+
+ bool Test() const override
+ {
+ return Settings::GetAdminSetting(m_setting) == m_value;
+ }
+
+ bool Set() const override
+ {
+ return m_value ? Settings::SetAdminSetting(m_setting, m_value.value()) : Settings::ResetAdminSetting(m_setting);
+ }
+
+ private:
+ Settings::StringAdminSetting m_setting;
+ std::optional m_value;
+ };
+ }
+
+ namespace
+ {
+ WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY(SettingsProperty, Json::Value, Settings, "settings", Resource::String::DscResourcePropertyDescriptionAdminSettingsSettings);
+
+ using AdminSettingsResourceObject = DscComposableObject;
+
+ struct AdminSettingsFunctionData
+ {
+ AdminSettingsFunctionData() = default;
+
+ AdminSettingsFunctionData(const std::optional& json) :
+ Input(json)
+ {
+ }
+
+ const AdminSettingsResourceObject Input;
+ AdminSettingsResourceObject Output;
+ std::vector> InputSettings;
+
+ // Converts the input settings into the appropriate settings object.
+ void ParseSettings()
+ {
+ if (Input.Settings())
+ {
+ const Json::Value& inputSettings = Input.Settings().value();
+ for (const auto& property : inputSettings.getMemberNames())
+ {
+ auto boolSetting = Settings::StringToBoolAdminSetting(property);
+ if (boolSetting != Settings::BoolAdminSetting::Unknown)
+ {
+ bool value = inputSettings[property].asBool();
+ AICLI_LOG(Config, Info, << "Bool admin setting: " << property << " => " << (value ? "true" : "false"));
+ InputSettings.emplace_back(std::make_unique(boolSetting, value));
+ continue;
+ }
+
+ auto stringSetting = Settings::StringToStringAdminSetting(property);
+ if (stringSetting != Settings::StringAdminSetting::Unknown)
+ {
+ const auto& propertyNode = inputSettings[property];
+ std::optional value;
+
+ if (propertyNode.isNull())
+ {
+ AICLI_LOG(Config, Info, << "String admin setting: " << property << " => null");
+ }
+ else
+ {
+ value = propertyNode.asString();
+ AICLI_LOG(Config, Info, << "String admin setting: " << property << " => `" << value.value() << "`");
+ }
+
+ InputSettings.emplace_back(std::make_unique(stringSetting, std::move(value)));
+ continue;
+ }
+
+ AICLI_LOG(Config, Warning, << "Unknown admin setting: " << property);
+ }
+ }
+ }
+
+ // Fills the Output object with the current state
+ void Get()
+ {
+ Json::Value adminSettings{ Json::objectValue };
+
+ for (const auto& setting : Settings::GetAllBoolAdminSettings())
+ {
+ auto str = std::string{ Settings::AdminSettingToString(setting) };
+ adminSettings[str] = Settings::IsAdminSettingEnabled(setting);
+ }
+
+ for (const auto& setting : Settings::GetAllStringAdminSettings())
+ {
+ auto name = std::string{ Settings::AdminSettingToString(setting) };
+ auto value = Settings::GetAdminSetting(setting);
+ if (value)
+ {
+ adminSettings[name] = value.value();
+ }
+ }
+
+ Output.Settings(std::move(adminSettings));
+ }
+
+ // Determines if the current Output values match the Input values state.
+ bool Test()
+ {
+ for (const auto& setting : InputSettings)
+ {
+ if (!setting->Test())
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ // Sets all of the input settings.
+ void Set()
+ {
+ for (const auto& setting : InputSettings)
+ {
+ if (!setting->Test())
+ {
+ if (!setting->Set())
+ {
+ auto message = Resource::String::DisabledByGroupPolicy(setting->SettingName());
+ THROW_HR_MSG(APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY, "%hs", message.get().c_str());
+ }
+ }
+ }
+ }
+
+ Json::Value DiffJson(bool inDesiredState)
+ {
+ Json::Value result{ Json::ValueType::arrayValue };
+
+ if (!inDesiredState)
+ {
+ result.append(std::string{ SettingsProperty::Name() });
+ }
+
+ return result;
+ }
+ };
+ }
+
+ DscAdminSettingsResource::DscAdminSettingsResource(std::string_view parent) :
+ DscCommandBase(parent, "admin-settings", DscResourceKind::Resource,
+ DscFunctions::Get | DscFunctions::Set | DscFunctions::Test | DscFunctions::Export | DscFunctions::Schema,
+ DscFunctionModifiers::ImplementsPretest | DscFunctionModifiers::ReturnsStateAndDiff)
+ {
+ }
+
+ Resource::LocString DscAdminSettingsResource::ShortDescription() const
+ {
+ return Resource::String::DscAdminSettingsResourceShortDescription;
+ }
+
+ Resource::LocString DscAdminSettingsResource::LongDescription() const
+ {
+ return Resource::String::DscAdminSettingsResourceLongDescription;
+ }
+
+ std::string DscAdminSettingsResource::ResourceType() const
+ {
+ return "AdminSettings";
+ }
+
+ void DscAdminSettingsResource::ResourceFunctionGet(Execution::Context& context) const
+ {
+ AdminSettingsFunctionData data;
+ data.Get();
+ WriteJsonOutputLine(context, data.Output.ToJson());
+ }
+
+ void DscAdminSettingsResource::ResourceFunctionSet(Execution::Context& context) const
+ {
+ if (auto json = GetJsonFromInput(context))
+ {
+ AdminSettingsFunctionData data{ json };
+
+ data.ParseSettings();
+
+ bool inDesiredState = data.Test();
+ if (!inDesiredState)
+ {
+ Workflow::EnsureRunningAsAdmin(context);
+
+ if (context.IsTerminated())
+ {
+ return;
+ }
+
+ data.Set();
+ }
+
+ data.Get();
+ WriteJsonOutputLine(context, data.Output.ToJson());
+ WriteJsonOutputLine(context, data.DiffJson(inDesiredState));
+ }
+ }
+
+ void DscAdminSettingsResource::ResourceFunctionTest(Execution::Context& context) const
+ {
+ if (auto json = GetJsonFromInput(context))
+ {
+ AdminSettingsFunctionData data{ json };
+
+ data.ParseSettings();
+
+ data.Get();
+ data.Output.InDesiredState(data.Test());
+
+ WriteJsonOutputLine(context, data.Output.ToJson());
+ WriteJsonOutputLine(context, data.DiffJson(data.Output.InDesiredState().value()));
+ }
+ }
+
+ void DscAdminSettingsResource::ResourceFunctionExport(Execution::Context& context) const
+ {
+ ResourceFunctionGet(context);
+ }
+
+ void DscAdminSettingsResource::ResourceFunctionSchema(Execution::Context& context) const
+ {
+ WriteJsonOutputLine(context, AdminSettingsResourceObject::Schema(ResourceType()));
+ }
+}
diff --git a/src/AppInstallerCLICore/Commands/DscAdminSettingsResource.h b/src/AppInstallerCLICore/Commands/DscAdminSettingsResource.h
new file mode 100644
index 0000000000..0ad9330cb1
--- /dev/null
+++ b/src/AppInstallerCLICore/Commands/DscAdminSettingsResource.h
@@ -0,0 +1,25 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+#pragma once
+#include "DscCommandBase.h"
+
+namespace AppInstaller::CLI
+{
+ // A resource for managing admin settings.
+ struct DscAdminSettingsResource : public DscCommandBase
+ {
+ DscAdminSettingsResource(std::string_view parent);
+
+ Resource::LocString ShortDescription() const override;
+ Resource::LocString LongDescription() const override;
+
+ protected:
+ std::string ResourceType() const override;
+
+ void ResourceFunctionGet(Execution::Context& context) const override;
+ void ResourceFunctionSet(Execution::Context& context) const override;
+ void ResourceFunctionTest(Execution::Context& context) const override;
+ void ResourceFunctionExport(Execution::Context& context) const override;
+ void ResourceFunctionSchema(Execution::Context& context) const override;
+ };
+}
diff --git a/src/AppInstallerCLICore/Commands/DscCommand.cpp b/src/AppInstallerCLICore/Commands/DscCommand.cpp
index a0e9a57673..3b88245e83 100644
--- a/src/AppInstallerCLICore/Commands/DscCommand.cpp
+++ b/src/AppInstallerCLICore/Commands/DscCommand.cpp
@@ -5,6 +5,7 @@
#include "DscPackageResource.h"
#include "DscUserSettingsFileResource.h"
#include "DscSourceResource.h"
+#include "DscAdminSettingsResource.h"
#ifndef AICLI_DISABLE_TEST_HOOKS
#include "DscTestFileResource.h"
@@ -33,6 +34,7 @@ namespace AppInstaller::CLI
std::make_unique(FullName()),
std::make_unique(FullName()),
std::make_unique(FullName()),
+ std::make_unique(FullName()),
#ifndef AICLI_DISABLE_TEST_HOOKS
std::make_unique(FullName()),
std::make_unique(FullName()),
diff --git a/src/AppInstallerCLICore/Commands/DscComposableObject.h b/src/AppInstallerCLICore/Commands/DscComposableObject.h
index 3b3fa7ddc5..45b55dc011 100644
--- a/src/AppInstallerCLICore/Commands/DscComposableObject.h
+++ b/src/AppInstallerCLICore/Commands/DscComposableObject.h
@@ -162,7 +162,7 @@ namespace AppInstaller::CLI
static void FromJson(Derived* self, const Json::Value* value, bool ignoreFieldRequirements)
{
- if (value)
+ if (value && !value->isNull())
{
self->m_value = GetJsonTypeValue::Get(*value);
}
diff --git a/src/AppInstallerCLICore/Commands/SettingsCommand.cpp b/src/AppInstallerCLICore/Commands/SettingsCommand.cpp
index 70620f5bca..e27b69e26c 100644
--- a/src/AppInstallerCLICore/Commands/SettingsCommand.cpp
+++ b/src/AppInstallerCLICore/Commands/SettingsCommand.cpp
@@ -162,7 +162,8 @@ namespace AppInstaller::CLI
std::vector SettingsResetCommand::GetArguments() const
{
return {
- Argument { Execution::Args::Type::SettingName, Resource::String::SettingNameArgumentDescription, ArgumentType::Positional, true },
+ Argument { Execution::Args::Type::SettingName, Resource::String::SettingNameArgumentDescription, ArgumentType::Positional },
+ Argument { Execution::Args::Type::All, Resource::String::ResetAllAdminSettingsArgumentDescription, ArgumentType::Flag },
};
}
@@ -183,6 +184,21 @@ namespace AppInstaller::CLI
void SettingsResetCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const
{
+ if (execArgs.Contains(Execution::Args::Type::All))
+ {
+ if (execArgs.Contains(Execution::Args::Type::SettingName))
+ {
+ throw CommandException(Resource::String::MultipleExclusiveArgumentsProvided("all|setting"_liv));
+ }
+
+ return;
+ }
+
+ if (!execArgs.Contains(Execution::Args::Type::SettingName))
+ {
+ throw CommandException(Resource::String::RequiredArgError(ArgumentCommon::ForType(Execution::Args::Type::SettingName).Name));
+ }
+
// Get admin setting string for all available options except Unknown.
// We accept both bool and string settings
std::vector adminSettingList;
@@ -208,6 +224,6 @@ namespace AppInstaller::CLI
{
context <<
Workflow::EnsureRunningAsAdmin <<
- Workflow::ResetAdminSetting;
+ (context.Args.Contains(Execution::Args::Type::All) ? Workflow::ResetAllAdminSettings : Workflow::ResetAdminSetting);
}
}
diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h
index 4d01bf8705..1b651a3219 100644
--- a/src/AppInstallerCLICore/Resources.h
+++ b/src/AppInstallerCLICore/Resources.h
@@ -223,6 +223,8 @@ namespace AppInstaller::CLI::Resource
WINGET_DEFINE_RESOURCE_STRINGID(Downloading);
WINGET_DEFINE_RESOURCE_STRINGID(DscCommandLongDescription);
WINGET_DEFINE_RESOURCE_STRINGID(DscCommandShortDescription);
+ WINGET_DEFINE_RESOURCE_STRINGID(DscAdminSettingsResourceShortDescription);
+ WINGET_DEFINE_RESOURCE_STRINGID(DscAdminSettingsResourceLongDescription);
WINGET_DEFINE_RESOURCE_STRINGID(DscPackageResourceShortDescription);
WINGET_DEFINE_RESOURCE_STRINGID(DscPackageResourceLongDescription);
WINGET_DEFINE_RESOURCE_STRINGID(DscResourceFunctionDescriptionGet);
@@ -239,6 +241,7 @@ namespace AppInstaller::CLI::Resource
WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionExist);
WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionInDesiredState);
WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionAcceptAgreements);
+ WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionAdminSettingsSettings);
WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionPackageId);
WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionPackageSource);
WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionPackageVersion);
@@ -571,6 +574,8 @@ namespace AppInstaller::CLI::Resource
WINGET_DEFINE_RESOURCE_STRINGID(ReservedFilenameError);
WINGET_DEFINE_RESOURCE_STRINGID(ResetAdminSettingFailed);
WINGET_DEFINE_RESOURCE_STRINGID(ResetAdminSettingSucceeded);
+ WINGET_DEFINE_RESOURCE_STRINGID(ResetAllAdminSettingsArgumentDescription);
+ WINGET_DEFINE_RESOURCE_STRINGID(ResetAllAdminSettingsSucceeded);
WINGET_DEFINE_RESOURCE_STRINGID(ResumeCommandLongDescription);
WINGET_DEFINE_RESOURCE_STRINGID(ResumeCommandShortDescription);
WINGET_DEFINE_RESOURCE_STRINGID(ResumeIdArgumentDescription);
diff --git a/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp b/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp
index 39051083aa..280570275b 100644
--- a/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp
+++ b/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp
@@ -52,6 +52,7 @@ namespace AppInstaller::CLI::Workflow
constexpr std::wstring_view s_UnitType_WinGetPackage_DSCv3 = WINGET_DSCV3_MODULE_NAME_WIDE L"/Package";
constexpr std::wstring_view s_UnitType_WinGetSource_DSCv3 = WINGET_DSCV3_MODULE_NAME_WIDE L"/Source";
constexpr std::wstring_view s_UnitType_WinGetUserSettingsFile_DSCv3 = WINGET_DSCV3_MODULE_NAME_WIDE L"/UserSettingsFile";
+ constexpr std::wstring_view s_UnitType_WinGetAdminSettings_DSCv3 = WINGET_DSCV3_MODULE_NAME_WIDE L"/AdminSettings";
constexpr std::wstring_view s_UnitType_PowerShellModuleGet = L"PowerShellGet/PSModule";
constexpr std::wstring_view s_Module_WinGetClient = L"Microsoft.WinGet.DSC";
@@ -86,7 +87,7 @@ namespace AppInstaller::CLI::Workflow
std::vector PredefinedResourcesForExport()
{
return {
- { {}, { { s_UnitType_WinGetUserSettingsFile_DSCv3 } } },
+ { {}, { { s_UnitType_WinGetUserSettingsFile_DSCv3 }, { s_UnitType_WinGetAdminSettings_DSCv3, true } } },
{ L"Microsoft.Windows.Settings", { { L"Microsoft.Windows.Settings/WindowsSettings", true } } },
};
}
diff --git a/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp b/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp
index 49eca405cd..f730d5208d 100644
--- a/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp
+++ b/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp
@@ -113,6 +113,12 @@ namespace AppInstaller::CLI::Workflow
}
}
+ void ResetAllAdminSettings(Execution::Context& context)
+ {
+ Settings::ResetAllAdminSettings();
+ context.Reporter.Info() << Resource::String::ResetAllAdminSettingsSucceeded << std::endl;
+ }
+
void OpenUserSetting(Execution::Context& context)
{
// Show warnings only when the setting command is executed.
diff --git a/src/AppInstallerCLICore/Workflows/SettingsFlow.h b/src/AppInstallerCLICore/Workflows/SettingsFlow.h
index 50998295a5..c937e88adf 100644
--- a/src/AppInstallerCLICore/Workflows/SettingsFlow.h
+++ b/src/AppInstallerCLICore/Workflows/SettingsFlow.h
@@ -29,6 +29,12 @@ namespace AppInstaller::CLI::Workflow
// Outputs: None
void ResetAdminSetting(Execution::Context& context);
+ // Resets all admin settings to the default.
+ // Required Args: None
+ // Inputs: None
+ // Outputs: None
+ void ResetAllAdminSettings(Execution::Context& context);
+
// Opens the user settings.
// Required Args: None
// Inputs: None
diff --git a/src/AppInstallerCLIE2ETests/DSCv3AdminSettingsResourceCommand.cs b/src/AppInstallerCLIE2ETests/DSCv3AdminSettingsResourceCommand.cs
new file mode 100644
index 0000000000..4f547f94cb
--- /dev/null
+++ b/src/AppInstallerCLIE2ETests/DSCv3AdminSettingsResourceCommand.cs
@@ -0,0 +1,372 @@
+// -----------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
+//
+// -----------------------------------------------------------------------------
+
+namespace AppInstallerCLIE2ETests
+{
+ using System.Collections.Generic;
+ using System.Text.Json;
+ using System.Text.Json.Nodes;
+ using System.Text.Json.Serialization;
+ using AppInstallerCLIE2ETests.Helpers;
+ using NUnit.Framework;
+
+ ///
+ /// `Configure` command tests.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1010:Opening square brackets should be spaced correctly", Justification = "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3687 pending SC 1.2 release")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1011:Closing square brackets should be spaced correctly", Justification = "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3687 pending SC 1.2 release")]
+ public class DSCv3AdminSettingsResourceCommand : DSCv3ResourceTestBase
+ {
+ private const string AdminSettingsResource = "admin-settings";
+ private const string SettingsPropertyName = "settings";
+
+ // Bool settings
+ private const string BypassCertificatePinningForMicrosoftStore = "BypassCertificatePinningForMicrosoftStore";
+ private const string InstallerHashOverride = "InstallerHashOverride";
+ private const string LocalArchiveMalwareScanOverride = "LocalArchiveMalwareScanOverride";
+ private const string LocalManifestFiles = "LocalManifestFiles";
+ private const string ProxyCommandLineOptions = "ProxyCommandLineOptions";
+
+ // String settings
+ private const string DefaultProxy = "DefaultProxy";
+
+ // Not a setting
+ private const string NotAnAdminSettingName = "NotAnAdminSetting";
+
+ ///
+ /// Setup done once before all the tests here.
+ ///
+ [OneTimeSetUp]
+ public void OneTimeSetup()
+ {
+ WinGetSettingsHelper.ConfigureFeature("dsc3", true);
+ EnsureTestResourcePresence();
+ }
+
+ ///
+ /// Teardown done once after all the tests here.
+ ///
+ [OneTimeTearDown]
+ public void OneTimeTeardown()
+ {
+ WinGetSettingsHelper.ConfigureFeature("dsc3", false);
+ ResetAllSettings();
+ GroupPolicyHelper.DeleteExistingPolicies();
+ }
+
+ ///
+ /// Set up.
+ ///
+ [SetUp]
+ public void Setup()
+ {
+ ResetAllSettings();
+ GroupPolicyHelper.DeleteExistingPolicies();
+ }
+
+ ///
+ /// Calls `get` on the `admin-settings` resource with the value not present.
+ ///
+ /// The resource function to invoke.
+ [TestCase(GetFunction)]
+ [TestCase(ExportFunction)]
+ public void AdminSettings_Get(string function)
+ {
+ var result = RunDSCv3Command(AdminSettingsResource, function, null);
+ AssertSuccessfulResourceRun(ref result);
+
+ AdminSettingsResourceData output = GetSingleOutputLineAs(result.StdOut);
+ Assert.IsNotNull(output);
+ Assert.IsNotNull(output.Settings);
+ Assert.IsTrue(output.Settings.ContainsKey(LocalManifestFiles));
+ Assert.IsFalse(output.Settings.ContainsKey(DefaultProxy));
+ Assert.IsFalse(output.Settings.ContainsKey(NotAnAdminSettingName));
+ }
+
+ ///
+ /// Calls `test` on the `admin-settings` resource with a bool setting.
+ ///
+ /// The setting to test.
+ [TestCase(BypassCertificatePinningForMicrosoftStore)]
+ [TestCase(InstallerHashOverride)]
+ [TestCase(LocalArchiveMalwareScanOverride)]
+ [TestCase(LocalManifestFiles)]
+ [TestCase(ProxyCommandLineOptions)]
+ public void AdminSettings_Test_BoolSetting(string settingName)
+ {
+ AdminSettingsResourceData resourceData = new AdminSettingsResourceData() { Settings = new JsonObject() };
+
+ resourceData.Settings[settingName] = true;
+ var result = RunDSCv3Command(AdminSettingsResource, TestFunction, resourceData);
+ AssertTestOfBoolSetting(ref result, settingName, false, true);
+
+ resourceData.Settings[settingName] = false;
+ result = RunDSCv3Command(AdminSettingsResource, TestFunction, resourceData);
+ AssertTestOfBoolSetting(ref result, settingName, false, false);
+
+ Assert.AreEqual(Constants.ErrorCode.S_OK, TestCommon.RunAICLICommand("settings", $"--enable {settingName}").ExitCode);
+
+ resourceData.Settings[settingName] = false;
+ result = RunDSCv3Command(AdminSettingsResource, TestFunction, resourceData);
+ AssertTestOfBoolSetting(ref result, settingName, true, false);
+
+ resourceData.Settings[settingName] = true;
+ result = RunDSCv3Command(AdminSettingsResource, TestFunction, resourceData);
+ AssertTestOfBoolSetting(ref result, settingName, true, true);
+ }
+
+ ///
+ /// Calls `test` on the `admin-settings` resource with a string setting.
+ ///
+ /// The setting to test.
+ [TestCase(DefaultProxy)]
+ public void AdminSettings_Test_StringSetting(string settingName)
+ {
+ const string testValue = "A string to test";
+ const string differentTestValue = "A different value";
+
+ AdminSettingsResourceData resourceData = new AdminSettingsResourceData() { Settings = new JsonObject() };
+
+ resourceData.Settings[settingName] = null;
+ var result = RunDSCv3Command(AdminSettingsResource, TestFunction, resourceData);
+ AssertTestOfStringSetting(ref result, settingName, null, null);
+
+ resourceData.Settings[settingName] = testValue;
+ result = RunDSCv3Command(AdminSettingsResource, TestFunction, resourceData);
+ AssertTestOfStringSetting(ref result, settingName, null, testValue);
+
+ Assert.AreEqual(Constants.ErrorCode.S_OK, TestCommon.RunAICLICommand("settings set", $"{settingName} \"{testValue}\"").ExitCode);
+
+ resourceData.Settings[settingName] = null;
+ result = RunDSCv3Command(AdminSettingsResource, TestFunction, resourceData);
+ AssertTestOfStringSetting(ref result, settingName, testValue, null);
+
+ resourceData.Settings[settingName] = testValue;
+ result = RunDSCv3Command(AdminSettingsResource, TestFunction, resourceData);
+ AssertTestOfStringSetting(ref result, settingName, testValue, testValue);
+
+ resourceData.Settings[settingName] = differentTestValue;
+ result = RunDSCv3Command(AdminSettingsResource, TestFunction, resourceData);
+ AssertTestOfStringSetting(ref result, settingName, testValue, differentTestValue);
+ }
+
+ ///
+ /// Calls `test` on the `admin-settings` resource with a complex input.
+ ///
+ [Test]
+ public void AdminSettings_Test_Complex()
+ {
+ Assert.AreEqual(Constants.ErrorCode.S_OK, TestCommon.RunAICLICommand("settings", $"--enable {LocalArchiveMalwareScanOverride}").ExitCode);
+ Assert.AreEqual(Constants.ErrorCode.S_OK, TestCommon.RunAICLICommand("settings", $"--enable {LocalManifestFiles}").ExitCode);
+
+ AdminSettingsResourceData resourceData = new AdminSettingsResourceData() { Settings = new JsonObject() };
+ resourceData.Settings[LocalArchiveMalwareScanOverride] = true;
+ resourceData.Settings[BypassCertificatePinningForMicrosoftStore] = false;
+ resourceData.Settings[DefaultProxy] = null;
+
+ var result = RunDSCv3Command(AdminSettingsResource, TestFunction, resourceData);
+ AssertSuccessfulResourceRun(ref result);
+
+ (AdminSettingsResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut);
+ Assert.IsNotNull(output);
+ Assert.IsTrue(output.InDesiredState);
+ Assert.IsNotNull(output.Settings);
+
+ AssertDiffState(diff, []);
+ }
+
+ ///
+ /// Calls `set` on the `admin-settings` resource with a bool setting.
+ ///
+ /// The setting to test.
+ [TestCase(BypassCertificatePinningForMicrosoftStore)]
+ [TestCase(InstallerHashOverride)]
+ [TestCase(LocalArchiveMalwareScanOverride)]
+ [TestCase(LocalManifestFiles)]
+ [TestCase(ProxyCommandLineOptions)]
+ public void AdminSettings_Set_BoolSetting(string settingName)
+ {
+ AdminSettingsResourceData resourceData = new AdminSettingsResourceData() { Settings = new JsonObject() };
+
+ resourceData.Settings[settingName] = true;
+ var result = RunDSCv3Command(AdminSettingsResource, SetFunction, resourceData);
+ AssertSetOfBoolSetting(ref result, settingName, false, true);
+
+ result = RunDSCv3Command(AdminSettingsResource, SetFunction, resourceData);
+ AssertSetOfBoolSetting(ref result, settingName, true, true);
+
+ resourceData.Settings[settingName] = false;
+ result = RunDSCv3Command(AdminSettingsResource, SetFunction, resourceData);
+ AssertSetOfBoolSetting(ref result, settingName, true, false);
+
+ result = RunDSCv3Command(AdminSettingsResource, SetFunction, resourceData);
+ AssertSetOfBoolSetting(ref result, settingName, false, false);
+ }
+
+ ///
+ /// Calls `set` on the `admin-settings` resource with a string setting.
+ ///
+ /// The setting to test.
+ [TestCase(DefaultProxy)]
+ public void AdminSettings_Set_StringSetting(string settingName)
+ {
+ const string testValue = "A string to test";
+ const string differentTestValue = "A different value";
+
+ AdminSettingsResourceData resourceData = new AdminSettingsResourceData() { Settings = new JsonObject() };
+
+ resourceData.Settings[settingName] = null;
+ var result = RunDSCv3Command(AdminSettingsResource, SetFunction, resourceData);
+ AssertSetOfStringSetting(ref result, settingName, null, null);
+
+ resourceData.Settings[settingName] = testValue;
+ result = RunDSCv3Command(AdminSettingsResource, SetFunction, resourceData);
+ AssertSetOfStringSetting(ref result, settingName, null, testValue);
+
+ resourceData.Settings[settingName] = testValue;
+ result = RunDSCv3Command(AdminSettingsResource, SetFunction, resourceData);
+ AssertSetOfStringSetting(ref result, settingName, testValue, testValue);
+
+ resourceData.Settings[settingName] = differentTestValue;
+ result = RunDSCv3Command(AdminSettingsResource, SetFunction, resourceData);
+ AssertSetOfStringSetting(ref result, settingName, testValue, differentTestValue);
+
+ resourceData.Settings[settingName] = null;
+ result = RunDSCv3Command(AdminSettingsResource, SetFunction, resourceData);
+ AssertSetOfStringSetting(ref result, settingName, differentTestValue, null);
+ }
+
+ ///
+ /// Calls `set` on the `admin-settings` resource with a complex input.
+ ///
+ [Test]
+ public void AdminSettings_Set_Complex()
+ {
+ const string testValue = "A string to test";
+
+ AdminSettingsResourceData resourceData = new AdminSettingsResourceData() { Settings = new JsonObject() };
+ resourceData.Settings[LocalArchiveMalwareScanOverride] = true;
+ resourceData.Settings[BypassCertificatePinningForMicrosoftStore] = false;
+ resourceData.Settings[DefaultProxy] = testValue;
+
+ var result = RunDSCv3Command(AdminSettingsResource, SetFunction, resourceData);
+ AssertSuccessfulResourceRun(ref result);
+
+ (AdminSettingsResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut);
+ Assert.IsNotNull(output);
+ Assert.IsNotNull(output.Settings);
+ Assert.AreEqual(JsonValueKind.True, output.Settings[LocalArchiveMalwareScanOverride].AsValue().GetValueKind());
+ Assert.AreEqual(JsonValueKind.False, output.Settings[BypassCertificatePinningForMicrosoftStore].AsValue().GetValueKind());
+ Assert.AreEqual(testValue, output.Settings[DefaultProxy].AsValue().GetValue());
+
+ AssertDiffState(diff, [ SettingsPropertyName ]);
+ }
+
+ ///
+ /// Calls `set` on the `admin-settings` resource attempting to change a setting with group policy enabled.
+ ///
+ [Test]
+ public void AdminSettings_Set_GroupPolicyBlocked()
+ {
+ GroupPolicyHelper.EnableHashOverride.Disable();
+
+ AdminSettingsResourceData resourceData = new AdminSettingsResourceData() { Settings = new JsonObject() };
+ resourceData.Settings[InstallerHashOverride] = true;
+
+ var result = RunDSCv3Command(AdminSettingsResource, SetFunction, resourceData);
+ Assert.AreEqual(Constants.ErrorCode.ERROR_BLOCKED_BY_POLICY, result.ExitCode);
+ }
+
+ private static void ResetAllSettings()
+ {
+ Assert.AreEqual(Constants.ErrorCode.S_OK, TestCommon.RunAICLICommand("settings reset", "--all").ExitCode);
+ }
+
+ private static void AssertTestOfBoolSetting(ref TestCommon.RunCommandResult result, string settingName, bool expectedState, bool testState)
+ {
+ AssertSuccessfulResourceRun(ref result);
+
+ bool inDesiredState = expectedState == testState;
+
+ (AdminSettingsResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut);
+ Assert.IsNotNull(output);
+ Assert.AreEqual(inDesiredState, output.InDesiredState);
+ Assert.IsNotNull(output.Settings);
+ Assert.IsTrue(output.Settings.ContainsKey(settingName));
+ Assert.AreEqual(expectedState ? JsonValueKind.True : JsonValueKind.False, output.Settings[settingName].AsValue().GetValueKind());
+
+ AssertDiffState(diff, inDesiredState ? [] : [ SettingsPropertyName ]);
+ }
+
+ private static void AssertSetOfBoolSetting(ref TestCommon.RunCommandResult result, string settingName, bool previousState, bool desiredState)
+ {
+ AssertSuccessfulResourceRun(ref result);
+
+ bool inDesiredState = previousState == desiredState;
+
+ (AdminSettingsResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut);
+ Assert.IsNotNull(output);
+ Assert.IsNotNull(output.Settings);
+ Assert.IsTrue(output.Settings.ContainsKey(settingName));
+ Assert.AreEqual(desiredState ? JsonValueKind.True : JsonValueKind.False, output.Settings[settingName].AsValue().GetValueKind());
+
+ AssertDiffState(diff, inDesiredState ? [] : [ SettingsPropertyName ]);
+ }
+
+ private static void AssertTestOfStringSetting(ref TestCommon.RunCommandResult result, string settingName, string expectedState, string testState)
+ {
+ AssertSuccessfulResourceRun(ref result);
+
+ bool inDesiredState = expectedState == testState;
+
+ (AdminSettingsResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut);
+ Assert.IsNotNull(output);
+ Assert.AreEqual(inDesiredState, output.InDesiredState);
+ Assert.IsNotNull(output.Settings);
+ if (expectedState != null)
+ {
+ Assert.IsTrue(output.Settings.ContainsKey(settingName));
+ Assert.AreEqual(expectedState, output.Settings[settingName].AsValue().GetValue());
+ }
+ else
+ {
+ Assert.IsFalse(output.Settings.ContainsKey(settingName));
+ }
+
+ AssertDiffState(diff, inDesiredState ? [] : [SettingsPropertyName]);
+ }
+
+ private static void AssertSetOfStringSetting(ref TestCommon.RunCommandResult result, string settingName, string previousState, string desiredState)
+ {
+ AssertSuccessfulResourceRun(ref result);
+
+ bool inDesiredState = previousState == desiredState;
+
+ (AdminSettingsResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut);
+ Assert.IsNotNull(output);
+ Assert.IsNotNull(output.Settings);
+ if (desiredState != null)
+ {
+ Assert.IsTrue(output.Settings.ContainsKey(settingName));
+ Assert.AreEqual(desiredState, output.Settings[settingName].AsValue().GetValue());
+ }
+ else
+ {
+ Assert.IsFalse(output.Settings.ContainsKey(settingName));
+ }
+
+ AssertDiffState(diff, inDesiredState ? [] : [SettingsPropertyName]);
+ }
+
+ private class AdminSettingsResourceData
+ {
+ [JsonPropertyName(InDesiredStatePropertyName)]
+ public bool? InDesiredState { get; set; }
+
+ public JsonObject Settings { get; set; }
+ }
+ }
+}
diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw
index c53c35b31a..530fe634f2 100644
--- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw
+++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw
@@ -3355,4 +3355,19 @@ Please specify one of them using the --source option to proceed.
Failed to install Desired State Configuration package. Install the package manually or provide the path to dsc.exe through --processor-path argument.
{Locked="dsc.exe","--processor-path"}
+
+ An object containing the administrator settings and their values.
+
+
+ Manage administrator settings
+
+
+ Allows management of administrator settings via the DSC v3 command line interface protocol. See the help link for details.
+
+
+ Resets all admin settings
+
+
+ All admin settings reset.
+
diff --git a/src/AppInstallerCommonCore/AdminSettings.cpp b/src/AppInstallerCommonCore/AdminSettings.cpp
index 89a23d64ea..6d2d8634c1 100644
--- a/src/AppInstallerCommonCore/AdminSettings.cpp
+++ b/src/AppInstallerCommonCore/AdminSettings.cpp
@@ -60,6 +60,8 @@ namespace AppInstaller::Settings
bool GetAdminSettingValue(BoolAdminSetting setting) const;
std::optional GetAdminSettingValue(StringAdminSetting setting) const;
+ void Reset();
+
private:
void LoadAdminSettings();
[[nodiscard]] bool SaveAdminSettings();
@@ -171,6 +173,11 @@ namespace AppInstaller::Settings
}
}
+ void AdminSettingsInternal::Reset()
+ {
+ m_settingStream.Remove();
+ }
+
void AdminSettingsInternal::LoadAdminSettings()
{
auto stream = m_settingStream.Get();
@@ -406,6 +413,11 @@ namespace AppInstaller::Settings
return true;
}
+ void ResetAllAdminSettings()
+ {
+ AdminSettingsInternal{}.Reset();
+ }
+
std::optional GetAdminSetting(StringAdminSetting setting)
{
// Check for a policy that overrides this setting.
diff --git a/src/AppInstallerCommonCore/Public/winget/AdminSettings.h b/src/AppInstallerCommonCore/Public/winget/AdminSettings.h
index d46fafdf01..b36b19789e 100644
--- a/src/AppInstallerCommonCore/Public/winget/AdminSettings.h
+++ b/src/AppInstallerCommonCore/Public/winget/AdminSettings.h
@@ -38,6 +38,7 @@ namespace AppInstaller::Settings
bool DisableAdminSetting(BoolAdminSetting setting);
bool SetAdminSetting(StringAdminSetting setting, std::string_view value);
bool ResetAdminSetting(StringAdminSetting setting);
+ void ResetAllAdminSettings();
bool IsAdminSettingEnabled(BoolAdminSetting setting);
std::optional GetAdminSetting(StringAdminSetting setting);