Mass edit computer information (particularly purchasing information)

georgecm12
Contributor III

I found a feature request to allow mass-editing computer information, which was marked as "not planned." That's a little disappointing, as it seems a natural thing an administrator would want to do in response to a search result. Oh, well.

In particular, I'd like to find machines of a particular model, or alternatively, those whose PO date is within a particular range. I'd like to mark these items as leased, update the lease expiration date, and add a PO number.

It looks like JAMF's suggestion is to use the API. Has anyone done anything along these lines that would be able to provide their "recipe?"

1 ACCEPTED SOLUTION

stevewood
Honored Contributor II
Honored Contributor II

@georgecm12 I did not go as far as searching for specific information, I simply used the API to update lease expiration data for all of my machines. I was able to take a CSV file that had the serial number and lease expiration information in it and run it against the API. In turn, all of my machines have lease data now. The discussion we had about this on JAMF Nation is here:

JSS API PUT

You could adjust the script I used, or the other posted there by @luispalumbo to accomplish some of what you want. If you created a CSV file that held the serial numbers, lease data and PO information, you could update all of your systems, or just the ones that you have info for. Writing these values to a machine that already has these values does no harm that I can tell.

View solution in original post

5 REPLIES 5

stevewood
Honored Contributor II
Honored Contributor II

@georgecm12 I did not go as far as searching for specific information, I simply used the API to update lease expiration data for all of my machines. I was able to take a CSV file that had the serial number and lease expiration information in it and run it against the API. In turn, all of my machines have lease data now. The discussion we had about this on JAMF Nation is here:

JSS API PUT

You could adjust the script I used, or the other posted there by @luispalumbo to accomplish some of what you want. If you created a CSV file that held the serial numbers, lease data and PO information, you could update all of your systems, or just the ones that you have info for. Writing these values to a machine that already has these values does no harm that I can tell.

georgecm12
Contributor III

That works, Steve. Looks like newer versions of the API default to JSON output, not XML. As a result, the script as you wrote it doesn't work anymore, but you can send a header that tells the server to send XML instead.

rcorbin
Contributor II

Would be great if you could do this via an inventory action. I did see a feature request that appears to be alive. It says under review. I just want to be able to find a bunch of machines and then mass update information about purchased vs leased as well as lease start and expiration dates.

The request is here : https://jamfnation.jamfsoftware.com/featureRequest.html?id=545

@stevewood Are you still using your script and API thing ? Works well ? Looks harder than it needs to be.

stevewood
Honored Contributor II
Honored Contributor II

@rcorbin yep, that's the method I use. Of course I only have to run it once every few months when I have new leases to put into the JSS. The process isn't really that complicated if you ask me. Gather the lease data into a CSV file, run the script, and you're done.

efranson
New Contributor

I recently had to do this as well - wrote some python to make it happen. You'll need the following things on your machine:

  1. Python 3 - you could edit the script to run in python 2, I'm sure.
  2. The following python modules: sys, csv, datetime, requests, argparse
  3. A csv with at least the following columns: Serial_num, End_date. End_date should be in the format M/D/Year, like 08/22/2017. You can change the date format in the init section if you like. Other columns won't cause problems.
  4. A 'jamfconfig.py' file that provides a username as "user", a password as "password", and a hostname as "url". I use the python module 'keyring' to store user/pass info in the OS X keychain, but you could keep them directly in the jamfconfig.py file if you like. You could also just set them directly in the script, if you're truly wild.

You should be able to just run the script with 'python <script-name> <csv-name>'.

Here's the python:

#!/usr/bin/env python

import sys
import csv
from datetime import datetime
import requests
import argparse
import jamfconfig

parser = argparse.ArgumentParser()
parser.add_argument("leaseList", help="CSV of leased machines")
args = parser.parse_args()

epoch = datetime(1970, 1, 1)


def dictify_csv(csvFile):
    '''Import the csv from commandline, turn that into a list of dicts (one per machine).'''
    reader = csv.DictReader(open(csvFile, 'r'))
    dict_list = []
    for row in reader:
        dict_list.append(row)
    return dict_list


class computer:

    def __init__(self, computerDict):
        self.serial = computerDict['Serial_num']
        if self.serial[0] == "S":
            self.serial = self.serial[1:]
        self.lease_expires = datetime.strptime(computerDict['End_date'], '%m/%d/%Y')
        self.lease_expires_epoch = (self.lease_expires - epoch).total_seconds()

    def update_purchase_data(self):
        url = jamfconfig.url + '/JSSResource/computers/udid/' + self.udid
        payload = ("<computer><purchasing><is_purchased>false</is_purchased><is_leased>true</is_leased><lease_expires_epoch>" + str(int(self.lease_expires_epoch * 1000)) + "</lease_expires_epoch></purchasing></computer>")
        b = requests.put(url, auth=(jamfconfig.user, jamfconfig.password), headers={'Accept': 'application/json'}, data=payload, verify=False)
        if b.status_code == 201:
            print("	Added lease expiration date for " + self.serial)
        else:
            print("[***] UNABLE TO ADD DATA")

    def exists_in_jamf(self):
        url = jamfconfig.url + '/JSSResource/computers/match/' + self.serial
        b = requests.get(url, auth=(jamfconfig.user, jamfconfig.password), headers={'Accept': 'application/json'}, verify=False)
        record = b.json()
        if len(record['computers']) == 0:
            print(machine.serial + " doesn't exist in jss")
            return False
        if len(record['computers']) >= 1:
            for i in record['computers']:
                self.udid = i['udid']
                print(self.serial + " exists in jss. Adding leasing data.")
            return True

try:
    for line in dictify_csv(args.leaseList):
        machine = computer(line)
        if machine.exists_in_jamf():
            machine.update_purchase_data()
except KeyboardInterrupt:
    sys.exit()

A note about json/xml - I was originally trying to update the records by sending json, but was always getting a 415 (unsupported media?). Uploading as XML resolved.
Good luck!