@@ -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 ;
0 commit comments