Skip to content

[DSC] Implement Microsoft.PowerToys.Configure DSCResource & winget support #30918

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
Apr 1, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d7663d6
[DSC] Microsoft.PowerToys.Configure module + winget configuration fil…
yuyoyuppe Mar 15, 2024
f8e4737
f: fix for an incorrect directory id reference
yuyoyuppe Mar 20, 2024
cd69d09
f: update comment
yuyoyuppe Mar 21, 2024
92d373f
f: address review comments
yuyoyuppe Mar 21, 2024
251e967
f: file locksmith bug fix
yuyoyuppe Mar 21, 2024
1275da5
f: add explorer preview switches in samples
yuyoyuppe Mar 21, 2024
f3fce0e
f: remove debug
yuyoyuppe Mar 22, 2024
9fcae1a
Sign DSC files
jaimecbernardo Mar 22, 2024
6e3eb15
Merge branch 'main' into dsc_configure_module
yuyoyuppe Mar 22, 2024
64f9fe0
f: implement docs/samples generator
yuyoyuppe Mar 22, 2024
b1e2286
[ci]Sign FancyZonesEditorCommon.dll
jaimecbernardo Mar 24, 2024
3b61840
Sign DSC files in the Generated folder
jaimecbernardo Mar 25, 2024
a39158d
f: address review comments
yuyoyuppe Mar 28, 2024
da2ef06
Merge branch 'main' into dsc_configure_module
yuyoyuppe Apr 1, 2024
4e9b334
f: update usable options
yuyoyuppe Apr 1, 2024
6c00343
f: add autogenerated sample
yuyoyuppe Apr 1, 2024
a2f5e2f
[Installer] Don't use same GUID for different components
jaimecbernardo Apr 1, 2024
6da3d6c
[Installer]Don't remove folders shared by other modules
jaimecbernardo Apr 1, 2024
eaee7e0
Allow configuring PTRun MaximumNumberOfResults
jaimecbernardo Apr 1, 2024
54800c5
Remove all settings DSC sample. Just random data
jaimecbernardo Apr 1, 2024
b0285d1
Allow configuring Hosts Run as Administrator
jaimecbernardo Apr 1, 2024
dea50c3
Revert "[Installer]Don't remove folders shared by other modules"
jaimecbernardo Apr 1, 2024
dd3f35f
Add all PTRun plugins and Global and keyboard to DSC sample
jaimecbernardo Apr 1, 2024
940080b
Fix issues with context menu modules not disabling
jaimecbernardo Apr 1, 2024
b6f0472
Fix default enabled values when setting with DSC
jaimecbernardo Apr 1, 2024
5d6eeaf
Fix tests regarding default modules in Settings
jaimecbernardo Apr 1, 2024
c91d519
Merge branch 'main' into pr30918
jaimecbernardo Apr 1, 2024
f25955f
Fix merge error
jaimecbernardo Apr 1, 2024
b7fad12
Restart PowerToys process if we stopped it
jaimecbernardo Apr 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/actions/spell-check/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,7 @@ NOSIZE
NOTIFICATIONSDLL
NOTIFYICONDATAW
NOTIMPL
notlike
NOTOPMOST
NOTRACK
NOTSRCCOPY
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ bld/
# Visual Studio 2017 auto generated files
Generated\ Files/

Generated/

# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
Expand Down
4 changes: 4 additions & 0 deletions .pipelines/ESRPSigning_core.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
"*.resources.dll",

"WinUI3Apps\\Assets\\Settings\\Scripts\\*.ps1",

"Microsoft.PowerToys.Configure.psm1",
"Microsoft.PowerToys.Configure.psd1",

"PowerToys.ActionRunner.exe",
"PowerToys.Update.exe",
Expand Down Expand Up @@ -54,6 +57,7 @@
"fancyzones.dll",
"PowerToys.FancyZonesEditor.exe",
"PowerToys.FancyZonesEditor.dll",
"PowerToys.FancyZonesEditorCommon.dll",
"PowerToys.FancyZonesModuleInterface.dll",
"PowerToys.FancyZones.exe",

