Skip to content

BAI: InstallApplication command shape for user-enrolled hosts #44005

Description

@georgekarrv

07 — InstallApplication command shape for user-enrolled hosts

Goal

For user-enrolled iOS/iPadOS hosts, Fleet's InstallApplication MDM command must:

  1. Omit the <key>ChangeManagementState</key><string>Managed</string> pair — Apple returns The MDM request is invalid. when this key is present on a user enrollment.
  2. Keep ManagementFlags=1 (default; removes the app on enrollment removal — matches User Enrollment's data-separation semantics).
  3. Keep InstallAsManaged=true and Options.PurchaseMethod=1.
  4. Route via the user channel if required by the enrollment (see subtask 06 step 4).

Applies to both VPP (iTunesStoreID) and in-house .ipa (ManifestURL) install paths.

Reference — current builders

Both are hardcoded XML strings in SQL:

  • VPP: server/datastore/mysql/vpp.go:2630–2679. ChangeManagementState at line 2658.
  • In-house .ipa: server/datastore/mysql/activities.go:1310–1407. ChangeManagementState at line 1345; ManagementFlags=1 for mobile at line 1403.

Marko's verified working XML for user enrollments (from the issue comment):

<plist version="1.0">
<dict>
    <key>Command</key>
    <dict>
        <key>InstallAsManaged</key><true/>
        <key>ManagementFlags</key><integer>1</integer>
        <key>InstallAsManaged</key><true/>
        <key>Options</key>
        <dict><key>PurchaseMethod</key><integer>1</integer></dict>
        <key>RequestType</key><string>InstallApplication</string>
        <key>iTunesStoreID</key><integer>989804926</integer>
    </dict>
    <key>CommandUUID</key><string>...</string>
</dict>
</plist>

Work

1. Move command assembly out of inline SQL

Inline SQL string concatenation can't cleanly do per-row branching. Refactor both builders to assemble the plist in Go:

  • New package/function, e.g., server/mdm/apple/installapp/build.go:
    type InstallApplicationInput struct {
        CommandUUID       string
        ITunesStoreID     string // VPP
        ManifestURL       string // .ipa
        ManagementFlags   int
        IsUserEnrollment  bool   // drives ChangeManagementState omission
    }
    func BuildInstallApplicationXML(in InstallApplicationInput) (string, error)
  • VPP call site (vpp.go:2630) and .ipa call site (activities.go:1310) both call the new builder and insert the resulting string into nano_commands.

2. User-enrollment flag

The builder's IsUserEnrollment comes from host_mdm.is_personal_enrollment, loaded at enqueue time along with the other host fields already fetched.

3. User-channel routing (if required)

If Apple requires the command on the user channel (not the device channel) for user enrollments, the nano_commands insert needs to target the user enrollment's command queue:

  • Check nanomdm's user-channel API — server/mdm/nanomdm/mdm/type.go:91 populates UserChannelID from EnrollmentUserID.
  • Fleet's enqueue path (InsertHostVPPSoftwareInstall + upcoming-activity mechanism) needs to know whether to enqueue on the device enrollment or the user enrollment.
  • Easiest path: let nanomdm deliver via the user channel automatically when a user enrollment is present for that device. Verify empirically before writing fresh plumbing.

4. Tests

  • Snapshot test on the XML output for:
    • VPP + manually-enrolled → includes ChangeManagementState
    • VPP + user-enrolled → omits ChangeManagementState
    • .ipa + user-enrolled → omits ChangeManagementState, has ManifestURL instead of iTunesStoreID
  • Unit test on the builder directly.

Deliverables

  • Refactored command builder in Go.
  • Both VPP and .ipa enqueue paths route through it.
  • Tests.

Acceptance

  • go test ./server/mdm/apple/... ./server/service/... ./server/datastore/mysql/... passes.
  • Integration test (subtask 12) verifies the simulated device receives the correct XML per enrollment type.

Blockers

  • Benefits from subtask 03 being in place (host → enrollment type lookup is easier with the new helper), but can be done without it.

Metadata

Metadata

Assignees

Labels

#g-auto-patchingProduct group focused on auto patching software~backendBackend-related issue.~sub-taskA technical sub-task that is part of a story. (Not QA'd. Not estimated.)

Type

No type

Projects

Status
Done

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions