Archive

Archive for the ‘Tech’ Category

Postman Advanced – Passing Data

April 22, 2022 Leave a comment

In my previous posts about Postman I showed you how to setup Postman for working with Jamf Pro, how to create and update policies, how to gather our queries into collections, and much more. In this post I’m going to expand a little on our use of the Runner functionality which I covered in Part 4 and Part 5.

Often times we want to perform an action on more than just one object. Maybe we want update a list of devices with PO Number or some other data. Sure, we can run a search, export the data as a CSV, and then use that CSV to feed a runner, but what if we could grab a search in Postman and parse out the devices we want to update? I went down this rabbit trail today and wanted to share the results with you.

Our use case, for this post, is to update the PO Number on a group of computers that we will gather using an Advanced Search in Jamf Pro. To do this we will need an Advanced Search in Jamf Pro to capture our devices and in Postman we will use two API endpoints: “Find computer search by ID” and “Update computer by SN”.

Pre-Request Scripts & Tests

Postman provides two features that allow us to utilize JavaScript to manipulate data, either before or after a request: Pre-Request Scripts and Tests. A Pre-Request Script can allow us to set variables before the running of a request. We used a pre-request script in Part 2 when we briefly talked about using variables. In that instance we used the command pm.environment.set to set the “id” variable to the ID of a policy. We did it this way so that we did not change the Params tab of the request and hardcode the ID variable so we can use the request in a runner later.

Similar to the Pre-Request Scritps tab, the Tests tab allows you to utlize JavaScript to perform actions after the request has run. This could be testing the response code that is returned to make sure the request ran, or it can be storing your results for use in the next request in a Runner (which is what we will be doing).

Investigate the Data

