Skip to content

Commit

Permalink
Not zipping and unzipping means more speed (#148)
Browse files Browse the repository at this point in the history
  • Loading branch information
svrooij authored Dec 5, 2024
1 parent 118f882 commit 15f81e0
Show file tree
Hide file tree
Showing 10 changed files with 130 additions and 42 deletions.
43 changes: 41 additions & 2 deletions src/Svrooij.WinTuner.CmdLets/Commands/DeployWtWin32App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@ public class DeployWtWin32App : BaseIntuneCmdlet
[ServiceDependency]
private WingetIntune.Graph.GraphClientFactory? gcf;

private bool isPartialPackage;
private string? metadataFilename;

/// <inheritdoc/>
protected override async Task ProcessAuthenticatedAsync(IAuthenticationProvider provider, CancellationToken cancellationToken)
{
Expand Down Expand Up @@ -241,7 +244,14 @@ protected override async Task ProcessAuthenticatedAsync(IAuthenticationProvider
if (RoleScopeTags is not null && RoleScopeTags.Any())
{
logger?.LogDebug("Adding role scope tags to app");
App.RoleScopeTagIds = RoleScopeTags.AsList();
if (App.RoleScopeTagIds is null)
{
App.RoleScopeTagIds = new();
}
foreach (var tag in RoleScopeTags)
{
App.RoleScopeTagIds.Add(tag);
}
logger?.LogInformation("Role scope tags added to app {@RoleScopeTags}", App?.RoleScopeTagIds);
}

Expand All @@ -252,7 +262,36 @@ protected override async Task ProcessAuthenticatedAsync(IAuthenticationProvider

logger?.LogInformation("Uploading Win32App {DisplayName} to Intune with file {IntuneWinFile}", App!.DisplayName, IntuneWinFile);
var graphServiceClient = gcf!.CreateClient(provider);
var newApp = await graphAppUploader!.CreateNewAppAsync(graphServiceClient, App, IntuneWinFile!, LogoPath, cancellationToken);

if (IntuneWinFile is null)
{
var ex = new ArgumentException("No IntuneWinFile was provided");
logger?.LogError(ex, "No IntuneWinFile was provided");
throw ex;
}

// Check if the file exists
if (!File.Exists(IntuneWinFile))
{
var partialFileName = Path.Combine(Path.GetDirectoryName(IntuneWinFile)!, Path.GetFileNameWithoutExtension(IntuneWinFile) + ".partial.intunewin");
metadataFilename = Path.Combine(Path.GetDirectoryName(IntuneWinFile)!, "metadata.xml");
if (File.Exists(partialFileName) && File.Exists(metadataFilename))
{
logger?.LogDebug("Found partial file {PartialFileName} and metadata.xml, using that instead of {IntuneWinFile}", partialFileName, IntuneWinFile);
IntuneWinFile = partialFileName;
isPartialPackage = true;
}
else
{
var ex = new FileNotFoundException("IntuneWin file not found", IntuneWinFile);
logger?.LogError(ex, "IntuneWin file not found");
throw ex;
}
}

var newApp = isPartialPackage
? await graphAppUploader!.CreateNewAppAsync(graphServiceClient, App!, IntuneWinFile!, metadataFilename!, logoPath: LogoPath, cancellationToken: cancellationToken)
: await graphAppUploader!.CreateNewAppAsync(graphServiceClient, App!, IntuneWinFile!, LogoPath, cancellationToken);
logger?.LogInformation("Created Win32App {DisplayName} with id {appId}", newApp!.DisplayName, newApp.Id);

// Check if we need to supersede an app
Expand Down
15 changes: 14 additions & 1 deletion src/Svrooij.WinTuner.CmdLets/Commands/NewWtWingetPackage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,18 @@ public class NewWtWingetPackage : DependencyCmdlet<Startup>
HelpMessage = "Prefered installer type")]
public WingetIntune.Models.InstallerType PreferedInstaller { get; set; } = WingetIntune.Models.InstallerType.Msi;

/// <summary>
/// Creating a partial package means that the files are not zipped into the intunewin file, but are left as is.
/// </summary>
[Parameter(
Mandatory = false,
Position = 10,
ValueFromPipeline = false,
ValueFromPipelineByPropertyName = false,
DontShow = true, // this is still experimental
HelpMessage = "Creating a partial package means that the files are not zipped into the intunewin file, but are left as is.")]
public SwitchParameter PartialPackage { get; set; }

[ServiceDependency]
private ILogger<NewWtWingetPackage> logger;

Expand Down Expand Up @@ -176,7 +188,8 @@ public override async Task ProcessRecordAsync(CancellationToken cancellationToke
PackageScript = PackageScript,
Locale = Locale,
OverrideArguments = InstallerArguments,
InstallerType = PreferedInstaller
InstallerType = PreferedInstaller,
PartialPackage = PartialPackage,
},
cancellationToken: cancellationToken);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public ComputeBestInstallerForPackageCommand(ILogger<ComputeBestInstallerForPack

public void Execute(ref PackageInfo package, PackageOptions? packageOptions = null)
{
packageOptions ??= new PackageOptions();
packageOptions ??= PackageOptions.Create();
LogComputingBestInstaller(package.PackageIdentifier!, package.Version!, packageOptions.Architecture);
var installer = package.GetBestInstaller(packageOptions);
if (installer is null)
Expand Down
78 changes: 48 additions & 30 deletions src/WingetIntune/Graph/GraphAppUploader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,46 @@ public GraphAppUploader(ILogger<GraphAppUploader> logger, IFileManager fileManag
{
throw new FileNotFoundException("IntuneWin file not found", intunePackageFile);
}

var tempFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
try
{
// Extract intunewin file to get the metadata file

await fileManager.ExtractFileToFolderAsync(intunePackageFile, tempFolder, cancellationToken);
var metadataFile = IntuneMetadata.GetMetadataPath(tempFolder);
var intuneWinFile = IntuneMetadata.GetContentsPath(tempFolder);

if (!fileManager.FileExists(metadataFile))
{
throw new FileNotFoundException("Metadata file not found", metadataFile);
}
if (!fileManager.FileExists(intuneWinFile))
{
throw new FileNotFoundException("IntuneWin file not found", intuneWinFile);
}

return await CreateNewAppAsync(graphServiceClient, win32LobApp, intuneWinFile, metadataFile, logoPath, cancellationToken);

}
finally
{
fileManager.DeleteFileOrFolder(tempFolder);
}

}

public async Task<Win32LobApp?> CreateNewAppAsync(GraphServiceClient graphServiceClient, Win32LobApp win32LobApp, string partialIntuneWinFile, string metadataFile, string? logoPath = null, CancellationToken cancellationToken = default)
{
#if NET8_0_OR_GREATER
ArgumentNullException.ThrowIfNull(graphServiceClient);
ArgumentNullException.ThrowIfNull(win32LobApp);
ArgumentException.ThrowIfNullOrEmpty(partialIntuneWinFile);
#endif
if (!fileManager.FileExists(partialIntuneWinFile))
{
throw new FileNotFoundException("IntuneWin file not found", partialIntuneWinFile);
}
if (win32LobApp.LargeIcon is null && !string.IsNullOrEmpty(logoPath) && fileManager.FileExists(logoPath))
{
win32LobApp.LargeIcon = new MimeContent
Expand All @@ -50,7 +90,7 @@ public GraphAppUploader(ILogger<GraphAppUploader> logger, IFileManager fileManag
await Task.Delay(1000, cancellationToken);

// Upload the content and update the app with the latest commited file id.
await CreateNewContentVersionAsync(graphServiceClient, app!.Id!, intunePackageFile, cancellationToken);
await CreateNewContentVersionAsync(graphServiceClient, app!.Id!, partialIntuneWinFile, metadataFile, cancellationToken);

// Load the app again to get the final state
Win32LobApp? updatedApp = await graphServiceClient.DeviceAppManagement.MobileApps[app.Id].GetAsync(cancellationToken: cancellationToken) as Win32LobApp;
Expand Down Expand Up @@ -97,26 +137,21 @@ public GraphAppUploader(ILogger<GraphAppUploader> logger, IFileManager fileManag
}
}

public async Task<Win32LobApp?> CreateNewContentVersionAsync(GraphServiceClient graphServiceClient, string appId, string intunePackageFile, CancellationToken cancellationToken = default)
public async Task<Win32LobApp?> CreateNewContentVersionAsync(GraphServiceClient graphServiceClient, string appId, string partialIntuneWinFile, string metadataFile, CancellationToken cancellationToken = default)
{
#if NET8_0_OR_GREATER
ArgumentNullException.ThrowIfNull(graphServiceClient);
ArgumentException.ThrowIfNullOrEmpty(appId);
ArgumentException.ThrowIfNullOrEmpty(intunePackageFile);
ArgumentException.ThrowIfNullOrEmpty(partialIntuneWinFile);
#endif
if (!fileManager.FileExists(intunePackageFile))
if (!fileManager.FileExists(partialIntuneWinFile))
{
throw new FileNotFoundException("IntuneWin file not found", intunePackageFile);
throw new FileNotFoundException("IntuneWin file not found", partialIntuneWinFile);
}
logger.LogInformation("Creating new content version for app {appId}", appId);

var tempFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());

logger.LogDebug("Extracting intunewin file {file} to {tempFolder}", intunePackageFile, tempFolder);
await fileManager.ExtractFileToFolderAsync(intunePackageFile, tempFolder, cancellationToken);

// Load the metadata file
var info = IntuneMetadata.GetApplicationInfo(await fileManager.ReadAllBytesAsync(IntuneMetadata.GetMetadataPath(tempFolder), cancellationToken))!;
var info = IntuneMetadata.GetApplicationInfo(await fileManager.ReadAllBytesAsync(metadataFile, cancellationToken))!;

// Create the content version
var contentVersion = await graphServiceClient.DeviceAppManagement.MobileApps[appId].GraphWin32LobApp.ContentVersions.PostAsync(new MobileAppContent(), cancellationToken: cancellationToken);
Expand All @@ -128,42 +163,27 @@ public GraphAppUploader(ILogger<GraphAppUploader> logger, IFileManager fileManag
Name = info.FileName,
IsDependency = false,
Size = info.UnencryptedContentSize,
SizeEncrypted = fileManager.GetFileSize(IntuneMetadata.GetContentsPath(tempFolder)),
SizeEncrypted = fileManager.GetFileSize(partialIntuneWinFile),
Manifest = null,
};

logger.LogDebug("Creating content file {name} {size} {sizeEncrypted}", mobileAppContentFileRequest.Name, mobileAppContentFileRequest.Size, mobileAppContentFileRequest.SizeEncrypted);

MobileAppContentFile? mobileAppContentFile = await graphServiceClient.DeviceAppManagement.MobileApps[appId].GraphWin32LobApp.ContentVersions[contentVersion.Id!].Files.PostAsync(mobileAppContentFileRequest, cancellationToken: cancellationToken);
//var mobileAppContentFile = await graphServiceClient.Intune_CreateWin32LobAppContentVersionFileAsync(appId, contentVersion.Id!, mobileAppContentFileRequest, cancellationToken);

logger.LogDebug("Created content file {id}", mobileAppContentFile?.Id);
// Wait for a bit (it's generating the azure storage uri)
await Task.Delay(3000, cancellationToken);

MobileAppContentFile? updatedMobileAppContentFile = await graphServiceClient.DeviceAppManagement.MobileApps[appId].GraphWin32LobApp.ContentVersions[contentVersion.Id!].Files[mobileAppContentFile!.Id!].GetAsync(cancellationToken: cancellationToken);

//MobileAppContentFile? updatedMobileAppContentFile = await graphServiceClient.Intune_GetWin32LobAppContentVersionFileAsync(appId,
// contentVersion!.Id!,
// mobileAppContentFile!.Id!,
// cancellationToken);

logger.LogDebug("Loaded content file {id} {blobUri}", updatedMobileAppContentFile?.Id, updatedMobileAppContentFile?.AzureStorageUri);

await azureFileUploader.UploadFileToAzureAsync(
IntuneMetadata.GetContentsPath(tempFolder),
partialIntuneWinFile,
new Uri(updatedMobileAppContentFile!.AzureStorageUri!),
cancellationToken);

logger.LogDebug("Uploaded content file {id} {blobUri}", updatedMobileAppContentFile.Id, updatedMobileAppContentFile.AzureStorageUri);
fileManager.DeleteFileOrFolder(tempFolder);



//await graphServiceClient.DeviceAppManagement.MobileApps[appId].GraphWin32LobApp.ContentVersions[contentVersion.Id!].Files[mobileAppContentFile!.Id!].Commit
// .PostAsync(new Microsoft.Graph.Beta.DeviceAppManagement.MobileApps.Item.GraphWin32LobApp.ContentVersions.Item.Files.Item.Commit.CommitPostRequestBody {
// FileEncryptionInfo = mapper.ToGraphEncryptionInfo(info.EncryptionInfo)
// }, cancellationToken: cancellationToken);

var encryptionInfo = mapper.ToFileEncryptionInfo(info.EncryptionInfo);
await graphServiceClient.Intune_CommitWin32LobAppContentVersionFileAsync(appId,
Expand All @@ -183,6 +203,4 @@ await graphServiceClient.Intune_CommitWin32LobAppContentVersionFileAsync(appId,

return app;
}


}
2 changes: 1 addition & 1 deletion src/WingetIntune/Interfaces/IIntunePackager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ namespace WingetIntune.Interfaces;

public interface IIntunePackager
{
Task<string> CreatePackage(string inputFolder, string outputFolder, string installerFilename, PackageInfo? packageInfo = null, CancellationToken cancellationToken = default);
Task<string> CreatePackage(string inputFolder, string outputFolder, string installerFilename, PackageInfo? packageInfo = null, bool partialPackage = false, CancellationToken cancellationToken = default);
}
4 changes: 2 additions & 2 deletions src/WingetIntune/Intune/IntuneManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public IntuneManager(ILoggerFactory? loggerFactory, IFileManager fileManager, IP
var packageFolder = fileManager.CreateFolderForPackage(outputFolder, packageInfo.PackageIdentifier!, packageInfo.Version!);
var installerPath = await DownloadInstallerAsync(packageTempFolder, packageInfo, cancellationToken);
LoadMsiDetails(installerPath, ref packageInfo, packageOptions.OverrideArguments);
var intunePackage = await intunePackager.CreatePackage(packageTempFolder, packageFolder, packageInfo.InstallerFilename!, packageInfo, cancellationToken);
var intunePackage = await intunePackager.CreatePackage(packageTempFolder, packageFolder, packageInfo.InstallerFilename!, packageInfo, packageOptions.PartialPackage, cancellationToken: cancellationToken);
await DownloadLogoAsync(packageFolder, packageInfo.PackageIdentifier!, cancellationToken);
await WriteReadmeAsync(packageFolder, packageInfo, cancellationToken);
await WritePackageInfo(packageFolder, packageInfo, cancellationToken);
Expand Down Expand Up @@ -173,7 +173,7 @@ await fileManager.WriteAllTextAsync(
cancellationToken);
}

var intuneFile = await intunePackager.CreatePackage(packageTempFolder, packageFolder, packageInfo.InstallerFilename!, packageInfo, cancellationToken);
var intuneFile = await intunePackager.CreatePackage(packageTempFolder, packageFolder, packageInfo.InstallerFilename!, packageInfo, packageOptions.PartialPackage, cancellationToken: cancellationToken);
await DownloadLogoAsync(packageFolder, packageInfo.PackageIdentifier!, cancellationToken);

var detectionScript = IntuneManagerConstants.PsDetectionCommandTemplate.Replace("{packageId}", packageInfo.PackageIdentifier!).Replace("{version}", packageInfo.Version);
Expand Down
21 changes: 19 additions & 2 deletions src/WingetIntune/Intune/LibraryIntunePackager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public LibraryIntunePackager(ILogger<Packager> logger)
this.logger = logger;
}

public async Task<string> CreatePackage(string inputFolder, string outputFolder, string installerFilename, PackageInfo? packageInfo = null, CancellationToken cancellationToken = default)
public async Task<string> CreatePackage(string inputFolder, string outputFolder, string installerFilename, PackageInfo? packageInfo = null, bool partialPackage = false, CancellationToken cancellationToken = default)
{
logger.LogInformation("Creating Intune package from {inputFolder}", inputFolder);
var details = new SvRooij.ContentPrep.Models.ApplicationDetails
Expand All @@ -32,8 +32,25 @@ public async Task<string> CreatePackage(string inputFolder, string outputFolder,
};
}

var info = await packager.CreatePackage(inputFolder, Path.Combine(inputFolder, installerFilename), outputFolder, details, cancellationToken);
if (partialPackage)
{
if (!Directory.Exists(outputFolder))
{
Directory.CreateDirectory(outputFolder);
}
var outputFilename = Path.GetFileNameWithoutExtension(installerFilename) + ".partial.intunewin";

using var partialFile = new FileStream(Path.Combine(outputFolder, outputFilename), FileMode.Create, FileAccess.ReadWrite, FileShare.Read, 4096, true);

var info = await packager.CreateUploadablePackage(inputFolder, partialFile, details, cancellationToken);
await partialFile.FlushAsync(cancellationToken);
await File.AppendAllTextAsync(Path.Combine(outputFolder, "metadata.xml"), info!.ToXml(), cancellationToken);

return outputFilename;
}


_ = await packager.CreatePackage(inputFolder, Path.Combine(inputFolder, installerFilename), outputFolder, details, cancellationToken);
return Path.GetFileNameWithoutExtension(installerFilename) + ".intunewin";
}
}
2 changes: 1 addition & 1 deletion src/WingetIntune/Intune/MetadataManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,6 @@ public Win32LobApp ConvertPackageInfoToWin32App(PackageInfo packageInfo)
/// <returns></returns>
public string GetIntuneWinFileName(string packageFolder, PackageInfo packageInfo)
{
return Path.Combine(packageFolder, Path.GetFileNameWithoutExtension(packageInfo.InstallerFilename!) + ".intunewin");
return Path.GetFullPath(Path.Combine(packageFolder, Path.GetFileNameWithoutExtension(packageInfo.InstallerFilename!) + ".intunewin"));
}
}
4 changes: 2 additions & 2 deletions src/WingetIntune/Intune/ProcessIntunePackager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ public ProcessIntunePackager(IProcessManager processManager, IFileManager fileMa
this.logger = logger;
}

public Task CreatePackage(string inputFolder, string outputFolder, string installerFilename, CancellationToken cancellationToken) => CreatePackage(inputFolder, outputFolder, installerFilename, null, cancellationToken);
public Task CreatePackage(string inputFolder, string outputFolder, string installerFilename, CancellationToken cancellationToken) => CreatePackage(inputFolder, outputFolder, installerFilename, null, false, cancellationToken);

public async Task<string> CreatePackage(string inputFolder, string outputFolder, string installerFilename, PackageInfo? _ = null, CancellationToken cancellationToken = default)
public async Task<string> CreatePackage(string inputFolder, string outputFolder, string installerFilename, PackageInfo? _ = null, bool partialPackage = false, CancellationToken cancellationToken = default)
{
#if NET8_0_OR_GREATER
ArgumentException.ThrowIfNullOrEmpty(inputFolder);
Expand Down
1 change: 1 addition & 0 deletions src/WingetIntune/Models/PackageOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ public class PackageOptions
public bool PackageScript { get; init; }
public string? Locale { get; init; }
public string? OverrideArguments { get; init; }
public bool PartialPackage { get; init; }
public static PackageOptions Create() => new PackageOptions { Architecture = Architecture.X64, InstallerContext = InstallerContext.System, PackageScript = false };
}

0 comments on commit 15f81e0

Please sign in to comment.