Skip to content

Commit 67eb06a

Browse files
authored
docs: payload_logic (#922)
* Create plist_payloads.md * Update plist_payloads.md
1 parent 1f6e0a1 commit 67eb06a

File tree

1 file changed

+103
-0
lines changed

1 file changed

+103
-0
lines changed

docs/plist_payloads.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
```mermaid
2+
flowchart TD
3+
Start([Start: mode != update]) --> GetPayloads[Get payloads from Terraform schema<br/>d.Get payloads string]
4+
GetPayloads --> Escape[HTML Escape String<br/>html.EscapeString payloads]
5+
Escape --> Assign[Assign to resource<br/>resource.General.Payloads = escapedPayloads]
6+
Assign --> End([End: Return resource])
7+
8+
style Start fill:#e1f5e1
9+
style End fill:#e1f5e1
10+
style GetPayloads fill:#fff4e1
11+
style Escape fill:#fff4e1
12+
style Assign fill:#fff4e1
13+
```
14+
15+
16+
17+
# Payloads Flow: CREATE vs UPDATE
18+
19+
## **CREATE Flow - Payloads**
20+
21+
```
22+
1. Get payloads string from Terraform schema
23+
└─> d.Get("payloads").(string)
24+
25+
2. HTML escape the entire string
26+
└─> html.EscapeString(payloads)
27+
28+
3. Assign directly to resource
29+
└─> resource.General.Payloads = escapedPayloads
30+
31+
4. Done - No further processing
32+
```
33+
34+
**Simple and straightforward**: Just escape and assign.
35+
36+
---
37+
38+
## **UPDATE Flow - Payloads**
39+
40+
```
41+
1. Fetch existing profile from Jamf Pro API
42+
└─> client.GetMacOSConfigurationProfileByID(resourceID)
43+
└─> Extract: existingProfile.General.Payloads (Jamf Pro's version with modified UUIDs)
44+
45+
2. Decode EXISTING payloads (from Jamf Pro)
46+
└─> plist.NewDecoder(existingPayload).Decode(&existingPlist)
47+
└─> Result: existingPlist map[string]any (contains Jamf Pro's UUIDs)
48+
49+
3. Decode NEW payloads (from Terraform state)
50+
└─> newPayload = d.Get("payloads").(string)
51+
└─> plist.NewDecoder(newPayload).Decode(&newPlist)
52+
└─> Result: newPlist map[string]any (contains user's original UUIDs)
53+
54+
4. Sync TOP-LEVEL UUIDs (Jamf Pro → Terraform)
55+
└─> newPlist["PayloadUUID"] = existingPlist["PayloadUUID"]
56+
└─> newPlist["PayloadIdentifier"] = existingPlist["PayloadIdentifier"]
57+
58+
5. Sync NESTED UUIDs
59+
└─> helpers.ExtractUUIDs(existingPlist, uuidMap, true)
60+
└─> helpers.ExtractPayloadIdentifiers(existingPlist, identifierMap, true)
61+
└─> helpers.UpdateUUIDs(newPlist, uuidMap, identifierMap, true)
62+
63+
6. Validate UUID matching
64+
└─> helpers.ValidatePayloadUUIDsMatch(existingPlist, newPlist, ...)
65+
└─> If mismatches found → return error
66+
67+
7. Re-encode the updated plist
68+
└─> encoder := plist.NewEncoder(&buf)
69+
└─> encoder.Indent(" ")
70+
└─> encoder.Encode(newPlist)
71+
└─> Result: buf contains XML plist with synced UUIDs
72+
73+
8. XML normalization (for embedding plist XML in Jamf Pro XML)
74+
└─> preMarshallingXMLPayloadUnescaping(buf.String())
75+
└─> Replace &#34; with "
76+
└─> preMarshallingXMLPayloadEscaping(unquotedContent)
77+
└─> Replace & with &amp;
78+
79+
9. Assign to resource
80+
└─> resource.General.Payloads = escapedContent
81+
```
82+
83+
---
84+
85+
## **Why This Complexity?**
86+
87+
**Jamf Pro modifies UUIDs after creation**:
88+
- When you CREATE a profile, Jamf Pro changes the top-level `PayloadUUID` and `PayloadIdentifier`
89+
- Nested payload UUIDs stay the same
90+
- If you UPDATE without preserving Jamf Pro's UUIDs, it treats it as a different profile
91+
- This causes deployment problems on managed devices
92+
93+
**The UPDATE flow preserves UUID continuity** by:
94+
1. Fetching what Jamf Pro actually stored
95+
2. Extracting Jamf Pro's modified UUIDs
96+
3. Injecting them into the new configuration
97+
4. Re-encoding everything together
98+
99+
This ensures the update is recognized as a modification to the *same* profile, not a new profile.
100+
101+
102+
103+

0 commit comments

Comments
 (0)