Jamf 300 Certification – Complete Reference Guide

Full step-by-step instructions • All tasks & solutions • Instructor-preferred methods

Lesson 1: LDAP

Objective: Integrate Jamf Pro with an LDAP server.

Key discussion points: Integration service account, Search base, Attribute mappings (and how to modify them), Target users, Extension attributes + Inventory collection.

Useful navigation: Settings → System → LDAP servers | Settings → Computer management → Inventory collection | Settings → Device management → Inventory collection

Task 1 – Create LDAP Integration
  1. Go to Settings → System → LDAP Servers and click New.
  2. Enter the classroom LDAP server information provided by the instructor.
  3. Important: Do not select “Use SSL” when entering the classroom LDAP Server Information.
  4. Map the following attributes exactly as listed:
    • Email attribute → LDAP mapping mail
    • Building attribute → LDAP mapping division
  5. Save the LDAP server connection.
Task 2 – Change Display Name and Search Base
  1. Edit the LDAP server you just created.
  2. Change the Display Name to Faculty.
  3. Under User Mappings, change the LDAP search base so that it queries only the Faculty organizational unit (OU).
  4. Save.
Task 3 – Web Clip Configuration Profile for Jamf Nation
  1. Go to Devices → Configuration Profiles → New.
  2. General tab:
    • Distribution Method: Install Automatically
  3. Payloads → Web Clip:
    • Label / Name: anything descriptive
    • URL: https://community.jamf.com/
  4. Scope:
    • Targets: All Mobile Devices
    • Limitations: Support LDAP group
  5. Save the profile.
Task 4 – Enable Directory Service Inventory Collection
  1. Go to Settings → Computer Management → Inventory Collection.
  2. Enable the checkbox Collect user and location information from Directory Service.
  3. Go to Settings → Device Management → Inventory Collection.
  4. Enable the same option for mobile devices.
  5. Save both pages.
Task 5 – Extension Attribute mapped to carLicense
  1. Create a Computer Extension Attribute that pulls the LDAP attribute carLicense (used for the tech agreement).
  2. Create the same Extension Attribute for Mobile Devices.
  3. Both should be configured to collect the value from the directory service.

Lesson 2: Infrastructure

Objective: Create infrastructure components for package distribution and inventory management.

Key discussion points: Distribution points, Jamf Cloud Distribution Service, Sites (creation, access, assignment, objects), Network segments, Self Service for iOS and macOS, Jamf Pro health check page, using curl to transfer data.

Useful navigation: Settings → Server → Cloud distribution point / File share distribution points | Settings → Network → Sites / Network segments | Settings → Self Service

Task 1 – Confirm Cloud Distribution Point
  1. Navigate to Settings → Server → Cloud distribution point.
  2. Verify that it is configured to use Jamf Cloud.
Task 2 – Create the three Sites
  1. Go to Settings → Network → Sites.
  2. Create three new sites with these exact names:
    • AMER
    • APAC
    • EMEIA
Task 3 – User Group with AMER Site Access
  1. Create a standard Jamf Pro user group.
  2. Give the group site access to the AMER site.
  3. Assign the group administrator privileges.
Task 4 – User Account assigned to the group
  1. Create a standard Jamf Pro user account.
  2. Assign the account to the group you created in Task 3.
  3. Choose any fictional username and configure a password.
Task 5 – Duplicate Smart Groups into each Site
  1. Locate the built-in smart computer group named All Managed Clients.
  2. Duplicate it three times – once for each site (AMER, APAC, EMEIA).
  3. Locate the built-in smart mobile device group named All Managed iPads.
  4. Duplicate it three times as well (one copy per site).
Task 6 – Create the Class Network Segment
  1. Run the provided script printNetworkSegment.zsh to obtain the correct IP range / values.
  2. Go to Settings → Network → Network segments.
  3. Create a new network segment named Class and fill in the values from the script output.
Task 7 – Set Self Service+ as the default
  1. Go to Settings → Jamf Apps.
  2. Select Use Self Service+ as the default end-user application.
Task 8 – Self Service for iOS distribution method
  1. Go to Settings → Self Service → iOS (or the equivalent Self Service settings page).
  2. Confirm that the distribution method for the Self Service app for iOS is set to Automatically install Self Service app.

Lesson 3: API Roles and Clients

Objective: Integrate tools into Jamf Pro using API Roles and Clients.

Key discussion points: Jamf Pro API, API Authentication (API Roles & Clients, Bearer Token), Integrating external tools (Jamf Sync), using curl to obtain tokens and call the API.

Navigation: Settings → System → API Roles and Clients

Task 1 – Import Z Shell Script language into CodeRunner
  1. Download the file referenced in the lesson Files section (usually named something like zshShellScript.crLanguage.zip).
  2. Import that language definition into the CodeRunner application so you can write and run zsh scripts for the rest of the course.
Task 2 – Create six single-privilege API Roles
  1. Go to Settings → System → API Roles and Clients.
  2. Create six separate API roles. Each role must contain exactly one of the following privileges:
    • Read Activation Code
    • Read Computers
    • Read Sites
    • Read Smart Computer Groups
    • Read Static Computer Groups
    • Read Mobile Devices
Task 3 – API Client named “Lesson 3 Tasks Client”
  1. Create a new API Client and name it Lesson 3 Tasks Client.
  2. Assign it the following five roles that you created in Task 2:
    • Read Activation Code
    • Read Computers
    • Read Sites
    • Read Smart Computer Groups
    • Read Mobile Devices
  3. Save the client and keep the Client ID / Secret if you need them later.
