JamfUploader AutoPkg Processors - grahampugh/jamf-upload GitHub Wiki

Note: these Processors are hosted in the autopkg/grahampugh-recipes repo.

Users of AutoPkg can use the following processors to upload items to a Jamf Pro server:

  • JamfAccountUploader
  • JamfCategoryUploader
  • JamfClassicAPIObjectUploader
  • JamfComputerGroupUploader
  • JamfComputerProfileUploader
  • JamfDockItemUploader
  • JamfExtensionAttributeUploader
  • JamfIconUploader
  • JamfMacAppUploader
  • JamfMobileDeviceGroupUploader
  • JamfMobileDeviceProfileUploader
  • JamfPackageUploader
  • JamfPolicyUploader
  • JamfPatchUploader
  • JamfScriptUploader
  • JamfSoftwareRestrictionUploader

These processors will upload categories, (smart- and static-) computer groups, dock items, extension attributes, Mac App Store apps, packages, policies (with icons), Patch policies, scripts, Software Restrictions, and user/group accounts respectively.

Each processor is designed to upload one item at a time. Multiple processors can be added to a single AutoPkg recipe.

In addition, the following processors have other functions:

  • JamfPackageCleaner - deletes packages matching a defined string.
  • JamfPatchChecker - checks for a matching pkg version in a Patch Software Title.
  • JamfPolicyDeleter - deletes a named policy.
  • JamfPolicyLogFlusher - flushes a named policy.
  • JamfUploaderSlacker - sends a notification to Slack
  • JamfUploaderTeamsNotifier - sends a notification to Microsoft Teams

For an introduction into JamfUploader, watch the 2021 JNUC presentation Making package uploading and deployment easier with JamfUploader by Graham Pugh and Anthony Reimer.

Using the processors

To use these processors, you need to add the grahampugh-recipes repo to your AutoPkg preferences:

autopkg repo-add grahampugh-recipes

Or, if you want the bleeding-edge development versions, add the Jamf-upload repo:

autopkg repo-add grahampugh/jamf-upload

Note that if you add both, the first one in the search hierarchy will win. So typically you'll want to ensure that the jamf-uploa repo is higher up the list.

In a recipe in plist format, you call the processor as follows (in this example we are calling JamfPackageUploader):

<dict>
    <key>Processor</key>
    <string>com.github.grahampugh.jamf-upload.processors/JamfPackageUploader</string>
</dict>

In a recipe in yaml format, this would look like:

Processor: com.github.grahampugh.jamf-upload.processors/JamfPackageUploader

Jamf Account Credentials

If you haven't done so already, you'll need to create a service account with which the processors can interact with the API. It is recommended to create a user named something like "AutoPkg", which you can do in the Jamf Pro admin interface in Management Settings > Jamf Pro User Accounts and Groups.

All the processors require these keys to be populated in your AutoPkg preferences, Input variables, or by command line keys:

  • JSS_URL
  • API_USERNAME
  • API_PASSWORD

JSS_URL means the URL of your Jamf Pro server in its full form, without a trailing slash, e.g.:

  • https://myserver.jamfcloud.com
  • https://jamfpro.myorg.com:8443
  • https://jamfpro.myorg.com/instance

The easiest way to add these to your AutoPkg preferences is with the defaults command. For example:

defaults write ~/Library/Preferences/com.github.autopkg.plist JSS_URL "https://myjamfserver.com"
defaults write ~/Library/Preferences/com.github.autopkg.plist API_USERNAME AutoPkg
defaults write ~/Library/Preferences/com.github.autopkg.plist API_PASSWORD AVerySecretThing123

Alternatively, instead of using a user account for API requests, you can use API Clients. In this case you need to supply the following variables instead:

  • JSS_URL
  • CLIENT_ID
  • CLIENT_SECRET

Make sure not to supply both types of credential! And please note that API Clients cannot be used to upload packages to Jamf Cloud instances that have not been migrated to JCDS 2.0.

