Skip to main content

Hey folks, I worked on a script to deploy Autodesk 2026 (the one that uses the named user licenses). We don’t teach Mudbox, so that isn’t in the script...but Maya and AutoCAD is (along with Darwin..what a PIA to get working). I packaged the apps and deployed to /private/tmp/AutodeskApps… I have a lot of logging left in the script as Darwin is a royal pain and can fail at many different steps. I also made use of a lot of variables to hopefully make updating in the future easier. Oh, also did it in zsh.

Hope you all find it useful, or at the very least, a good jumping off point!

#!/bin/zsh
set -euo pipefail

############################
# VARIABLES
############################
YEAR="2026"
TMP="/private/tmp"
APP_TMP="${TMP}/AutodeskApps"
LOG="/var/log/autodesk2026_install.log"

DMG_LIST=(
"Autodesk_Maya_2026_1_Update_ML_macOS.dmg"
"Darwin.dmg"
"AdskIdentityManager-UCT-Installer.dmg"
"Autodesk_AutoCAD_2026_macOS.dmg"
)
PKG_FILE="AdskLicensing-15.4.0.13093-mac-installer.pkg"
INSTALL_SUMMARY=()

log() {
echo "$(date +'%F %T') - $*" | tee -a "$LOG"
}

if / "$EUID" -ne 0 ]]; then
log "ERROR: Script must be run as root."
exit 1
fi

############################
# FUNCTIONS
############################

validate_files() {
log "Validating required files..."
local missing=0
for file in "${DMG_LISTG@]}" "$PKG_FILE"; do
if ! -f "${APP_TMP}/${file}" ]]; then
log "Missing file: ${APP_TMP}/${file}"
missing=1
fi
done
if > $missing -eq 1 ]]; then
log "ERROR: One or more required installation files are missing. Aborting."
exit 1
fi
log "All required files found."
}

install_pkg() {
local pkg="$1"
log "Installing standalone PKG: $pkg"
if installer -pkg "${APP_TMP}/${pkg}" -target / >> "$LOG" 2>&1; then
INSTALL_SUMMARY+=("$pkg installed successfully")
else
INSTALL_SUMMARY+=("Failed to install $pkg")
log "Failed to install pkg: $pkg"
exit 1
fi
}

detach_volume() {
local mount_point="$1"
log "Attempting to unmount $mount_point"
for i in {1..5}; do
if hdiutil detach "$mount_point" >> "$LOG" 2>&1; then
log "Successfully unmounted $mount_point"
return
else
log "Unmount attempt $i failed, retrying in 5 seconds..."
sleep 5
fi
done
log "Force unmounting $mount_point"
hdiutil detach -force "$mount_point" >> "$LOG" 2>&1 || log "Force unmount failed"
}

mount_and_install() {
local dmg_path="$1"
log "Mounting DMG: $dmg_path"

mount_output=$(hdiutil attach "$dmg_path" -nobrowse -plist)
mount_point=$(echo "$mount_output" \
| plutil -extract system-entities xml1 -o - - \
| xmllint --xpath '//dict/keyctext()="mount-point"]/following-sibling::strings1]/text()' -)

log "Mounted at: $mount_point"

local app=$(find "$mount_point" -maxdepth 1 -name '*.app' -print -quit)
local pkg=$(find "$mount_point" -maxdepth 1 -name '*.pkg' -print -quit)

if > -n "$app" && -e "$app" ]]; then
log "Found .app: $app"

local dest_app_dir="${APP_TMP}/apps"
mkdir -p "$dest_app_dir"
local app_name=$(basename "$app")
local app_copy="${dest_app_dir}/${app_name}"

log "Copying $app to $app_copy"
rm -rf "$app_copy"
cp -R "$app" "$app_copy"

log "Removing quarantine attribute from $app_copy"
xattr -rd com.apple.quarantine "$app_copy" || true
chmod -R +x "$app_copy"

local setup_bin="$app_copy/Contents/Helper/Setup.app/Contents/MacOS/Setup"