Task 4 – API Client for Jamf Sync integration
  1. Create another new API Client.
  2. Assign it privileges required for package and cloud-distribution operations:
    • Create Packages
    • Read Packages
    • Update Packages
    • Delete Packages
    • Jamf Packages Action
    • Read Distribution Points
    • Read Cloud Distribution Point
    • Update Cloud Distribution Point
    • Read Cloud Services Settings
    • Create Jamf Cloud Distribution Service Files
    • Read Jamf Cloud Distribution Service Files
    • Delete Jamf Cloud Distribution Service Files
  3. Use this client to establish the integration between Jamf Sync and Jamf Pro.

Bearer Token Example (for testing)

baseURL="JAMF_PRO_URL"
client_id="CLIENT_ID"
client_secret="CLIENT_SECRET"

access_token=$(curl --silent --location \
    --request POST "https://$baseURL/api/oauth/token" \
    --header "Content-Type: application/x-www-form-urlencoded" \
    --data-urlencode "client_id=$client_id" \
    --data-urlencode "grant_type=client_credentials" \
    --data-urlencode "client_secret=$client_secret" | plutil -extract "access_token" raw -)

# Quick test that the token works
curl --silent --header "Authorization: Bearer $access_token" \
     https://$baseURL/api/v2/computers-inventory

Lesson 4: Enrollment

Objective: Enroll computers and mobile devices with an associated LDAP user.

Key discussion points: Device Enrollment & Supervision, User-Initiated Enrollment with LDAP, Enrollment Invitations / Enrollment Profiles, Local Administrator Password Solution (LAPS), Secure Token / Bootstrap Token, Login & Background Items (macOS 13+).

Useful navigation: Settings → Global → User-initiated enrollment | Settings → Computer Management → Security | Devices → Enrollment Profiles | Computers → Search Inventory → History → Managed Local Administrator Account History

Task 1 – Enable LAPS for PreStage accounts
  1. Locate the LAPS (Local Administrator Password Solution) settings.
  2. Enable LAPS for PreStage accounts.
  3. Set the Rotation Interval to 7 days.
  4. Note the available options for “Rotation After Viewing Interval”.
Task 2 – User-Initiated Enrollment + Managed Admin Account
  1. Go to Settings → Global → User-initiated enrollment.
  2. Enable user-initiated enrollment for computers.
  3. Enable the option to create a managed local administrator account.
  4. Set the username of that account to jamfManage.
Task 3 – Restrict the All Directory Service Users group
  1. In the enrollment access settings find the group All Directory Service Users.
  2. Deselect the checkbox Allow group to enroll institutionally owned devices.
Task 4 – Grant access to the Support LDAP group
  1. Add Enrollment Access for the Support LDAP group.
  2. Select Allow group to enroll institutionally owned devices and set the destination to Any site.
Task 5 – Grant limited access to the Information Technology LDAP group
  1. Add Enrollment Access for the Information Technology LDAP group.
  2. Allow the group to enroll institutionally owned devices, but restrict them to the EMEIA site only.
Task 6 – Enroll devices as user kcox
  1. Enroll both your test computer and your test mobile device using the LDAP user kcox.
  2. During the enrollment process assign:
    • Computer → AMER site
    • Mobile device → APAC site