If you are using a Jamf Cloud Distribution Point, you do not need to provide any additional credentials. It is recommended to use jcds2_mode unless you are using a Jamf Pro Cloud server that has not yet been migrated to JCDS2.

If you are using an AWS S3 Cloud Distribution Point, it is recommended to use aws_cdp_mode. To use this method, you must install and configure the aws-cli tools, supplying your AWS S3 Access Key ID, Secret Access Key and region. Also, supply the bucket name in the following variable:

  • S3_BUCKET_NAME

If you are using an SMB Fileshare Distribution Point, you should additionally provide the following keys:

  • SMB_URL - this must be the full URL including share name, e.g. smb://my.dp.server.com/JPShare.
  • SMB_USERNAME
  • SMB_PASSWORD

For example:

defaults write ~/Library/Preferences/com.github.autopkg.plist SMB_URL "smb://myjamfdp.com/JPShare"
defaults write ~/Library/Preferences/com.github.autopkg.plist SMB_USERNAME jamfuser
defaults write ~/Library/Preferences/com.github.autopkg.plist SMB_PASSWORD AVerySecretThing456

Note that local folders instead of SMB shares can be specified. Use the SMB_URL key but supply a file path instead, e.g. file://path/to/my/repo. You currently have to supply dummy values for SMB_USERNAME and SMB_PASSWORD - these will be ignored but are required.

Advanced Settings for distribution points

If you are using multiple File Share Distribution Points, you have two options for supplying the credentials.

In the AutoPkg prefs file, you can provide an array of dictionaries with the key name SMB_SHARES, containing SMB_URL, SMB_USERNAME and SMB_PASSWORD for each File Share Distribution Point, as shown below. The array can have as many entries as required:

	<key>SMB_SHARES</key>
	<array>
		<dict>
			<key>SMB_URL</key>
			<string>smb://my.dist.point/JamfShare</string>
			<key>SMB_USERNAME</key>
			<string>jamfadmin</string>
			<key>SMB_PASSWORD</key>
			<string>password</string>
		</dict>
		<dict>
			<key>SMB_URL</key>
			<string>smb://second.dist.point/JPShare</string>
			<key>SMB_USERNAME</key>
			<string>jamfadmin</string>
			<key>SMB_PASSWORD</key>
			<string>password2</string>
		</dict>
	</array>

Alternatively, you can supply keys directly in the form SMB_URL, SMB_USERNAME, SMB_PASSWORD for the first DP, SMB2_URL, SMB2_USERNAME, SMB2_PASSWORD for the second DP, and so on. Any keys supplied in this form will override the SMB_SHARES array entirely. The advantage of using these keys directly is that they can be overridden, supplied directly in recipes, overrides, recipe lists or at the command line.

If you are using both a Cloud Distribution Point and SMB shares, you need to set the key CLOUD_DP to True. This is not necessary if you are only using a Cloud Distribution Point.

defaults write ~/Library/Preferences/com.github.autopkg.plist CLOUD_DP -bool True

Jamf Account Privileges

It is recommended to give the service account only the permissions it needs to do its work. The service account will need Create, Read, and Update privileges on some or all of the following, or even more than these, depending which processors you use:

  • Jamf Pro Server Objects:

    • Categories
    • Computer Extension Attributes
    • Smart Computer Groups
    • Static Computer Groups
    • File Share Distribution Points (only needs "Read")
    • Packages
    • Policies
    • Scripts
    • macOS Configuration Profiles
    • iOS Configuration Profiles
    • Mac Applications
    • Patch Policies
    • Patch Software Titles (required by JamfPatchUploader)
  • Jamf Pro Server Settings:

    • Cloud Distribution Point (only needs "Read")

If you are using the jcds_mode option in conjunction with JamfPackageUploader, and your Jamf instance is configured to use Single Sign-On, your AutoPkg service account will require Read and Update privileges on SSO settings. jcds_mode essentially scrapes the web interface, so your service account needs to be able to make use of your failover log-in page.

