Skip to content

Commit 6430ee2

Browse files
authored
Add support for DSCv3 in configuration PowerShell modules (microsoft#5470)
1 parent 326558c commit 6430ee2

File tree

15 files changed

+330
-27
lines changed

15 files changed

+330
-27
lines changed

azure-pipelines.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,17 @@ jobs:
603603
failTaskOnFailedTests: true
604604
condition: succeededOrFailed()
605605

606+
- task: PowerShell@2
607+
displayName: Copy WinGet Logs
608+
inputs:
609+
pwsh: true
610+
targetType: inline
611+
script: |
612+
$sourceDir = Join-Path $env:LocalAppData Packages\WinGetDevCLI_8wekyb3d8bbwe\LocalState\DiagOutputDir
613+
$destinationDir = Join-Path $(Build.ArtifactStagingDirectory) WinGetLogs
614+
Copy-Item -Path $sourceDir -Destination $destinationDir -Recurse -Force
615+
condition: succeededOrFailed()
616+
606617
- task: PublishPipelineArtifact@1
607618
displayName: Publish PowerShell Module Artifacts
608619
inputs:

src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Properties/AssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// -----------------------------------------------------------------------------
1+
// -----------------------------------------------------------------------------
22
// <copyright file="AssemblyInfo.cs" company="Microsoft Corporation">
33
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
44
// </copyright>
@@ -10,7 +10,7 @@
1010

1111
// Forcibly set the target and supported platforms due to the internal build setup.
1212
// Keep in sync with project versions.
13-
[assembly: TargetPlatform("Windows10.0.22000.0")]
13+
[assembly: TargetPlatform("Windows10.0.26100.0")]
1414
[assembly: SupportedOSPlatform("Windows10.0.18362.0")]
1515

1616
#endif

src/PowerShell/Microsoft.WinGet.Client.Engine/Properties/AssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// -----------------------------------------------------------------------------
1+
// -----------------------------------------------------------------------------
22
// <copyright file="AssemblyInfo.cs" company="Microsoft Corporation">
33
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
44
// </copyright>
@@ -10,7 +10,7 @@
1010

1111
// Forcibly set the target and supported platforms due to the internal build setup.
1212
// Keep in sync with project versions.
13-
[assembly: TargetPlatform("Windows10.0.22000.0")]
13+
[assembly: TargetPlatform("Windows10.0.26100.0")]
1414
[assembly: SupportedOSPlatform("Windows10.0.18362.0")]
1515

1616
#endif

src/PowerShell/Microsoft.WinGet.Configuration.Cmdlets/Cmdlets/Common/OpenConfiguration.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ public abstract class OpenConfiguration : PSCmdlet
5151
ValueFromPipelineByPropertyName = true)]
5252
public string ModulePath { get; set; }
5353

54+
/// <summary>
55+
/// Gets or sets custom DSCv3 processor path.
56+
/// </summary>
57+
[Parameter(ValueFromPipelineByPropertyName = true)]
58+
public string ProcessorPath { get; set; }
59+
5460
/// <summary>
5561
/// Gets the execution policy to use.
5662
/// </summary>

src/PowerShell/Microsoft.WinGet.Configuration.Cmdlets/Cmdlets/GetWinGetConfigurationCmdlet.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ protected override void ProcessRecord()
3131
this.File,
3232
this.ModulePath,
3333
this.ExecutionPolicy,
34+
this.ProcessorPath,
3435
this.CanUseTelemetry);
3536
}
3637
else if (this.ParameterSetName == Helpers.Constants.ParameterSet.OpenConfigurationSetFromHistory)
@@ -39,13 +40,15 @@ protected override void ProcessRecord()
3940
this.InstanceIdentifier,
4041
this.ModulePath,
4142
this.ExecutionPolicy,
43+
this.ProcessorPath,
4244
this.CanUseTelemetry);
4345
}
4446
else if (this.ParameterSetName == Helpers.Constants.ParameterSet.OpenAllConfigurationSetsFromHistory)
4547
{
4648
configCommand.GetAllFromHistory(
4749
this.ModulePath,
4850
this.ExecutionPolicy,
51+
this.ProcessorPath,
4952
this.CanUseTelemetry);
5053
}
5154
}