Task 7 – Change LAPS rotation time via the API
  1. Open the Jamf Pro API documentation (usually at https://your-jamf-pro-url/api/doc).
  2. Locate the endpoint PUT /v2/local-admin-password/settings.
  3. Set the value of passwordRotationTime to 300 (seconds = 5 minutes).
Task 8 & 9 – View the password and confirm rotation
  1. Open the inventory record for your computer.
  2. Go to General → Managed Local Administrator Accounts.
  3. Click View accounts and passwords, then click the View button.
  4. You should receive a notification that the password will be rotated in 5 minutes. Copy the current password and note the time.
  5. Wait at least 5 minutes, view the password again, and confirm that it has changed.

Lesson 5: Framework

Objective: Configure the management framework and create scheduled tasks using LaunchAgents and LaunchDaemons.
Instructor preference: Always use the modern launchctl bootstrap / bootout commands. Do not use the older load / unload commands.

Key discussion points: Login Events, Check-in, Daemons, Agents, the structure of a launchd plist, the defaults command, launchctl, the id utility.

Important paths:

  • Per-user agents → ~/Library/LaunchAgents/
  • System-wide agents → /Library/LaunchAgents/
  • System daemons → /Library/LaunchDaemons/
Task 1 – LaunchAgent that opens the Jamf Learning Hub

Create an agent whose label is com.Lesson5Task1.agent and that opens https://learn.jamf.com when it loads.

#!/bin/zsh --no-rcs

label="com.Lesson5Task1.agent"
plistPath="$HOME/Library/LaunchAgents/$label.plist"

# Clean up any previous version
launchctl bootout gui/$(id -u)/$label 2>/dev/null || true
rm -f "$plistPath"

# Build the plist
defaults write "$plistPath" Label -string "$label"
defaults write "$plistPath" ProgramArguments -array \
    -string "/usr/bin/open" \
    -string "https://learn.jamf.com"
defaults write "$plistPath" RunAtLoad -boolean TRUE

chmod 644 "$plistPath"

# Load it into the current user's GUI session
launchctl bootstrap gui/$(id -u) "$plistPath"
Task 2 – Script that updates the management framework and writes a timestamp

The script must run jamf manage and append the current UTC date/time to /Users/Shared/managementTime.txt.

#!/bin/zsh --no-rcs

/usr/local/jamf/bin/jamf manage
date -u +%F\ %T >> /Users/Shared/managementTime.txt

Recommended way to place it on disk (adjust the path if your instructor prefers the Desktop):

sudo tee /usr/local/bin/managementUpdate.sh << 'EOF'
#!/bin/zsh --no-rcs

/usr/local/jamf/bin/jamf manage
date -u +%F\ %T >> /Users/Shared/managementTime.txt
EOF

sudo chmod +x /usr/local/bin/managementUpdate.sh
Task 3 – LaunchDaemon that runs the script every 20 minutes

Create a daemon with label com.Lesson5Task3.daemon that executes the script from Task 2 every 1200 seconds.

#!/bin/zsh --no-rcs

label="com.Lesson5Task3.daemon"
plistPath="/Library/LaunchDaemons/$label.plist"
scriptPath="/usr/local/bin/managementUpdate.sh"

sudo rm -f "$plistPath"
sudo launchctl bootout system/$label 2>/dev/null || true

sudo defaults write "$plistPath" Label -string "$label"
sudo defaults write "$plistPath" ProgramArguments -array \
    -string "/bin/zsh" \
    -string "$scriptPath"
sudo defaults write "$plistPath" RunAtLoad -boolean TRUE
sudo defaults write "$plistPath" StartInterval -integer 1200

sudo chown root:wheel "$plistPath"
sudo chmod 644 "$plistPath"

sudo launchctl bootstrap system "$plistPath"
Task 4 – Bootstrap everything and verify
  1. Make sure both the agent and the daemon have been bootstrapped (commands are in the scripts above).
  2. Restart the computer.
  3. After login the Jamf Learning Hub should open automatically.
  4. Confirm that timestamps are being written to /Users/Shared/managementTime.txt.
# Useful verification commands
launchctl list | grep Lesson5Task1
sudo launchctl list | grep Lesson5Task3
cat /Users/Shared/managementTime.txt

Lesson 6: User Management

Objective: Configure settings and preferences for users (Notifications, Application & Custom Settings, Custom Schemas).

Key discussion points: Mobile device configuration profiles (Notifications payload), Computer configuration profiles (Application & Custom Settings, using defaults and plutil, Custom Schemas created with the Managed App Schema Builder), User-level profiles, Payload variables.

Task 1 – Mobile Device Profile for Self Service Notifications
  1. Go to Devices → Configuration Profiles → New.
  2. General:
    • Give the profile a clear name (e.g. “Self Service Notifications – APAC”).
    • Site: APAC
    • Distribution Method: Install Automatically
  3. Select the Notifications payload and click Configure.
  4. Fill in the settings exactly as required:
    • Bundle ID: com.jamfsoftware.selfservice
    • Notifications: Enabled
    • Banner alert type: Persistent
    • Notifications on Lock Screen: Hidden
    • Notifications in Notification Center: Displayed
    • Badge app icon: Displayed
    • Play sound for notifications: Enabled
  5. Scope: Supervised mobile devices that have the Self Service app installed.
  6. Save the profile.
Task 2 – Computer Profile that forces Safari to show the full URL

Step A – Create the property list on your Mac:

defaults write ~/Desktop/com.apple.Safari ShowFullURLInSmartSearchField -boolean true

This creates a file named com.apple.Safari.plist on your Desktop.

Step B – Build the configuration profile:

  1. Go to Computers → Configuration Profiles → New.
  2. General:
    • Site: AMER
    • Distribution Method: Install Automatically
    • Level: Computer Level
  3. Select the Application & Custom Settings payload.
  4. Choose the Upload method.
  5. Preference Domain: com.apple.Safari
  6. Upload the com.apple.Safari.plist file that is sitting on your Desktop.
  7. Scope: computers running macOS 15.3.1 or later.
  8. Save.
Task 3 – Custom Schema for Dock Orientation (Managed App Schema Builder)
  1. Open the Managed App Schema Builder application (or use the provided com.apple.dock.json.zip if the instructor supplies it).
  2. At the top of the window set:
    • App Title / Preference Domain → com.apple.dock
    • Domain Description → Dock Orientation
  3. Click the + button to add a new key and fill in the fields:
    FieldValue
    Key Nameorientation
    Key Friendly NameDock orientation
    Select Key Typestring (from list)
    List of options (human-readable)Screen Left, Screen Bottom, Screen Right
    Associated list of valuesleft, bottom, right
  4. Save / export the resulting JSON schema file.
  5. In Jamf Pro create a new Computer Configuration Profile:
    • Site: AMER
    • Distribution Method: Install Automatically
    • Level: Computer Level
    • Payload: Application & Custom Settings → External Applications
    • Preference Domain: com.apple.dock
    • Upload the JSON schema you just created
    • Use the Orientation pop-up menu that appears to choose your preferred dock location
    • Scope: All Computers in AMER
  6. Save the profile.

Lesson 7: Managed Login Items

Objective: Safelist and manage scheduled tasks and third-party helper apps.

Key discussion points: Managed Login Items payload, the different Rule Types (Bundle identifier, Bundle identifier prefix, Label, Label prefix, Team identifier), how to obtain Team IDs and Labels.

Navigation: Computers → Configuration Profiles → Managed Login Items

Task 1 – Safelist Google Chrome by Team Identifier

First discover the Team ID on a Mac that has Chrome installed:

codesign --display --verbose /Applications/Google\ Chrome.app 2>&1 | grep TeamIdentifier
# You should see: TeamIdentifier=EQHXZ8M8AV
  1. Create a new Computer Configuration Profile.
  2. Distribution Method: Install Automatically.
  3. Add the Managed Login Items payload.
  4. Rule type: Team identifier.
  5. Rule value: EQHXZ8M8AV.
  6. Scope: All Managed Clients.
  7. Save.
Task 2 – Safelist the OBS camera extension by Bundle Identifier
  1. New Computer Configuration Profile → Managed Login Items.
  2. Rule type: Bundle identifier.
  3. Rule value: com.obsproject.obs-studio.mac-camera-extension.systemextension.
  4. Scope: All Managed Clients | Install Automatically.
  5. Save.
Task 3 – Safelist the agent created in Lesson 5
  1. Rule type: Label.
  2. Rule value: com.Lesson5Task1.agent.
  3. Scope: All Managed Clients.
Task 4 – Safelist the daemon created in Lesson 5
  1. Rule type: Label.
  2. Rule value: com.Lesson5Task3.daemon.
  3. Scope: All Managed Clients.

Lesson 8: System Extensions

Objective: Set the allow list for system extensions.

Key discussion points: System Extension Types (Allowed System Extensions, Non-removable system extensions, Non-removable from UI), Team ID + Bundle ID pairing, the systemextensionsctl command, the database at /Library/SystemExtensions/db.plist.

Task 1 – Install OBS and observe the system extension prompt
  1. Install and launch the OBS application.
  2. On the “Review App Permission” screen click Continue (do not grant any access yet).
  3. Note the macOS alert that a system extension tried to load and requires user consent.
  4. Open System Settings → General → Login Items & Extensions and confirm that OBS has installed an extension.
Task 2 – Obtain the Team ID and Bundle ID of the OBS system extension
# Information about the main OBS application
codesign -dv /Applications/OBS.app

# Information about the camera system extension (this is the one you need)
codesign -dv /Applications/OBS.app/Contents/Library/SystemExtensions/com.obsproject.obs-studio.mac-camera-extension.systemextension

Typical values seen in class:

  • Team Identifier → 2MMRE5MTB8
  • Bundle Identifier → com.obsproject.obs-studio.mac-camera-extension
Task 3 – Configuration Profile that makes the OBS extension non-removable
  1. Go to Computers → Configuration Profiles → New.
  2. General: Site = AMER, Distribution Method = Install Automatically.
  3. Select the System Extension Types payload.
  4. From the pop-up choose Non-removable system extensions.
  5. Enter the Team Identifier: 2MMRE5MTB8.
  6. Critical UI step: Click the small + (Add) button that appears next to the Team Identifier field. A new text field for the Bundle Identifier will appear.
  7. Type the Bundle Identifier: com.obsproject.obs-studio.mac-camera-extension.
  8. Click the small Save button that sits next to that field (do not rely only on the big Save button at the bottom of the page).
  9. Scope:
    • Targets: Supervised computers in the Minneapolis building
    • Exclusions: Less than macOS 26
  10. Save the whole profile.
Task 4 – Print the contents of the System Extensions database
defaults read /Library/SystemExtensions/db.plist

# Alternative readable form
plutil -p /Library/SystemExtensions/db.plist

# Also useful
systemextensionsctl list

Lesson 9: Privacy Preferences Policy Control (PPPC)

Objective: Manage Privacy Preferences Policy Control (TCC) settings.

Key discussion points: Transparency, Consent and Control (TCC), user vs MDM approval, the various services (Camera, Microphone, Accessibility, ScreenCapture, AppleEvents, etc.), Identifier (Bundle ID or Path), Code Requirement / code signature, the difference between a PPPC payload and the PPPC Utility app, pre-approving the Jamf management framework and AppleEvents, the tccutil command.

Task 1 – Practice with osascript and tccutil
# Create a new folder on the Desktop via AppleScript
osascript -e 'tell application "Finder" to make new folder at desktop'

# After the dialog appears, open System Settings → Privacy & Security → Automation
# and confirm that Terminal has permission for Finder.

# Reset all privacy decisions for the Terminal app
tccutil reset All com.apple.Terminal
Task 2 – PPPC profile for OBS Studio
  1. Create a new Computer Configuration Profile.
  2. Distribution Method: Install Automatically | Level: Computer Level.
  3. Add the Privacy Preferences Policy Control payload.
  4. Identifier: com.obsproject.obs-studio
  5. Identifier Type: Bundle ID
  6. Code Requirement (copy exactly):
identifier "com.obsproject.obs-studio" 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] = "2MMRE5MTB8"
  1. Under the Services list set:
    • Accessibility → Deny
    • Screen Capture → Allow Standard User to Set System Service (wording may be “Allow Standard Users to Allow Access”)
  2. Scope: All Managed Clients
    Exclusions: macOS 15.6 or earlier that belong to the Information Technology department.
  3. Save.
Task 3 – Deploy the provided JamfAppleEvents.mobileconfig
  1. Download the file JamfAppleEvents.mobileconfig from the lesson Files section.
  2. In Jamf Pro go to Computers → Configuration Profiles and choose Upload.
  3. Upload the mobileconfig file.
  4. Set Distribution Method to Install Automatically and Level to Computer Level.
  5. Scope the profile to All Managed Clients.
  6. Save.
Task 4 – PPPC profile that denies Camera and Microphone for Google Chrome
  1. New Computer Configuration Profile → Privacy Preferences Policy Control.
  2. Identifier: com.google.Chrome
  3. Identifier Type: Bundle ID
  4. Code Requirement:
(identifier "com.google.Chrome" or identifier "com.google.Chrome.beta" or identifier "com.google.Chrome.dev" or identifier "com.google.Chrome.canary") 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] = EQHXZ8M8AV
  1. Services:
    • Camera → Deny
    • Microphone → Deny
  2. Scope: All Managed Clients | Exclusions: New York building.
  3. Save.

Lesson 10: Restrictions

Objective: Set restrictions for mobile devices and computers, create Restricted Software records, and control software updates.
Task 1 – Mobile Device Restrictions (block specific apps + functionality)
  1. Devices → Configuration Profiles → New.
  2. Site: APAC | Distribution Method: Install Automatically.
  3. Payload: Restrictions
    • Under Apps → App usage choose Some apps not allowed.
    • Add these three bundle IDs:
      • com.apple.mobileslideshow (Photos)
      • com.apple.MobileSMS (Messages)
      • com.apple.facetime (FaceTime)
    • Under Functionality restrict:
      • Modifying Personal Hotspot settings
      • Network drive access in Files app
  4. Scope: All Managed iPads.
  5. Save.
Task 2 – Managed ↔ Unmanaged data-flow restrictions
  1. Create another Mobile Device Configuration Profile (Site APAC, Install Automatically).
  2. Payload: Restrictions → Functionality.
  3. Enable the restrictions that control:
    • Managed apps using iCloud sync
    • Documents from managed sources opening in unmanaged destinations
    • Pasteboard respecting managed/unmanaged boundaries
    • Managed apps writing contacts to unmanaged accounts
    • Unmanaged apps reading contacts from managed accounts
    • AirDrop treated as an unmanaged destination
  4. Scope: All Managed iPads.
  5. Save.
Task 3 – Restricted Software record for FaceTime
Instructor correction: The Site must be AMER (the original materials incorrectly said APAC).
  1. Go to Computers → Restricted Software → New.
  2. Give the record a clear name (e.g. “Block FaceTime”).
  3. Site: AMER.
  4. Process Name: FaceTime.
  5. Check the box Kill process.
  6. Check the box Display a notification / message to the user.
  7. In the message field type exactly:
    Please use approved conferencing software.
  8. Scope: All Computers in AMER | Exclusions: Development department.
  9. Save.
Task 4 – Extension Attribute for Apple Beta Software Program enrollment
  1. Settings → Computer Management → Extension Attributes → New.
  2. Display Name: e.g. “Apple Beta Software Program Status”.
  3. Data Type: String | Input Type: Script.
  4. Paste the following script:
#!/bin/zsh --no-rcs
if [ -f "/Users/Shared/.BetaEnrollmentData.plist" ]; then
    echo "<result>Enrolled</result>"
else
    echo "<result>Not Enrolled</result>"
fi
  1. Save the extension attribute.
Task 5 – Block macOS beta releases and defer updates for 90 days
  1. Computers → Configuration Profiles → New.
  2. Site: AMER | Level: Computer Level | Install Automatically.
  3. Add the Software Update payload and deselect “Allow installation of macOS beta releases”.
  4. Add the Restrictions payload and under Functionality set “Defer software updates” to 90 days.
  5. Scope: All Managed Clients
    Exclusions: Computers that are enrolled in the Apple Beta Software Program.
  6. Save.

Lesson 11: FileVault

Objective: Deploy and manage FileVault 2.

Key discussion points: Security overview, fdesetup, enabling FileVault with a configuration profile, Enabled User / Secure Token / Bootstrap Token, Personal Recovery Key escrow, audit logs.

The Who_Has_SecureToken.zsh file supplied in the lesson Files section is optional exploration material; it is not required for the three scored tasks.
Task 1 – FileVault configuration profile
  1. Computers → Configuration Profiles → New.
  2. General:
    • Site: AMER
    • Distribution Method: Self Service +
    • Level: Computer Level
  3. Select the Security & Privacy payload and configure the FileVault options:
    • Enable FileVault: Include
    • Event to prompt FileVault enablement: At Login
    • Recovery keys: Personal Recovery Key
    • User adjustment of FileVault options: Include
    • Escrow Personal Recovery Key: Include
    • Escrow Location Description: Jamf Pro
    • “Record Number” Message: $SERIALNUMBER
  4. Scope:
    • Targets: All Managed Clients
    • Exclusions: Computers that are not Supervised
  5. Save.
Task 2 – Install the profile via Self Service+ and encrypt the disk
  1. On the test Mac open the Self Service+ application.
  2. Locate and install the FileVault profile you created in Task 1.
  3. Log out of the current user account.
  4. Log back in.
  5. macOS will prompt you to enable FileVault – complete the encryption process.
Task 3 – Submit inventory and view the recovery key
  1. Force an inventory update with sudo jamf recon (or wait for the next check-in).
  2. In Jamf Pro open the computer’s inventory record.
  3. Look in the Security / FileVault section for the escrowed Personal Recovery Key and confirm it is present.

