Skip to content

Commit

Permalink
Add command to show msi info
Browse files Browse the repository at this point in the history
  • Loading branch information
svrooij committed Dec 23, 2024
1 parent 1d6f429 commit 8a41ee6
Show file tree
Hide file tree
Showing 10 changed files with 356 additions and 44 deletions.
5 changes: 5 additions & 0 deletions global.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"sdk": {
"version": "8.0.104"
}
}
95 changes: 95 additions & 0 deletions src/Svrooij.WinTuner.CmdLets/Commands/ShowMsiInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using System;
using System.IO;
using System.Management.Automation;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Svrooij.PowerShell.DependencyInjection;
using WingetIntune;
using WingetIntune.Msi;

namespace Svrooij.WinTuner.CmdLets.Commands;

/// <summary>
/// <para type="synopsis">Show information about an MSI file</para>
/// <para type="description">Show information about an MSI file, this includes the MSI code and version.</para>
/// </summary>
/// <psOrder>100</psOrder>
/// <example>
/// <para type="name">Show information about an MSI file</para>
/// <para type="description">Show information about an MSI file, this includes the MSI code and version.</para>
/// <code>Show-MsiInfo -MsiPath "C:\path\to\file.msi"</code>
/// </example>
/// <example>
/// <para type="name">Show information about an MSI file from URL</para>
/// <para type="description">Download an MSI file and show the details</para>
/// <code>Show-MsiInfo -MsiUrl "https://example.com/file.msi" -OutputPath "C:\path\to"</code>
/// </example>
[Cmdlet(VerbsCommon.Show, "MsiInfo", HelpUri = "https://wintuner.app/docs/wintuner-powershell/Show-MsiInfo", DefaultParameterSetName = nameof(MsiPath))]
[OutputType(typeof(Models.MsiInfo))]
public class ShowMsiInfo : DependencyCmdlet<Startup>
{
/// <summary>
/// <para type="description">Path to the MSI file</para>
/// </summary>
[Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = nameof(MsiPath))]
public string? MsiPath { get; set; }

/// <summary>
/// <para type="description">URL to the MSI file</para>
/// </summary>
[Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = nameof(MsiUrl))]
public Uri? MsiUrl { get; set; }

/// <summary>
/// <para type="description">Path to save the MSI file</para>
/// </summary>
[Parameter(Mandatory = true, Position = 1, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = nameof(MsiUrl))]
public string? OutputPath { get; set; }

/// <summary>
/// <para type="description">Filename to save the MSI file, if cannot be discovered from url</para>
/// </summary>
[Parameter(Mandatory = false, Position = 2, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = nameof(MsiUrl))]
public string? OutputFilename { get; set; }

[ServiceDependency]
private ILogger<ShowMsiInfo>? logger;

[ServiceDependency]
private IFileManager? fileManager;

/// <inheritdoc/>
public override async Task ProcessRecordAsync(CancellationToken cancellationToken)
{
if (MsiPath is not null)
{
logger?.LogInformation("Reading MSI from path: {MsiPath}", MsiPath);
}
else if (MsiUrl is not null)
{
logger?.LogInformation("Downloading MSI from URL to {OutputPath}: {MsiUrl}", MsiUrl, OutputPath);
var outputFile = Path.Combine(OutputPath!, OutputFilename ?? Path.GetFileName(MsiUrl.LocalPath));
// The file managed does automatic chunking of the download, so it will also work for very large files.
await fileManager!.DownloadFileAsync(MsiUrl!.ToString(), outputFile, cancellationToken: cancellationToken);
MsiPath = outputFile;
}
else
{
throw new InvalidOperationException("Either MsiPath or MsiUrl must be set");
}

using var msiStream = new FileStream(MsiPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, FileOptions.Asynchronous);
var decoder = new MsiDecoder(msiStream);
var codeFromMsi = decoder.GetCode();
var versionFromMsi = decoder.GetVersion();

WriteObject(new Models.MsiInfo
{
Path = MsiPath,
ProductCode = codeFromMsi,
ProductVersion = versionFromMsi
});
}

}
21 changes: 21 additions & 0 deletions src/Svrooij.WinTuner.CmdLets/Models/MsiInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace Svrooij.WinTuner.CmdLets.Models;

