public Task CreateVolumes(IReadOnlyCollection <string> packagePaths, string volumeDirectory,
                                  AppAttachVolumeType type            = AppAttachVolumeType.Vhd, bool extractCertificate = false, bool generateScripts = true,
                                  CancellationToken cancellationToken = default, IProgress <ProgressData> progressReporter = null)
        {
            var cmd = new CreateVolumeDto(packagePaths, volumeDirectory)
            {
                ExtractCertificate = extractCertificate,
                GenerateScripts    = generateScripts,
                Type = type
            };

            return(client.Invoke(cmd, cancellationToken, progressReporter));
        }
        public Task CreateVolume(string packagePath, string volumePath, uint size, AppAttachVolumeType type = AppAttachVolumeType.Vhd,
                                 bool extractCertificate = false, bool generateScripts = true, CancellationToken cancellationToken = default,
                                 IProgress <ProgressData> progressReporter = null)
        {
            var cmd = new CreateVolumeDto(packagePath, volumePath)
            {
                ExtractCertificate = extractCertificate,
                GenerateScripts    = generateScripts,
                SizeInMegaBytes    = size,
                Type = type
            };

            return(client.Invoke(cmd, cancellationToken, progressReporter));
        }
        public async Task CreateVolumes(
            IReadOnlyCollection <string> packagePaths,
            string volumeDirectory,
            AppAttachVolumeType type                  = AppAttachVolumeType.Vhd,
            bool extractCertificate                   = false,
            bool generateScripts                      = true,
            CancellationToken cancellationToken       = default,
            IProgress <ProgressData> progressReporter = null)
        {
            if (volumeDirectory == null)
            {
                throw new ArgumentNullException(nameof(volumeDirectory));
            }

            var diskExtension = type.ToString("G").ToLowerInvariant();

            if (!Directory.Exists(volumeDirectory))
            {
                Logger.Info("Creating directory {0}...", volumeDirectory);
                Directory.CreateDirectory(volumeDirectory);
            }

            var allPackagesCount = packagePaths.Count;
            var currentPackage   = 0;

            var volumeCreationStrategy = await this.GetVolumeCreationStrategy(type).ConfigureAwait(false);

            IAppAttachVolumeCreationStrategyInitialization initialization = null;

            try
            {
                initialization = await volumeCreationStrategy.Initialize(cancellationToken).ConfigureAwait(false);

                foreach (var packagePath in packagePaths)
                {
                    var currentVolumeDirectory = type == AppAttachVolumeType.Cim ? Path.Combine(volumeDirectory, Path.GetFileNameWithoutExtension(packagePath)) : volumeDirectory;

                    var packageProgressReporter = new RangeProgress(progressReporter, (int)(currentPackage * 80.0 / allPackagesCount), (int)((currentPackage + 1) * 80.0 / allPackagesCount));
                    currentPackage++;

                    var packageFileInfo = new FileInfo(packagePath);
                    if (!packageFileInfo.Exists)
                    {
                        throw new FileNotFoundException($"File {packagePath} does not exist.", packagePath);
                    }

                    var volumeFileInfo = new FileInfo(Path.Combine(currentVolumeDirectory, Path.GetFileNameWithoutExtension(packagePath) + "." + diskExtension));
                    if (volumeFileInfo.Exists)
                    {
                        volumeFileInfo.Delete();
                    }
                    else if (volumeFileInfo.Directory?.Exists == false)
                    {
                        volumeFileInfo.Directory.Create();
                    }

                    packageProgressReporter.Report(new ProgressData(0, $"Analyzing {Path.GetFileName(packagePath)}..."));

                    await volumeCreationStrategy.CreateVolume(packagePath, volumeFileInfo.FullName, null, cancellationToken, packageProgressReporter).ConfigureAwait(false);

                    if (extractCertificate)
                    {
                        packageProgressReporter.Report(new ProgressData(100, $"Extracting certificate from {Path.GetFileName(packagePath)}..."));
                        var actualSigningManager = this.signingManager ?? await this.managerFactory.GetProxyFor(SelfElevationLevel.AsInvoker, cancellationToken).ConfigureAwait(false);

                        cancellationToken.ThrowIfCancellationRequested();

                        // ReSharper disable once AssignNullToNotNullAttribute
                        await actualSigningManager.ExtractCertificateFromMsix(packagePath, Path.Combine(volumeFileInfo.DirectoryName, Path.GetFileNameWithoutExtension(volumeFileInfo.FullName)) + ".cer", cancellationToken).ConfigureAwait(false);
                    }
                }

                if (type == AppAttachVolumeType.Cim)
                {
                    // Currently JSON and PS1 are only supported for VHD(X)...
                }
                else
                {
                    var jsonData = new List <JsonData>();

                    if (generateScripts)
                    {
                        progressReporter?.Report(new ProgressData(90, "Creating scripts..."));
                        await CreateScripts(volumeDirectory, cancellationToken).ConfigureAwait(false);
                    }

                    progressReporter?.Report(new ProgressData(95, "Creating JSON file..."));
                    foreach (var volumePath in packagePaths.Select(p => Path.Combine(volumeDirectory, Path.GetFileNameWithoutExtension(p) + "." + diskExtension)))
                    {
                        var volumeData = await this.GetExpandedPackageData(volumePath, cancellationToken).ConfigureAwait(false);

                        var volumeGuid           = volumeData.Item1;
                        var volumeMsixFolderName = volumeData.Item2;

                        jsonData.Add(new JsonData(Path.GetFileName(volumePath), Path.GetFileNameWithoutExtension(volumePath), volumeGuid, volumeMsixFolderName));
                    }

                    var jsonPath = Path.Combine(volumeDirectory, "app-attach.json");

                    await CreateJson(jsonPath, jsonData, cancellationToken).ConfigureAwait(false);
                }
            }
            finally
            {
                await volumeCreationStrategy.Finish(initialization, cancellationToken).ConfigureAwait(false);
            }
        }
        private async Task <IAppAttachVolumeCreationStrategy> GetVolumeCreationStrategy(AppAttachVolumeType type)
        {
            IAppAttachVolumeCreationStrategy volumeCreationStrategy;

            if (type == AppAttachVolumeType.Cim)
            {
                volumeCreationStrategy = new AppAttachVolumeCreationMsixMgrStrategy();
            }
            else
            {
                var config = await this.configurationService.GetCurrentConfigurationAsync().ConfigureAwait(false);

                if (config.AppAttach?.UseMsixMgrForVhdCreation == true)
                {
                    // Forced MSIXMGR by the user configuration (non-default)
                    volumeCreationStrategy = new AppAttachVolumeCreationMsixMgrStrategy();
                }
                else
                {
                    volumeCreationStrategy = new AppAttachVolumeCreationMsixHeroStrategy();
                }
            }

            return(volumeCreationStrategy);
        }
        public async Task CreateVolume(
            string packagePath,
            string volumePath,
            uint size,
            AppAttachVolumeType type                  = AppAttachVolumeType.Vhd,
            bool extractCertificate                   = false,
            bool generateScripts                      = true,
            CancellationToken cancellationToken       = default,
            IProgress <ProgressData> progressReporter = null)
        {
            if (packagePath == null)
            {
                throw new ArgumentNullException(nameof(packagePath));
            }

            if (volumePath == null)
            {
                throw new ArgumentNullException(nameof(volumePath));
            }

            var packageFileInfo = new FileInfo(packagePath);

            if (!packageFileInfo.Exists)
            {
                Logger.Error($"File {packagePath} does not exist.");
                throw new FileNotFoundException($"File {packagePath} does not exist.", packagePath);
            }

            var volumeFileInfo = new FileInfo(volumePath);

            if (volumeFileInfo.Directory != null && !volumeFileInfo.Directory.Exists)
            {
                Logger.Info($"Creating directory {volumeFileInfo.Directory.FullName}...");
                volumeFileInfo.Directory.Create();
            }

            var volumeCreationStrategy = await this.GetVolumeCreationStrategy(type);

            if (File.Exists(volumePath))
            {
                Logger.Info($"Deleting existing file {volumePath}...");
                File.Delete(volumePath);
            }

            progressReporter?.Report(new ProgressData(20, "Unpacking MSIX..."));

            IAppAttachVolumeCreationStrategyInitialization initialization = null;

            try
            {
                initialization = await volumeCreationStrategy.Initialize(cancellationToken).ConfigureAwait(false);

                await volumeCreationStrategy.CreateVolume(packagePath, volumePath, size, cancellationToken).ConfigureAwait(false);

                if (extractCertificate)
                {
                    progressReporter?.Report(new ProgressData(80, "Extracting certificate..."));
                    var actualSigningManager = this.signingManager ?? await this.managerFactory.GetProxyFor(SelfElevationLevel.AsInvoker, cancellationToken).ConfigureAwait(false);

                    cancellationToken.ThrowIfCancellationRequested();

                    // ReSharper disable once AssignNullToNotNullAttribute
                    await actualSigningManager.ExtractCertificateFromMsix(packagePath, Path.Combine(volumeFileInfo.DirectoryName, Path.GetFileNameWithoutExtension(volumeFileInfo.FullName)) + ".cer", cancellationToken).ConfigureAwait(false);
                }

                if (type == AppAttachVolumeType.Cim)
                {
                    // Currently JSON and PS1 are only supported for VHD(X)...
                }
                else if (File.Exists(volumePath))
                {
                    var volumeData = await this.GetExpandedPackageData(volumePath, cancellationToken).ConfigureAwait(false);

                    var volumeGuid           = volumeData.Item1;
                    var volumeMsixFolderName = volumeData.Item2;

                    if (generateScripts)
                    {
                        await CreateScripts(volumeFileInfo.Directory?.FullName, cancellationToken).ConfigureAwait(false);
                    }

                    await CreateJson(
                        // ReSharper disable once AssignNullToNotNullAttribute
                        Path.Combine(Path.GetDirectoryName(volumePath), "app-attach.json"),
                        new[]
                    {
                        new JsonData(volumeFileInfo.Name, Path.GetFileNameWithoutExtension(packagePath), volumeGuid, volumeMsixFolderName)
                    },
                        cancellationToken).ConfigureAwait(false);
                }
            }
            finally
            {
                await volumeCreationStrategy.Finish(initialization, cancellationToken).ConfigureAwait(false);
            }
        }