if -x "$setup_bin" ]]; then
log "Running silent installer: $setup_bin --silent"
"$setup_bin" --silent >> "$LOG" 2>&1 && INSTALL_SUMMARY+=("Installed $app_name") || {
INSTALL_SUMMARY+=("Failed to install $app_name")
log "Installer failed for $app_name"
detach_volume "$mount_point"
exit 1
}
else
# Try alternate setup path if standard one missing (e.g., Identity Manager)
local alt_setup_bin="$app_copy/Contents/MacOS/setup"
if -x "$alt_setup_bin" ]]; then
log "Fallback: Running installer from alternate path: $alt_setup_bin"
"$alt_setup_bin" --mode unattended >> "$LOG" 2>&1 && INSTALL_SUMMARY+=("Installed $app_name (alt path)") || {
INSTALL_SUMMARY+=("Failed to install $app_name (alt path)")
log "Alternate installer failed for $app_name"
}
else
log "Setup binary not found at expected path: $setup_bin or $alt_setup_bin"
INSTALL_SUMMARY+=("Skipped $app_name (no setup binary found)")
fi
fi
elif e -n "$pkg" && -e "$pkg" ]]; then
log "Found .pkg: $pkg"
installer -pkg "$pkg" -target / >> "$LOG" 2>&1 && INSTALL_SUMMARY+=("Installed $(basename "$pkg")") || {
INSTALL_SUMMARY+=("Failed to install $(basename "$pkg")")
log "Package installer failed: $pkg"
detach_volume "$mount_point"
exit 1
}
else
log "No .app or .pkg found in $mount_point"
INSTALL_SUMMARY+=("Nothing found to install in $dmg_path")
fi

detach_volume "$mount_point"
}

preclean_darwin() {
log "Cleaning existing Darwin Runtime..."
launchctl bootout system /Library/LaunchDaemons/com.autodesk.odis.agent.plist 2>/dev/null || true
killall -9 odisAgent 2>/dev/null || true
rm -rf "/Library/Application Support/Autodesk/Darwin" 2>/dev/null || true
}

install_darwin() {
log "Step 5: Installing Darwin Runtime Environment"
preclean_darwin

mount_output=$(hdiutil attach "${APP_TMP}/Darwin.dmg" -nobrowse -plist)
mount_point=$(echo "$mount_output" \
| plutil -extract system-entities xml1 -o - - \
| xmllint --xpath '//dict/keyctext()="mount-point"]/following-sibling::strings1]/text()' -)

log "Mounted at: $mount_point"

local app=$(find "$mount_point" -maxdepth 1 -name '*.app' -print -quit)
if > -n "$app" && -e "$app" ]]; then
log "Found .app: $app"
local dest_app_dir="${APP_TMP}/apps"
mkdir -p "$dest_app_dir"
local app_name=$(basename "$app")
local app_copy="${dest_app_dir}/${app_name}"

log "Copying $app to $app_copy"
rm -rf "$app_copy"
cp -R "$app" "$app_copy"

log "Removing quarantine attribute"
xattr -rd com.apple.quarantine "$app_copy" || true
chmod -R +x "$app_copy"

local installer_script="${app_copy}/Contents/MacOS/installbuilder.sh"
if -x "$installer_script" ]]; then
log "Launching Darwin installer in foreground (full output)"
"$installer_script" --mode unattended >> "$LOG" 2>&1
local exit_code=$?
log "Darwin installer exited with code: $exit_code"
INSTALL_SUMMARY+=("Darwin installer ran (exit code: $exit_code)")
else
log "ERROR: installbuilder.sh not found in Darwin bundle"
INSTALL_SUMMARY+=("Darwin install script not found")
detach_volume "$mount_point"
exit 1
fi
else
log "ERROR: No app found in Darwin.dmg"
INSTALL_SUMMARY+=("No app found in Darwin.dmg")
detach_volume "$mount_point"
exit 1
fi