Please note that jcds_mode is not currently functional and will be removed from JamfPackageUploader.

If you are using jcds2_mode option in conjunction with JamfPackageUploader, you need to add Create privileges on Jamf Content Distribution Server Files.

If you use the JamfPackageCleaner processor, you will need to add Delete permissions to Packages.

If you use the JamfPolicyDeleter processor, you will need to add Delete permissions to Policies.

If you use the JamfPolicyLogFlusher processor, you will need to add Delete permissions to Policies and enable Flush Policy Logs in the Jamf Pro Server Actions.

If you use the JamfPrivilegesUploader processor, it is recommended that you run this manually with a different account to normal AutoPkg runs. To do this, use the following arguments to override the normal credentials: --key API_USERNAME=someusername --key API_PASSWORD=somepassword

Uploading a package only

For people who just want to upload a package, JamfPackageUploader.py can be run as a post-processor, e.g.:

autopkg run FoldingAtHome.pkg --post com.github.grahampugh.jamf-upload.processors/JamfPackageUploader

The processor could also be added to an override, or a new -pkg-upload.jamf recipe could be made, with the .pkg recipe as its parent.

Please do not use the .jss suffix for a package-upload-only recipe if you publish it, as that would confuse the recipe with recipes that use JSSImporter. I suggest .jamf-upload.recipe.

The package at pkg_path will be uploaded to Jamf Pro if a package of the same name is not already on the server. If no pkg_path is supplied, JamfPackageUploader will check if the file at pathname ends with .pkg and will use this package. This has the potential, in certain circumstances, to run the .download recipe directly:

autopkg run AdoptOpenJDK11_Signed.download --post com.github.grahampugh.jamf-upload.processors/JamfPackageUploader --key pkg_path="%pathname%"

Note that you don't have control over the package name if you upload a package directly from a .download recipe. You can override the pkg_name key in the command line, but there is no way to populate the version key into the pkg_name key by this method. If no version is provided in a .download recipe I recommend that you use or create a .pkg recipe to achieve this.

JamfUploader as an alternative to JSSImporter

These processors can be used instead of JSSImporter to perform an analogous workflow. The modular processors mean there is a higher degree of flexibility than JSSmporter. For example:

  • You can create/update categories, scripts, extension attributes and computer groups from an AutoPkg recipe independent of policies.
  • You can supply script parameter values to policies as well as parameter titles to scripts all in the same recipe.
  • There is no need to supply a template XML file to upload scripts or extension attributes.
  • Dependent icon, script and template files do not need to be copied to a RecipeOverride folder - they will be found if they are anywhere in the RECIPE_SEARCH_DIRS.
  • You can create as many policies in one recipe as you like.
  • You can add as many packages to a policy as you like.
  • There is full flexibility as to whether you wish to replace (update) existing categories, packages, computer groups, scripts, extension attributes, policies and self-service policy icons.
  • AutoPkg .download recipes that provide a pkg directly can be used as a parent recipe to .jamf recipes - no need for a .pkg recipe.

This additional flexibility means a slight increase in complexity of recipes and templates. Over time, I intend to provide extensive documentation, examples and automation scripts to make adoption of the Jamf-Upload Processors as easy as possible.

Making a .jamf recipe

There are myriad variations of how you may want your policies in Jamf to look, and what is scoped. As a basis, here I explain how to set up a recipe that replicates the JSSImporter "standard" .jss recipe format. This provides the following:

  • A category is created with the category_name input variable, if not already on the Jamf Pro server.
  • The package is uploaded, with its category determined by the pkg_category input variable.
  • A Smart Computer Group is created or updated, called %NAME%-update-smart, where NAME is determined in the input variables of the recipe. Normally, NAME corresponds to the Application Name, e.g. Google Chrome, Visual Studio Code, etc. The default criteria of this group is that a computer is in the Testing Computer Group, and the application is installed, but not the current version.
  • A policy is created or updated, called Install Latest %NAME%. The policy is given Testing category and scoped to the %NAME%-update-smart smart group. The install and reinstall buttons are given the label Install %version% where version was determined in the parent recipes. A Self Service Description is provided. The Self Service Name and Category match the policy name and category respectively.
  • The application icon is uploaded to the policy if it has not previously been uploaded, or the name does not match the existing icon.