/// <summary>
/// Information about an MSI file
/// </summary>
public class MsiInfo
{
/// <summary>
/// The path to the MSI file
/// </summary>
public string? Path { get; set; }
/// <summary>
/// The product code of the MSI file
/// </summary>
public string? ProductCode { get; set; }

/// <summary>
/// The version of the MSI file
/// </summary>
public string? ProductVersion { get; set; }
}
173 changes: 167 additions & 6 deletions src/Svrooij.WinTuner.CmdLets/Svrooij.WinTuner.CmdLets.dll-Help.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1684,6 +1684,17 @@ You could run this on a weekly bases.
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="10" aliases="none">
<maml:name>PartialPackage</maml:name>
<maml:description>
<maml:para>Creating a partial package means that the files are not zipped into the intunewin file, but are left as is.</maml:para>
</maml:description>
<command:parameterValue required="false" variableLength="false">SwitchParameter</command:parameterValue>
<dev:type>
<maml:name>SwitchParameter</maml:name>
</dev:type>
<dev:defaultValue>False</dev:defaultValue>
</command:parameter>
</command:syntaxItem>
</command:syntax>
<command:parameters>
Expand Down Expand Up @@ -1823,6 +1834,17 @@ You could run this on a weekly bases.
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="10" aliases="none">
<maml:name>PartialPackage</maml:name>
<maml:description>
<maml:para>Creating a partial package means that the files are not zipped into the intunewin file, but are left as is.</maml:para>
</maml:description>
<command:parameterValue required="false" variableLength="false">SwitchParameter</command:parameterValue>
<dev:type>
<maml:name>SwitchParameter</maml:name>
</dev:type>
<dev:defaultValue>False</dev:defaultValue>
</command:parameter>
</command:parameters>
<command:returnValues>
<command:returnValue>
Expand Down Expand Up @@ -1970,12 +1992,151 @@ You could run this on a weekly bases.
</dev:remarks>
</command:example>
</command:examples>
<command:relatedLinks>
<maml:navigationLink>
<maml:linkText>Online Version</maml:linkText>
<maml:uri>https://wintuner.app/docs/wintuner-powershell/Search-WtWingetPackage</maml:uri>
</maml:navigationLink>
</command:relatedLinks>
</command:command>
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10">
<command:details>
<command:name>Show-MsiInfo</command:name>
<command:verb>Show</command:verb>
<command:noun>MsiInfo</command:noun>
<maml:description>
<maml:para>Show information about an MSI file</maml:para>
</maml:description>
</command:details>
<maml:description>
<maml:para>Show information about an MSI file, this includes the MSI code and version.</maml:para>
</maml:description>
<command:syntax>
<command:syntaxItem>
<maml:name>Show-MsiInfo</maml:name>
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName, ByValue)" position="0" aliases="none">
<maml:name>MsiPath</maml:name>
<maml:description>
<maml:para>Path to the MSI file</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String</command:parameterValue>
<dev:type>
<maml:name>String</maml:name>
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
</command:syntaxItem>
<command:syntaxItem>
<maml:name>Show-MsiInfo</maml:name>
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName, ByValue)" position="0" aliases="none">
<maml:name>MsiUrl</maml:name>
<maml:description>
<maml:para>URL to the MSI file</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">Uri</command:parameterValue>
<dev:type>
<maml:name>Uri</maml:name>
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName, ByValue)" position="1" aliases="none">
<maml:name>OutputPath</maml:name>
<maml:description>
<maml:para>Path to save the MSI file</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String</command:parameterValue>
<dev:type>
<maml:name>String</maml:name>
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName, ByValue)" position="2" aliases="none">
<maml:name>OutputFilename</maml:name>
<maml:description>
<maml:para>Filename to save the MSI file, if cannot be discovered from url</maml:para>
</maml:description>
<command:parameterValue required="false" variableLength="false">String</command:parameterValue>
<dev:type>
<maml:name>String</maml:name>
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
</command:syntaxItem>
</command:syntax>
<command:parameters>
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName, ByValue)" position="0" aliases="none">
<maml:name>MsiPath</maml:name>
<maml:description>
<maml:para>Path to the MSI file</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String</command:parameterValue>
<dev:type>
<maml:name>String</maml:name>
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName, ByValue)" position="0" aliases="none">
<maml:name>MsiUrl</maml:name>
<maml:description>
<maml:para>URL to the MSI file</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">Uri</command:parameterValue>
<dev:type>
<maml:name>Uri</maml:name>
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName, ByValue)" position="1" aliases="none">
<maml:name>OutputPath</maml:name>
<maml:description>
<maml:para>Path to save the MSI file</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String</command:parameterValue>
<dev:type>
<maml:name>String</maml:name>
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName, ByValue)" position="2" aliases="none">
<maml:name>OutputFilename</maml:name>
<maml:description>
<maml:para>Filename to save the MSI file, if cannot be discovered from url</maml:para>
</maml:description>
<command:parameterValue required="false" variableLength="false">String</command:parameterValue>
<dev:type>
<maml:name>String</maml:name>
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
</command:parameters>
<command:returnValues>
<command:returnValue>
<dev:type>
<maml:name>Svrooij.WinTuner.CmdLets.Models.MsiInfo</maml:name>
</dev:type>
<maml:description>
<maml:para>Svrooij.WinTuner.CmdLets.Models.MsiInfo</maml:para>
</maml:description>
</command:returnValue>
</command:returnValues>
<command:examples>
<command:example>
<maml:title>--------------------- Show information about an MSI file ---------------------</maml:title>
<dev:code>PS C:\&gt; Show-MsiInfo -MsiPath "C:\path\to\file.msi"</dev:code>
<dev:remarks>
<maml:para>Show information about an MSI file, this includes the MSI code and version.
</maml:para>
</dev:remarks>
</command:example>
<command:example>
<maml:title>---------------- Show information about an MSI file from URL -----------------</maml:title>
<dev:code>PS C:\&gt; Show-MsiInfo -MsiUrl "https://example.com/file.msi" -OutputPath "C:\path\to"</dev:code>
<dev:remarks>
<maml:para>Download an MSI file and show the details
</maml:para>
</dev:remarks>
</command:example>
</command:examples>
<command:relatedLinks>
<maml:navigationLink>
<maml:linkText>Online Version</maml:linkText>
<maml:uri>https://wintuner.app/docs/wintuner-powershell/Show-MsiInfo</maml:uri>
</maml:navigationLink>
</command:relatedLinks>
</command:command>
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10">
<command:details>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public async Task<AuthenticationResult> AccuireTokenAsync(IEnumerable<string> sc
: await publicClientApplication.AcquireTokenSilent(scopes, account).ExecuteAsync(cancellationToken);
return authenticationResult;
}
catch (MsalUiRequiredException ex)
catch (MsalUiRequiredException)
{
return await AcquireTokenInteractiveAsync(scopes, tenantId, account?.Username ?? userId, cancellationToken);
}
Expand Down
9 changes: 2 additions & 7 deletions src/WingetIntune/Intune/IntuneManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,13 @@
using Microsoft.Graph.Beta.Models.ODataErrors;
using Microsoft.Kiota.Abstractions.Authentication;
using Microsoft.Kiota.Abstractions.Serialization;
using OpenMcdf;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using WingetIntune.Commands;
using WingetIntune.Graph;
using WingetIntune.Interfaces;
using WingetIntune.Internal.Msal;
using WingetIntune.Internal.Msi;
using WingetIntune.Msi;
using WingetIntune.Intune;
using WingetIntune.Models;

Expand Down Expand Up @@ -605,7 +600,7 @@ private GraphServiceClient CreateGraphClientFromOptions(IntunePublishOptions opt
}
if (options.Credential is not null)
{
provider = new Microsoft.Graph.Authentication.AzureIdentityAuthenticationProvider(options.Credential, null, null, RequiredScopes);
provider = new Microsoft.Graph.Authentication.AzureIdentityAuthenticationProvider(options.Credential, null, null, isCaeEnabled: false, RequiredScopes);
}
else if (!string.IsNullOrEmpty(options.Token))
{
Expand Down
Loading

0 comments on commit 8a41ee6

Please sign in to comment.