Lesson 12: Scripting Part 1

Objective: Compose scripts that use variables, prompts, parameters, if statements and here documents.

Key discussion points: Variables, command substitution, positional parameters, if statements, here documents (often written with tee), creating files with touch, reading files, redirects, cat.

During the live class many students saved scripts to ~/Desktop instead of /Library/Scripts. Both locations work for testing; follow whatever path the instructor requests on exam day.
Task 1 – Script that prompts for a Policy ID, runs the policy, and saves the output
#!/bin/zsh --no-rcs

echo "Please enter a Policy ID: "
read policyid

sudo jamf policy -id "$policyid" > /Users/Shared/policy"$policyid".txt
Task 2 – Write the Task 1 script to disk with a here document
#!/bin/zsh --no-rcs

sudo tee /Library/Scripts/Lesson12.zsh << "EOF"
#!/bin/zsh --no-rcs

echo "Please enter a Policy ID: "
read policyid

sudo jamf policy -id "$policyid" > /Users/Shared/policy"$policyid".txt
EOF

sudo chmod +x /Library/Scripts/Lesson12.zsh
Task 3 – Report the last time Google Chrome or Firefox was opened
#!/bin/zsh --no-rcs

echo "Google Chrome last used:"
mdls "/Applications/Google Chrome.app" -name kMDItemLastUsedDate

echo ""
echo "Firefox last used:"
mdls "/Applications/Firefox.app" -name kMDItemLastUsedDate
Task 4 – Test whether /Library/Scripts/Lesson12.zsh exists
#!/bin/zsh --no-rcs

if [[ -f /Library/Scripts/Lesson12.zsh ]]; then
    echo "File exists"
else
    echo "File does not exist"
fi
Task 5 – Create Lesson12Task5.zsh with a here document
#!/bin/zsh --no-rcs

sudo tee /Library/Scripts/Lesson12Task5.zsh << "EOF"
#!/bin/zsh --no-rcs

if [[ -f /Library/Scripts/Lesson12.zsh ]]; then
    echo "File exists"
else
    echo "File does not exist"
fi
EOF

sudo chmod +x /Library/Scripts/Lesson12Task5.zsh
Task 6 – Run the script created in Task 5
/Library/Scripts/Lesson12Task5.zsh

Lesson 13: User Interaction

Objective: Display messages to users and collect input from them using jamfHelper and osascript.

Key discussion points: jamfHelper window types, buttons and return codes, executing AppleScript with osascript, capturing the text a user types into a dialog.

jamfHelper does not need sudo. When you run osascript commands inside CodeRunner, keep the language set to Shell Script / Z Shell Script – pure AppleScript mode will produce syntax errors.
Task 1 – Simple “Hello world” with jamfHelper
#!/bin/zsh --no-rcs

jamfHelper="/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper"
"$jamfHelper" -windowType hud -description "Hello world"
Task 2 – Ask the user whether to run an inventory update
#!/bin/zsh --no-rcs

jamfHelper="/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper"

returnCode=$("$jamfHelper" -windowType utility \
    -description "Would you like to perform an inventory update?" \
    -button1 "Yes" \
    -button2 "No")

if [[ $returnCode == 0 ]]; then
    # User clicked the Yes button
    sudo jamf recon
else
    # User clicked the No button
    "$jamfHelper" -windowType utility \
        -description "Inventory will not be updated." \
        -button1 "OK"
fi
Task 3 – “Hello world” with AppleScript
#!/bin/zsh --no-rcs

osascript -e 'display dialog "Hello world" buttons {"OK"} default button 1'

If you encounter quoting problems, use a here-document instead:

#!/bin/zsh --no-rcs

osascript <
Task 4 – Prompt for an asset tag and update the computer record
#!/bin/zsh --no-rcs

assetTag=$(osascript -e 'text returned of (display dialog "Please enter your asset tag" default answer "" buttons {"OK"} default button 1)')

sudo jamf recon -assetTag "$assetTag"

Lesson 14: Scripting Part 2

Objective: Compose scripts that use loops and that accept policy script parameters.

Key discussion points: for / while / until loops, the test / [[ ]] expression, policy script parameters ($4, $5, $6, \ldots).

Task 1 – Until-loop that prints “File not found”
#!/bin/zsh --no-rcs

until [[ -f /Users/Shared/file.txt ]]; do
    echo "File not found"
    sleep 1
done

echo "File found!"
Task 2 – While-loop that keeps asking for the magic number 42
#!/bin/zsh --no-rcs

number=0

while [[ $number -ne 42 ]]; do
    number=$(osascript -e 'text returned of (display dialog "Guess the magic number:" default answer "" buttons {"OK"} default button 1)')
done

echo "Correct! You guessed 42."
Task 3 – Script that reacts to Parameters 4 and 5

Parameter 4 = path of the file to test
Parameter 5 = message that jamfHelper should display if the file exists.

#!/bin/zsh --no-rcs

fileToCheck="$4"
messageToDisplay="$5"

jamfHelper="/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper"

if [[ -e "$fileToCheck" ]]; then
    "$jamfHelper" -windowType utility -description "$messageToDisplay" -button1 "OK"
