Since Intune (and probably other software distribution mechanisms) only support MSI or EXE execution, the Install.cmd script that was developed for each package becomes, at first glance, useless. We were able to work around this using the Inno Setup package, which is available for free. To facilitate this, we've authored a PowerShell script that will create an ISS script file and then compile it using the Inno Setup utilities, producing a single EXE.

To execute the EXE, just run it. To run silently, add the /VERYSILENT switch at the end and it will handle everything and return a reboot code (3010) if one is needed, letting Intune marshall the actual reboot.

The PowerShell script uses a set of parameters to work:

  • PackagePath - The path to the folder containing the package flat files. This is required. The folder name holding the files will become the EXE file name.
  • InstallCmd - The file to execute to perform the installation. Defaults to Install.cmd.
  • AppName - The name of the application. Populates the file header in the EXE generated. This is optional.
  • Publisher - The publisher of the application. Populates the file header in the EXE generated. This is optional.
  • Version - The version of the application. Populates the file header in the EXE generated. This is optional.
param([string]$PackagePath = $(throw "PackagePath parameter is required."),[string]$InstallCmd = "Install.cmd",[string]$AppName,[string]$Publisher,[string]$Version)

The script checks to be sure that the supplied PackagePath and InstallCmd exist.

$A = Test-Path -Path $PackagePath
$B = Test-Path -Path ($PackagePath + "\" + $InstallCmd)
if ($A -eq $false) { Write-Error "The specified PackagePath does not exist or is not accessible."; Return; }
if ($B -eq $false) { Write-Error "The specified InstallCmd does not exist or is not accessible."; Return; }

The script then creates the first part of the ISS script file using some standardized settings.

$ISSName = ("E:\Repackage\" + $PackagePath.Substring($PackagePath.LastIndexOfAny("\")+1) + ".iss")
"[Setup]" | Out-File $ISSName -encoding ASCII
if ($AppName) { ("AppName=`"" + $AppName + "`"") | Out-File $ISSName -encoding ASCII -append }
if ($Publisher) { ("AppPublisher=`"" + $Publisher + "`"") | Out-File $ISSName -encoding ASCII -append }
if ($Version) { ("AppVersion=`"" + $Version + "`"") | Out-File $ISSName -encoding ASCII -append }
"CreateAppDir=no" | Out-File $ISSName -encoding ASCII -append
"Compression=lzma" | Out-File $ISSName -encoding ASCII -append
"SolidCompression=yes" | Out-File $ISSName -encoding ASCII -append
"Uninstallable=no" | Out-File $ISSName -encoding ASCII -append
"DisableWelcomePage=True" | Out-File $ISSName -encoding ASCII -append
"DisableReadyPage=True" | Out-File $ISSName -encoding ASCII -append
"DisableReadyMemo=True" | Out-File $ISSName -encoding ASCII -append
"DisableFinishedPage=True" | Out-File $ISSName -encoding ASCII -append
"DisableStartupPrompt=yes" | Out-File $ISSName -encoding ASCII -append
"RestartIfNeededByRun=False" | Out-File $ISSName -encoding ASCII -append
("OutputBaseFilename=`"" + $PackagePath.Substring($PackagePath.LastIndexOfAny("\")+1) + "`"") | Out-File $ISSName -encoding ASCII -append
"OutputDir=`"E:\Repackage`"" | Out-File $ISSName -encoding ASCII -append
"" | Out-File $ISSName -encoding ASCII -append

The script then adds the subfolders found in the package folder to the list of folders.

$FL = Get-ChildItem -Path $PackagePath -Recurse -Directory
if ($FL.count -ne 0) { 
  "[Dirs]" | Out-File $ISSName -encoding ASCII -append 
  $FL | foreach { 
    ("Name: `"{tmp}" + $_.FullName.Substring($PackagePath.Length) + "`"") | Out-File $ISSName -encoding ASCII -append
  }
  "" | Out-File $ISSName -encoding ASCII -append
}

Next, it adds the list of files to the ISS file so that they can be included in the archive within the EXE. It makes an exception for the $InstallCmd file since we need to handle that one in a slightly different way.

$FF = Get-ChildItem -Path $PackagePath -Recurse -File
"[Files]" | Out-File $ISSName -encoding ASCII -append
$FF | foreach {
  if (-not (($_.DirectoryName -eq $PackagePath) -and ($_.Name -eq $InstallCmd))) {
    ("Source: `"" + $_.FullName + "`"; DestDir: `"{tmp}" + $_.DirectoryName.Substring($PackagePath.Length) + "`"") | Out-File $ISSName -encoding ASCII -append
  } }
("Source: `"" + $PackagePath + "\" + $InstallCmd + "`"; DestDir: `"{tmp}`"; AfterInstall: CustomSetup") | Out-File $ISSName -encoding ASCII -append
"" | Out-File $ISSName -encoding ASCII -append

Next, it adds a blob of JavaScript code to the ISS file that handles the launching of the $InstallCmd file and returning the result code back through to the calling application.

"[Code]" | Out-File $ISSName -encoding ASCII -append
"var" | Out-File $ISSName -encoding ASCII -append
"  ResultCode: Integer;" | Out-File $ISSName -encoding ASCII -append
"  FinalResult: Integer;" | Out-File $ISSName -encoding ASCII -append
"" | Out-File $ISSName -encoding ASCII -append
"function GetCustomSetupExitCode(): Integer;" | Out-File $ISSName -encoding ASCII -append
"begin" | Out-File $ISSName -encoding ASCII -append
"  Result := FinalResult;" | Out-File $ISSName -encoding ASCII -append
"end;" | Out-File $ISSName -encoding ASCII -append
"" | Out-File $ISSName -encoding ASCII -append
"procedure CustomSetup();" | Out-File $ISSName -encoding ASCII -append
"begin" | Out-File $ISSName -encoding ASCII -append
("  if Exec(ExpandConstant('{tmp}\" + $InstallCmd + "'), '', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then") | Out-File $ISSName -encoding ASCII -append
"  begin" | Out-File $ISSName -encoding ASCII -append
"    FinalResult := ResultCode;" | Out-File $ISSName -encoding ASCII -append
"  end" | Out-File $ISSName -encoding ASCII -append
"  else begin" | Out-File $ISSName -encoding ASCII -append
"    FinalResult := ResultCode;" | Out-File $ISSName -encoding ASCII -append
"  end" | Out-File $ISSName -encoding ASCII -append
"end;" | Out-File $ISSName -encoding ASCII -append
"" | Out-File $ISSName -encoding ASCII -append

Finally, the script runs the Inno Setup compliler to create the EXE.

Start-Process -FilePath "C:\Program Files (x86)\Inno Setup 5\ISCC.exe" -ArgumentList ("`"" + $ISSName + "`"")