ConfigurationCommand.cs (30286B)
1 // ----------------------------------------------------------------------------- 2 // <copyright file="ConfigurationCommand.cs" company="Microsoft Corporation"> 3 // Copyright (c) Microsoft Corporation. Licensed under the MIT License. 4 // </copyright> 5 // ----------------------------------------------------------------------------- 6 7 namespace Microsoft.WinGet.Configuration.Engine.Commands 8 { 9 using System; 10 using System.Collections.Generic; 11 using System.IO; 12 using System.Linq; 13 using System.Management.Automation; 14 using System.Management.Automation.Runspaces; 15 using System.Text; 16 using System.Threading.Tasks; 17 using Microsoft.Management.Configuration; 18 using Microsoft.Management.Configuration.Processor; 19 using Microsoft.Management.Configuration.Processor.PowerShell.Extensions; 20 using Microsoft.PowerShell; 21 using Microsoft.WinGet.Common.Command; 22 using Microsoft.WinGet.Configuration.Engine.Exceptions; 23 using Microsoft.WinGet.Configuration.Engine.Helpers; 24 using Microsoft.WinGet.Configuration.Engine.PSObjects; 25 using Microsoft.WinGet.Resources; 26 using Microsoft.WinGet.SharedLib.PolicySettings; 27 using Windows.Storage; 28 using Windows.Storage.Streams; 29 using WinRT; 30 31 /// <summary> 32 /// Class that deals configuration commands. 33 /// </summary> 34 public sealed class ConfigurationCommand : PowerShellCmdlet 35 { 36 private const string ProcessorEngineDSCv3 = "dscv3"; 37 private const string ProcessorEnginePowerShell = "pwsh"; 38 39 private const string DSCv3FactoryMapKeyDscExecutablePath = "DscExecutablePath"; 40 private const string DSCv3FactoryMapKeyFoundDscExecutablePath = "FoundDscExecutablePath"; 41 private const string DSCv3FactoryMapKeyFindDscStateMachine = "FindDscStateMachine"; 42 43 private const string WinGetClientModule = "Microsoft.WinGet.Client"; 44 private const string StableDSCv3PackageId = "9NVTPZWRC6KQ"; 45 private const string PreviewDSCv3PackageId = "9PCX3HX4HZ0Z"; 46 47 /// <summary> 48 /// Initializes a new instance of the <see cref="ConfigurationCommand"/> class. 49 /// </summary> 50 /// <param name="psCmdlet">PSCmdlet.</param> 51 public ConfigurationCommand(PSCmdlet psCmdlet) 52 : base(psCmdlet, new HashSet<Policy> { Policy.WinGet, Policy.Configuration, Policy.WinGetCommandLineInterfaces }) 53 { 54 } 55 56 /// <summary> 57 /// Verify user accept agreements. 58 /// </summary> 59 /// <param name="psCmdlet">PSCmdlet.</param> 60 /// <param name="hasAccepted">Has already accepted.</param> 61 /// <param name="isApply">If prompt is for apply.</param> 62 /// <returns>If accepted.</returns> 63 public static bool ConfirmConfigurationProcessing(PSCmdlet psCmdlet, bool hasAccepted, bool isApply) 64 { 65 bool result = false; 66 if (!hasAccepted) 67 { 68 var prompt = isApply ? Resources.ConfigurationWarningPromptApply : Resources.ConfigurationWarningPromptTest; 69 bool yesToAll = false; 70 bool noToAll = false; 71 result = psCmdlet.ShouldContinue(prompt, Resources.ConfigurationWarning, true, ref yesToAll, ref noToAll); 72 73 if (yesToAll) 74 { 75 result = true; 76 } 77 else if (noToAll) 78 { 79 result = false; 80 } 81 } 82 else 83 { 84 // This way even if they set WarningActionPreference.Ignore we will still print the 85 // warning message if the agreements didn't get accepted. 86 psCmdlet.WriteWarning(Resources.ConfigurationWarning); 87 result = true; 88 } 89 90 return result; 91 } 92 93 /// <summary> 94 /// Open a configuration set. 95 /// </summary> 96 /// <param name="configFile">Configuration file path.</param> 97 /// <param name="modulePath">The module path to use.</param> 98 /// <param name="executionPolicy">Execution policy.</param> 99 /// <param name="processorPath">The processor path to use.</param> 100 /// <param name="canUseTelemetry">If telemetry can be used.</param> 101 public void Get( 102 string configFile, 103 string modulePath, 104 ExecutionPolicy executionPolicy, 105 string processorPath, 106 bool canUseTelemetry) 107 { 108 var openParams = new OpenConfigurationParameters( 109 this, configFile, modulePath, executionPolicy, processorPath, canUseTelemetry); 110 111 // Start task. 112 var runningTask = this.RunOnMTA<PSConfigurationSet>( 113 async () => 114 { 115 return (await this.OpenConfigurationSetAsync(openParams)) !; 116 }); 117 118 this.Wait(runningTask); 119 this.Write(StreamType.Object, runningTask.Result); 120 } 121 122 /// <summary> 123 /// Open a configuration set from history. 124 /// </summary> 125 /// <param name="instanceIdentifier">Instance identifier.</param> 126 /// <param name="modulePath">The module path to use.</param> 127 /// <param name="executionPolicy">Execution policy.</param> 128 /// <param name="processorPath">The processor path to use.</param> 129 /// <param name="canUseTelemetry">If telemetry can be used.</param> 130 public void GetFromHistory( 131 string instanceIdentifier, 132 string modulePath, 133 ExecutionPolicy executionPolicy, 134 string processorPath, 135 bool canUseTelemetry) 136 { 137 var openParams = new OpenConfigurationParameters( 138 this, instanceIdentifier, modulePath, executionPolicy, processorPath, canUseTelemetry, fromHistory: true); 139 140 // Start task. 141 var runningTask = this.RunOnMTA<PSConfigurationSet?>( 142 async () => 143 { 144 return await this.OpenConfigurationSetAsync(openParams); 145 }); 146 147 this.Wait(runningTask); 148 if (runningTask.Result != null) 149 { 150 this.Write(StreamType.Object, runningTask.Result); 151 } 152 } 153 154 /// <summary> 155 /// Opens all configuration sets from history. 156 /// </summary> 157 /// <param name="modulePath">The module path to use.</param> 158 /// <param name="executionPolicy">Execution policy.</param> 159 /// <param name="processorPath">The processor path to use.</param> 160 /// <param name="canUseTelemetry">If telemetry can be used.</param> 161 public void GetAllFromHistory( 162 string modulePath, 163 ExecutionPolicy executionPolicy, 164 string processorPath, 165 bool canUseTelemetry) 166 { 167 var openParams = new OpenConfigurationParameters( 168 this, modulePath, executionPolicy, processorPath, canUseTelemetry); 169 170 // Start task. 171 var runningTask = this.RunOnMTA<PSConfigurationSet[]>( 172 async () => 173 { 174 return await this.GetConfigurationSetHistoryAsync(openParams); 175 }); 176 177 this.Wait(runningTask); 178 this.Write(StreamType.Object, runningTask.Result); 179 } 180 181 /// <summary> 182 /// Gets the details of a configuration set. 183 /// </summary> 184 /// <param name="psConfigurationSet">PSConfigurationSet.</param> 185 public void GetDetails(PSConfigurationSet psConfigurationSet) 186 { 187 psConfigurationSet.PsProcessor.UpdateDiagnosticCmdlet(this); 188 189 if (!psConfigurationSet.HasDetails) 190 { 191 if (!psConfigurationSet.CanProcess()) 192 { 193 throw new InvalidOperationException(); 194 } 195 196 var runningTask = this.RunOnMTA<PSConfigurationSet>( 197 async () => 198 { 199 try 200 { 201 psConfigurationSet = await this.GetSetDetailsAsync(psConfigurationSet, false); 202 } 203 finally 204 { 205 psConfigurationSet.DoneProcessing(); 206 } 207 208 return psConfigurationSet; 209 }); 210 211 this.Wait(runningTask); 212 psConfigurationSet = runningTask.Result; 213 } 214 else 215 { 216 this.Write(StreamType.Warning, "Details already obtained for this set"); 217 } 218 219 this.Write(StreamType.Object, psConfigurationSet); 220 } 221 222 /// <summary> 223 /// Starts configuration. 224 /// </summary> 225 /// <param name="psConfigurationSet">PSConfigurationSet.</param> 226 public void StartApply(PSConfigurationSet psConfigurationSet) 227 { 228 // if (psConfigurationSet.Set.State == ConfigurationSetState.Completed) 229 if (psConfigurationSet.ApplyCompleted) 230 { 231 this.Write(StreamType.Warning, "Processing this set is completed"); 232 throw new InvalidOperationException(); 233 } 234 235 if (!psConfigurationSet.CanProcess()) 236 { 237 throw new InvalidOperationException(); 238 } 239 240 var configurationJob = this.StartApplyInternal(psConfigurationSet); 241 this.Write(StreamType.Object, configurationJob); 242 } 243 244 /// <summary> 245 /// Applies configuration. 246 /// </summary> 247 /// <param name="psConfigurationSet">PSConfigurationSet.</param> 248 public void Apply(PSConfigurationSet psConfigurationSet) => this.ContinueHelper(this.StartApplyInternal(psConfigurationSet)); 249 250 /// <summary> 251 /// Continue a configuration job. 252 /// </summary> 253 /// <param name="psConfigurationJob">The configuration job.</param> 254 public void Continue(PSConfigurationJob psConfigurationJob) 255 { 256 if (psConfigurationJob.ApplyConfigurationTask.IsCompleted) 257 { 258 // It is safe to print all output. 259 psConfigurationJob.StartCommand.ConsumeAndWriteStreams(this); 260 261 this.Write(StreamType.Verbose, "The task was completed before waiting"); 262 if (psConfigurationJob.ApplyConfigurationTask.IsCompletedSuccessfully) 263 { 264 this.Write(StreamType.Verbose, "Completed successfully"); 265 this.Write(StreamType.Object, psConfigurationJob.ApplyConfigurationTask.Result); 266 return; 267 } 268 else if (psConfigurationJob.ApplyConfigurationTask.IsFaulted) 269 { 270 this.Write(StreamType.Verbose, "Completed faulted before waiting"); 271 272 // Maybe just write error? 273 throw psConfigurationJob.ApplyConfigurationTask.Exception!; 274 } 275 } 276 277 this.ContinueHelper(psConfigurationJob); 278 } 279 280 /// <summary> 281 /// Test configuration. 282 /// </summary> 283 /// <param name="psConfigurationSet">PSConfigurationSet.</param> 284 public void Test(PSConfigurationSet psConfigurationSet) 285 { 286 psConfigurationSet.PsProcessor.UpdateDiagnosticCmdlet(this); 287 288 if (!psConfigurationSet.CanProcess()) 289 { 290 throw new InvalidOperationException(); 291 } 292 293 var runningTask = this.RunOnMTA<PSTestConfigurationSetResult>( 294 async () => 295 { 296 try 297 { 298 return await this.TestConfigurationAsync(psConfigurationSet); 299 } 300 finally 301 { 302 psConfigurationSet.DoneProcessing(); 303 } 304 }); 305 306 this.Wait(runningTask); 307 this.Write(StreamType.Object, runningTask.Result); 308 } 309 310 /// <summary> 311 /// Validates configuration. 312 /// </summary> 313 /// <param name="psConfigurationSet">PSConfigurationSet.</param> 314 public void Validate(PSConfigurationSet psConfigurationSet) 315 { 316 psConfigurationSet.PsProcessor.UpdateDiagnosticCmdlet(this); 317 318 if (!psConfigurationSet.CanProcess()) 319 { 320 throw new InvalidOperationException(); 321 } 322 323 var runningTask = this.RunOnMTA<PSValidateConfigurationSetResult>( 324 async () => 325 { 326 try 327 { 328 var setResult = await this.ApplyConfigurationAsync(psConfigurationSet, ApplyConfigurationSetFlags.PerformConsistencyCheckOnly); 329 return new PSValidateConfigurationSetResult(setResult); 330 } 331 finally 332 { 333 psConfigurationSet.DoneProcessing(); 334 } 335 }); 336 337 this.Wait(runningTask); 338 this.Write(StreamType.Object, runningTask.Result); 339 } 340 341 /// <summary> 342 /// Cancels a configuration job. 343 /// </summary> 344 /// <param name="psConfigurationJob">PSConfiguration job.</param> 345 public void Cancel(PSConfigurationJob psConfigurationJob) 346 { 347 psConfigurationJob.StartCommand.Cancel(); 348 } 349 350 /// <summary> 351 /// Removes a configuration set from history. 352 /// </summary> 353 /// <param name="psConfigurationSet">PSConfiguration set.</param> 354 public void Remove(PSConfigurationSet psConfigurationSet) 355 { 356 psConfigurationSet.Set.Remove(); 357 } 358 359 /// <summary> 360 /// Serializes a configuration set and outputs the string. 361 /// </summary> 362 /// <param name="psConfigurationSet">PSConfiguration set.</param> 363 public void Serialize(PSConfigurationSet psConfigurationSet) 364 { 365 // Start task. 366 var result = this.RunOnMTA<string>( 367 () => 368 { 369 return this.SerializeMTA(psConfigurationSet); 370 }); 371 372 this.Write(StreamType.Object, result); 373 } 374 375 private void ContinueHelper(PSConfigurationJob psConfigurationJob) 376 { 377 // Signal the command that it can write to streams and wait for task. 378 this.Write(StreamType.Verbose, "Waiting for task to complete"); 379 psConfigurationJob.StartCommand.Wait(psConfigurationJob.ApplyConfigurationTask, this); 380 this.Write(StreamType.Object, psConfigurationJob.ApplyConfigurationTask.Result); 381 } 382 383 private IConfigurationSetProcessorFactory CreatePowerShellProcessorFactory(OpenConfigurationParameters openParams) 384 { 385 var factory = new PowerShellConfigurationSetProcessorFactory(); 386 387 var properties = factory.As<IPowerShellConfigurationProcessorFactoryProperties>(); 388 properties.Policy = openParams.Policy; 389 properties.ProcessorType = PowerShellConfigurationProcessorType.Default; 390 properties.Location = openParams.Location; 391 if (properties.Location == PowerShellConfigurationProcessorLocation.Custom) 392 { 393 properties.CustomLocation = openParams.CustomLocation; 394 } 395 396 return factory; 397 } 398 399 private async Task<IConfigurationSetProcessorFactory> CreateDSCv3ProcessorFactory(OpenConfigurationParameters openParams) 400 { 401 var factory = new DSCv3ConfigurationSetProcessorFactory(); 402 403 var factoryMap = factory.As<IDictionary<string, string>>(); 404 if (!string.IsNullOrEmpty(openParams.ProcessorPath)) 405 { 406 factoryMap.Add(DSCv3FactoryMapKeyDscExecutablePath, openParams.ProcessorPath); 407 } 408 else 409 { 410 while (true) 411 { 412 string? nextTransition = null; 413 factoryMap.TryGetValue(DSCv3FactoryMapKeyFindDscStateMachine, out nextTransition); 414 415 if (nextTransition == "Found") 416 { 417 break; 418 } 419 else if (nextTransition == "InstallStable") 420 { 421 this.Write(StreamType.Verbose, "Installing stable DSC..."); 422 await this.InstallDSCv3Package(openParams, StableDSCv3PackageId); 423 } 424 else if (nextTransition == "InstallPreview") 425 { 426 this.Write(StreamType.Verbose, "Installing preview DSC..."); 427 await this.InstallDSCv3Package(openParams, PreviewDSCv3PackageId); 428 } 429 else if (nextTransition == "NotFound") 430 { 431 this.Write(StreamType.Warning, Resources.ConfigurationInstallDscPackageFailed); 432 throw new FileNotFoundException(Resources.DscExeNotFound, "dsc.exe"); 433 } 434 else 435 { 436 this.Write(StreamType.Warning, $"Unrecognized value from FindDscStateMachine: {nextTransition ?? "<null>"}"); 437 throw new InvalidOperationException($"Internal error: Unrecognized value from FindDscStateMachine: {nextTransition ?? "<null>"}"); 438 } 439 } 440 } 441 442 return factory; 443 } 444 445 private async Task InstallDSCv3Package(OpenConfigurationParameters openParams, string productId) 446 { 447 this.Write(StreamType.Information, Resources.ConfigurationInstallDscPackage); 448 449 InitialSessionState initialSessionState = InitialSessionState.CreateDefault(); 450 initialSessionState.ExecutionPolicy = openParams.ExecutionPolicy; 451 Runspace runspace = RunspaceFactory.CreateRunspace(initialSessionState); 452 runspace.Open(); 453 PowerShell installDSCv3 = PowerShell.Create(runspace).AddScript( 454 $@" 455 if (-not (Get-Module -ListAvailable -Name {WinGetClientModule})) 456 {{ 457 Install-Module -Name {WinGetClientModule} -Confirm:$False -Force 458 }} 459 460 $installResult = Install-WingetPackage -Id {productId} -Source msstore 461 if ($installResult.Status -ne 'Ok') 462 {{ 463 Write-Error ""Failed to install DSCv3 package. Status: $($installResult.Status). ExtendedErrorCode: $($installResult.ExtendedErrorCode)."" 464 }} 465 "); 466 467 await installDSCv3.InvokeAsync(); 468 469 if (installDSCv3.HadErrors) 470 { 471 this.Write(StreamType.Verbose, installDSCv3.GetErrorMessage() ?? "<Unknown error>"); 472 this.Write(StreamType.Warning, Resources.ConfigurationInstallDscPackageFailed); 473 throw new FileNotFoundException(Resources.DscExeNotFound, "dsc.exe"); 474 } 475 } 476 477 private async Task<PSConfigurationProcessor> CreateConfigurationProcessorWithSet(OpenConfigurationParameters openParams, ConfigurationSet set) 478 { 479 string processorIdentifier = set.Environment.ProcessorIdentifier; 480 481 if (string.IsNullOrEmpty(processorIdentifier) || ProcessorEnginePowerShell.Equals(processorIdentifier, StringComparison.OrdinalIgnoreCase)) 482 { 483 // Default to PowerShell 484 return new PSConfigurationProcessor(this.CreatePowerShellProcessorFactory(openParams), this, openParams.CanUseTelemetry); 485 } 486 else if (ProcessorEngineDSCv3.Equals(processorIdentifier, StringComparison.OrdinalIgnoreCase)) 487 { 488 return new PSConfigurationProcessor(await this.CreateDSCv3ProcessorFactory(openParams), this, openParams.CanUseTelemetry); 489 } 490 else 491 { 492 throw new NotSupportedException(string.Format(Resources.ProcessorEngineNotSupported, processorIdentifier)); 493 } 494 } 495 496 private async Task<PSConfigurationSet?> OpenConfigurationSetAsync(OpenConfigurationParameters openParams) 497 { 498 this.Write(StreamType.Verbose, Resources.ConfigurationInitializing); 499 500 var processorWithoutFactory = new PSConfigurationProcessor(null, this, openParams.CanUseTelemetry); 501 502 if (!openParams.FromHistory) 503 { 504 this.Write(StreamType.Verbose, Resources.ConfigurationReadingConfigFile); 505 var stream = await FileRandomAccessStream.OpenAsync(openParams.ConfigFile, FileAccessMode.Read); 506 507 OpenConfigurationSetResult openResult = await processorWithoutFactory.Processor.OpenConfigurationSetAsync(stream); 508 if (openResult.ResultCode != null) 509 { 510 throw new OpenConfigurationSetException(openResult, openParams.ConfigFile); 511 } 512 513 var set = openResult.Set; 514 515 // This should match winget's OpenConfigurationSet or OpenConfigurationSetAsync 516 // should be modify to take the full path and handle it. 517 set.Name = Path.GetFileName(openParams.ConfigFile); 518 set.Origin = Path.GetDirectoryName(openParams.ConfigFile); 519 set.Path = openParams.ConfigFile; 520 521 return new PSConfigurationSet(await this.CreateConfigurationProcessorWithSet(openParams, set), set); 522 } 523 else 524 { 525 Guid instanceIdentifier = Guid.Parse(openParams.ConfigFile); 526 527 this.Write(StreamType.Verbose, Resources.ConfigurationReadingConfigHistory); 528 529 var historySets = await processorWithoutFactory.Processor.GetConfigurationHistoryAsync(); 530 531 ConfigurationSet? result = null; 532 foreach (var historySet in historySets) 533 { 534 if (historySet.InstanceIdentifier == instanceIdentifier) 535 { 536 result = historySet; 537 break; 538 } 539 } 540 541 return result != null ? new PSConfigurationSet(await this.CreateConfigurationProcessorWithSet(openParams, result), result) : null; 542 } 543 } 544 545 private async Task<PSConfigurationSet[]> GetConfigurationSetHistoryAsync(OpenConfigurationParameters openParams) 546 { 547 this.Write(StreamType.Verbose, Resources.ConfigurationInitializing); 548 549 var processorWithoutFactory = new PSConfigurationProcessor(null, this, openParams.CanUseTelemetry); 550 551 this.Write(StreamType.Verbose, Resources.ConfigurationReadingConfigHistory); 552 553 var historySets = await processorWithoutFactory.Processor.GetConfigurationHistoryAsync(); 554 555 PSConfigurationSet[] result = new PSConfigurationSet[historySets.Count]; 556 for (int i = 0; i < historySets.Count; ++i) 557 { 558 result[i] = new PSConfigurationSet(await this.CreateConfigurationProcessorWithSet(openParams, historySets[i]), historySets[i]); 559 } 560 561 return result; 562 } 563 564 private PSConfigurationJob StartApplyInternal(PSConfigurationSet psConfigurationSet) 565 { 566 psConfigurationSet.PsProcessor.UpdateDiagnosticCmdlet(this); 567 568 var runningTask = this.RunOnMTA<PSApplyConfigurationSetResult>( 569 async () => 570 { 571 try 572 { 573 var setResult = await this.ApplyConfigurationAsync(psConfigurationSet, ApplyConfigurationSetFlags.None); 574 psConfigurationSet.ApplyCompleted = true; 575 return new PSApplyConfigurationSetResult(setResult); 576 } 577 finally 578 { 579 psConfigurationSet.DoneProcessing(); 580 } 581 }); 582 583 return new PSConfigurationJob(runningTask, this); 584 } 585 586 private async Task<ApplyConfigurationSetResult> ApplyConfigurationAsync(PSConfigurationSet psConfigurationSet, ApplyConfigurationSetFlags flags) 587 { 588 if (!psConfigurationSet.HasDetails) 589 { 590 this.Write(StreamType.Verbose, "Getting details for configuration set"); 591 await this.GetSetDetailsAsync(psConfigurationSet, true); 592 } 593 594 var processor = psConfigurationSet.PsProcessor.Processor; 595 var set = psConfigurationSet.Set; 596 597 var applyProgressOutput = new ApplyConfigurationSetProgressOutput( 598 this, 599 this.GetNewProgressActivityId(), 600 Resources.ConfigurationApply, 601 Resources.OperationInProgress, 602 Resources.OperationCompleted, 603 set.Units.Count); 604 605 var applyTask = processor.ApplySetAsync(set, flags); 606 applyTask.Progress = applyProgressOutput.Progress; 607 608 try 609 { 610 var result = await applyTask.AsTask(this.GetCancellationToken()); 611 applyProgressOutput.HandleProgress(result); 612 return result; 613 } 614 finally 615 { 616 applyProgressOutput.CompleteProgress(); 617 } 618 } 619 620 private async Task<PSTestConfigurationSetResult> TestConfigurationAsync(PSConfigurationSet psConfigurationSet) 621 { 622 if (!psConfigurationSet.HasDetails) 623 { 624 this.Write(StreamType.Verbose, "Getting details for configuration set"); 625 await this.GetSetDetailsAsync(psConfigurationSet, true); 626 } 627 628 var processor = psConfigurationSet.PsProcessor.Processor; 629 var set = psConfigurationSet.Set; 630 631 var testProgressOutput = new TestConfigurationSetProgressOutput( 632 this, 633 this.GetNewProgressActivityId(), 634 Resources.ConfigurationAssert, 635 Resources.OperationInProgress, 636 Resources.OperationCompleted, 637 set.Units.Count); 638 639 var testTask = processor.TestSetAsync(set); 640 testTask.Progress = testProgressOutput.Progress; 641 642 try 643 { 644 var result = await testTask.AsTask(this.GetCancellationToken()); 645 testProgressOutput.HandleProgress(result); 646 647 return new PSTestConfigurationSetResult(result); 648 } 649 finally 650 { 651 testProgressOutput.CompleteProgress(); 652 } 653 } 654 655 private async Task<PSConfigurationSet> GetSetDetailsAsync(PSConfigurationSet psConfigurationSet, bool warnOnError) 656 { 657 var processor = psConfigurationSet.PsProcessor.Processor; 658 var set = psConfigurationSet.Set; 659 var totalUnitsCount = set.Units.Count; 660 661 if (totalUnitsCount == 0) 662 { 663 this.Write(StreamType.Warning, Resources.ConfigurationFileEmpty); 664 return psConfigurationSet; 665 } 666 667 try 668 { 669 var detailsProgressOutput = new GetConfigurationSetDetailsProgressOutput( 670 this, 671 this.GetNewProgressActivityId(), 672 Resources.ConfigurationGettingDetails, 673 Resources.OperationInProgress, 674 Resources.OperationCompleted, 675 totalUnitsCount); 676 677 var detailsTask = processor.GetSetDetailsAsync(set, ConfigurationUnitDetailFlags.ReadOnly); 678 detailsTask.Progress = detailsProgressOutput.Progress; 679 680 try 681 { 682 var result = await detailsTask.AsTask(this.GetCancellationToken()); 683 detailsProgressOutput.HandleProgress(result); 684 685 if (result.UnitResults.Where(u => u.ResultInformation.ResultCode != null).Any()) 686 { 687 throw new GetDetailsException(result.UnitResults); 688 } 689 690 if (detailsProgressOutput.UnitsShown == 0) 691 { 692 throw new GetDetailsException(); 693 } 694 695 psConfigurationSet.HasDetails = true; 696 } 697 finally 698 { 699 detailsProgressOutput.CompleteProgress(); 700 } 701 } 702 catch (Exception e) 703 { 704 if (warnOnError) 705 { 706 this.Write(StreamType.Warning, e.Message); 707 } 708 else 709 { 710 throw; 711 } 712 } 713 714 return psConfigurationSet; 715 } 716 717 /// <summary> 718 /// Serializes a configuration set and outputs the string. 719 /// </summary> 720 /// <param name="psConfigurationSet">PSConfiguration set.</param> 721 /// <returns>The string version of the set.</returns> 722 private string SerializeMTA(PSConfigurationSet psConfigurationSet) 723 { 724 MemoryStream stream = new MemoryStream(); 725 psConfigurationSet.Set.Serialize(stream.AsOutputStream()); 726 return Encoding.UTF8.GetString(stream.ToArray()); 727 } 728 } 729 }