These recipes can substitute for existing .jss recipes with no change in workflow, i.e. you do not need to change your policy names.

However, the PolicyTemplate.xml used in .jss recipes cannot be used with .jamf recipes. This repo provides the replacement which is called PolicyTemplate-install-latest.xml. This policy template is designed to be more flexible than those used in JSSImporter - you can completely override the policy name, self service name and install/reinstall button labels.

Note that there is no PROD_NAME variable in .jamf recipes (I never knew what this variable was used for). You can set names up for the policy and Self Service Display Name as you wish with any variable name. However, please note that the policy name that is passed to the template must match the value of policy_name that is passed directly to the JamfPolicyUploader processor. Therefore it is recommended to set a value for POLICY_NAME and apply this to both the policy_name key of the processor and the name key in the Policy Template (policy/general/name).

Similarly, the SmartGroupTemplate.xml is slightly changed in comparison to that provided for the standard .jss recipes. Here, we provide SmartGroupTemplate-update-smart.xml. Note that the GROUP_NAME value is provided in the Inputs, and by default is %NAME%-update-smart. I have kept the JSS_INVENTORY_NAME key that is used in JSSImporter, which by default equates to %NAME%.app but can be overridden.

Please do not use the .jss suffix for recipes that use these new processors if you publish it, as that would confuse the recipe with recipes that use JSSImporter. I suggest .jamf.recipe.

Packages in .jamf recipes

The package at pkg_path will be uploaded to Jamf Pro if a package of the same name is not already on the server. If no pkg_path is supplied, JamfPackageUploader will check if the file at pathname ends with .pkg and will use this package.

Note that .download recipes that download a package directly may not provide a version, and the name of the file may not conform to your standard naming convention. If a .pkg recipe exists, I recommend using this as the parent, and if not, and the .download recipe does not provide a version, I recommend that you write a .pkg recipe rather than add a Versioner processor to your .jamf recipe.

If you wish to enforce the overwrite of an existing package, set replace_pkg to True. I do not recommend this being set by default as it would hugely extend your AutoPkg run time, potentially unnecessarily, and also because package upload to Jamf Pro is not 100% reliable. I would therefore recommend only to override this value from the command line when you need to replace a package of the same name because something has changed, e.g.:

AutoPkg run GoogleChrome.jamf --key replace_pkg=True

When a package already exists on the server and is not overwritten, the computer group and policy are not touched when using standard .jamf recipes as provided in this repo. This default is achieved using the StopProcessingIf processor, with the predicate set by default to pkg_uploaded == False, and is equivalent to setting STOP_IF_NO_JSS_UPLOAD to True when using JSSImporter. To override this default and force the creation/overwriting of the smart group and policy in the recipe every time, set the UPDATE_PREDICATE value to FALSEPREDICATE.

Scripts and Extension Attributes in .jamf recipes

Unlike JSSImporter, Scripts and Extension Attributes do not require XML templates. Simply provide the path to the script/extension attribute. There are additional keys for each type of script that are provided directly in the recipe rather than via a template.

For scripts, the following keys are assignable:

Key Default Value Description
script_path Full path to the script to be uploaded
script_name filename Name of the script in Jamf
script_category Script category
script_priority AFTER Script priority (BEFORE or AFTER)
osrequirements Script OS requirements
script_info Script info field
script_notes Script notes field
script_parameter4 Script parameter 4 title
script_parameter5 Script parameter 5 title
script_parameter6 Script parameter 6 title
script_parameter7 Script parameter 7 title
script_parameter8 Script parameter 8 title
script_parameter9 Script parameter 9 title
script_parameter10 Script parameter 10 title
script_parameter11 Script parameter 11 title
replace_script False Overwrite an existing script if True.
skip_script_key_substitution False Skip key substitution in processing the script
sleep False Pause after running this processor for specified seconds.

