JamfUploader AutoPkg Processors - grahampugh/jamf-upload Wiki

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

Users of AutoPkg can use the JamfScriptUploader, JamfCategoryUploader, JamfComputerGroupUploader, JamfExtensionAttributeUploader, JamfPackageUploader and JamfPolicyUploader processors to upload scripts, categories, computer groups, extension attributes, packages and policies (with icons) respectively. These Processors share the functionality of the standalone scripts, though will only upload one object per process. Multiple processors can be added to a single AutoPkg recipe.

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

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


Jamf Credentials

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


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.

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

The user will need Create, Read, and Update privileges on some or all of the following:

  • 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
    • Mac Applications
  • Jamf Pro Server Settings:

    • Cloud Distribution Point (only needs "Read")

If you are using the jcds_mode flag in conjunction with JamfPackageUploader, and your Jamf instance is configured to use Single Sign-On, your AutoPkg service account will require 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.

If you are using Jamf Cloud or other Cloud Distribution Point, you do not need to provide any additional credentials.

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


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

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 .jamf-upload 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.

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). Instead we have NAME (a value that is normally the name of the application), POLICY_NAME (which in standard recipes is Install Latest %NAME%, but can be overridden) and SELF_SERVICE_NAME (which in standard recipes is the same as POLICY_NAME, but can be independently overridden).

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:

  • script_path
  • script_category
  • script_priority
  • osrequirements
  • script_info
  • script_notes
  • script_parameter4
  • script_parameter5
  • script_parameter6
  • script_parameter7
  • script_parameter8
  • script_parameter9
  • script_parameter10
  • script_parameter11

Set replace_script to True to overwrite an existing script. This is set to False by default.

For extension attributes, the following keys are assignable:

  • ea_name
  • ea_script_path

Set replace_ea to True to overwrite an existing extension attribute. This is set to False by default.

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

  • There is no support for copying packages to local repos or multiple distribution points at this time. Raise an Issue if this is important to you.

  • 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** ⚠️