#!/bin/zsh ## Bits shamelessly borrowed from Graham Pugh - https://gist.github.com/grahampugh/836547859c18fefe1dba6ba8c093accc ## Then reversed to only pull down files which either do not exist in the DP or whose md5 doesn't match ## ## This script relies on a Jamf API role/client and a local identity file of the following format: ## {"client_name":"test_for_screenshot","client_id":"a7682e67-a276-448b-83af-9c83be5e02f4","client_secret":"ubPrXqRRyc0jRLFOY9iJYGpCjHiJc1M-tRLlCFb9aWZhtqf7iC9UpQG_dO1ZkvNF","grant_type":"client_credentials"} ## ## This is most easily obtained by creating an api client, enabling it and copying the data from the popup, then pasting it into a file ## ## usage() { echo "Usage: sync_jcds_local_dp.zsh --id /path/to/identity_file --jss your.jamf.server --localpath /path/to/dp/share --log /path/to/log/file" echo "(don't include https:// but do include the port if needed)" } logStatement(){ # Function to log informational output for review echo "$(date) - $1" >> "$logFile" } ## Borrowed from https://gist.github.com/lucasad/6474224 urlencode() { setopt localoptions extendedglob input=( ${(s::)1} ) print ${(j::)input/(#b)([^A-Za-z0-9_.\!~*\'\(\)-])/%${(l:2::0:)$(([##16]#match))}} } getToken() { client_id=$(plutil -extract client_id raw "$identity_file") client_secret=$(plutil -extract client_secret raw "$identity_file") cred_type=$(plutil -extract grant_type raw "$identity_file") token=$(curl --silent --location --request POST "${jss_url}/api/oauth/token" \ --header "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "client_id=${client_id}" \ --data-urlencode "grant_type=${cred_type}" \ --data-urlencode "client_secret=${client_secret}" \ | plutil -extract access_token raw -) # echo "$token" } getPackageList(){ # list all the packages echo "Getting a list of packages from JCDS" logStatement "Getting a list of packages from JCDS" getToken http_response=$( curl --request GET \ --silent \ --header "authorization: Bearer $token" \ --header 'Accept: application/json' \ "$jss_url/api/v1/jcds/files" \ --write-out "%{http_code}" \ --output "$output_file_list" ) logStatement "Package List response code: $http_response" # echo "HTTP response: $http_response" } syncPackages(){ if [[ $http_response -eq 200 ]]; then # convert the list to a plist so we can actually work with it in bash plutil -convert xml1 "$output_file_list" # count the number of items in the list pkg_count=$(grep -c fileName "$output_file_list") # loop through each item in the JSON response jcds_pkg="" jcds_pkg_md5="" # assign empty value to avoid errors local_pkg="" local_pkg_md5="" echo "Looping through $pkg_count files" for ((i=1; i<=pkg_count; i++)); do jcds_pkg=$(/usr/libexec/PlistBuddy -c "Print :$i:fileName" "$output_file_list") jcds_pkg_md5=$(/usr/libexec/PlistBuddy -c "Print :$i:md5" "$output_file_list") local_pkg="${rsync_path}/${jcds_pkg}" if [ -f "${local_pkg}" ]; then local_pkg_md5=$(md5 -q "${local_pkg}") fi if [[ "$local_pkg_md5" == "$jcds_pkg_md5" ]]; then echo "$jcds_pkg is the same" logStatement "$jcds_pkg is the same" else getToken # urlencode package names for paces download_pkg=$(urlencode "$jcds_pkg") # retrieve the download URL from the API endpoint get_download_url=$(curl --request GET \ --silent \ --location \ --header "authorization: Bearer $token" \ --header 'Accept: application/json' \ "$jss_url/api/v1/jcds/files/${download_pkg}" ) download_url=$(echo "$get_download_url" | plutil -extract uri raw -) # Get the file, please echo "Downloading $jcds_pkg ..." logStatement "Downloading $jcds_pkg ..." curl "${download_url}" -o "${rsync_path}/${jcds_pkg}" fi done fi } while test $# -gt 0 ; do case "$1" in -i|--id) shift identity_file="$1" ;; --localpath) shift rsync_path="$1" ;; --log) shift logFile="$1" ;; --jss) shift jss="$1" ;; *) usage exit ;; esac shift done if [[ ! "$identity_file" || ! "$rsync_path" ]]; then usage exit fi jss_url="https://${jss}" output_file_list="/tmp/jcds.txt" getPackageList syncPackages