Intune packaging made simple

Intune packaging made simple
Excerpt from the PowerShell execution of the command Invoke-Upload.ps1

I like to work with installation wrappers for packaging. It doesn’t matter whether it’s Microsoft System Center Configuration Manager (SCCM) or Microsoft Endpoint Configuration Manager (MECM) or the Microsoft Deploment Tollkit (MDT), I have also used them for Microsoft Intune packaging.

The thought at the time was, I have an install script and an uninstall script. Why not use that for Intune as well? So I had a look at the INTUNEWIN format for Windows Apps (Win32). This is basically a container in which the installation files are packaged. This is done with the Microsoft Win32 Content Prep Tool, which is available for free on GitHub. With a few parameters, the IntuneWIN file is created here.

To make it easier for me, I wrote a small script that goes through my source folder and creates the appropriate IntuneWin files. Since the schema is always the same, this wasn’t hard.

Next I wanted to extend the script with an automatic upload and other useful features like group creation and assignments. Does this sound like something useful? It did to me. So I started looking for suitable PowerShell modules and commands. However, I found something else, as they always say with BING you can only search, but not find…

IntuneScripts by Greg Nottage

I stumbled across the IntueScripts by Greg Nottage, a Microsoft employee. He wrote exactly what I wanted to start with, a framework for Intune packaging. Since it’s available under the MIT licence, I forked it and am now starting to add functions and application templates one by one.

The beauty is that Greg has written a very nice PowerShell wrapper for the installation that takes care of a lot. For example, the topic of logs and registry entries that can be used for the detection rules. There are also examples for hotfix installation for dependencies and the detection of virtual machines to handle them differently.

Configuring Intune packaging is done via an XML file that is used to create the IntuneWin file and build the application in Microsoft Intune. I mainly use the PowerShell wrapper for all packages. This can be accessed via the parameters “-Install” and “-Uninstall”. Here is an example using Adobe Acrobat Reader.

Example for Acrobat Reader

For the installation with the PowerShell wrapper, I use the MSI variant of Adobe Acrobat Reader for Enterprises. However, I do not use the EXE for the unattended installation but take the MSI and the MSP (update file for MSI) from the EXE. It is essential that the Acrobat Customization Wizard DC is used to create the Transform file. This configures the installation.

I have designed the wrapper in such a way that only the last MSP is used. The wrapper determines this itself from the file name. This way, the wrapper does not always have to be adjusted. I also check beforehand whether a suitable Adobe Acrobat Reader is already installed, if so, only the update is installed. The code for this that I have inserted into the framework is quite clear:

$MSP = $(Get-ChildItem *.msp | Sort-Object -Property Name -Descending)[0].Name
$ARG = " /update " + $MSP + " /t AcroRead.mst /qn /norestart"
Write-Verbose "Install Base Installer"
IF (!(get-wmiobject Win32_Product | ? { $_.IdentifyingNumber -like "{AC76BA86-7AD7-FFFF-7B44-AC0F074E4100}"})) { Start-Process -NoNewWindow -FilePath "msiexec.exe" -ArgumentList " /i AcroRead.msi /t AcroRead.mst /qn /norestart" -Wait } ELSE {Write-Output "Acrobar already installed"}
Write-Verbose "Install Update"
Start-Process -NoNewWindow -FilePath "msiexec.exe" -ArgumentList $ARG -Wait

The uninstallation is done via the MSI GUID. Here the code is even simpler:

Start-Process -NoNewWindow -FilePath "msiexec.exe" -ArgumentList ' /x "{AC76BA86-7AD7-FFFF-7B44-AC0F074E4100}" /qn' -Wait

Now the XML file for the configuration must be filled. The template comes with a very good documentation, so that further explanations are not really necessary.

