Public/New-IntuneWin32AppPackage.ps1
|
function New-IntuneWin32AppPackage { <# .SYNOPSIS Creates an Intune Win32 application package (.intunewin file) from source files. .DESCRIPTION The New-IntuneWin32AppPackage function wraps application source files and the main setup file into an encrypted .intunewin package using Microsoft's IntuneWinAppUtil.exe tool. This package can then be uploaded to Microsoft Intune for Win32 app deployment. .PARAMETER SourceFolder The full path to the source folder containing the setup file and all dependency files. .PARAMETER SetupFile The complete setup file name including extension (e.g., Setup.exe or Installer.msi). .PARAMETER OutputFolder The full path to the output folder where the packaged .intunewin file will be saved. .PARAMETER Force Switch to overwrite an existing .intunewin file if already present in the output folder. .PARAMETER IntuneWinAppUtilPath The full path to the IntuneWinAppUtil.exe file. When not specified, the per-user tool folder (LocalApplicationData\tcs.intune.packaging) is used and the tool is downloaded there when missing. .OUTPUTS System.Management.Automation.PSCustomObject Name, FileName, SetupFile, UnencryptedContentSize and Path of the package. Name, FileName and UnencryptedContentSize are read with Get-IntuneWin32AppMetaData (IntuneWin32App module) when it is installed; otherwise Name is the setup file name and UnencryptedContentSize is $null. .EXAMPLE New-IntuneWin32AppPackage -SourceFolder "C:\Apps\MyApp" -SetupFile "setup.exe" -OutputFolder "C:\Packages" Creates an .intunewin package from the MyApp folder. .EXAMPLE New-IntuneWin32AppPackage -SourceFolder "C:\Apps\MyApp" -SetupFile "installer.msi" -OutputFolder "C:\Packages" -Force Creates an .intunewin package and overwrites any existing package in the output folder. .NOTES The IntuneWinAppUtil.exe tool is downloaded automatically when IntuneWinAppUtilPath is not specified and the tool is missing. The source folder should contain all files required for the application installation. .LINK https://github.com/microsoft/Microsoft-Win32-Content-Prep-Tool #> [CmdletBinding(SupportsShouldProcess = $true)] [OutputType([PSCustomObject])] param( [parameter(Mandatory = $true, HelpMessage = "Specify the full path of the source folder where the setup file and all of it's potential dependency files reside.")] [ValidateNotNullOrEmpty()] [string]$SourceFolder, [parameter(Mandatory = $true, HelpMessage = "Specify the complete setup file name including it's file extension, e.g. Setup.exe or Installer.msi.")] [ValidateNotNullOrEmpty()] [string]$SetupFile, [parameter(Mandatory = $true, HelpMessage = "Specify the full path of the output folder where the packaged .intunewin file will be exported to.")] [ValidateNotNullOrEmpty()] [string]$OutputFolder, [parameter(Mandatory = $false, HelpMessage = "Specify to overwrite existing packaged .intunewin file if already present in output folder.")] [switch]$Force, [parameter(Mandatory = $false, HelpMessage = "Specify the full path to the IntuneWinAppUtil.exe file.")] [ValidateNotNullOrEmpty()] [string]$IntuneWinAppUtilPath = (Get-IntuneWinAppUtilPath) ) begin { $TelemetryArgs = @{ ModuleName = $MyInvocation.MyCommand.Module.Name ModuleVersion = [string]$MyInvocation.MyCommand.Module.Version CommandName = $MyInvocation.MyCommand.Name ExecutionID = [guid]::NewGuid().ToString() } Invoke-TelemetryCollection @TelemetryArgs -Stage Start -ClearTimer $TelemetryFailed = $false } Process { try { # Trim trailing path separators from input paths $SourceFolder = $SourceFolder.TrimEnd('\', '/') $OutputFolder = $OutputFolder.TrimEnd('\', '/') if ((Test-Path -Path $SourceFolder) -or (Test-Path -LiteralPath $SourceFolder)) { Write-Verbose -Message "Successfully detected specified source folder: $($SourceFolder)" $SetupFilePath = (Join-Path -Path $SourceFolder -ChildPath $SetupFile) if ((Test-Path -Path $SetupFilePath) -or (Test-Path -LiteralPath $SetupFilePath)) { Write-Verbose -Message "Successfully detected specified setup file '$($SetupFile)' in source folder" if ((Test-Path -Path $OutputFolder) -or (Test-Path -LiteralPath $OutputFolder)) { Write-Verbose -Message "Successfully detected specified output folder: $($OutputFolder)" if ((-not(Test-Path -Path $IntuneWinAppUtilPath)) -or (-not(Test-Path -LiteralPath $IntuneWinAppUtilPath))) { if (-not($PSBoundParameters["IntuneWinAppUtilPath"])) { # Download IntuneWinAppUtil.exe to the per-user tool folder $ToolFolder = Split-Path -Path $IntuneWinAppUtilPath -Parent Write-Verbose -Message "Unable to detect IntuneWinAppUtil.exe, attempting to download to: $ToolFolder" $null = Get-IntunePackagingTool -Path $ToolFolder -Force } } if ((Test-Path -Path $IntuneWinAppUtilPath) -or (Test-Path -LiteralPath $IntuneWinAppUtilPath)) { Write-Verbose -Message "Successfully detected IntuneWinAppUtil.exe in: $($IntuneWinAppUtilPath)" # If .intunewin already exists, only continue if Force parameter is passed on command line $ProcessPackage = $true $IntuneWinAppPackage = Join-Path -Path $OutputFolder -ChildPath "$([System.IO.Path]::GetFileNameWithoutExtension($SetupFile)).intunewin" if ((Test-Path -Path $IntuneWinAppPackage) -or (Test-Path -LiteralPath $IntuneWinAppPackage)) { if ($Force) { Write-Verbose -Message "Package file already exist, but Force parameter was specified to overwrite existing file" } else { Write-Warning -Message "Package file already exist, specify the Force parameter to overwrite existing file" $ProcessPackage = $false } } # Continue processing if allowed if ($ProcessPackage -eq $true -and $PSCmdlet.ShouldProcess($IntuneWinAppPackage, 'Create .intunewin package')) { # Invoke IntuneWinAppUtil.exe with parameter inputs Write-Verbose -Message "Invoking IntuneWinAppUtil.exe to initialize packaging process" $PackageInvocation = Invoke-Executable -FilePath $IntuneWinAppUtilPath -Arguments "-c ""$($SourceFolder)"" -s ""$($SetupFile)"" -o ""$($OutPutFolder)"" -q" -RedirectStandardOutput $false -RedirectStandardError $false -CreateNoWindow $false -UseShellExecute $true if ($PackageInvocation.ExitCode -eq 0) { Write-Verbose -Message "IntuneWinAppUtil.exe packaging process completed with exit code $($PackageInvocation.ExitCode)" # Test if .intunewin file exists after packaging process completed if ((Test-Path -Path $IntuneWinAppPackage) -or (Test-Path -LiteralPath $IntuneWinAppPackage)) { Write-Verbose -Message "Successfully created Win32 app package object" # Construct output object with package details. Get-IntuneWin32AppMetaData comes from # the optional IntuneWin32App module and is used when it is installed. $PSObject = [PSCustomObject]@{ "Name" = $SetupFile "FileName" = Split-Path -Path $IntuneWinAppPackage -Leaf "SetupFile" = $SetupFile "UnencryptedContentSize" = $null "Path" = $IntuneWinAppPackage } if (Get-Command -Name 'Get-IntuneWin32AppMetaData' -ErrorAction SilentlyContinue) { $IntuneWinAppMetaData = Get-IntuneWin32AppMetaData -FilePath $IntuneWinAppPackage $PSObject.Name = $IntuneWinAppMetaData.ApplicationInfo.Name $PSObject.FileName = $IntuneWinAppMetaData.ApplicationInfo.FileName $PSObject.SetupFile = $IntuneWinAppMetaData.ApplicationInfo.SetupFile $PSObject.UnencryptedContentSize = $IntuneWinAppMetaData.ApplicationInfo.UnencryptedContentSize } Write-Output -InputObject $PSObject } else { Write-Warning -Message "Unable to detect expected '$($SetupFile).intunewin' file after IntuneWinAppUtil.exe invocation" } } else { Write-Warning -Message "Unexpected error occurred while packaging Win32 app. Return code from invocation: $($PackageInvocation.ExitCode)" } } } else { Write-Warning -Message "Unable to detect IntuneWinAppUtil.exe in: $($IntuneWinAppUtilPath)" } } else { Write-Warning -Message "Unable to detect specified output folder: $($OutputFolder)" } } else { Write-Warning -Message "Unable to detect specified setup file '$($SetupFile)' in source folder: $($SourceFolder)" } } else { Write-Warning -Message "Unable to detect specified source folder: $($SourceFolder)" } } catch { if (-not $TelemetryFailed) { $TelemetryFailed = $true Invoke-TelemetryCollection @TelemetryArgs -Stage End -Failed $true -Exception $_ } throw } } end { if (-not $TelemetryFailed) { Invoke-TelemetryCollection @TelemetryArgs -Stage End } } } |