Before we get too far down the rabbit trail, we will need to understand where the serial numbers are in the response body of an Advanced Search, and how far they are nested. To do this we will need the ID number of our Advanced Search (ID can be found in the URL of your advanced search, like: https://<jpsURL>/advancedComputerSearches.html?id=6). With the ID number in hand, we can use the “Find Computer Search by ID” API endpoint ({{url}}/JSSResource/advancedcomputersearches/id/:id) to pull back the XML of that search. In our demo case the XML from our search for devices that have a model identifier like “macmini” results in:

<?xml version="1.0" encoding="UTF-8"?>
<advanced_computer_search>
    <id>6</id>
    <name>Update PO Numbers</name>
    <view_as>Standard Web Page</view_as>
    <sort_1/>
    <sort_2/>
    <sort_3/>
    <criteria>
        <size>1</size>
        <criterion>
            <name>Model Identifier</name>
            <priority>0</priority>
            <and_or>and</and_or>
            <search_type>like</search_type>
            <value>Macmini</value>
            <opening_paren>false</opening_paren>
            <closing_paren>false</closing_paren>
        </criterion>
    </criteria>
    <display_fields>
        <size>4</size>
        <display_field>
            <name>Computer Name</name>
        </display_field>
        <display_field>
            <name>Model Identifier</name>
        </display_field>
        <display_field>
            <name>PO Number</name>
        </display_field>
        <display_field>
            <name>Serial Number</name>
        </display_field>
    </display_fields>
    <computers>
        <size>5</size>
        <computer>
            <name>MinneMini’s Mac mini</name>
            <udid>3CBC248B-0E2B-5D12-AB55-7F14D13D0103</udid>
            <id>1</id>
            <Computer_Name>MinneMini’s Mac mini</Computer_Name>
            <Model_Identifier>Macmini9,1</Model_Identifier>
            <PO_Number/>
            <Serial_Number>H2WDV8K6Q6NV</Serial_Number>
        </computer>
        <computer>
            <name>Jeremy’s Mac mini</name>
            <udid>156DBF18-45A6-5429-BE12-DA32ADC50621</udid>
            <id>6</id>
            <Computer_Name>Jeremy’s Mac mini</Computer_Name>
            <Model_Identifier>Macmini7,1</Model_Identifier>
            <PO_Number/>
            <Serial_Number>A02X1111HV2X</Serial_Number>
        </computer>
        <computer>
            <name>Jake’s Mac mini</name>
            <udid>CF3753BF-ADCE-5B38-B821-381C0A4B1182</udid>
            <id>10</id>
            <Computer_Name>Jake’s Mac mini</Computer_Name>
            <Model_Identifier>Macmini8,1</Model_Identifier>
            <PO_Number/>
            <Serial_Number>A02X1111HV3X</Serial_Number>
        </computer>
        <computer>
            <name>McGonagall's Magical Mac Mini</name>
            <udid>EE867891-ECBA-45EB-B3D8-7D40842ACA7A</udid>
            <id>11</id>
            <Computer_Name>McGonagall's Magical Mac Mini</Computer_Name>
            <Model_Identifier>Macmini7,1</Model_Identifier>
            <PO_Number/>
            <Serial_Number>49B113C952DF</Serial_Number>
        </computer>
        <computer>
            <name>H2WFNFQUQ6NV</name>
            <udid>C033C746-76A3-5EA8-8B3C-50F050C4AE01</udid>
            <id>36</id>
            <Computer_Name>H2WFNFQUQ6NV</Computer_Name>
            <Model_Identifier>Macmini9,1</Model_Identifier>
            <PO_Number/>
            <Serial_Number>H2WFNFQUQ6NV</Serial_Number>
        </computer>
    </computers>
    <site>
        <id>-1</id>
        <name>None</name>
    </site>
</advanced_computer_search>

Wow, that’s a lot of data, and you can see (based on the tab indents) that we have a few nests to work out. To get to the serial number of our computers, we have to traverse into the <advanced_computer_search> section, then into the <computers> section, then into each <computer> object, and finally pull the <Serial_Number> key. Once we have the serial number, we will need to store that in an array that can be used by the next request in our chain of requests: “Update Computer By SN”.

To capture the serial numbers we will use the Test tab to place the response body from our request into an array variable. First we need to convert the output from XML to JSON, since JSON is much easier to work with here. We’ll start with grabbing the entire response body and outputting it to the console so we can see what we’re getting. On the Tests tab enter the following:

const response = xml2Json(responseBody);
console.log(response);

Open the console in Postman by clicking on “Console” in the status bar of the window:

With the Console open, go ahead and send your request to your Jamf Pro server. You should get something like this in the Console:

We’re really interested in the very last line of the console (highlighted above). This is the response body in JSON format. We can use the disclosure triangle to open this up and see what our dataset looks like. From this view we can see that we have to go down 4 levels to get the serial number:

If you’ve never dealt with JSON before or had to get nested values, don’t be afraid. Using JavaScript you can “dot walk” to the data you need. Dot walking, in simple terms, means seprating each nested level by a period when you are pulling data. For our example, if I wanted the size of the array set we would use the following: response.advanced_computer_search.computers.computer[0].Serial_Number. If we use the console.log function to print that out as a test, we get:

“But what is that bracket notation in the dot walk” you may be asking. Because the list of computers is actually an array of values we need to use the index value of the specific item we want in that array. You can think of an array as a container of items where each item has a specific location (index) to be stored, almost like a line of children on the playground. Each child is in a specific spot and you can reference the child by that spot in line (array indexes start at 0). So if I wanted to ask the name of the child in the second position in line, I could refer to child[1] and ask that child their name. I know, kind of a clumsy analogy, but hopefully it works. You can read a little more about arrays in this post.

Gather Our Data

Ok, back to our use case. Since we need all of the serial numbers from our Advanced Search for our next API request, we will need to store those in a Postman variable. And since we have more than one serial number to get, guess what we need to use? That’s right, an array. First we have to declare a blank array:

var serial_numbers = [];

Since the <computer> item in our JSON is an array of computers, we will need to loop over each item in that array to grab the serial number value. To do this we will use the forEach function:

response.advanced_computer_search.computers.computer.forEach(function(computer) {
    if(computer.Serial_Number){
        serial_numbers.push(computer.Serial_Number);
    };
});

The above bit of JavaScript goes over each item in the <computer> array, and if there is a value in the <Serial_Number> field, it adds that serial number to our serial_numbers array using a push. The last step is for us to store this in a Postman variable that we can use in our next API request:

pm.variables.set("savedData", serial_numbers);

This line simply says “take our serial_numbers array and store it in the variable that I am calling savedData“. You can use console.log(pm.variables.get("savedData")); to output the variable to the console to verify that you have only the serial numbers.

So putting everything together, our Tests tab should look like:

const response = xml2Json(responseBody);
var serial_numbers = [];

response.advanced_computer_search.computers.computer.forEach(function(computer) {
    if(computer.Serial_Number){
        serial_numbers.push(computer.Serial_Number);
    };
});

pm.variables.set("savedData", serial_numbers);
console.log(pm.variables.get("savedData"));

And the output looks like:

Use The Stuff

Now that we have a Postman variable with our serial numbers, we can use that in our next API request to update PO numbers. To do this we will make use of the Pre-Request Scripts tab in Postman. The first thing we need to do is grab the dataset we saved in our first request so we can manipulate the data:

const myData = pm.variables.get('savedData');

Now that we have the data, we’ll set our variable to grab the serial number for each entry:

pm.variables.set('serialnumber',myData.shift());

What we are doing here is grabbing the serial number value from our data, myData, and storing it in the serialnumber variable. The use of .shift()is what allows us to move to the next value in the myData array each time we run the request. Think of it like walking that line of children, stopping at each one and asking their name, and then moving on to the next to ask the same question.

Next we will utilize an if/then statement to queue up the next run of the API request:

if(Array.isArray(myData) && myData.length > 0) {
    postman.setNextRequest('Update Computer PO');
} else {
    postman.setNextRequest(null);
}

What this is doing is checking to make sure that the length of our myData array is greater than 0, insuring we still have values in the array. See, each time we use .shift()we are actually removing an item from the array (this is known as a pop in other programming languages where we pop something off the stack). The postman.setNextRequest('Update Computer PO'); command is telling Postman to set the next API request to run as the name of the API request that is currently running. This might make more sense with an image so I’ll put one below. The else portion of the if/then statement handles the case when our array of values has a length of 0, meaning it is empty. In that case we are setting the next request as null which tells Postman to stop.

For our use case we are going to simply assume that our collection of devices all have the same PO number. So we’ll set the PO number as a static value in the body of our API request. And since that’s all we are updating, our body is pretty small:

<computer>
    <purchasing>
        <po_number>123456</po_number>
    </purchasing>
</computer>

Run It

Now that we have everything together, we can use a Runner to run our two requests and have the serial numbers passed from the Advanced Search to our Update Computer PO request. If I look at the values in Jamf Pro before the run we can see that PO Number is blank:

If we watch the console as we run the runner, we can see that each subsequent request is a different serial number:

And we can check Jamf Pro to see that the PO numbers are now present:

Wrapping Up

The use of Postman variables to store data to pass to each subsequent request is a real gamechanger and can level up your use of Postman. There are plenty of use cases for this and I’ll cover one or two more in future posts.

I want to acknowledge a couple of articles and videos that helped me understand this and get it working:

https://medium.com/@knoldus/postman-extract-value-from-the-json-object-array-af8a9a68cc01

Categories: Jamf Pro, Tech Tags: , ,

Home Office Setup

April 2, 2022 Leave a comment

This post will be a bit of a departure from my normal ones. Normally I’m trying to pass on some tech knowledge that I might have, or make you aware of some new feature, but not this time. This time I’m shamelessly plugging some products with Amazon Affiliate links so I can make some dough. I could lie to you and say it was just to share my office setup, but I know y’all are smarter than that. Hey, I mean, if you see something in this post that you like and you might purchase for yourself, why not let Amazon give me a few pennies in return. Right. Right?

Let’s start from the top.

Blue Yeticaster bundle – Blue Yeti mic, Compass boom, and Radius III shock mount. I love this mic combo and use it everyday for the many of zoom calls I am on. My only regret/con, and it’s not really one aimed at the product, but it’s difficult to get the boom into a position that stays out of the field of my camera. I don’t really want to show off that I’m on an external mic.

Aureday 6″ Video Conferencing Light – based on the time of the year and the time of day, I found that I wasn’t getting enough light for zoom to show my face clearly. This light provides three different color temperatures and 10 different brightness settings. It comes with a clamp that you can attach to your computer or monitor, but I used the flexible tripod below to attach to the monitor arm.

UBeesize Tripod – This flexible tripod allowed me to wrap it around the arm holding the monitor to get my video conferencing light above the monitor and camera.

Logitech C922x Pro Stream Webcam – This is a great camera for the price. It operates at 1080p and does a good job in low light.

LG 34WN80C-B UltraWide Monitor 34″ – It took me a few days to adjust to using a curved ultra wide display. My eyes would get tired/sore and I’d have to walk away for a bit. But once I got used to it, I absolutley love this monitor! It has pass thru power (60W) via USB-C and it has inputs for two HDMI ports and 2 USB ports.

Fully Jarvis Monitor Mounting Arm – while this arm is made to support up to 32″ displays, it has worked fine for the 34″ LG. This has been great for clearing a little bit of space from the desk and allowed me to utilize the space between the display a bit better.

Apple AirPods Pro – these are great for calls and listening to audiobooks on the go. I, like many others, have problems with the handoff between devices if these are connected to multiple devices. The microphone is also not the greatest for zoom calls, hence the Blue Yeti I linked above.

Belkin Wireless Charger – great for keeping the AirPods Pro charged or for putting a little juice into my iPhone 11.

Lamicall S1 Cell Phone Stand – this keeps my iPhone 11 in front of me and at the right angle to utilize FaceID when I need to grab an MFA code quickly.

SOULWIT Cable Holder Clips – I’ve used other desk clips for holding cables in the past. What I liked about this particluar one is that I could get 3 different ones for the price of one of the other manufacturers.

HumanCentric Vertical Laptop Stand – I like having my laptop off the desktop and out of the way. This keeps it in one spot and off the desktop so I’m not eating up space.

Apple Magic Keyboard – I find I do not need the number keypad on my keyboard, plus I like the minimal look of having this keyboard on my desk. If I could have fewer items on my desk, I would.

Logitech M705 Marathon Mouse – What I love about this mouse is the feel and the ability to customize the buttons. I have the two side buttons set to do App Expose and Mission Control on my MacBook Pro, which allows me to see all open windows for an app or all open windows period. Combined with the ability to change desktops by pressing the scroll wheel to either side, this mouse hits all the spots.

Uplift V2 Desk with Curved Bamboo Top – I looked at several standing desks and settled on the Uplift for two reasons: customer reviews and where they are located. Uplift is located in Austin, TX and I wanted to spend money on a Texas company. I ordered the 72″ wide desk top, along with the wire organizer, two power outlet grommet, wheels, and the under desk hammock for my feet. I have really enjoyed this desk, althoug I do not use the hammock as often as I thought I would.

Topo Comfort Mat by Ergodriven – I try to stand at least 75% of the time I spend in front of my computer. I wanted a floor mat that would help prevent knee issues, back pain, and exhaustion. The beauty about this mat is it checks those boxes and provides something for my feet to do while I’m standing. The different areas of the mat allow me to stretch my legs and help prevent boredom when standing.

TP-Link TL-SG108 8 Port Switch – Hidden under my desk, this allows me to feed all three computers that are on the desk from one ethernet drop that goes back to my router. It’s unmanaged, which doesn’t matter, quiet, and super light so it sticks under the desk easily.

Raspberry Pi – I’m running Pi-hole on a Raspberry Pi that is sitting under the desk. This open source network wide ad blocker has been awesome! No more ads coming in, it blocks unwanted noise, and running on a Pi makes it super quiet and easy to hide.

eero 6 Wi-Fi 6 Mesh Network – I’m running the eero 5 system here, but if I had the money I would upgrade to the eero 6 system to get Wi-Fi 6 running. This mesh system was super easy to setup and helps provide strong wireless coverage to my entire house.

ASUS AC2900 Gaming Router – we have Frontier FIOS (formerly Verizon FIOS) and I got tired of paying Frontier for a router. This one came highly recommended and has operated perfectly for us.

Well, that’s about everything. I guess I could hype my Yeti K-State Wildcat tumbler too (Go Cats!). I hope you find at least something on this page that might help you out. And hey, if you don’t purchase using my links, that’s perfectly fine.

Until next time…

Categories: Tech Tags:

Using Postman with Jamf Pro Part 5 – More Runners

April 1, 2022 1 comment

Welcome back to my series on using Postman with Jamf Pro. If you haven’t checked out the previous posts, or you’ve never used Postman with the Jamf Pro API, you may want to go read through these:

Using Postman for Jamf Pro API Testing – Part 1: The Basics 

Using Postman for Jamf Pro API Testing – Part 2: Creating And Updating Policies

Using Postman with Jamf Pro Part 3: Computer Groups

Using Postman with Jamf Pro Part 4 – Variables & Runners

In my last post, I showed how you can use the Collection Runner feature in Postman to either run multiple API commands in sequence, or pass a CSV file full of data to an API command. The example we used was disabling a bunch of policies all at once.

In this post, I want to show how I used a Runner to create new installer policies for packages. In the environment, I used to manage we followed Bill Smith’s advice given in his JNUC 2017 talk Moving Beyond “Once Per Computer” Workflows and we created separate installer policies for each application that could then be called by other policies or scripts. When Adobe Creative Cloud would inevitably revision up to the next version (2021 to 2022 for example) we would have several new installer policies we’d have to create (at least 7 or more). Well, clicking around the GUI to do that is just nuts and a waste of time when we can create a CSV file to do it for us.

Setup API Command

First thing we need to do is to setup an API command and save it into a collection in Postman. We will want to set variables for the information that is different in each policy. For our Adobe example, the list of information that is unique is:

  • Policy Name
  • Custom Trigger
  • Package ID
  • Package Name

We will create variables for each of those values so that we can pass a CSV file to a Runner to create each policy. So we need to edit our XML and then save it to a collection in Postman. Our edited XML looks like this:

<policy>
	<general>
		<name>{{name}}</name>
		<enabled>true</enabled>
        <trigger_other>{{trigger}}</trigger_other>
		<frequency>Ongoing</frequency>
        <category>
            <id>14</id>
            <name>zInstallers</name>
        </category>
	</general>
    <scope>
        <all_computers>true</all_computers>
    </scope>
    <package_configuration>
        <packages>
            <size>1</size>
            <package>
                <id>{{pkg-id}}</id>
                <name>{{pkg-name}}</name>
                <action>Install</action>
            </package>
        </packages>
    </package_configuration>
	<reboot><no_user_logged_in>Do not restart</no_user_logged_in><user_logged_in>Do not restart</user_logged_in>
	</reboot>
    <maintenance>
        <recon>true</recon>
    </maintenance>
</policy>

Sidebar: if you wanted to make this a completely generic template for creating policies, you could use variables for Frequency, Category ID (do not have to have the category name as it will pull up based on ID), and more.

Package Info

One thing we’ll need is the name of each package and the corresponding ID value. Obviously, before we can get that information we’ll need to upload each package to our Jamf Pro server. Go ahead and do that. I’ll wait…..

Ok, once you’ve uploaded everything, go into Postman and locate the “Finds All Packages” API command in the Jamf Postman collection (see Part 1 for the Jamf collection). Run that command, which will pull back every package you have uploaded to Jamf Pro, and then use the “Save Response” drop down to save the output to a file. This will save out as an XML file, but you can easily open that in Excel to have it convert to a spreadsheet.

Sidebar: If you do not have Excel, or don’t want to load it on your computer, you can find online services to convert XML to CSV, like Data.page. Also, you do not have to convert to CSV or Excel, it’s just easier to grab the package ID and package name from these formats than it is from XML.

After we have the IDs and names, we’ll want to create our CSV file. When we create the CSV we want to make sure the column headers match the variable names we use. For our example the variables we’re using are:

  • name
  • trigger
  • pkg-id
  • pkg-name

Our CSV file file should look something like this:

name,trigger,pkg-id,pkg-name
Adobe Photoshop 2022,photoshop2022,2,Adobe_Photoshop_2022.pkg
Adobe Illustrator 2022,illustrator2022,3,Adobe_Illustrator_2022.pkg
Adobe InDesign 2022,indesign2022,4,Adobe_InDesign_2022.pkg

Runner

Once we have all of these pieces together, we can open a new Runner tab and run our command. Drag the proper Collection into the Runner tab, select the CSV file you created, and click Run. The Runner will cycle through each line of the CSV file and create the necessary policies in Jamf Pro via the API command.

Wrap Up

Now that we’ve covered a basic Runner example and a little more complicated one using more variables, you can hopefully see the power of Postman and how it can help in the day-to-day administration of Jamf Pro.

Categories: Jamf Pro, Tech Tags: , , ,

Using Postman with Jamf Pro Part 4 – Variables & Runners

March 31, 2022 2 comments

Welcome back to my series on using Postman with Jamf Pro. If you haven’t checked out the previous posts, or you’ve never used Postman with the Jamf Pro API, you may want to go read through these:

Using Postman for Jamf Pro API Testing – Part 1: The Basics 

Using Postman for Jamf Pro API Testing – Part 2: Creating And Updating Policies

Using Postman with Jamf Pro Part 3: Computer Groups

Today we’re going to dive a little deeper into the use of variables and the Runner feature in Postman. We touched briefly on variables in Part 2 when we discussed the use of variables to set the ID of a policy.

Just like in computer programming, we can leverage variables in Postman to store data that we need to re-use. We saw this in Part 1 when we setup our environment variables to store username, password, and URL, and again in Part 2 where we were able to set the ID of a policy using a variable and a Pre-request script.

Runner

To me, the real power of Postman is the Collection Runner function. A Collection Runner allows you to run a sequence of API commands in a specific order. It also allows you to feed values into those commands using the variables that we talked about in Part 2. For example, if you needed to disable a group of policies, you could pass a CSV file with a list of policy IDs to a Collection Runner and allow it to send the necessary PUT commands. Let’s see how we can do this.

The first thing we want to do is create our API command that we want to run and save it to a collection. The reason we do this is so we can gather commands that are similar for use over and over again. To disable a policy you just need to pass the following XML using a PUT command:

<policy>
	<general>
		<enabled>false</enabled>
    </general>
</policy>

That’s all we need to send as a PUT to the policies API endpoint. So once we’ve edited a PUT command and saved it to our collection, we can create a CSV file that contains a list of policy numbers. You can use Excel, Numbers, or a code IDE like Visual Studio Code, to create the CSV file. It is important for the first cell, basically the header, to contain the variable that we are replacing. In this case we want to have it set to “id” since our variable is {{id}} (most Postman API commands from the Jamf collection use that variable for a policy ID). So our CSV file should look something like:

id
1
2
3
4

Obviously the numbers will be different based on which policy IDs you need to update. Save that CSV file somewhere on your system and then head back over to Postman.

In Postman go under the File menu and choose “New Runner Tab” (or press Command-Shift-R).

This will open a new tab named “Runner” in your Postman window. Locate the collection you saved your API command in and drag that into the Runner window.

This will place all of the commands you have in that collection into the Runner tab with a checkmark, indicating they are “active”. If you have multiple commands but only want to run one, uncheck (or use the Deselect All link at the top of the Runner window) all of the commands you do not want to run.

Now that we have our command in the Runner tab, use the “Select File” button to the right and select the CSV file you created.

All we have to do now is click on the blue “Run” button and watch Postman do its thing. Once the Runner is done, you can go back to Jamf Pro and check the policies you put in the CSV file to see that they are now disabled. Pretty sweet, huh?

What about enabling a bunch of policies? You guessed it, just create a command that sets the <enabled></enabled> key to “TRUE” instead of “FALSE” and run it through a Runner.

You can use the Console in Postman to debug your commands and get feedback for what went right, or wrong.

Wrap Up

There’s plenty more you can do with Runners, like chaining API commands together and passing values between the calls, or creating more complicated policies or groups. In the next post I’m going to cover one use case for using Runners to create a series of policies.

Categories: Jamf Pro, Tech Tags: , , ,

Using Postman with Jamf Pro Part 3: Computer Groups

January 11, 2022 2 comments

Welcome back to my series on using Postman with Jamf Pro. If you haven’t checked out the previous posts, or you’ve never used Postman with the Jamf Pro API, you may want to go read through these:

Using Postman for Jamf Pro API Testing – Part 1: The Basics

Using Postman for Jamf Pro API Testing – Part 2: Creating And Updating Policies

I’ve decided to change the title of this series, slightly, to reflect the fact that Postman can be used for far more than simply testing the API, but actually using the API to get work done. In the upcoming posts in this series I will go over the use of variables for more than just storing username and password and how to use the Runner functionality to run more than one API command.

But before we get too far into this post, I wanted to bring up an important update that is coming to the Jamf Pro Classic API: the use of Bearer Tokens for authorization. Up until the 10.35 release of Jamf Pro, the only method for authentication was “Basic Authentication” which meant the sending of a username and password combination. From a security standpoint, this is not the best way to do API calls. When Jamf released the “Jamf Pro API” they made it to only work with OAuth (ROPC grant type). This is more secure than basic auth and now they have brought that to the Classic API (sidebar: to read more about the two API frameworks in Jamf Pro, go here.) The release notes for 10.35 have a link to the changes made in the Classic API for authentication. In these notes it is mentioned that the Basic Authentication method has been deprecated and will be removed later this year.

Ok, back to our series. In our last post I showed you how to use Postman to create and update a policy. We also talked about how to create Collections within Postman to store these API requests for later use. By creating API requests for specific tasks we will be able to re-use them more quickly, and as you’ll see in a later post, we can use them via a Runner to perform more than one request at a time.

Create A Smart Group

Just like creating a Policy, creating a Computer Group can be as simple as providing just a name for the group, or it can be as complicated as setting the the criteria for a Smart Group. We are going to create a Smart Group that searches for all computers that have not checked in for more than 90 days. I feel like this is a typical task that a Jamf admin might complete.

We are going to be using the “Create Computer Group by ID” POST call from within Postman. The API endpoint on a Jamf Pro server for this is:

/JSSResource/computergroups/id/<id>

The default XML code that is provided in Postman is as follows:

<computer_group>
	<name>Group Name</name>
	<is_smart>true</is_smart>
	<site>
		<id>-1</id>
		<name>None</name>
	</site>
	<criteria>
		<criterion>
			<name>Last Inventory Update</name>
			<priority>0</priority>
			<and_or>and</and_or>
			<search_type>more than x days ago</search_type>
			<value>7</value>
			<opening_paren>false</opening_paren>
			<closing_paren>false</closing_paren>
		</criterion>
	</criteria>
</computer_group>

We can provide as much information as we want, or as little. For our Smart Group we’re going to use the following:

  • Name: Last Check-In More Than 90 Days
  • Is_smart: true
  • Criterion Name: Last Check-in
  • Criterion and_or: and
  • Criterion Search_type: more than x days ago
  • Citerion Value: 90

The rest of the information in the XML is optional. Since we are only providing one criteria we do not need to worry about the “opening_paren” or “closing_paren” fields. With our specific information, our new XML should look like this:

<computer_group>
	<name>Last Check-In More Than 90 Days</name>
	<is_smart>true</is_smart>
	<criteria>
		<criterion>
			<name>Last Check-in</name>
			<and_or>and</and_or>
			<search_type>more than x days ago</search_type>
			<value>90</value>
		</criterion>
	</criteria>
</computer_group>

If we send that to Jamf via Postman, we should have a new Smart Computer Group in our Jamf Pro server.

Postman – Create Smart Group
Jamf Pro Server Smart Groups

Pretty simple, right? From here we can get more complicated if we need to, adding more criteria to the query. Perhaps we want to refine our search to machines that haven’t checked in for more than 90 days and that have Adobe Photoshop 2021 installed. This type of search would allow us to identify stale Photoshop licenses. The XML for that Group might look like this:

<computer_group>
	<name>Adobe Photoshop Stale Licenses</name>
	<is_smart>true</is_smart>
	<criteria>
		<criterion>
			<name>Last Check-in</name>
			<and_or>and</and_or>
			<search_type>more than x days ago</search_type>
			<value>90</value>
		</criterion>
		<criterion>
			<name>Application Title</name>
			<and_or>and</and_or>
			<search_type>is</search_type>
			<value>Adobe Photoshop 2021.app</value>
		</criterion>        
	</criteria>
</computer_group>

As you can see, it’s easy to create these groups. If you need to do more complicated Smart Groups, you can always create the group in the Jamf Pro interface and then use the GET call in Postman to inspect the XML. Using that method will allow you to understand how to construct even more complicated groups (like those with parenthesis and such).

Modify A Smart Group

When it comes to modifying an existing Smart Group, the process is very similar to the creation process. I suggest using the GET method to find the Smart Group you need to modify, then copy the XML out, make the changes, and paste that into the PUT method for updating.

Let’s use our last example from above, the Adobe Photoshop Smart Group. Maybe we made a mistake and it’s not the 2021 version we want to find, but the 2020 version. From my demo server, using the GET method, I get the following XML returned:

<?xml version="1.0" encoding="UTF-8"?>
<computer_group>
    <id>9</id>
    <name>Adobe Photoshop Stale Licenses</name>
    <is_smart>true</is_smart>
    <site>
        <id>-1</id>
        <name>None</name>
    </site>
    <criteria>
        <size>2</size>
        <criterion>
            <name>Last Check-in</name>
            <priority>0</priority>
            <and_or>and</and_or>
            <search_type>more than x days ago</search_type>
            <value>90</value>
            <opening_paren>false</opening_paren>
            <closing_paren>false</closing_paren>
        </criterion>
        <criterion>
            <name>Application Title</name>
            <priority>0</priority>
            <and_or>and</and_or>
            <search_type>is</search_type>
            <value>Adobe Photoshop 2021.app</value>
            <opening_paren>false</opening_paren>
            <closing_paren>false</closing_paren>
        </criterion>
    </criteria>
    <computers>
        <size>0</size>
    </computers>
</computer_group>

For me to change the version I want to look at, the Application Title, I need to send the following XML to the server:

<computer_group>
    <id>9</id>
    <name>Adobe Photoshop 2020 Stale Licenses</name>
    <criteria>
        <criterion>
            <name>Last Check-in</name>
            <priority>0</priority>
            <and_or>and</and_or>
            <search_type>more than x days ago</search_type>
            <value>90</value>
            <opening_paren>false</opening_paren>
            <closing_paren>false</closing_paren>
        </criterion>
        <criterion>
            <name>Application Title</name>
            <priority>0</priority>
            <and_or>and</and_or>
            <search_type>is</search_type>
            <value>Adobe Photoshop 2020.app</value>
            <opening_paren>false</opening_paren>
            <closing_paren>false</closing_paren>
        </criterion>
    </criteria>
</computer_group>

Again, using the PUT method, sending that XML to the server will update the Smart Group. You can see that now we’ve corrected the name of the Group and we’ve changed the Application Title we are looking for:

Wrap Up

That’s it for Smart Groups. Using the skills you’ve learned with the previous post and this one, you should be able to leverage the API via Postman to create, update, list, and delete just about any object in the Jamf Pro server.

Next up in our series we’re going to talk about variables and the Runner functionality. Leveraging these two things will allow us to create batch jobs to do things like setup policies or create smart groups or even delete items. So stay tuned for that next post.

Categories: Jamf Pro, Tech Tags: , , ,

Back At It

January 4, 2022 Leave a comment

It’s been a minute, hasn’t it? 2020 started off so well, nice and slow (thanks pandemic), and I thought for sure I’d have time to post something new each month. Then in the blink of an eye, I was overwhelmed with projects at work and the next thing you know it’s 2022. Yikes!

2021 saw even more workload, along with the security team driving the project list, and there was just no time to write. Then towards the end of 2021 I decided to make a change of careers, and I joined Jamf as a Sales Engineer. Still in the Mac tech industry, but now on the vendor side which is somewhere I haven’t been for well over 20 years.

So here we are, January 2022 and I’m going to make another run at reviving this blog. I have some ideas for posts, including completing my Postman series. So keep an eye out and we’ll see if I cannot keep this going.

Happy New Year!

Categories: Ramblings, Tech Tags: ,

Using Postman for Jamf Pro API Testing – Part 2: Creating And Updating Policies

April 22, 2020 4 comments

In my previous post I walked through the basic steps necessary to get Postman ready to do API requests to your Jamf Pro server. In this post we’ll get into using Postman and the API to create and update policies, including saving our API requests so we can use them again.

Before we get started I feel like I need to re-iterate my disclaimer:

Prod is NOT test. Be careful with your updates, deletes, and creates. I highly suggest practicing in a dev environment first if you can. If you do not have a dev environment, then use test items like test policies and test computers. The API is a powerful tool.

Ok, now that the disclaimer is out of the way, let’s get to it!

Create a New Policy

You can create a policy with as little information as the name of the policy, or with as much information as the packages to install, scripts to run, and just about anything else you can do in the GUI. One thing I know you cannot do is remove the Restart options from a policy you create. Even if you do not set anything in the Restart section of XML, the policy will still create with that “tab” added to the policy.

UPDATE January 2022: Well, my friend Rich Trouton proved me wrong on the above paragraph. Turns out there is a way to remove the Restart options from a policy. Rather than re-write what he wrote, I direct you to his blog post, Removing the Restart Options section from Jamf Pro policies using the API.

The Jamf Pro API takes XML code blocks to create pieces. If we were to open the POST request for creating a policy (title: “Creates a new policy by ID”) we would see a basic representation of some of the information we can send using this request to create a policy.

Let’s say we wanted to just create a shell of a policy that had a name, a trigger, and a frequency. That XML would look something like this.

<policy>
	<general>
		<name>Our Cool Policy</name>
		<enabled>false</enabled>
		<trigger_checkin>true</trigger_checkin>
		<frequency>Ongoing</frequency>
	</general>
</policy>

We would put that block of XML into the Body section of our API request.

If we were to run that by clicking the Send button we would now have a policy named “Our Cool Policy” that was not enabled, with a trigger of Recurring Check-in, and a frequency of Ongoing. Pretty cool. The Jamf Pro API returns the ID of our new policy and Postman displays that for us.

We can go open up our JPS web interface to look up that policy, but why not just use Postman to look at it. Go find the GET request named “Finds policies by ID” and enter that new policy ID into the “id” Key under Path Variables. Just replace {{id}} with the ID number returned after we ran our POST request.

Example

Now if we run that GET request we’ll get a bunch of XML back showing our new policy, even though we only entered a few items.

Look at all that info! Now, if you’re more of a visual person and need to see it in the web interface, by all means go pull up the policy. We’ll wait until you come back.

Sidebar: When you close out the GET request where we wrote over the {{id}} variable, do not save the request. We want the variable to be there for later use. I’ll show you later in this post a better way to put the ID value in.

That was all neat and all, but what about installing a package as part of the policy? For us to do that we would need a little bit of information about the package. We will need to add the ID of the package and we will need to add the action we want to take (install or cache).

You can grab the package information straight from the web interface, from Jamf Admin, or even better, right from the API here in Postman. This time I’ll let you figure it out on your own. Go find the GET request to list packages and then locate the ID of the package you want to install in our policy.

For my demo I’ll be using a package ID of 1265 and an action of Install. Here’s what the XML looks like.

<policy>
    <general>
	<name>Our Cool Policy</name>
	<enabled>false</enabled>
	<trigger_checkin>true</trigger_checkin>
	<frequency>Ongoing</frequency>
    </general>
    <package_configuration>
        <packages>
            <size>1</size>
            <package>
                <id>1265</id>
                <action>Install</action>
            </package>
        </packages>
    </package_configuration>
</policy>

If we replace our XML in our POST request and run this in Postman, we will get error because there is already a policy named “Our Cool Policy”. We’re going to pretend like we didn’t already create that policy since we’re talking about creating new policies. I’ll discuss updating an existing policy later. (Of course, we could just use Postman to delete the test policy we created above, or use the GUI to delete the policy before we continue on.)

After running the POST we now have a policy in Jamf Pro that is disabled but has a package attached to it.

There you have it, we’ve created our very first policy that actually does something. Of course we would still need to put a scope on the policy and enable it, so why don’t we do that now.

Updating A Policy

Let’s continue building on the policy we were creating above. The ultimate goal of this policy is to be an installer policy that can be used on any computer and triggered via a Custom Trigger. This is one of the methods many Mac admins use for doing installs: have an install policy and call that install policy from other policies or scripts. Go watch Bill Smith’s (@meck or Talking Moose) JNUC presentation from 2017 titled “Moving Beyond “Once Per Computer” Workflows” for more info behind this.

We already have the framework of our policy created we just need to make a few changes and additions. We’ll need to do the following to make this into a true installer policy:

  1. Change the trigger to a custom trigger
  2. Add an inventory update
  3. Add a scope
  4. Add a category

We will want to use the PUT request named “Updates an existing policy by ID” to do these updates. Once again, we only need to put in the XML for items we need to change or add to the policy.

<policy>
    <general>
        <trigger_other>install-our-cool-app</trigger_other>
        <trigger_checkin>false</trigger_checkin>
        <category>
            <id>14</id>
        </category>
    </general>
    <scope>
        <all_computers>true</all_computers>
    </scope>
    <maintenance>
        <recon>false</recon>
    </maintenance>
</policy>

Those are all the XML keys that we’ll need to update in our policy (category ID 14 is the category where I put installers). We’re going to drop that XML into the body section of our API request and we’re going to set the ID variable again like we did above. Just put the ID number of our cool policy.

Change ID variable

After running that API request, our installer policy should be all set and ready to be enabled.

Collecting Our Requests

Now that we’ve used a couple of different API requests, let’s put those into a collection that we can re-use later when we need to do the same thing. Let’s create a collection of requests for manipulating our policies.

Click on the down arrow next to the Save button next to the Send button and choose Save As.

In the window that opens, go ahead and give your request a descriptive name, you can even give it a description. Since this is the first request we’re saving, we’ll want to create a collection for it. Click on the “+Create Collection” link towards the bottom of the window. Give your collection a good name and click the orange checkmark.

Finally, click the big orange Save button at the bottom.

Continue saving all of the requests that you want to use when creating these installer policies, or whatever type of policy. Just remember, if you have changed the ID key from {{id}} to a number, change it back to {{id}} before saving. You’ll see why in a minute.

Now that we have all of these requests in one location, it makes it easier for us in the future when we need to create or update the policy. If we set all of the XML in the POST, then in the future we just have to edit those values in the XML for the new policy.

In a future post, we’ll go over a method for converting those values into variables that we can feed with a JSON file or CSV file so that we do not have to edit our request.

One more thing you’ll need to do. The Classic API collection from Jamf comes with the authentication variables username and password already configured. We’ll need to do that to our new Installer Policies collection.

Click on the ellipses, the three dots, to the right of our collection name and choose Edit.

In the window that opens, click on the Authorization tab. Now click on the drop down box on the left and choose Basic Auth. Fill in the username and password boxes with the variables that we need. Once you have done that click on the orange Update button at the bottom.

Using Variables

I told you I’d show you how to use variables, like the {{id}} variable we’ve already seen. We’re going to use the “Pre-request Script” section of our request window to fill those variables. We use this method rather than directly editing the “Path Variables” on the Param tab so that we can re-use our API request in a more automated fashion later.

On the “Pre-request Script” tab you’ll notice some snippets on the far right. Click on the “Set an environment variable” snippet to add the code to the window.

I’ll bet you can figure out what we’re going to do, right? Yep, go ahead and replace “variable_key” with the variable “id”. Now anytime you want to do an PUT (update) or a GET (retrieve) for a specific policy, you can simply put the policy ID in the “variable_value” field.

For example, if we wanted to update (PUT) our cool installer policy, id = 3338, we would simply add that ID.

When we send that request, the ID variable on the Params tab will get switched out with the ID we placed here in our Pre-request script. That will in turn replace the “:id” in the URL of our request. Clear as mud, right?

Wrap Up

We covered a lot here, a lot more than I expected. Postman is a pretty powerful tool for learning about the Jamf Pro API and how to use it to manipulate your environment. I would suggest doing some Google searches and watching some YouTube videos on how to use Postman to get more advanced.

In our next post we’ll talk about creating and editing computer groups.

Until next time!

Categories: Jamf Pro, Tech Tags: , ,

How To Quickly Open a Policy in Jamf Pro

January 9, 2019 Leave a comment

We have a lot of policies. I mean over 1,000 policies in our Jamf Pro Server. Don’t ask. Part of it is out of necessity, but I’ll bet some of it is just because we were running so fast in 2018 to get systems enrolled and agencies under management, that we didn’t have time to, as Mike Levenick (@levenimc) recently put it, “trim the fat”. That’s what 2019 is all about. But I’m missing the point of this post: how to quickly open a policy. You can imagine how long it takes to load the list of policies when you have over 1,000 of them.

There are a couple of tools you’ll need. First up you’ll want a tool like Text Expander to create snippets or macros. I’m sure there are some free alternatives out there that will expand a text shortcut into something but Text Expander is what I’ve been using for many years, of course I’m using version 5 which is a perpetual license version and not the current subscription model. (Here’s an article about text expansion apps)

The second tool you’ll need is jss_helper from Shea Craig (@shea_craig). This will help us pull a list of the policies in our system, including the ID of the policy.

Now that you have your tools in place, the first thing you want to do is grab the URL of one of your policies. Just open a policy and copy the URL. Now go into Text Expander (or whatever tool you chose) and create a new snippet from the contents of the clipboard. Edit the URL removing everything after the equals (=) sign in the URL. Give your new snippet a shortcut and voila! You now have an easy way to get 90% of the way to quickly opening policies. Your URL snippet should look similar to this:

https://jss.yourcompany.com/policies.html?id=

Now let’s turn our attention to jss_helper. Once you have it installed and configured to talk to your JPS, you’re going to want to pull a list of the policies in your system. Open up Terminal (if it isn’t already) and run jss_helper with the following options:

jss_helper policies > ~/Desktop/policy_list.txt

Obviously you want to name that file whatever you want, but the cool thing is that you now have a list of every policy in your JPS along with its ID. If you open that file up in Excel or a text editor, you’ll see something like this:

ID: 2034 NAME: Installer Adobe Acrobat DC 19

ID: 1214 NAME: Installer Adobe Acrobat DC Reader

ID: 2030 NAME: Installer Adobe After Effects CC 2019

ID: 2031 NAME: Installer Adobe Animate CC 2019

ID: 2032 NAME: Installer Adobe Audition CC 2019

ID: 2033 NAME: Installer Adobe Bridge CC 2019

ID:  638 NAME: Installer Adobe Codecs

ID:  532 NAME: Installer Adobe Creative Cloud Desktop elevated App

ID:  314 NAME: Installer Adobe Creative Cloud Desktop Non elevated App

Now let’s put it together. Open up your web browser and in the address bar type whatever shortcut you created for the policy URL above. Once the URL expands, before pressing enter, type in the ID number of the policy you want to open and then press enter. The policy should open up without having to wait for the list of policies to load or having to search the web interface for the specific policy.

Hopefully this will help speed up your game and help you become quicker and getting stuff done.

Categories: Jamf Pro, Tech Tags: , ,

Using AWS Lambda To Relay Jamf Pro Webhooks to Slack

January 8, 2019 Leave a comment

I recently got interested in utilizing webhooks in Jamf Pro but had no idea where to start. I went and watched Bryson Tyrrell’s (https://twitter.com/bryson3gps) presentation from JNUC 2017 Webhooks Part Deaux! and then went over to take a peek at Jackalope on the Jamf Marketplace. I read the docs, I tried to figure out how to do this in AWS ElasticBeanstalk, but I just couldn’t get it going. Just too much going on to devote enough time to it. So, I went over to Zapier and signed up for their free account so I could get this going. I got it working, but I quickly got throttled because I decided to enable the “ComputerCheckIn” webhook to make sure it worked. I think we flooded the 100 connection limit within 30 seconds and wound up having thousands of items in Zapier.

Well, that wasn’t going to work, so I changed it to “ComputerAdded” and waited for my month of Zapier to renew so I’d get 100 new “zaps”. That worked, until we went over the 100 limit again and had to wait. There had to be a better way that wasn’t going to cost me a ton of money. So I went Googling and came across an article on how to use AWS Lambda to do what I wanted to do: AWS Lambda For Forwarding Webhook To Slack.

I walked through the steps outlined on the page to setup the function in Lambda and everything worked great until I got to the part where I was making requests out to Slack. Lambda had a problem with the request method. Specifically this line of code:

 var post_req = https.request(post_options, function(res) {

So another round of Googling and I came up with the Node.js docs page on HTTPS and I figured out how to properly make the call:

const req = https.request(post_options,
(res) => res.on("data", () => callback(null, "OK")))
req.on("error", (error) => callback(JSON.stringify(error)));
req.write(post_data);
req.end();
view raw gistfile1.txt hosted with ❤ by GitHub

Once I was able to get past the https connection issues, I was able to utilize the rest of Patrick’s example to get my webhook from Jamf feeding into a Slack channel. We uploaded a custom emoji to our Slack channel and used the Slack documentation on basic message formatting and on attachments to get the notification to look how we wanted.

Ultimately we created two Lambda functions, one for ComputerAdded and another for ComputerInventoryComplete, each feeding into their own channel in our Slack. This was fairly easy to accomplish, the next step is to find a way to feed DataDog, or some other service, the ComputerCheckIn webhook so I can get a count of how many check-ins we have each day.

The code we used is below, but I wanted to point out one or two things. Where I got hung up the most was how to pull things like Computer Name or Serial Number from the JSON we were getting from the Jamf Pro server. Since the JSON contains two arrays, “webhook” and “event”, it took me a little bit to understand how to grab that data. To be honest, my skills here are lacking considerably so it took me longer than it should. Ultimately I figured out that you just have to dot walk to get the data you want. So to get the Computer Name it’s:

body.event.deviceName

“body” is the JSON object that we parse the webhook into. Once I figured that out I was all set to grab whatever data from the event, or webhook, array that I needed. Hopefully my head banging will help others not stumble quite so much.

Here’s the Node.js code we used as the template:

var https = require('https');
exports.handler = (event, context, callback) => {
console.log("MYLOG" + JSON.stringify(event))
var body = JSON.parse(event.body);
var name = body.event.deviceName;
var sernum = body.event.serialNumber;
var user_name = body.event.username;
var building = body.event.building;
var curr_time = Math.floor(new Date() / 1000);
var post_data = JSON.stringify({
"username": "Jamf Pro Server",
"icon_emoji": ":balloon:",
"channel": "#<yourslackchannelname>",
"text": "Computer Enrolled",
"attachments": [
{
"color": "good",
"fields": [
{
"title": "Computer: " + name,
"value": "Serial number: " + sernum
+ "\nUser name: " + user_name
+ "\nBuilding: " + building,
"short": false
}
],
"footer": "Jamf Webhook",
"footer_icon": ":balloon:",
"ts": curr_time
}
]
});
var post_options = {
host: 'hooks.slack.com',
port: '443',
path: '/services/YOURWEBHOOK',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(post_data)
}
};
const req = https.request(post_options,
(res) => res.on("data", () => callback(null, "OK")))
req.on("error", (error) => callback(JSON.stringify(error)));
req.write(post_data);
req.end();
var details = {
"status": "OK"
}
var response = {
'statusCode': 200,
'headers': { 'Content-Type': 'application/json' },
'body': JSON.stringify(details)
}
console.log("LOG:: " + JSON.stringify(response))
callback(null, response);
};
view raw gistfile1.txt hosted with ❤ by GitHub

 

 

Categories: Jamf Pro, Tech Tags: , ,

Using lpoptions To Identify Printer Options

October 20, 2018 1 comment

In my previous post on adding printers via script I mentioned using lpoptions to identify the different option settings for a printer. Let’s open up Terminal and get started with identifying the options. You’ll need to have the printer already installed on the system, so if it isn’t installed go follow my previous post and get it installed.

First, let’s find the name of the printer. For that we will use lpstat -a:

Screen Shot 2018-10-20 at 3.46.45 PM

Now that we know the name, Wayne_HOLD, let’s figure out what the options are that we can set. For that we’ll use lpoptions -p Wayne_HOLD -l. Now this list is way too much information to post here, so I’ll just cut it off at a few lines:

Screen Shot 2018-10-20 at 3.48.56 PM

Wow, and there’s plenty more information beyond this. This part of setting the options can be a bit trial and error. We probably aren’t going to want to set everything,  but we will want to add any options like the “Fiery Graphic Arts Package” that is installed, or the “Output option”, or perhaps the “Xerox high capacity feeder”.

One way we can figure out what option we need to set is by using grep along with the lpoptions command. For example, to know which option sets the “Fiery Graphic Arts Package” we might try this:

lpoptions -p Wayne_HOLD -l | grep -i "graphic arts"

This gives us the following:

Screen Shot 2018-10-20 at 3.53.52 PM

That’s great, but what does “GA2” and “GA1” mean? Open up the Options & Supplies window for the printer by going to System Preferences -> Printers & Scanners -> click on the printer and then click on the Options & Supplies button. For this printer, we can see that “GA1” is the “Fiery Graphics Arts Package”.

Screen Shot 2018-10-20 at 3.55.54 PM

What happens when we change that in the GUI to:

Screen Shot 2018-10-20 at 3.57.26 PM

This is what we see from Terminal:

Screen Shot 2018-10-20 at 3.57.41 PM

So now we know that the “GA2” option is the “Fiery Graphics Arts Package, Premium Edition”.

For other settings we need to do some investigation in the Print pane when printing a document. For most printers we’ll want to see what the default view is in the Print dialog window and then make the change in the Terminal using lpoptions and finally go back to the Print dialog window to see what that change did.

In our case we want to set the Output option and the High Capacity Feeder option from their default settings:

Screen Shot 2018-10-20 at 4.09.10 PM

To use a high capacity paper source, and to put the output in a different tray:

Screen Shot 2018-10-20 at 4.09.41 PM

Notice that in the second screen shot we now have Tray 6 available to us. We can acheive this using the lpadmin command to set the settings (note: the lpoptions command works most of the time, but I have far more success using lpadmin).

Screen Shot 2018-10-20 at 4.12.46 PM

By figuring out which settings we want to use, we can now configure all of the options for a printer from a script. One more example would be setting a color printer to default to B&W and duplex print. This is often done as a cost savings measure.

lpa='/usr/sbin/lpadmin'
${lpa} -p CopyThat -E -o printer-is-shared=false -v lpd://10.89.170.5 \
-P "/Library/Printers/PPDs/Contents/Resources/Xerox WorkCentre 7855.gz"
${lpa} -p CopyThat -o Duplex=DuplexNoTumble -o XROutputColor=PrintAsGrayscale
view raw gistfile1.txt hosted with ❤ by GitHub

I hope this has inspired you to dive further into setting up printers via script.

Categories: Casper, Jamf Pro, Tech Tags: , ,