Skip to content

App Management permission

Issue #47: pre-approving AAP’s TCC App Management permission via a com.apple.TCC.configuration-profile-policy profile.

macOS Sequoia (15) and later require a new TCC permission called App Management for any process that wants to modify an installed .app bundle in /Applications. Installomator’s whole job is to replace .app bundles in /Applications, so without this permission patching either silently no-ops on a label or interactively prompts the user to grant access on first use.

On consumer Macs, the OS prompt is acceptable. On managed Macs the standard practice is to pre-approve the permission via a TCC configuration profile so end-users never see the prompt and admins don’t have to coordinate with help-desk to re-approve after every OS upgrade.

This is the part that has historically confused admins (it confused Dan when he assembled the first attempt for issue #47): the GUI app — xyz.techitout.appAutoPatch — never modifies installed app bundles. The actual .app replacement happens inside Installomator.sh, which is launched as a child process of the root helper LaunchDaemon (bundle ID xyz.techitout.appAutoPatch.roothelper, launchd label xyz.techitout.appAutoPatch.roothelper, binary embedded at Contents/MacOS/AppAutoPatchRootHelper inside the AAP .app bundle).

A TCC profile that only targets the GUI app’s bundle identifier won’t do anything useful — the GUI never asks for App Management because it never modifies installed apps. The grant has to be against the helper. That’s why a manually-assembled profile of the GUI’s identifier alone “appears correct” but “doesn’t show as enabled” in System Settings → Privacy & Security → App Management — the OS quietly drops the rule because the identified subject never requests the right.

A ready-to-sign example is at Profile Manifests/com.apple.TCC.configuration-profile-policy-AppAutoPatch.example.mobileconfig. It contains two TCC SystemPolicyAppBundles entries — one for the GUI (used for AppleEvents AAP sends to coordinate with swiftDialog etc.) and one for the helper (used for the actual app-bundle replacement during patching) — plus the existing com.apple.servicemanagement rules so Login Items & Extensions approval comes through the same profile.

Sign with productsign / your MDM’s own signing flow before deployment. Sample plist looks like:

<key>Services</key>
<dict>
<key>SystemPolicyAppBundles</key>
<array>
<!-- 1. GUI app -->
<dict>
<key>Allowed</key>
<true/>
<key>Identifier</key>
<string>xyz.techitout.appAutoPatch</string>
<key>IdentifierType</key>
<string>bundleID</string>
<key>CodeRequirement</key>
<string>identifier "xyz.techitout.appAutoPatch" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = "6GBSVT64FS"</string>
</dict>
<!-- 2. Root helper -->
<dict>
<key>Allowed</key>
<true/>
<key>Identifier</key>
<string>xyz.techitout.appAutoPatch.roothelper</string>
<key>IdentifierType</key>
<string>bundleID</string>
<key>CodeRequirement</key>
<string>identifier "xyz.techitout.appAutoPatch.roothelper" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = "6GBSVT64FS"</string>
</dict>
</array>
</dict>

The subject.OU value has to be the Team ID that signed the installed app. The sample uses 6GBSVT64FS. See Which Team ID below before deploying the profile.

After deploying the profile, on a target Mac:

Terminal window
sudo log show --predicate 'subsystem == "com.apple.TCC"' --info --last 5m | grep -i "appauto\|appautopatch"

You should see lines like granted permission kTCCServiceSystemPolicyAppBundles for both bundle identifiers. The tccutil reset path for that service is also useful when reproducing pre-approval issues:

Terminal window
sudo tccutil reset SystemPolicyAppBundles xyz.techitout.appAutoPatch.roothelper

System Settings → Privacy & Security → App Management will list the helper as Managed by your organization once the profile is installed and the rule has matched.

Privacy and Service Management profiles match the app’s signing certificate. certificate leaf[subject.OU] and a Service Management TeamIdentifier rule must equal TeamIdentifier from the copy that is actually installed. The team ID checked into source is not always that certificate.

Team ID Certificate Use it when
6GBSVT64FS Developer ID Application: Robert Schroeder The package was signed with the repo default (AppAutoPatchConstants.developmentTeamID, AAP_TEAM_ID in Scripts/release.sh).
4XSYV3BTMX Developer ID Application: Andrew Spokes The build was signed locally or with DEVELOPMENT_TEAM=4XSYV3BTMX.

Confirm on a Mac that has the build you are deploying:

Terminal window
codesign -dv --verbose=2 "/Applications/App Auto-Patch.app" 2>&1 | grep TeamIdentifier

A profile that still names 6GBSVT64FS does not grant Full Disk Access, App Management, or AppleEvents to a build whose TeamIdentifier is 4XSYV3BTMX. macOS ignores the rule. Do Not Disturb detection then fails closed: the GUI log says the assertions database could not be opened, and focus deferrals do not run (#94). Replace every 6GBSVT64FS in that profile’s CodeRequirement and RuleValue strings with 4XSYV3BTMX for those builds.

Full Disk Access is the SystemPolicyAllFiles service on the GUI bundle xyz.techitout.appAutoPatch. The example profile includes it. The root helper does not read the Do Not Disturb database.

If you need to (re)derive the strings for a fork, run on a built copy of AAP:

Terminal window
codesign -dr - /Applications/App\\ Auto-Patch.app
codesign -dr - /Applications/App\\ Auto-Patch.app/Contents/MacOS/AppAutoPatchRootHelper

Each prints a designated => … line; that’s the value to paste into the profile’s CodeRequirement field.

  • Profile Manifests/MDM-ServiceManagement.md — com.apple.servicemanagement payload that pre-approves the helper LaunchDaemon’s Login Items & Extensions registration. Different permission, but the two profiles are usually deployed together.
  • Profile Manifests/MDM-Notifications.md — pre-approves user notifications for AAP.
  • Profile Manifests/MDM-LaunchAgent.md — keeps the GUI running silently in the user’s session.