winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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 }