PSAppDeployToolkit 4.x Application Deployment Template

A reusable, self-contained application packaging template built on PSAppDeployToolkit 4.x.

Copy this repository, edit one region, drop the installation media into Files, and the package is ready to deploy.

What it adds over the stock v4 template

Addition Detail
Elevation wrapper Test-ProcessElevationStatus gates the entire script. When not elevated it relaunches itself elevated through the first available interpreter (powershell.exe, then pwsh.exe) and forwards every supplied parameter.
Deployment type switch A single Switch ($adtSession.DeploymentType) with Install / Uninstall / Repair branches instead of three separate dispatch functions.
Automatic MSI/EXE installation Installation media is located across the architecture aware content directories and executed through Start-ADTMsiProcess / Start-ADTProcess using splatted parameters, with transform and patch detection.
Self contained The PSAppDeployToolkit module ships inside the package and is imported without a version constraint, so the package runs on any endpoint and the module is updated by replacing the directory.

Everything the toolkit already provides is used as-is rather than being mirrored into a parallel configuration structure:

Concern Where it lives
Application identity, install titles, exit codes, processes to close, content directories, log paths $adtSession (the deployment session)
OS architecture, culture, well known paths, task sequence detection $adtEnv (Get-ADTEnvironmentTable)
Logging, dialog style, timeouts, cache and temp paths Config\config.psd1
Media discovery, executable arguments, software removal criteria, dialog behavior $PackageOptions (the only custom structure)

Layout

Everything that gets deployed to an endpoint lives under src/. That directory is the package — copy its contents (not the repository root) into your MECM/Intune source folder.

src/                               <- the deployable package
  Invoke-AppDeployToolkit.ps1      Deployment script (edit the "Package Configuration" region)
  Invoke-AppDeployToolkit.exe      Launcher used by MECM/Intune
  AppDeployToolkitExtensions.ps1   Dot sourced; loads the bundled dependencies below
  PSAppDeployToolkit/              The toolkit module that ships with the package
  Assets/                          Package branding (icon, banner)
  Config/                          Toolkit configuration overrides
  Files/
    X64/                           64-bit installation media
    X86/                           32-bit installation media
    All/                           Architecture neutral installation media
  SupportFiles/
    Functions/                     .ps1 files, dot sourced automatically
    Modules/                       Module folders, imported automatically
    Libraries/
      X64/  X86/  All/             .dll assemblies, loaded automatically
    Icons/                         Shortcut icons

Templates/                         Copy-ready authoring material (function skeleton)
docs/                              In-depth documentation
Scratch/                           Working/reference material (git ignored)
README.md

Bundled dependencies

AppDeployToolkitExtensions.ps1 is dot sourced right after the session opens — so $adtSession, $adtEnv, and toolkit logging are all live — and loads whatever the package carries, in this order:

  1. Modules — every module folder under SupportFiles\Modules. The newest version of each wins, and any prerequisite declared through RequiredModules that is also bundled is imported first, so load order does not matter.
  2. Assemblies — every .dll under SupportFiles\Libraries\<OS Architecture> and SupportFiles\Libraries\All, loaded from its bytes so the file is not locked on disk.
  3. Functions — every .ps1 under SupportFiles\Functions, dot sourced so its functions are available to all deployment phases.

Each stage is skipped when its directory is empty or absent, and a failure to load one dependency is logged at severity 3 and the deployment continues. Every step is written to the toolkit log, so you can see exactly what loaded and in what order.

Quick start

  1. Copy the contents of src\ to a new package folder.
  2. Stage the installation media under Files\X64, Files\X86, and/or Files\All.
  3. Open Invoke-AppDeployToolkit.ps1 and edit the Package Configuration region:
    • $adtSession — vendor, name, version, revision, processes to close.
    • $PackageOptions — executable arguments, MSI properties, software removal filter, dialog behavior.
  4. Test:
powershell.exe -ExecutionPolicy Bypass -NoProfile -NoLogo -File ".\Invoke-AppDeployToolkit.ps1" -DeploymentType 'Install' -DeployMode 'Silent'

Leave InstallName and InstallTitle empty and the toolkit derives them from the identity (MyApplicationVendor_MyApplicationName_1.0.0.0_EN_01).

Updating the bundled toolkit module

No version is pinned. The script prefers .\PSAppDeployToolkit\PSAppDeployToolkit.psd1 and falls back to the PowerShell module path when the directory is absent. To update the bundled module, replace the directory:

Copy-Item -Path (Get-Module -ListAvailable PSAppDeployToolkit | Sort-Object Version -Descending | Select-Object -First 1).ModuleBase -Destination '.\src\PSAppDeployToolkit' -Recurse -Force

The version that was actually loaded is written to the deployment log on every run.

How the automatic installation works

Discovery and execution live in AppDeployToolkitExtensions.ps1 as Get-ADTDeploymentMedia and Invoke-ADTDeploymentMedia, so the Installation phase of the deployment script is a single splatted call rather than a hundred lines of loop. The behavior below is what those functions do.

The content directories are searched in this order, which establishes execution precedence:

  1. Files\<OS Architecture> — for example Files\X64, from $adtEnv.envOSArchitecture
  2. Files\All
  3. Files

Within each directory the search patterns are applied in the order they are listed (*.msi, then *.exe), and files are ordered largest first so the primary installer runs before its companions.

  • Windows installer packages are executed with Start-ADTMsiProcess. A .mst in the same directory is applied when its base name matches the package base name, the culture name (en-US), or the culture LCID (1033). Every .msp in the same directory is applied as a patch.
  • Executables are executed with Start-ADTProcess. Arguments come from $PackageOptions.ExecutableArgumentDictionary — each key is a regular expression matched against the file name, and the first key that matches wins, so the most specific pattern must be listed first. An executable that matches no entry uses $PackageOptions.ExecutableDefaultArgumentList (/S by default).

Success and reboot exit codes are inherited from $adtSession.AppSuccessExitCodes / AppRebootExitCodes, so they are not repeated on the individual calls.

Uninstallation uses Uninstall-ADTApplication driven by $PackageOptions.SoftwareRemovalFilterScript, which replaces the dynamic software removal function used in the 3.x template.

Zero-Config MSI: the toolkit's built-in Zero-Config MSI support only activates when AppName is empty, and it only looks in the root of Files. This template sets AppName, so discovery is owned by the loop above. If you deliberately blank AppName to use Zero-Config, also set $PackageOptions.PerformAutomaticInstallation = $False so the same MSI is not executed twice.

Deployment types and modes

-DeploymentType is still a parameter in 4.x and still drives the flow. The session resolves the final value, so the script switches on $adtSession.DeploymentType rather than the raw parameter.

-DeployMode gained an Auto value in 4.x (the default). Auto resolves to interactive when a user is logged on, the device has completed the OOBE, and there are no processes to close, and to silent otherwise. The resolved value is available as $adtSession.DeployMode.

Each of those three checks can be bypassed individually. All three are exposed in the package configuration region and default to $False:

Setting Set $True to
$adtSession.NoOobeDetection Show the UI during the OOBE / Autopilot ESP phase
$adtSession.NoSessionDetection Show the UI when no user is logged on and the SYSTEM process is interactive
$adtSession.NoProcessDetection Show the UI even when there are no processes to close

They only take effect when DeployMode is Auto; an explicit -DeployMode always wins.

Exit codes

Range Meaning
0 Success
1641 / 3010 Success, restart required
60001 Unhandled error in Invoke-AppDeployToolkit.ps1
60008 The PSAppDeployToolkit module failed to load
69000 - 69999 Available for package specific exit codes

Further documentation

S
Description
Template repository for Powershell App Deployment Toolkit
Readme 6.6 MiB
2026-07-27 21:51:49 +00:00
Languages
PowerShell 100%