Skip to content
Beer Mug Logo
IntuneBrewby UgurLabs.com
Back to home

Install Experience

The scripts behind the prompt

Inspect the pre- and post-install scripts IntuneBrew generates for macOS PKG apps. These are zsh shell scripts that call swiftDialog for the interface.

View & download the scripts

View & download the scripts

Anyone can inspect and download these examples; no sign-in is needed. They use the same generator as app deployments, with public example values rather than any team's settings.

The example uses Example App, bundle ID com.example.app, version 2.0.0 and the Soft notification preset: three deferrals, a five-minute timer that defers when it expires, and a completion notification. Replace the example app details before using the scripts with your own PKG.

Pre-install script

Download .sh
View pre-install script
#!/bin/zsh
# Intune script content must remain below 15360 characters.
DIALOG_BIN='/usr/local/bin/dialog'
if [[ ! -x "$DIALOG_BIN" ]]; then
  exit 0
fi
CONSOLE_USER=$(/usr/bin/stat -f "%Su" /dev/console 2>/dev/null || true)
if [[ -z "$CONSOLE_USER" || "$CONSOLE_USER" == "root" || "$CONSOLE_USER" == "loginwindow" ]]; then
  exit 0
fi
CONSOLE_UID=$(/usr/bin/id -u "$CONSOLE_USER" 2>/dev/null || true)
if [[ -z "$CONSOLE_UID" ]]; then
  exit 0
fi
CONFIG_TITLE='Update available for Example App'
CONFIG_BODY='Version **2.0.0** is ready. Save your work, then install when convenient.'
CONFIG_BUTTON1='Install now'
BUNDLE_ID='com.example.app'
ALLOW_DEFERRALS=true
MAX_DEFERRALS=3
FORCE_AT_MAX=true
TIMER_SECONDS=300
TIMER_EXPIRY='defer'
DEFERRAL_DIR="/Library/Application Support/IntuneBrew/deferrals"
DEFERRAL_FILE="${DEFERRAL_DIR}/${BUNDLE_ID}.plist"
DEFER_COUNT=$(/usr/bin/defaults read "$DEFERRAL_FILE" count 2>/dev/null || /bin/echo 0)
[[ "$DEFER_COUNT" == <-> ]] || DEFER_COUNT=0
CAN_DEFER=false
if [[ "$ALLOW_DEFERRALS" == "true" ]]; then
  if [[ "$FORCE_AT_MAX" != "true" || "$DEFER_COUNT" -lt "$MAX_DEFERRALS" ]]; then
    CAN_DEFER=true
  fi
fi
DIALOG_MESSAGE="$CONFIG_BODY"
SUPPORT_NAME=''
SUPPORT_EMAIL=''
SUPPORT_PHONE=''
SUPPORT_TEXT=""
if [[ -n "$SUPPORT_NAME" ]]; then
  SUPPORT_TEXT="Name: ${SUPPORT_NAME}"
fi
if [[ -n "$SUPPORT_EMAIL" ]]; then
  [[ -n "$SUPPORT_TEXT" ]] && SUPPORT_TEXT+=$'\n'
  SUPPORT_TEXT+="Email: ${SUPPORT_EMAIL}"
fi
if [[ -n "$SUPPORT_PHONE" ]]; then
  [[ -n "$SUPPORT_TEXT" ]] && SUPPORT_TEXT+=$'\n'
  SUPPORT_TEXT+="Phone: ${SUPPORT_PHONE}"
fi
if [[ -n "$SUPPORT_TEXT" ]]; then
  DIALOG_MESSAGE+=$'\n\n**Support**\n'"$SUPPORT_TEXT"
fi
json_escape() {
  local value="$1"
  local escaped=""
  local char=""
  local index
  for (( index = 1; index <= ${#value}; index++ )); do
    char="${value[index]}"
    case "$char" in
      \\) escaped+="\\\\" ;;
      \") escaped+="\\\"" ;;
      $'\n') escaped+="\\n" ;;
      $'\r') escaped+="\\r" ;;
      $'\t') escaped+="\\t" ;;
      *) escaped+="$char" ;;
    esac
  done
  /usr/bin/printf "%s" "$escaped"
}
DIALOG_JSON=$(/usr/bin/printf '{"title":"%s","message":"%s","button1text":"%s","timer":%s' "$(json_escape "$CONFIG_TITLE")" "$(json_escape "$DIALOG_MESSAGE")" "$(json_escape "$CONFIG_BUTTON1")" "$TIMER_SECONDS")
if [[ "$CAN_DEFER" == "true" ]]; then
  DIALOG_JSON+=',"button2text":"Defer"'