For extension attributes, the following keys are assignable:

Key Default Value Description
ea_name Extension Attribute name
ea_script_path Full path to the script to be uploaded
replace_ea False Overwrite an existing category if True.
ea_inventory_display False Inventory Display value for the EA.
ea_data_type False Data type for the EA. One of String, Integer or Date.
sleep False Pause after running this processor for specified seconds.

Categories in .jamf recipes

It is not common to update category objects once created. However, I have made this possible with the JamfCategoryUploader processor in case you wish to change the priority of the category while retaining the name. You can assign the category_priority key in a .jamf recipe.

Set replace_category to True to replace an existing category object when changing priority.

Configuration Profiles in .jamf recipes

You can create and update configuration profiles with AutoPkg, using the JamfComputerProfileUploader processor. As the name suggests, this can only update computer-based configuration profiles, as Mobile Device profiles have a different API endpoint. I would recommend that you only use this method for creating custom profiles. This is because the Jamf Classic API does not allow for the uploading of signed configuration profiles, so any profile that you upload which includes keys managed by Jamf will be mangled.

There are two ways in which you can create or update a configuration profile:

  • Similar to how you can upload a plist file to the Jamf Pro GUI to provide a custom payload, you can upload the same plist file using the JamfComputerProfileUploader processor. As with the GUI, you must additionally provide the profile name, identifier, organisation and description. These can be supplied in the AutoPkg recipe. What actually happens is that the processor compiles a mobileconfig file from the details you provided, generating a new UUID, and uploads it. If you are replacing an existing profile (identified by name), the processor will grab the existing UUID from the server and use the same one. It also ensures that the changes are pushed to all devices.

  • The other option is to upload a complete mobileconfig file, similar to how you use the "Upload" button in the Jamf Pro GUI. In the case you don't need to supply the profile name, identifier, organisation or description, as these should already be embedded in the mobileconfig file. Again, if you are replacing an existing profile (identified by name), the processor will grab the existing UUID from the server and use the same one.

Both methods also need you to provide an XML template, similarly to what you provide for policies and smart groups in JamfUpload and JSSImporter recipes. The mobileconfig contents are substituted into the template along with name, category and scope. You can also use the template to determine whether the profile should be issued automatically or made available in Self Service. Unfortunately, a bug (or probably more accurately, an oversight) in the Jamf Pro API means that you cannot upload an icon to a Self Service configuration profile.

Set replace_profile to True to replace an existing configuration profile object.

Example recipes

I have published some example/reference .jamf recipes at grahampugh-recipes:Jamf_Recipes. To try out these recipes, don't forget to add this repo to your AutoPkg repo-list:

autopkg repo-add grahampugh-recipes

Then you can try out a recipe, for example the Firefox recipe:

autopkg make-override Firefox.jamf
autopkg run -v Firefox.jamf

I encourage you to try verbose runs (-vv) and very verbose runs (-vvv) to see the extent of output that is made available for debugging.

Example recipes involving scripts and profiles can also be found at autopkg/grahampugh-recipes:Jamf_Script_Recipes and grahampugh/recipes-yaml:Jamf_Profile_Recipes respectively. The templates are all found in autopkg/grahampugh-recipes:Jamf_Templates.

Known issues

  • Mounting and/or unmounting may fail if you have the Jamf Admin app open and connected to the same FileShare that you have configured. It is therefore safest to close Jamf Admin before running AutoPkg recipes that use the JamfPackageUploader processor.

  • An API account password must not have an exclamation mark (!) in it, otherwise authentication will fail.

Raising issues and bug reports

Please raise any new issues concerning the processors in this repo, not in autopkg/grahampugh-recipes.

⚠️ **GitHub.com Fallback** ⚠️