<!-- <></> -->
     <!-- Apart from username, leave Azure_Settings unchanged -->
     <!-- Completely remove the <Username> element and script will prompt -->
         <Username>[email protected]</Username>
         <!-- Edit the AppType element - supported options are MSI, EXE or PS1 -->
         <!-- Edit the installCmdLine element when using the EXE or MSI AppType -->
         <!-- Edit the uninstallCmdLine element when using the EXE or MSI AppType -->
         <!-- Edit the RuleType element - supported options are TAGFILE , FILE or REGTAG -->
         <!-- Ignored if AppType is MSI-->
         <!-- Edit the FilePath element when using the FILE RuleType -->
         <!-- !!! Do NOT Edit the ReturnCodeType element !!! -->
         <!-- Edit the InstallExperience element - supported options are System or User -->
         <!-- Edit the PackageName element to match the name of the .PS1 script that the IntuneWinAppUtil should reference -->
         <!-- For MSI or EXE AppType - this must be the name of the MSI or executable file in the ..\Source folder - without the .exe in the name -->
         <PackageName>Adobe Reader DC MUI 2100120155</PackageName>
         <!-- Edit the displayName element that will be shown for the imported package in the Intune console -->
         <displayName>Adobe Reader DC MUI 2100120155</displayName>
         <!-- Edit the Description element that will be shown for the imported package in the Intune console -->
         <Description>Adobe Reader DC MUI 2100120155</Description>
         <!-- Edit the Publisher name if required -->
         <!-- Edit the Category element that will be shown for the imported package in the Intune console -->
         <!-- Edit the LogoFile element to provide a logo shown in Company Portal -->
         <!-- Edit the AADGroupName element used for the required/available/uninstall group creation -->

More sample packages

Because PowerShell is very flexible, there are many possibilities open, so I prefer this one now, not only for Intune packaging. I will also gradually change my MDT and SCCM packages.

Some possibilities that are very easy with PowerShell is for example with the installation of the OpenJDK, here a few environment variables still have to be set. Or with Greenshot, here a shortcut still has to be created. 

Other package templates in my repository (yes, all of Greg’s original files are also included) are:

  • Adobe Acrobat DC DE
  • Adobe Acrobat DC MUI
  • Greenshot
  • VC++ 2013
  • VC++ 2017
  • Microsoft 365 Apps 4 Enterprises in SAEC in German (download via CDN)
  • Open JDK 16.0.1

The original templates (these are also partially documented in a word document) from Greg are:

  • 7zip
  • Visual Studio Code
  • Invoke-OoBUpdates (OoB = Out of Band)

Further functions of the framework for Intune packaging

In addition to creating the required checking rules and uploading them to Intune, the framework also directly creates 3 groups per application and assigns them to the software. These are a group for the required installation (Required), for the available installation (Available) and for the uninstallation (Uninstall). The Uninstall group is also directly set as an exception for the other two assignments.

Packaging and uploading

Packaging and uploading the package works simply with the command Invoke-Upload.ps1

.\Invoke-Upload.ps1 -Name "Dell Command Update 4.2.0" -userName [email protected]

Then the script starts up and takes about 3-5 minutes, with about 2 minutes being various wait times for Azure.

Excerpt from the PowerShell execution of the command Invoke-Upload.ps1 for Intune packaging
Excerpt from the PowerShell execution of the command Invoke-Upload.ps1 for Intune packaging

When the script is ready, the application is uploaded to Azure, set up and the appropriate groups would be created according to the XML file.

Screenshot Azure Portal: Properties Dell Command Update after Invoke-Upload.ps1
Screenshot Azure Portal: Properties Dell Command Update after Invoke-Upload.ps1

Für die Bulk Uploads gibt es noch eine JSON File die befüllt werden muss, danach können alle dort eingetragenen Pakete mit dem Befehl Invoke-BulkUpload.ps1 hochgeladen werden. Die Syntax ist wieder entsprechend einfach:

Invoke-BulkUpload.ps1 -userName [email protected]

What else do I have in mind with the solution?

One of the ideas I still have is to extend the whole thing with version management. This means that the versions are automatically taken into account during packaging and can then perhaps also be stored directly in Azure. So that newer versions can also be replaced directly. This could simplify versioning and patch management during Intune packaging. The new preview feature Superseedance could also be used for this purpose.

Another idea is to create a global configuration file and also support AD groups instead of only AAD groups. So that an easier integration into SCCM is possible. If the detection rules would then fit, this could simplify migrations.

Do you still have ideas / wishes for Intune packaging? Leave me a comment below.

You can find the repository here at GitHub.

Fabian Niesen has been working as an IT consultant for years. Here he writes privately and independently of his employer. Among others he is certified as MCSA Windows Server 2008 / 2012, MCSA Office 365, MCSA Windows 10, MCSE Messaging, MCT and Novell Certified Linux Administrator. Since 2016 he is also MCT Regional Lead for Germany. His hobbies are social media, blogging, medieval markets, historical songs and house building.

Leave a Reply

Your email address will not be published. Required fields are marked *