fi
DIALOG_JSON+='}'
DIALOG_STYLE_ARGS=(--style centered --position topright)
/bin/launchctl asuser "$CONSOLE_UID" /usr/bin/sudo -u "$CONSOLE_USER" "$DIALOG_BIN" "${DIALOG_STYLE_ARGS[@]}" --jsonstring "$DIALOG_JSON"
DIALOG_EXIT=$?
reset_deferrals() {
  /bin/rm -f "$DEFERRAL_FILE" >/dev/null 2>&1 || true
}
record_deferral() {
  /bin/mkdir -p "$DEFERRAL_DIR"
  local next_count=$(( DEFER_COUNT + 1 ))
  /usr/bin/defaults write "$DEFERRAL_FILE" count -int "$next_count" >/dev/null 2>&1 || true
  exit 1
}
case "$DIALOG_EXIT" in
  0)
    reset_deferrals
    exit 0
    ;;
  2)
    if [[ "$CAN_DEFER" == "true" ]]; then
      record_deferral
    fi
    reset_deferrals
    exit 0
    ;;
  4)
    if [[ "$TIMER_EXPIRY" == "defer" && "$CAN_DEFER" == "true" ]]; then
      record_deferral
    fi
    reset_deferrals
    exit 0
    ;;
  *)
    exit 0
    ;;
esac

Post-install script

Download .sh
View post-install script
#!/bin/zsh
# Intune script content must remain below 15360 characters.
DIALOG_BIN='/usr/local/bin/dialog'
if [[ ! -x "$DIALOG_BIN" ]]; then
  exit 0
fi
CONSOLE_USER=$(/usr/bin/stat -f "%Su" /dev/console 2>/dev/null || true)
if [[ -z "$CONSOLE_USER" || "$CONSOLE_USER" == "root" || "$CONSOLE_USER" == "loginwindow" ]]; then
  exit 0
fi
CONSOLE_UID=$(/usr/bin/id -u "$CONSOLE_USER" 2>/dev/null || true)
if [[ -z "$CONSOLE_UID" ]]; then
  exit 0
fi
CONFIG_TITLE='Example App is ready'
CONFIG_BODY='**Example App** version 2.0.0 was installed successfully.'
AUTO_DISMISS_SECONDS=15
DIALOG_MESSAGE="$CONFIG_BODY"
SUPPORT_NAME=''
SUPPORT_EMAIL=''
SUPPORT_PHONE=''
SUPPORT_TEXT=""
if [[ -n "$SUPPORT_NAME" ]]; then
  SUPPORT_TEXT="Name: ${SUPPORT_NAME}"
fi
if [[ -n "$SUPPORT_EMAIL" ]]; then
  [[ -n "$SUPPORT_TEXT" ]] && SUPPORT_TEXT+=$'\n'
  SUPPORT_TEXT+="Email: ${SUPPORT_EMAIL}"
fi
if [[ -n "$SUPPORT_PHONE" ]]; then
  [[ -n "$SUPPORT_TEXT" ]] && SUPPORT_TEXT+=$'\n'
  SUPPORT_TEXT+="Phone: ${SUPPORT_PHONE}"
fi
if [[ -n "$SUPPORT_TEXT" ]]; then
  DIALOG_MESSAGE+=$'\n\n**Support**\n'"$SUPPORT_TEXT"
fi
json_escape() {
  local value="$1"
  local escaped=""
  local char=""
  local index
  for (( index = 1; index <= ${#value}; index++ )); do
    char="${value[index]}"
    case "$char" in
      \\) escaped+="\\\\" ;;
      \") escaped+="\\\"" ;;
      $'\n') escaped+="\\n" ;;
      $'\r') escaped+="\\r" ;;
      $'\t') escaped+="\\t" ;;
      *) escaped+="$char" ;;
    esac
  done
  /usr/bin/printf "%s" "$escaped"
}
DIALOG_JSON=$(/usr/bin/printf '{"title":"%s","message":"%s","timer":%s}' "$(json_escape "$CONFIG_TITLE")" "$(json_escape "$DIALOG_MESSAGE")" "$AUTO_DISMISS_SECONDS")
(/bin/launchctl asuser "$CONSOLE_UID" /usr/bin/sudo -u "$CONSOLE_USER" "$DIALOG_BIN" --style centered --position topright --jsonstring "$DIALOG_JSON" >/dev/null 2>&1 &) || true
exit 0

Download scripts for your app

Open an app's Install Experience settings in Deploy apps or Updates, then expand Generated scripts. Those downloads use the app's details, the settings currently shown in the editor, including unsaved edits, and your support defaults when enabled. Downloading does not save settings or change the app in Intune.

Save the experience and deploy or update the app to apply it to Macs. Intune runs the pre-install script before the PKG installer and the post-install script afterwards. These scripts do not download or install the package themselves.

Before you use the scripts

  • Deploy swiftDialog as a required app before deploying an app with an install experience.
  • Use a macOS PKG app. Intune does not attach these scripts to DMG apps.
  • Test the scripts with your app and settings on a pilot Mac before a wider rollout.
Read the full setup guide