Private/Invoke-IntuneWinAppUtil.ps1

function Invoke-IntuneWinAppUtil {
    <#
    .SYNOPSIS
        Runs IntuneWinAppUtil.exe to wrap a source folder into a .intunewin package.

    .DESCRIPTION
        Shared by New-IntuneWin32AppPackage, New-IntuneApplication and the APF commands.

        Tool: ToolPath when given (it must exist); otherwise the per-user tool path
        (Get-IntuneWinAppUtilPath). When that is missing the tool is downloaded with
        Get-IntunePackagingTool (release DownloadTag, signature-checked) only after confirmation, or
        without asking when AllowDownload is used. Declining stops with an error.

        Arguments: -c <source> -s <setup file> -o <output> -q, each path quoted. Trailing path
        separators are removed first, because a backslash before the closing quote would escape it
        ("C:\Out\" would reach the tool as C:\Out"); a drive root becomes "C:\.".

        The tool writes "<setup file name without extension>.intunewin" to OutputFolder. An existing
        file is replaced only with Overwrite. With PackageName the output is renamed to
        "<PackageName>.intunewin". Throws when the tool fails or creates no package; returns the file.
    #>

    [CmdletBinding()]
    [OutputType([System.IO.FileInfo])]
    param (
        [Parameter(Mandatory)]
        [string]$SourceFolder,

        [Parameter(Mandatory)]
        [string]$SetupFile,

        [Parameter(Mandatory)]
        [string]$OutputFolder,

        [string]$ToolPath,

        [string]$PackageName,

        [string]$DownloadTag = 'v1.8.6',

        [switch]$AllowDownload,

        [switch]$Overwrite
    )

    # Quotes a path for the command line without a trailing separator
    function ConvertTo-ToolArgument {
        param ([string]$Path)
        $Trimmed = $Path.TrimEnd('\', '/')
        if ($Trimmed -match '^[A-Za-z]:$' -or $Trimmed -eq '') {
            $Trimmed = "$Trimmed\."
        }
        '"' + $Trimmed + '"'
    }

    #region tool
    if ($ToolPath) {
        if (-not (Test-Path -LiteralPath $ToolPath -PathType Leaf)) {
            throw "IntuneWinAppUtil.exe was not found at '$ToolPath'."
        }
    }
    else {
        $ToolPath = Get-IntuneWinAppUtilPath
        if (-not (Test-Path -LiteralPath $ToolPath -PathType Leaf)) {
            $ToolFolder = Split-Path -Path $ToolPath -Parent
            if (-not $AllowDownload -and -not (Confirm-ToolDownload -Folder $ToolFolder)) {
                throw "IntuneWinAppUtil.exe is required to create .intunewin packages and was not downloaded. Run 'Get-IntunePackagingTool -Path `"$ToolFolder`"', or use -AllowDownload."
            }
            Write-Verbose "Downloading IntuneWinAppUtil.exe $DownloadTag to '$ToolFolder'."
            $ToolPath = (Get-IntunePackagingTool -Path $ToolFolder -DownloadTag $DownloadTag -Force -ErrorAction Stop).FullName
        }
    }
    #endregion tool

    #region paths
    $SourceFullPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($SourceFolder)
    $OutputFullPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($OutputFolder)
    if (-not [System.IO.Path]::IsPathRooted($SetupFile)) {
        $SetupFile = Join-Path -Path $SourceFullPath -ChildPath $SetupFile
    }
    $SetupFullPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($SetupFile)
    foreach ($Folder in $SourceFullPath, $OutputFullPath) {
        if (-not (Test-Path -LiteralPath $Folder -PathType Container)) {
            throw "The folder '$Folder' does not exist."
        }
    }
    if (-not (Test-Path -LiteralPath $SetupFullPath -PathType Leaf)) {
        throw "The setup file '$SetupFullPath' does not exist."
    }

    $ToolOutput = Join-Path -Path $OutputFullPath -ChildPath "$([System.IO.Path]::GetFileNameWithoutExtension($SetupFullPath)).intunewin"
    $PackagePath = $ToolOutput
    if (-not [string]::IsNullOrEmpty($PackageName)) {
        Assert-SafePathSegment -Name $PackageName -ParameterName 'PackageName' -AllowWildcard
        $PackagePath = Join-Path -Path $OutputFullPath -ChildPath "$PackageName.intunewin"
    }
    foreach ($File in @($PackagePath, $ToolOutput) | Select-Object -Unique) {
        if (Test-Path -LiteralPath $File) {
            if (-not $Overwrite) {
                throw "The package '$File' already exists. Delete it or allow it to be overwritten."
            }
            Remove-Item -LiteralPath $File -Force -ErrorAction Stop
        }
    }
    #endregion paths

    $Arguments = '-c {0} -s {1} -o {2} -q' -f (ConvertTo-ToolArgument -Path $SourceFullPath), (ConvertTo-ToolArgument -Path $SetupFullPath), (ConvertTo-ToolArgument -Path $OutputFullPath)
    Write-Verbose "Running $ToolPath $Arguments"
    $Result = Invoke-Executable -FilePath $ToolPath -Arguments $Arguments -ErrorAction Stop
    if ($Result.ExitCode -ne 0 -or -not (Test-Path -LiteralPath $ToolOutput -PathType Leaf)) {
        $Details = (@($Result.StandardError, $Result.StandardOutput) | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) -join ' '
        throw "IntuneWinAppUtil.exe did not create '$ToolOutput' (exit code $($Result.ExitCode)). $Details".TrimEnd()
    }
    if ($ToolOutput -ne $PackagePath) {
        Move-Item -LiteralPath $ToolOutput -Destination $PackagePath -Force -ErrorAction Stop
    }
    Get-Item -LiteralPath $PackagePath
}