fi
Task 4 – Policy that executes the Task 3 script
  1. Computers → Policies → New.
  2. General:
    • Site: AMER
    • Trigger: Recurring check-in
    • Frequency: Once every day
  3. Scripts payload: add the script written in Task 3.
    • Parameter 4 → /Users/Shared/managementTime.txt
    • Parameter 5 → Your management framework was updated recently
  4. Maintenance: enable Update Inventory.
  5. Scope:
    • Targets: All Computers in AMER
    • Limitations: Class network segment
  6. Save the policy.

Lesson 15: Extension Attributes

Objective: Create extension attributes that collect extra computer information for Jamf Pro.

Key discussion points: The mandatory <result>...</result> tags, using awk, grep, tr and date inside extension-attribute scripts, choosing the correct Data Type (String, Date, Integer, \ldots).

Task 1 – Pop-up menu Extension Attribute “Help Desk Install”
  1. Settings → Computer Management → Extension Attributes → New.
  2. Display Name: Help Desk Install.
  3. Data Type: String.
  4. Input Type: Pop-up Menu.
  5. Add the two menu items:
    • Disabled
    • Enabled
  6. Save. (This EA is referenced by a later policy.)
Task 2 – Extension Attribute that lists users who possess a Secure Token
  1. New Extension Attribute.
  2. Display Name: e.g. “Users with Secure Token”.
  3. Data Type: String | Input Type: Script.
  4. Paste the contents of the Who_Has_SecureToken.zsh script that was supplied in the FileVault lesson Files section.
  5. Ensure the final output of the script is wrapped in result tags, for example:
    echo "<result>$someVariable</result>"
  6. Save.
Task 3 – Last-opened date of Google Chrome or Firefox (Date type)
  1. New Extension Attribute → Data Type Date → Input Type Script.
  2. Use a script similar to the following:
#!/bin/zsh --no-rcs

chromeDate=$(mdls -name kMDItemLastUsedDate "/Applications/Google Chrome.app" 2>/dev/null | awk '{print $3, $4}')
firefoxDate=$(mdls -name kMDItemLastUsedDate "/Applications/Firefox.app" 2>/dev/null | awk '{print $3, $4}')

if [[ -n $chromeDate && $chromeDate != "(null)" ]]; then
    echo "<result>$chromeDate</result>"
elif [[ -n $firefoxDate && $firefoxDate != "(null)" ]]; then
    echo "<result>$firefoxDate</result>"
else
    echo "<result></result>"
fi
Task 4 – Last restart / boot time (Date type)
  1. New Extension Attribute → Data Type Date → Input Type Script.
  2. Script:
#!/bin/zsh --no-rcs

bootTime=$(sysctl kern.boottime | awk '{print $5}' | tr -d ,)
bootTimeFormatted=$(date -juf %s "$bootTime" +"%Y-%m-%d %H:%M:%S")

echo "<result>$bootTimeFormatted</result>"

Lesson 16: iOS Workflows

Objective: Create managed preferences for mobile-device apps (Managed App Configuration).

Key discussion points: XML key/value pairs and the three value types (Integer, String, Boolean), Jamf variables such as $EMAIL, deploying managed apps, converting unmanaged apps to managed, preventing backup of app data, Per-App Networking.

Navigation: Devices → Mobile Device Apps → (select an app) → App Configuration / Managed App Configuration tab

Task 1 – Microsoft Edge Managed App Configuration
  1. Go to Devices → Mobile Device Apps.
  2. Locate Microsoft Edge (or add it if it is not already present).
  3. Open the app record and switch to the App Configuration (or Managed App Configuration) tab.
  4. Enter the following XML (or use the key/value editor if the UI provides one):
<dict>
	<key>NewTabPageLocation</key>
	<string>https://www.jamf.com</string>
	<key>EdgeDisabledFeatures</key>
	<string>password</string>
</dict>
  1. Explanation of the keys:
    • NewTabPageLocation forces the managed homepage to https://www.jamf.com.
    • EdgeDisabledFeatures set to password disables the iOS prompt that asks to save passwords.
  2. In the app’s General settings set:
    • Site: APAC
    • Distribution Method: Install Automatically
  3. Scope the app to All Managed iPads.
  4. Save / deploy.
Task 2 – Trello Managed App Configuration
  1. Go to Devices → Mobile Device Apps.
  2. Locate or add the Trello app.
  3. Open the App Configuration tab.
  4. Enter this configuration:
<dict>
	<key>atlassian.devicePolicyLoginAccount</key>
	<string>$EMAIL</string>
</dict>
  1. The key atlassian.devicePolicyLoginAccount restricts login to Atlassian mobile apps so that only the email address Jamf knows for the device/user ($EMAIL) is accepted.
  2. General settings: Site = APAC, Distribution Method = Install Automatically.
  3. Scope: All Managed iPads.
  4. Save.

Lesson 17: Composer

Objective: Create installer packages that can run scripts before and after the payload is installed.

Key discussion points: Composer sources, converting a finished PKG back into a source, preinstall and postinstall scripts, setting correct ownership and permissions, building the final .pkg.

Navigation: /Applications/Composer on the Mac | Settings → Packages inside Jamf Pro

Task 1 – Create the destination folder

On the Desktop create a folder named New Packages. All packages built in this lesson will be saved there.

Task 2 – Sequel Pro package that enables automatic update checks
  1. Open Composer.
  2. Drag Sequel Pro.app into the Composer window (or capture it with a Snapshot / Monitor).
  3. In the left-hand source list expand the package and select the Scripts section.
  4. Add a postinstall script containing:
#!/bin/zsh --no-rcs

defaults write /Library/Preferences/com.sequelpro.SequelPro.plist SUEnableAutomaticChecks -boolean true
  1. Build the package as a .pkg.
  2. Save the finished package into ~/Desktop/New Packages/.
Task 3 – Package that installs the management-framework script + daemon

The package must contain:

  • The management-update script written in Lesson 5 Task 2
  • The LaunchDaemon plist written in Lesson 5 Task 3
  • A preinstall script that boots out any older copy of the daemon
  • A postinstall script that sets correct permissions and bootstraps the new daemon

preinstall script:

#!/bin/zsh --no-rcs

# Remove any previous version of the daemon from launchd
launchctl bootout system /Library/LaunchDaemons/com.Lesson5Task3.daemon.plist 2>/dev/null || true

postinstall script:

#!/bin/zsh --no-rcs

# Correct ownership and mode
chown root:wheel /Library/LaunchDaemons/com.Lesson5Task3.daemon.plist
chmod 644 /Library/LaunchDaemons/com.Lesson5Task3.daemon.plist
chmod +x /usr/local/bin/managementUpdate.sh   # adjust the path if you stored the script elsewhere

# Load the daemon
launchctl bootstrap system /Library/LaunchDaemons/com.Lesson5Task3.daemon.plist
  1. In Composer create a new source and place the script and the plist into the proper filesystem locations.
  2. Attach the preinstall and postinstall scripts.
  3. Build a .pkg and save it to ~/Desktop/New Packages/.
Task 4 – Package the Jamf 300 application
  1. Download the Jamf 300 app from the lesson Files section.
  2. Move the application into /Applications/.
  3. Open Composer and capture the app (drag-and-drop or Snapshot).
  4. Build a .pkg and save it to ~/Desktop/New Packages/.
Task 5 – Upload the three packages to Jamf Pro
  1. In Jamf Pro navigate to Settings → Computer Management → Packages.
  2. Upload the three packages that were created in Tasks 2, 3 and 4.

Lesson 18: Policies

Objective: Create policies that cache packages, display notifications to users, and run at controlled times.

Key discussion points: Server-side versus client-side limitations, caching packages (and the “Collect package receipts” inventory setting), user interaction / Self Service+, the classic notifyUser.sh pattern, retry policies.

Task 1 – Verify that package receipts are collected
  1. Go to Settings → Computer Management → Inventory Collection.
  2. Confirm that the checkbox Collect package receipts is enabled.
Task 2 – Policy that caches the Sequel Pro package
  1. Computers → Policies → New.
  2. General:
    • Site: AMER
    • Trigger: Recurring check-in
    • Frequency: Ongoing
  3. Packages payload: add the Sequel Pro package and set its action to Cache.
  4. Maintenance: enable Update Inventory.
  5. Scope:
    • Targets: Support department
    • Limitations: Class network segment
    • Exclusions:
      • Computers that already have the Sequel Pro package cached
      • Computers that already have the Sequel Pro application installed
  6. Save.
Task 3 – Self Service+ policy that installs the cached package
  1. Create a new Policy.
  2. General:
    • Site: AMER
    • Trigger: Self Service +
    • Frequency: Ongoing
    • Server-side limitation: Activate tomorrow at 6:00 a.m.
  3. Packages: add the Sequel Pro package and choose the action Install Cached.
  4. Maintenance: Update Inventory.
  5. Scope: Computers that have the Sequel Pro package cached.
  6. Save the policy and write down its Policy ID – you will need it in the next task.
Task 4 – notifyUser.sh policy that points users at the install policy

First create (or upload) the helper script in Settings → Computer Management → Scripts:

#!/bin/zsh --no-rcs
messageToDisplay="$4"
policyID="$5"
policyAction="$6"
jamfHelper="/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper"
buttonClicked=$("$jamfHelper" -windowType utility -description "$messageToDisplay" -button1 "OK" -button2 "Cancel")
if [[ "$buttonClicked" = "0" ]]; then
    su "$3" -c "open 'jamfselfservice://content?entity=policy&id=$policyID&action=$policyAction'"
fi
  1. Create a new Policy.
  2. General:
    • Site: AMER
    • Trigger: Recurring check-in
    • Frequency: Once every day
    • Server-side limitation: Activate tomorrow at 6:00 a.m.
  3. Scripts: add notifyUser.sh and fill the parameters:
    • Parameter 4 (message) → The Sequel Pro app is now available in the Self Service + app
    • Parameter 5 (Policy ID) → the ID of the policy created in Task 3
    • Parameter 6 (action) → view
  4. Scope: Computers that have the Sequel Pro package cached.
  5. Save.
Task 5 – Policy that installs the Jamf 300 package
  1. New Policy.
  2. General:
    • Site: AMER
    • Trigger: Recurring check-in
    • Frequency: Once per computer
  3. Packages: install the Jamf 300 package.
  4. Maintenance: Update Inventory.
  5. Scope: Computers whose “Help Desk Install” extension attribute is set to Enabled.
  6. Save.
Task 6 – Change activation times and run the notification policy
  1. Edit the two policies created in Task 3 and Task 4.
  2. Change the server-side activation time from “tomorrow at 6:00 a.m.” to today at 6:00 a.m.
  3. On the test computer execute the Task 4 policy manually:
sudo jamf policy -id <PolicyID_from_Task_4>