src/PowerShell/Microsoft.WinGet.Configuration.Cmdlets/Properties/AssemblyInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
// Forcibly set the target and supported platforms due to the internal build setup.
1212
// Keep in sync with project versions.
13-
[assembly: TargetPlatform("Windows10.0.22000.0")]
13+
[assembly: TargetPlatform("Windows10.0.26100.0")]
1414
[assembly: SupportedOSPlatform("Windows10.0.18362.0")]
1515

1616
#endif

src/PowerShell/Microsoft.WinGet.Configuration.Engine/Commands/ConfigurationCommand.cs

Lines changed: 104 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ namespace Microsoft.WinGet.Configuration.Engine.Commands
1111
using System.IO;
1212
using System.Linq;
1313
using System.Management.Automation;
14+
using System.Management.Automation.Runspaces;
1415
using System.Text;
1516
using System.Threading.Tasks;
1617
using Microsoft.Management.Configuration;
@@ -31,6 +32,19 @@ namespace Microsoft.WinGet.Configuration.Engine.Commands
3132
/// </summary>
3233
public sealed class ConfigurationCommand : PowerShellCmdlet
3334
{
35+
private const string ProcessorEngineDSCv3 = "dscv3";
36+
private const string ProcessorEnginePowerShell = "pwsh";
37+
38+
private const string DSCv3FactoryMapKeyDscExecutablePath = "DscExecutablePath";
39+
private const string DSCv3FactoryMapKeyFoundDscExecutablePath = "FoundDscExecutablePath";
40+
41+
private const string WinGetClientModule = "Microsoft.WinGet.Client";
42+
#if USE_PROD_CLSIDS
43+
private const string DSCv3PackageId = "9NVTPZWRC6KQ";
44+
#else
45+
private const string DSCv3PackageId = "9PCX3HX4HZ0Z";
46+
#endif
47+
3448
/// <summary>
3549
/// Initializes a new instance of the <see cref="ConfigurationCommand"/> class.
3650
/// </summary>
@@ -83,15 +97,17 @@ public static bool ConfirmConfigurationProcessing(PSCmdlet psCmdlet, bool hasAcc
8397
/// <param name="configFile">Configuration file path.</param>
8498
/// <param name="modulePath">The module path to use.</param>
8599
/// <param name="executionPolicy">Execution policy.</param>
100+
/// <param name="processorPath">The processor path to use.</param>
86101
/// <param name="canUseTelemetry">If telemetry can be used.</param>
87102
public void Get(
88103
string configFile,
89104
string modulePath,
90105
ExecutionPolicy executionPolicy,
106+
string processorPath,
91107
bool canUseTelemetry)
92108
{
93109
var openParams = new OpenConfigurationParameters(
94-
this, configFile, modulePath, executionPolicy, canUseTelemetry);
110+
this, configFile, modulePath, executionPolicy, processorPath, canUseTelemetry);
95111

96112
// Start task.
97113
var runningTask = this.RunOnMTA<PSConfigurationSet>(
@@ -110,15 +126,17 @@ public void Get(
110126
/// <param name="instanceIdentifier">Instance identifier.</param>
111127
/// <param name="modulePath">The module path to use.</param>
112128
/// <param name="executionPolicy">Execution policy.</param>
129+
/// <param name="processorPath">The processor path to use.</param>
113130
/// <param name="canUseTelemetry">If telemetry can be used.</param>
114131
public void GetFromHistory(
115132
string instanceIdentifier,
116133
string modulePath,
117134
ExecutionPolicy executionPolicy,
135+
string processorPath,
118136
bool canUseTelemetry)
119137
{
120138
var openParams = new OpenConfigurationParameters(
121-
this, instanceIdentifier, modulePath, executionPolicy, canUseTelemetry, fromHistory: true);
139+
this, instanceIdentifier, modulePath, executionPolicy, processorPath, canUseTelemetry, fromHistory: true);
122140

123141
// Start task.
124142
var runningTask = this.RunOnMTA<PSConfigurationSet?>(
@@ -139,14 +157,16 @@ public void GetFromHistory(
139157
/// </summary>
140158
/// <param name="modulePath">The module path to use.</param>
141159
/// <param name="executionPolicy">Execution policy.</param>
160+
/// <param name="processorPath">The processor path to use.</param>
142161
/// <param name="canUseTelemetry">If telemetry can be used.</param>
143162
public void GetAllFromHistory(
144163
string modulePath,
145164
ExecutionPolicy executionPolicy,
165+
string processorPath,
146166
bool canUseTelemetry)
147167
{
148168
var openParams = new OpenConfigurationParameters(
149-
this, modulePath, executionPolicy, canUseTelemetry);
169+
this, modulePath, executionPolicy, processorPath, canUseTelemetry);
150170

151171
// Start task.
152172
var runningTask = this.RunOnMTA<PSConfigurationSet[]>(
@@ -361,7 +381,7 @@ private void ContinueHelper(PSConfigurationJob psConfigurationJob)
361381
this.Write(StreamType.Object, psConfigurationJob.ApplyConfigurationTask.Result);
362382
}
363383

364-
private IConfigurationSetProcessorFactory CreateFactory(OpenConfigurationParameters openParams)
384+
private IConfigurationSetProcessorFactory CreatePowerShellProcessorFactory(OpenConfigurationParameters openParams)
365385
{
366386
var factory = new PowerShellConfigurationSetProcessorFactory();
367387

@@ -377,18 +397,90 @@ private IConfigurationSetProcessorFactory CreateFactory(OpenConfigurationParamet
377397
return factory;
378398
}
379399

400+
private async Task<IConfigurationSetProcessorFactory> CreateDSCv3ProcessorFactory(OpenConfigurationParameters openParams)
401+
{
402+
var factory = new DSCv3ConfigurationSetProcessorFactory();
403+
404+
var factoryMap = factory.As<IDictionary<string, string>>();
405+
if (!string.IsNullOrEmpty(openParams.ProcessorPath))
406+
{
407+
factoryMap.Add(DSCv3FactoryMapKeyDscExecutablePath, openParams.ProcessorPath);
408+
}
409+
else
410+
{
411+
string? foundProcessorPath = null;
412+
if (!factoryMap.TryGetValue(DSCv3FactoryMapKeyFoundDscExecutablePath, out foundProcessorPath) ||
413+
string.IsNullOrEmpty(foundProcessorPath))
414+
{
415+
await this.InstallDSCv3Package(openParams);
416+
}
417+
}
418+
419+
return factory;
420+
}
421+
422+
private async Task InstallDSCv3Package(OpenConfigurationParameters openParams)
423+
{
424+
this.Write(StreamType.Information, Resources.ConfigurationInstallDscPackage);
425+
426+
InitialSessionState initialSessionState = InitialSessionState.CreateDefault();
427+
initialSessionState.ExecutionPolicy = openParams.ExecutionPolicy;
428+
Runspace runspace = RunspaceFactory.CreateRunspace(initialSessionState);
429+
runspace.Open();
430+
PowerShell installDSCv3 = PowerShell.Create(runspace).AddScript(
431+
$@"
432+
if (-not (Get-Module -ListAvailable -Name {WinGetClientModule}))
433+
{{
434+
Install-Module -Name {WinGetClientModule} -Confirm:$False -Force
435+
}}
436+
437+
$installResult = Install-WingetPackage -Id {DSCv3PackageId} -Source msstore
438+
if ($installResult.Status -ne 'Ok')
439+
{{
440+
Write-Error ""Failed to install DSCv3 package. Status: $($installResult.Status). ExtendedErrorCode: $($installResult.ExtendedErrorCode)."" -ErrorAction Stop
441+
}}
442+
");
443+
444+
await installDSCv3.InvokeAsync();
445+
446+
if (installDSCv3.HadErrors)
447+
{
448+
this.Write(StreamType.Error, Resources.ConfigurationInstallDscPackageFailed);
449+
throw new FileNotFoundException(Resources.DscExeNotFound, "dsc.exe");
450+
}
451+
}
452+
453+
private async Task<PSConfigurationProcessor> CreateConfigurationProcessorWithSet(OpenConfigurationParameters openParams, ConfigurationSet set)
454+
{
455+
string processorIdentifier = set.Environment.ProcessorIdentifier;
456+
457+
if (string.IsNullOrEmpty(processorIdentifier) || ProcessorEnginePowerShell.Equals(processorIdentifier, StringComparison.OrdinalIgnoreCase))
458+
{
459+
// Default to PowerShell
460+
return new PSConfigurationProcessor(this.CreatePowerShellProcessorFactory(openParams), this, openParams.CanUseTelemetry);
461+
}
462+
else if (ProcessorEngineDSCv3.Equals(processorIdentifier, StringComparison.OrdinalIgnoreCase))
463+
{
464+
return new PSConfigurationProcessor(await this.CreateDSCv3ProcessorFactory(openParams), this, openParams.CanUseTelemetry);
465+
}
466+
else
467+
{
468+
throw new NotSupportedException(string.Format(Resources.ProcessorEngineNotSupported, processorIdentifier));
469+
}
470+
}
471+
380472
private async Task<PSConfigurationSet?> OpenConfigurationSetAsync(OpenConfigurationParameters openParams)
381473
{
382474
this.Write(StreamType.Verbose, Resources.ConfigurationInitializing);
383475

384-
var psProcessor = new PSConfigurationProcessor(this.CreateFactory(openParams), this, openParams.CanUseTelemetry);
476+
var processorWithoutFactory = new PSConfigurationProcessor(null, this, openParams.CanUseTelemetry);
385477

386478
if (!openParams.FromHistory)
387479
{
388480
this.Write(StreamType.Verbose, Resources.ConfigurationReadingConfigFile);
389481
var stream = await FileRandomAccessStream.OpenAsync(openParams.ConfigFile, FileAccessMode.Read);
390482

391-
OpenConfigurationSetResult openResult = await psProcessor.Processor.OpenConfigurationSetAsync(stream);
483+
OpenConfigurationSetResult openResult = await processorWithoutFactory.Processor.OpenConfigurationSetAsync(stream);
392484
if (openResult.ResultCode != null)
393485
{
394486
throw new OpenConfigurationSetException(openResult, openParams.ConfigFile);
@@ -402,15 +494,15 @@ private IConfigurationSetProcessorFactory CreateFactory(OpenConfigurationParamet
402494
set.Origin = Path.GetDirectoryName(openParams.ConfigFile);
403495
set.Path = openParams.ConfigFile;
404496

405-
return new PSConfigurationSet(psProcessor, set);
497+
return new PSConfigurationSet(await this.CreateConfigurationProcessorWithSet(openParams, set), set);
406498
}
407499
else
408500
{
409501
Guid instanceIdentifier = Guid.Parse(openParams.ConfigFile);
410502

411503
this.Write(StreamType.Verbose, Resources.ConfigurationReadingConfigHistory);
412504

413-
var historySets = await psProcessor.Processor.GetConfigurationHistoryAsync();
505+
var historySets = await processorWithoutFactory.Processor.GetConfigurationHistoryAsync();
414506

415507
ConfigurationSet? result = null;
416508
foreach (var historySet in historySets)
@@ -422,24 +514,24 @@ private IConfigurationSetProcessorFactory CreateFactory(OpenConfigurationParamet
422514
}
423515
}
424516

425-
return result != null ? new PSConfigurationSet(psProcessor, result) : null;
517+
return result != null ? new PSConfigurationSet(await this.CreateConfigurationProcessorWithSet(openParams, result), result) : null;
426518
}
427519
}
428520

429521
private async Task<PSConfigurationSet[]> GetConfigurationSetHistoryAsync(OpenConfigurationParameters openParams)
430522
{
431523
this.Write(StreamType.Verbose, Resources.ConfigurationInitializing);
432524

433-
var psProcessor = new PSConfigurationProcessor(this.CreateFactory(openParams), this, openParams.CanUseTelemetry);
525+
var processorWithoutFactory = new PSConfigurationProcessor(null, this, openParams.CanUseTelemetry);
434526

435527
this.Write(StreamType.Verbose, Resources.ConfigurationReadingConfigHistory);
436528

437-
var historySets = await psProcessor.Processor.GetConfigurationHistoryAsync();
529+
var historySets = await processorWithoutFactory.Processor.GetConfigurationHistoryAsync();
438530

439531
PSConfigurationSet[] result = new PSConfigurationSet[historySets.Count];
440532
for (int i = 0; i < historySets.Count; ++i)
441533
{
442-
result[i] = new PSConfigurationSet(psProcessor, historySets[i]);
534+
result[i] = new PSConfigurationSet(await this.CreateConfigurationProcessorWithSet(openParams, historySets[i]), historySets[i]);
443535
}
444536

445537
return result;

src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/OpenConfigurationParameters.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,15 @@ internal class OpenConfigurationParameters
3030
/// <param name="file">The configuration file.</param>
3131
/// <param name="modulePath">The module path to use.</param>
3232
/// <param name="executionPolicy">Execution policy.</param>
33+
/// <param name="processorPath">The processor path to use.</param>
3334
/// <param name="canUseTelemetry">If telemetry can be used.</param>
3435
/// <param name="fromHistory">If the configuration is from history; changes the meaning of `ConfigFile` to the instance identifier.</param>
3536
public OpenConfigurationParameters(
3637
PowerShellCmdlet pwshCmdlet,
3738
string file,
3839
string modulePath,
3940
ExecutionPolicy executionPolicy,
41+
string processorPath,
4042
bool canUseTelemetry,
4143
bool fromHistory = false)
4244
{
@@ -50,7 +52,9 @@ public OpenConfigurationParameters(
5052
}
5153

5254
this.InitializeModulePath(modulePath);
55+
this.ExecutionPolicy = executionPolicy;
5356
this.Policy = this.GetConfigurationProcessorPolicy(executionPolicy);
57+
this.ProcessorPath = processorPath;
5458
this.CanUseTelemetry = canUseTelemetry;
5559
this.FromHistory = fromHistory;
5660
}
@@ -61,16 +65,20 @@ public OpenConfigurationParameters(
6165
/// <param name="pwshCmdlet">PowerShellCmdlet.</param>
6266
/// <param name="modulePath">The module path to use.</param>
6367
/// <param name="executionPolicy">Execution policy.</param>
68+
/// <param name="processorPath">The processor path to use.</param>
6469
/// <param name="canUseTelemetry">If telemetry can be used.</param>
6570
public OpenConfigurationParameters(
6671
PowerShellCmdlet pwshCmdlet,
6772
string modulePath,
6873
ExecutionPolicy executionPolicy,
74+
string processorPath,
6975
bool canUseTelemetry)
7076
{
7177
this.ConfigFile = string.Empty;
7278
this.InitializeModulePath(modulePath);
79+
this.ExecutionPolicy = executionPolicy;
7380
this.Policy = this.GetConfigurationProcessorPolicy(executionPolicy);
81+
this.ProcessorPath = processorPath;
7482
this.CanUseTelemetry = canUseTelemetry;
7583
}
7684

@@ -89,11 +97,21 @@ public OpenConfigurationParameters(
8997
/// </summary>
9098
public string? CustomLocation { get; private set; }
9199

100+
/// <summary>
101+
/// Gets the original ExecutionPolicy value.
102+
/// </summary>
103+
public ExecutionPolicy ExecutionPolicy { get; private set; } = ExecutionPolicy.Undefined;
104+
92105
/// <summary>
93106
/// Gets execution policy of the processor.
94107
/// </summary>
95108
public PowerShellConfigurationProcessorPolicy Policy { get; }
96109

110+
/// <summary>
111+
/// Gets DSCv3 processor path.
112+
/// </summary>
113+
public string ProcessorPath { get; }
114+
97115
/// <summary>
98116
/// Gets a value indicating whether to use telemetry or not.
99117
/// </summary>

0 commit comments

Comments
 (0)