Expand Down
19 changes: 19 additions & 0 deletions PowerToys.sln
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,12 @@ EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UITests-FancyZonesEditor", "src\modules\fancyzones\UITests-FancyZonesEditor\UITests-FancyZonesEditor.csproj", "{3A9A791E-94A9-49F8-8401-C11CE288D5FB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FancyZonesEditorCommon", "src\modules\fancyzones\FancyZonesEditorCommon\FancyZonesEditorCommon.csproj", "{C0974915-8A1D-4BF0-977B-9587D3807AB7}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DSC", "DSC", "{557C4636-D7E1-4838-A504-7D19B725EE95}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerToys.Settings.DSC.Schema.Generator", "src\dsc\PowerToys.Settings.DSC.Schema.Generator\PowerToys.Settings.DSC.Schema.Generator.csproj", "{1D6893CB-BC0C-46A8-A76C-9728706CA51A}"
ProjectSection(ProjectDependencies) = postProject
{020A7474-3601-4160-A159-D7B70B77B15F} = {020A7474-3601-4160-A159-D7B70B77B15F}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -2536,6 +2542,18 @@ Global
{C0974915-8A1D-4BF0-977B-9587D3807AB7}.Release|x64.Build.0 = Release|x64
{C0974915-8A1D-4BF0-977B-9587D3807AB7}.Release|x86.ActiveCfg = Release|x64
{C0974915-8A1D-4BF0-977B-9587D3807AB7}.Release|x86.Build.0 = Release|x64
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Debug|ARM64.ActiveCfg = Debug|ARM64
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Debug|ARM64.Build.0 = Debug|ARM64
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Debug|x64.ActiveCfg = Debug|x64
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Debug|x64.Build.0 = Debug|x64
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Debug|x86.ActiveCfg = Debug|x64
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Debug|x86.Build.0 = Debug|x64
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Release|ARM64.ActiveCfg = Release|ARM64
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Release|ARM64.Build.0 = Release|ARM64
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Release|x64.ActiveCfg = Release|x64
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Release|x64.Build.0 = Release|x64
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Release|x86.ActiveCfg = Release|x64
{1D6893CB-BC0C-46A8-A76C-9728706CA51A}.Release|x86.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -2748,6 +2766,7 @@ Global
{FE38FC07-1C05-4B57-ADA3-2FE2F53C6A52} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
{3A9A791E-94A9-49F8-8401-C11CE288D5FB} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
{C0974915-8A1D-4BF0-977B-9587D3807AB7} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
{1D6893CB-BC0C-46A8-A76C-9728706CA51A} = {557C4636-D7E1-4838-A504-7D19B725EE95}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
Expand Down
98 changes: 98 additions & 0 deletions doc/devdocs/settingsv2/dsc-configure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# What is it

We would like to enable our users to use [`winget configure`](https://learn.microsoft.com/en-us/windows/package-manager/winget/configure) command to install PowerToys and configure its settings with a [Winget configuration file](https://learn.microsoft.com/en-us/windows/package-manager/configuration/create). For example:

```yaml
properties:
resources:
- resource: Microsoft.WinGet.DSC/WinGetPackage
directives:
description: Install PowerToys
allowPrerelease: true
settings:
id: PowerToys (Preview)
source: winget

- resource: PowerToysConfigure
directives:
description: Configure PowerToys
settings:
ShortcutGuide:
Enabled: false
OverlayOpacity: 1
FancyZones:
Enabled: true
FancyzonesEditorHotkey: "Shift+Ctrl+Alt+F"
configurationVersion: 0.2.0
```

This should install PowerToys and make `PowerToysConfigure` resource available. We can use it in the same file.

# How it works

`PowerToysConfigure` is a [class-based DSC resource](https://learn.microsoft.com/en-us/powershell/dsc/concepts/class-based-resources?view=dsc-2.0). It looks up whether each setting was specified or not by checking whether it's `$null` or `0` for `enum`s and invokes `PowerToys.Settings.exe` with the updated value like so:
```
PowerToys.Settings.exe set <ModuleName>.<SettingName> <SettingValue>
```

So for the example the config above should perform 3 following invocations:
```
PowerToys.Settings.exe set ShortcutGuide.Enabled false
PowerToys.Settings.exe set FancyZones.Enabled true
PowerToys.Settings.exe set FancyZones.FancyzonesEditorHotkey "Shift+Ctrl+Alt+F"
```

`PowerToys.Settings` uses dotnet reflection capabilities to determine `SettingName` type and tries to convert the supplied `SettingValue` string accordingly. We use `ICmdReprParsable` for custom setting types.


# How DSC is implemented

We use `PowerToys.Settings.DSC.Schema.Generator` to generate the bulk of `PowerToysConfigure.psm1` file. It also uses dotnet reflection capabilities to inspect `PowerToys.Settings.UI.Lib.dll` assembly and generate properties for the modules we have. The actual generation is done as a `PowerToys.Settings.DSC.Schema.Generator.csproj` post-build action.

# Debugging DSC resources

First, make sure that PowerShell 7.4+ is installed. Then make sure that you have DSC installed:

```ps
Install-Module -Name PSDesiredStateConfiguration -RequiredVersion 2.0.7
```

After that, start a new `pwsh` session and `cd` to `src\dsc\Microsoft.PowerToys.Configure\Generated` directory. From there, you should execute:
```ps
$env:PSModulePath += ";$pwd"
```

Now build `PowerToys.sln` and **move** `src\dsc\Microsoft.PowerToys.Configure\Microsoft.PowerToys.Configure.psd1` temporarily to `src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\0.0.1\` folder, so it's located alongside with the generated `Microsoft.PowerToys.Configure.psm1`.

This will allow DSC to discover our DSC Resource module. See [PSModulePath](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_psmodulepath?view=powershell-7.4#long-description) for more info.

If everything works, you should see that your module is discovered by executing the following command:

```ps
Get-Module -ListAvailable | grep PowerToys
```

The resource itself should also be available:
```ps
Get-DSCResource | grep PowerToys
```

Otherwise, you can force-import the module to diagnose issues:

```
Import-Module .\Microsoft.PowerToys.Configure.psd1
```

If it's imported successfully, you could also try to invoke it directly:

```ps
Invoke-DscResource -Name PowerToysConfigure -Method Set -ModuleName Microsoft.PowerToys.Configure -Property @{ Debug = $true; Awake = @{ Enabled = $false; Mode = "TIMED"; IntervalMinutes = "10" } }
```

Note that we've supplied `Debug` option, so a `%TEMP\PowerToys.DSC.TestConfigure.txt` is created with the supplied properties, a current timestamp, and other debug output.

Finally, you can test it with winget by invoking it as such:

```ps
winget configure .\configuration.dsc.yaml --accept-configuration-agreements --disable-interactivity
```
44 changes: 44 additions & 0 deletions installer/PowerToysSetup/Core.wxs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,49 @@
</Component>
</DirectoryRef>

<?if $(var.PerUser) = "true" ?>
<DirectoryRef Id="PersonalFolder">
<Directory Id="WindowsPowerShellFolder" Name="PowerShell">
<Directory Id="PowerShellModulesFolder" Name="Modules">
<Directory Id="PowerToysDscFolder" Name="Microsoft.PowerToys.Configure">
<Directory Id="PowerToysDscVerFolder" Name="$(var.Version)">
<Component Id="PowerToysDSC" Win64="yes" Guid="4A033E3B-6590-43FD-8FBD-27F9DF557F7F">
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make the component not fail the installation if the component is not able to be installed?

<RegistryValue Root="HKCU"
Key="Software\[Manufacturer]\[ProductName]"
Name="DSCInstalled"
Type="integer"
Value="1"
KeyPath="yes"/>
<File Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Microsoft.PowerToys.Configure.psd1" Id="PTConf.psd1" />
<File Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$(var.Version)\Microsoft.PowerToys.Configure.psm1" Id="PTConf.psm1" />
<RemoveFolder Id="RemoveThisFolder" On="uninstall" />
<RemoveFolder Id="RemovePowerToysDscVerFolder" Directory="PowerToysDscVerFolder" On="uninstall" />
<RemoveFolder Id="RemovePowerToysDscFolder" Directory="PowerToysDscFolder" On="uninstall" />
<RemoveFolder Id="RemovePowerShellModulesFolder" Directory="PowerShellModulesFolder" On="uninstall" />
<RemoveFolder Id="RemoveWindowsPowerShellFolder" Directory="WindowsPowerShellFolder" On="uninstall" />
</Component>
</Directory>
</Directory>
</Directory>
</Directory>
</DirectoryRef>
<?else?>
<DirectoryRef Id="ProgramFiles64Folder">
<Directory Id="WindowsPowerShellFolder" Name="WindowsPowerShell">
<Directory Id="PowerShellModulesFolder" Name="Modules">
<Directory Id="PowerToysDscFolder" Name="Microsoft.PowerToys.Configure">
<Directory Id="PowerToysDscVerFolder" Name="$(var.Version)">
<Component Id="PowerToysDSC" Win64="yes" Guid="4A033E3B-6590-43FD-8FBD-27F9DF557F7F">
<File Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Microsoft.PowerToys.Configure.psd1" Id="PTConf.psd1" />
<File Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$(var.Version)\Microsoft.PowerToys.Configure.psm1" Id="PTConf.psm1" />
</Component>
</Directory>
</Directory>
</Directory>
</Directory>
</DirectoryRef>
<?endif?>

<DirectoryRef Id="ApplicationProgramsFolder">
<Component Id="PowerToysStartMenuShortcut" >
<Shortcut Id="ApplicationStartMenuShortcut"
Expand Down Expand Up @@ -101,6 +144,7 @@
<ComponentRef Id="License_rtf" />
<ComponentRef Id="Notice_md" />
<ComponentRef Id="DesktopShortcut" />
<ComponentRef Id="PowerToysDSC" />
</ComponentGroup>
</Fragment>
</Wix>
1 change: 1 addition & 0 deletions installer/PowerToysSetup/Product.wxs
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,7 @@
<Directory Id="ApplicationProgramsFolder" Name="PowerToys (Preview)"/>
</Directory>
<Directory Id="DesktopFolder" Name="Desktop" />
<Directory Id="PersonalFolder" Name="UserHomeDocuments" />
</Directory>
</Fragment>
</Wix>
23 changes: 14 additions & 9 deletions src/common/interop/interop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

#include <common/version/version.h>


using namespace System;
using namespace System::Runtime::InteropServices;
using System::Collections::Generic::List;
Expand All @@ -40,11 +39,17 @@ public
delete _map;
}

String ^ GetKeyName(DWORD key) {
String ^ GetKeyName(DWORD key)
{
return gcnew String(_map->GetKeyName(key).c_str());
}

void Updatelayout()
DWORD GetKeyValue(String ^ name)
{
return _map->GetKeyFromName(msclr::interop::marshal_as<std::wstring>(name));
}

void Updatelayout()
{
_map->UpdateLayout();
}
Expand Down Expand Up @@ -129,13 +134,13 @@ public
}

static List<String ^> ^ GetAllActiveMicrophoneDeviceNames() {
auto names = gcnew List<String ^>();
for (const auto& device : MicrophoneDevice::getAllActive())
{
names->Add(gcnew String(device->name().data()));
auto names = gcnew List<String ^>();
for (const auto& device : MicrophoneDevice::getAllActive())
{
names->Add(gcnew String(device->name().data()));
}
return names;
}
return names;
}

static List<String ^> ^
GetAllVideoCaptureDeviceNames() {
Expand Down
Loading
Loading