-
-
Notifications
You must be signed in to change notification settings - Fork 39
JamfUploader AutoPkg Processors
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.
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-upload 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
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. For customers using the Jamf Cloud Distribution Service (JCDS) that are already migrated to JCDS 2.0 (almost all customers should be migrated by now), you do not need to provide any additional options.
However, if you need to upload packages greater than 5.0 GB in size, it is recommended to set jcds2_mode
to true
. This can be provided on a recipe-by-recipe basis, at the command line, or by adding to the AutoPkg preferences file. Note that this requires the installation of the python boto3
module. To do this, run the following command after installing AutoPkg:
/usr/local/autopkg/python -m pip install boto3
If you are using an AWS S3 Cloud Distribution Point and are using at least Jamf Pro v11.5.0, you can also use the default mode. As an alternative, and for large packages, you can set aws_cdp_mode
to true
. 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 have an Akamai or Rackspace Cloud Distribution Point, the default mode should work so long as you are running at least Jamf Pro v11.5.0.
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.
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
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 fromJamfPackageUploader
.
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
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 thepkg_name
key in the command line, but there is no way to populate theversion
key into thepkg_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.
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 theRECIPE_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 apkg
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.
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
, whereNAME
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 theTesting
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 givenTesting
category and scoped to the%NAME%-update-smart
smart group. The install and reinstall buttons are given the labelInstall %version%
whereversion
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
.
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 aVersioner
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
.
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. |
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.
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 amobileconfig
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 themobileconfig
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.
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.
-
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.
Please raise any new issues concerning the processors in this repo, not in autopkg/grahampugh-recipes
.