def Main(): userPrefs = AutomationPreferences("JSS_URL", "API_USERNAME", "API_PASSWORD") if "https://" not in userPrefs["JSS_URL"]: userPrefs["JSS_URL"] = "https://" + userPrefs["JSS_URL"] jssApi = userPrefs["JSS_URL"] + "/JSSResource" apiUser = userPrefs["API_USERNAME"] apiPass = userPrefs["API_PASSWORD"] todayDate = date.today() packagesList = "JPSPackages.txt" logFile = "newpackage.log" Logging(logFile, "********************" + str(todayDate) + "********************") packageNames = CreateSoftwareIDList(jssApi, apiUser, apiPass) if len(packageNames) == 0: updatedSoftware = {} else: updatedSoftware = FindUpdatedSoftware(packageNames, packagesList) if len(updatedSoftware) == 0: Logging(logFile, "No Updates") SlackNotification(logFile, "No New Software") WriteToSoftwareList(packageNames, packagesList) else: WriteToSoftwareList(packageNames, packagesList) UpdatePatchserver(updatedSoftware, logFile) Logging(logFile, printLogLocation=True)
def CreateSoftwareIDList(APIEndpoint, apiUser, apiPass): logFile = "newpackage.log" api = APIEndpoint + "/packages" softwareIDList = [] jssSoftware = requests.get(api, auth=(apiUser, apiPass), headers={"Accept": "application/xml"}) if jssSoftware.status_code != 200: Logging( logFile, "There was an error connecting with the API with error code: " + str(jssSoftware.status_code), ) Logging(logFile, str(jssSoftware.content)) packageNameList = {} return packageNameList else: numPackagesTree = ElementTree.fromstring(jssSoftware.content) counter = 1 done = "false" while done: try: softwareIDList.append(numPackagesTree[counter][0].text) counter = counter + 1 except: done = "true" break packageNameList = GetPackageName(api, apiUser, apiPass, softwareIDList) return packageNameList
def UpdatePatchserver(updSftwreDict, log): userPrefs = AutomationPreferences("PATCH_REPO", "PATCH_URL", "PATCH_TOKEN") gitRepo = userPrefs["PATCH_REPO"] patchURL = userPrefs["PATCH_URL"] apiToken = userPrefs["PATCH_TOKEN"] # Make sure the endpoint is pointing to the correct URL for updating if "/api/v1/title" not in patchURL: patchURL = patchURL + "/api/v1/title" GithubActions(gitRepo) for software in updSftwreDict: jsonFilepath = UpdateJSON(software, updSftwreDict[software]) patchServer = patchURL + "/" + software + "/version" if pathlib.Path.exists(jsonFilepath): jsonFile = open(jsonFilepath, "r") patchResponse = requests.post( patchServer, data=jsonFile, headers={ "Content-type": "application/json", "Authorization": "Bearer " + apiToken, }, ) if patchResponse.status_code != 201: Logging( log, software + ": Patch update failed with error code " + str(patchResponse.status_code), ) Logging(log, str(patchResponse.content)) else: Logging(log, str(jsonFilepath) + " doesn't exist.") GithubActions(gitRepo, "commit")
def ModifyJSON(file, title, newVersion, logFile): todayDate = datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ") JSONFile = open(file, "r") JSONDict = json.load(JSONFile) JSONFile.close() oldVersion = JSONDict.get("version") for item in JSONDict: if item == "version": JSONDict[item] = newVersion if item == "releaseDate": JSONDict[item] = todayDate for item in JSONDict.get("components")[0]: if item == "version": try: JSONDict.get("components")[0][item] = newVersion except: Logging( logFile, "Unable to get object " + str(JSONDict.get("components")[0][item]), ) try: for appCritera in JSONDict.get("components")[0].get("criteria"): if appCritera["value"] == oldVersion: appCritera["value"] = newVersion except: Logging( logFile, "Unable to get object " + str(appCritera in JSONDict.get("components")[0].get("criteria")), ) JSONFile = open(file, "w") json.dump(JSONDict, JSONFile) JSONFile.close()
def CreateBackup(filepath): currentDate = datetime.today().strftime("%Y-%m-%d") patchPrefs = GetPreferences("PATCH_URL", "PATCH_TOKEN") logFile = "patchserver_backup.log" backupAPI = patchPrefs["PATCH_URL"] + "/api/v1/backup" Logging(logFile, currentDate) Logging(logFile, "----------------------------------------") Logging(logFile, "Creating backup...") response = requests.get( backupAPI, headers={ "Authorization": "Bearer " + patchPrefs["PATCH_TOKEN"], "Accept": "application/zip", }, ) if response.status_code != 200: Logging(logFile, "Error connecting to patchserver.") Logging(logFile, response.status_code) Logging(logFile, response.content) Logging(logFile, "Patchserver database not backed up.") else: zipFileName = str(currentDate) + "-patchserver-backup.zip" zipFile = open(filepath + zipFileName, "wb") zipFile.write(response.content) zipFile.close() Logging(logFile, "Backup successfully created.") Logging(logFile, zipFileName + " saved to " + filepath)
def Main(): logfile = "jamfPatchUpdate.log" todayDate = str(date.today()) Logging(logfile, "********************" + str(todayDate) + "********************") softwareUpdate = GetUpdatedSoftwareVersion() if len(softwareUpdate) != 0: AddPackageToDefinition(softwareUpdate) UpdateTargetVersion(softwareUpdate) else: Logging(logfile, "No new software updates.")
def UpdateJSON(title, version): logFile = "JSONUpdate.log" currUser = getuser() userPrefs = AutomationPreferences("PATCH_REPO") try: gitRepo = pathlib.Path(userPrefs["PATCH_REPO"]) except: gitRepo = pathlib.Path("/Users/" + currUser + "/Documents/GitHub") print("No patch repo set. Using default path: " + str(gitRepo)) JSONFile = title + "-patch.json" if pathlib.Path.exists(gitRepo / JSONFile): ModifyJSON(gitRepo / JSONFile, title, version, logFile) SlackNotification( logFile, str(date.today()) + " : " + title + " has been updated to " + version + ". " + title + " has been updated on the patch server", ) Logging(logFile, str(datetime.today()) + ": " + JSONFile + " was updated.") else: JSONFile = JSONFile.replace(" ", "%20") if pathlib.Path.exists(gitRepo / JSONFile): ModifyJSON(gitRepo / JSONFile, title, version, logFile) SlackNotification( logFile, str(date.today()) + " : " + title + " has been updated to " + version + ". " + title + " has been updated on the patch server", ) Logging(logFile, str(datetime.today()) + ": " + JSONFile + " was updated.") else: JSONFile = JSONFile.replace("%20", "") if pathlib.Path.exists(gitRepo / JSONFile): ModifyJSON(gitRepo / JSONFile, title, version, logFile) SlackNotification( logFile, str(date.today()) + " : " + title + " has been updated to " + version + ". " + title + " has been updated on the patch server", ) Logging( logFile, str(datetime.today()) + ": " + JSONFile + " was updated.") else: Logging(logFile, JSONFile + " does not exist.") SlackNotification( logFile, str(date.today()) + " : " + title + " has been updated to " + version + ". Please update patch definitions manually", ) return gitRepo / JSONFile
def FindUpdatedSoftware(packageList, packageFile): logFile = "newpackage.log" updatedSoftwareDict = {} softwareListPath = GetFileLocation() if not softwareListPath.exists(): os.mkdir(softwareListPath) softwareListLog = softwareListPath / packageFile if not softwareListLog.exists() or os.stat(softwareListLog).st_size <= 5: print("No file exists. Moving on...") else: softList = open(softwareListLog, "r").readlines() for software in packageList: try: if (str(packageList[software][0] + ":" + str(packageList[software][1]) + "\n") in softList): pass else: updatedSoftwareDict[packageList[software] [0]] = packageList[software][1] Logging( logFile, "updated software: " + packageList[software][0] + " " + str(packageList[software][1]), ) except: pass return updatedSoftwareDict
def ManageBackups(filepath): logFile = "patchserver_backup.log" fileList = os.listdir(filepath) try: fileList.remove(".DS_Store") except: print("No DS_Store file present") sortedFileList = sorted(fileList, reverse=True) print(sortedFileList) if len(sortedFileList) > 3: print("Deleting oldest backup") Logging(logFile, "Deleting oldest backup") fileToDelete = sortedFileList.pop() os.remove(filepath + "/" + fileToDelete) print(fileToDelete + " has been deleted") Logging(logFile, fileToDelete + " has been deleted") Logging(logFile) Logging(logFile, str(filepath))
def WriteToSoftwareList(packageDict, packageFile): logFile = "newpackage.log" softwareListPath = GetFileLocation() softwareListLog = softwareListPath / packageFile if not softwareListPath.exists(): os.mkdir(softwareListPath) softwareListFile = open(softwareListLog, "w+") if len(packageDict) == 0: Logging( logFile, "Could not reach the Jamf server. The master list of packages will remain unchanged.", ) else: for software in packageDict: try: softwareListFile.write(packageDict[software][0] + ":" + str(packageDict[software][1]) + "\n") except: pass Logging(logFile, "The master list of of packages has been updated.")
def GithubActions(patchRepo, commit=None): patchRepo = pathlib.Path(patchRepo) logFile = "newpackage.log" currentDir = os.getcwd() if patchRepo.exists(): if commit is None: Logging(logFile, "Getting updated JSON files") os.chdir(patchRepo) subprocess.call(["git", "pull"]) os.chdir(currentDir) else: Logging(logFile, "Pushing updated JSON back to Github") os.chdir(patchRepo) subprocess.call(["git", "commit", "-a", "-m", "'daily update'"]) subprocess.call(["git", "push"]) os.chdir(currentDir) else: Logging( logFile, "Github folder for Jamf Patch files does not exist. Github update failed", )
def AddPackageToDefinition(updatedSoftware): logfile = "jamfPatchUpdate.log" jamfPrefs = AutomationPreferences("JSS_URL", "API_USERNAME", "API_PASSWORD") patchURL = jamfPrefs["JSS_URL"] + "/JSSResource/patchsoftwaretitles" pkgSuffix = ".pkg" pkgList = [] for pkg in updatedSoftware: pkgList.append(pkg + "-" + updatedSoftware[pkg] + pkgSuffix) response = requests.get( patchURL, auth=(jamfPrefs["API_USERNAME"], jamfPrefs["API_PASSWORD"]), headers={"Accept": "application/xml"}, ) if response.status_code != 200: Logging(logfile, "Error connecting to Jamf: " + str(response.status_code)) Logging(logfile, str(response.content)) exit() responseTree = ElementTree.fromstring(response.content) for software in updatedSoftware: iterator = 1 software_not_found = False while iterator <= int(responseTree[0].text): if software == responseTree[iterator][0].text: Logging(logfile, "Updating patch policy for " + software) patchTitleResponse = requests.get( patchURL + "/id/" + responseTree[iterator][1].text, auth=(jamfPrefs["API_USERNAME"], jamfPrefs["API_PASSWORD"]), headers={"Accept": "application/xml"}, ) if patchTitleResponse.status_code != 200: Logging( logfile, "There was an error in retrieving XML: " + str(response.status_code), ) Logging(logfile, str(patchTitleResponse.content)) Logging(logfile, patchURL + "/id/" + responseTree[iterator][1].text) else: try: patchTitleTree = ElementTree.fromstring( patchTitleResponse.content) if patchTitleTree[6][0][0].text == updatedSoftware[ software]: for pkg in pkgList: if updatedSoftware[software] in pkg: xmlData = ( "<patch_software_title><versions><version><software_version>" + updatedSoftware[software] + "</software_version><package><name>" + pkg + "</name></package></version></versions></patch_software_title>" ) newResponse = requests.put( patchURL + "/id/" + responseTree[iterator][1].text, auth=( jamfPrefs["API_USERNAME"], jamfPrefs["API_PASSWORD"], ), data=xmlData, headers={ "content-type": "application/xml" }, ) if newResponse.status_code != 201: Logging( logfile, "Update of " + software + " failed with error code: " + str(newResponse.status_code), ) Logging(logfile, newResponse.content) else: Logging( logfile, pkg + " added to definition version " + updatedSoftware[software], ) # UpdateTargetVersion(updatedSoftware[software]) iterator = int(responseTree[0].text) + 50 else: Logging( logfile, "Updated Version does not match the latest version in Jamf.", ) Logging( logfile, "Jamf: " + str(patchTitleTree[6][0][0].text) + " != " + "Updated: " + updatedSoftware[software], ) iterator = int(responseTree[0].text) + 50 except: Logging(logfile, "There was a problem with the XML content") Logging(logfile, str(patchTitleResponse.content)) Logging( logfile, patchURL + "/id/" + responseTree[iterator][1].text) Logging(logfile, xmlData) iterator = iterator + 1 if iterator == int(responseTree[0].text): software_not_found = True if software_not_found: Logging( logfile, "No patch policy found for " + software + " " + str(updatedSoftware[software]), ) print("No patch policy found for " + software + " " + str(updatedSoftware[software]))
def UpdateTargetVersion(updatedSoftware): logfile = "jamfPatchUpdate.log" apiPrefs = AutomationPreferences("API_USERNAME", "API_PASSWORD", "JSS_URL") apiURL = apiPrefs["JSS_URL"] + "/JSSResource/patchpolicies" response = requests.get( apiURL, auth=(apiPrefs["API_USERNAME"], apiPrefs["API_PASSWORD"]), headers={"Accept": "application/xml"}, ) patchPolicyContent = ElementTree.fromstring(response.content) for software in updatedSoftware: counter = 1 Logging(logfile, "Updating target version for " + software) for title in patchPolicyContent: while counter <= int(patchPolicyContent[0].text): softwareTestPolicy = software + " Update Test" if softwareTestPolicy == patchPolicyContent[counter][1].text: patchPolicyID = patchPolicyContent[counter][0].text xmlData = ("<patch_policy><general><target_version>" + updatedSoftware[software] + "</target_version></general></patch_policy>") patchTrgtResponse = requests.put( apiURL + "/id/" + patchPolicyID, auth=(apiPrefs["API_USERNAME"], apiPrefs["API_PASSWORD"]), data=xmlData, headers={"Content-type": "application/xml"}, ) if patchTrgtResponse.status_code != 201: Logging( logfile, software + " target version update failed with response code: " + str(patchTrgtResponse.status_code), ) Logging( logfile, software + " target version " + updatedSoftware[software] + " was not updated to " + patchPolicyContent[counter][1].text, ) SlackNotification( logfile, ":failed: " + patchPolicyContent[counter][1].text + " was not updated. Please update " + patchPolicyContent[counter][1].text + " to " + updatedSoftware[software] + " manually.", ) else: Logging( logfile, patchPolicyContent[counter][1].text + " has been updated to target version " + updatedSoftware[software], ) SlackNotification( logfile, ":white_check_mark: " + patchPolicyContent[counter][1].text + " has been updated to target version " + updatedSoftware[software], ) counter = int(patchPolicyContent[0].text) + 1 else: counter = counter + 1 if counter == int(patchPolicyContent[0].text): Logging(logfile, softwareTestPolicy + " wasn't found.")