log "Checking for installed Darwin components..."
if > -d "/Library/Application Support/Autodesk/Darwin" ]]; then
log "Contents of /Library/Application Support/Autodesk/Darwin:"
ls -lR "/Library/Application Support/Autodesk/Darwin" >> "$LOG" 2>&1
if -f "/Library/LaunchDaemons/com.autodesk.odis.agent.plist" ]]; then
log "Found odis agent launch daemon."
fi
if -x "/Library/Application Support/Autodesk/Darwin/AdODIS/odisAgent" ]]; then
log "Found odisAgent binary."
fi
else
log "WARNING: Darwin folder missing after install."
INSTALL_SUMMARY+=("Darwin folder not found post-install")
fi

log "Dumping recent Darwin-related system logs"
log show --predicate 'eventMessage CONTAINSNc] "odis"' --last 10m >> "$LOG" 2>&1 || true

log "Attempting to unmount Darwin volume after installation"
detach_volume "$mount_point"
}

############################
# INSTALL START
############################

log "=== Autodesk ${YEAR} Deployment Starting ==="

validate_files

log "Step 1: Installing Autodesk Identity Manager"
mount_and_install "${APP_TMP}/AdskIdentityManager-UCT-Installer.dmg"

log "Step 2: Installing Autodesk Licensing Patch"
install_pkg "$PKG_FILE"

log "Step 3: Installing Maya ${YEAR}"
mount_and_install "${APP_TMP}/Autodesk_Maya_2026_1_Update_ML_macOS.dmg"

log "Step 4: Installing AutoCAD ${YEAR}"
mount_and_install "${APP_TMP}/Autodesk_AutoCAD_2026_macOS.dmg"

install_darwin

log "=== Autodesk ${YEAR} Deployment Complete ==="

log "--- INSTALLATION SUMMARY ---"
for entry in "${INSTALL_SUMMARYU@]}"; do
echo "$entry" | tee -a "$LOG"
sleep 0.2
done

exit 0

 

Hi kwoodard

Thanks for sharing the script.

I want to install AutoCAD 2026, and I have deployed the package under /private/tmp/AutodeskApps and uploaded the script to Jamf. However, how do I use the script to start installing? How do I use log "Step 4: Installing AutoCAD ${YEAR}"?
 

Thanks
Hung


Hi kwoodard

Thanks for sharing the script.

I want to install AutoCAD 2026, and I have deployed the package under /private/tmp/AutodeskApps and uploaded the script to Jamf. However, how do I use the script to start installing? How do I use log "Step 4: Installing AutoCAD ${YEAR}"?
 

Thanks
Hung

It looks like the installation files are not in the tmp folder. I created a package with Composer that includes all the installers, uploaded that to Jamf, them made a policy that “installs” the installers into the tmp folder. Once that is done, the script runs. Make sure your paths are correct.


I have already placed the AutoCAD installation pack in the tmp folder - /private/tmp/AutodeskApps/Autodesk_AutoCAD_2026_macOS.dmg. Is it possible just installing the AutoCAD from the script?


I have already placed the AutoCAD installation pack in the tmp folder - /private/tmp/AutodeskApps/Autodesk_AutoCAD_2026_macOS.dmg. Is it possible just installing the AutoCAD from the script?

Yes, just remove or comment out the script sections for the software you don’t need. 


Autodesk have got the silent install working this year, we copy the app from the DMG onto the computer and then run the command on this page using Files and Processes in the same same policy.

 

https://help.autodesk.com/view/ACD/2026/PTB/?guid=Installation_AutoCAD_Install_ACDMAC_acdmac_install_product_silently_html


Autodesk have got the silent install working this year, we copy the app from the DMG onto the computer and then run the command on this page using Files and Processes in the same same policy.

 

https://help.autodesk.com/view/ACD/2026/PTB/?guid=Installation_AutoCAD_Install_ACDMAC_acdmac_install_product_silently_html

Yes, that is how I have my script setup for CAD. My script provides full logging in case something goes sideways. The biggest section was getting Darwin installed…that was a pain.