Archive
Deploying Printers via Script
Deploying printers on the Mac in an enterprise environment, or heck, just in a small office environment, can be done in multiple ways. If you don’t have a management tool, or ARD, you’re going to be running around doing it by hand. If you have a management tool, like Munki or Jamf, then you can deploy printers in a more automated fashion. My preferred method is to use a Bash script to deploy printers because it provides me a little more flexibility
Identify The Driver
The first thing to do is to identify the printer and the driver that is required. For most printers this is pretty simple, just navigate to the IP address of the printer to verify the make and model, then head over to the vendor’s website to download the latest driver. Once you have the driver, install it on your machine and then go find the driver file on your system. On the Mac, most printer drivers are stored in:
/Library/Printers/PPDs/Contents/Resources
If your printer has a “RIP”, or “Raster Image Processor“, identifying those drivers can be a little trickier. Head over to this post on how to identify the driver, and download it, on an EFI Fiery RIP. Drivers for an EFI Fiery or other RIP are usually stored in:
/Library/Printers/PPDs/Contents/Resources/<localization folder>
For us here in North America, that folder path would be:
/Library/Printers/PPDs/Contents/Resources/en.lproj
Once you have identified the driver, copy the full path of the driver file (Option-Command-C, or hold down Command then Edit->Copy as Pathname, or right click while holding Command) to your clipboard.
Build The Command
The next thing we need to do is figure out the command to run to add the printer. Fire up Terminal and let’s figure out the commands to use. We will utilize the lpadmin
command to get the printer on the system. For this post this is what our command will look like:
sudo lpadmin -p <name> -E -o printer-is-shared=false -v ipp://1.1.1.1 -D "<name>" -P "/Library/Printers/PPDs/Contents/Resources/Xerox WorkCentre 5955.gz"
That looks a little daunting, so let’s break it down a little bit.
-p <name>
This flag sets the name of the printer as seen by the cups
process. Use a name with no spaces, or substitute underscores for the space.
-E
This flag enables the printer to accept jobs
-o printer-is-shared=false
The -o
flag allows us to pass options to the printer. In this case we are making sure the printer is not shared on the network.
-v ipp://1.1.1.1
The -v
flag sets the URI of the printer.
-D <name>
Where -p set the name the cups
process saw, the -D
flag sets what I call the “friendly” name, the name that is visible in the GUI.
-P <driver path>
Pretty self explanatory, the -P
flag sets the path to the driver.
Now that we have everything, run the command in Terminal to add the printer. You can verify the printer added by using the lpstat -a
command. With the printer added, open up a program and send a test print to the printer. It is important for us to do this step so that we know our handy work is working properly.
Put It In A Script
Let’s get to the script to add the printer. Open your favorite code editor (TextMate or Sublime Text for me) and start a new script. I utilize Bash, but you could just as easily do this in Python if you prefer. First we want to make sure the proper driver is on the system, and if it isn’t we want to install it.
if [[ ! -f "/Library/Printers/PPDs/Contents/Resources/Xerox WorkCentre 5955.gz" ]]; then | |
/usr/local/bin/jamf policy -trigger xeroxGenericDriver | |
fi |
If the driver file is not on the system, we call the jamf
binary to trigger our install policy. Adjust this to fit your management toolset.
Now with the driver check complete, we use a case
statement to choose the printer (or printers) to install. We pass the choice to the script using Script Parameters in our Jamf Pro server policy. Here’s an example showing how we can install one printer, or multiple printers in an office.
lpa='/usr/sbin/lpadmin' | |
case $printer in | |
Printer1) | |
${lpa} -p Printer1 -E -o printer-is-shared=false -v ipp://10.1.1.1/ipp/print \ | |
-D "Printer1" -P "/Library/Printers/PPDs/Contents/Resources/Xerox WorkCentre 5955.gz" | |
;; | |
Printer2) | |
${lpa} -p Printer2 -E -o printer-is-shared=false -v ipp://10.1.1.2/ipp/print \ | |
-D "Printer2" -P "/Library/Printers/PPDs/Contents/Resources/Xerox WorkCentre 5955.gz" | |
;; | |
OfficePrinters) | |
${lpa} -p Printer1 -E -o printer-is-shared=false -v ipp://10.1.1.1/ipp/print \ | |
-D "Printer1" -P "/Library/Printers/PPDs/Contents/Resources/Xerox WorkCentre 5955.gz" | |
${lpa} -p Printer2 -E -o printer-is-shared=false -v ipp://10.1.1.2/ipp/print \ | |
-D "Printer2" -P "/Library/Printers/PPDs/Contents/Resources/Xerox WorkCentre 5955.gz" | |
;; | |
esac |
You can hopefully see the flexibility this provides us for using one script to install multiple printers. Sure, we still have multiple policies in the JPS, but rather than have multiple printers or multiple scripts as well, we can do this with just the one. And, when a printer needs to change, we just edit the script.
Bonus Round
What about adding printer options, like paper trays or output trays or setting a printer to B&W instead of color? We can use lpoptions
to figure out what those options are and to set them. Since that can be a daunting task, head over to this post about using lpoptions
to identify the settings.
Hopefully this post has helped you evaluate the use of a script to add printers and has given you a new tool for your toolbox.
Collecting Data Using Plist Files
At our recent Dallas area Casper User Group meeting, we got into a discussion around collecting data during a Casper recon. Specifically we were discussing the use of Extension Attributes to collect information about virtual machines.
Extension Attributes are a way to capture information from your systems. You can use scripts to pull information or drop downs or text boxes to store static information in the database. In the instance of collecting info about virtual machines, a script would be run on the systems during the recon to gather the information. Running a script on the system each time a recon happens can be processor heavy, depending on the data that is being gathered. For example, gathering home folder size by running “du” each time a recon happens can be taxing.
Rather than run the script each recon, you can use a policy to run the script once a week, once a month, or just one time, to gather the information you need and place it in a plist file somewhere. During the standard recon period, you can then use an Extension Attribute to read the information in that plist file. This is much less taxing on the systems than running the script during a recon.
Stash The Data
For our example, rather than run through grabbing info about virtual machines, let’s work on grabbing the home folder size for the logged on user. We will store the info in a plist file that we will stash in /Library/IT_Data.
First we need to find the logged in user name. There are plenty of ways to do this, but we’ll use the “Apple approved” method, using a Python one liner. Okay, it’s not really a one liner, it’s just built like one.
loggedInUser=`python -c 'from SystemConfiguration import SCDynamicStoreCopyConsoleUser; import sys; username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]; sys.stdout.write(username + "\n");'` | |
view rawcontents.sh |
Now that we have our logged in user, we just need to find the user’s home folder and use du to grab the data. We’ll use dscl to grab the home folder location and then du to get the home folder size.
homeDir=`dscl . read /Users/$loggedInUser NFSHomeDirectory | awk '{ print $2 }'` | |
homeSize=`du -hs ${homeDir}` |
The next thing we need to do is to store this information into our plist file. Using the defaults command, we can write as much data as we want into the plist file, We can use different keys to store whatever data you want, and then recall it during recon by asking for those specific keys.
defaults write /Library/IT_Data/com.mycompany.homesize.plist HomeFolderSize -string "${homeSize}" |
Retrieve The Data
Now that we have the data stashed away, we just need to grab it during the recon process. To do this, we’ll use the defaults command again, to grab the data. We’ll use some variables for the folder path and the plist name, that way we can re-use this code fairly easily. We also want to make sure the file actually exists before trying to read data from it, hence the If statement.
if [[ -e ${dataFile} ]]; then | |
homeSize=`defaults read ${dataFile} HomeFolderSize` | |
fi |
Once we’ve read the data, all that’s left is to echo it out into the EA.
if [[ ${homeSize} ]] | |
echo "<result>${homeSize}</result>" | |
else | |
echo "<result>No Results Found</result>" | |
fi |
That’s All Folks
That’s pretty much all there is. Now that we know how save data to a plist and then read it back, this method could be used for any data we only need to gather once, or gather at infrequent times.
The full script to write the data and to then read the data are below.
#!/bin/bash | |
loggedInUser=`python -c 'from SystemConfiguration import SCDynamicStoreCopyConsoleUser; import sys; username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]; sys.stdout.write(username + "\n");'` | |
homeDir=`dscl . read /Users/$loggedInUser NFSHomeDirectory | awk '{ print $2 }'` | |
homeSize=`du -hs ${homeDir}` | |
# check for our storage folder, and create if missing | |
if [[ ! -d "/Library/IT_Data" ]]; then | |
mkdir "/Library/IT_Data" | |
fi | |
defaults write /Library/IT_Data/com.mycompany.homesize.plist HomeFolderSize -string "${homeSize}" |
#!/bin/bash | |
dataFolder="/Library/IT_Data" | |
dataFile="${dataFolder}/com.mycompany.homesize.plist" | |
if [[ -e ${dataFile} ]]; then | |
homeSize=`defaults read ${dataFile} HomeFolderSize` | |
fi | |
if [[ ${homeSize} ]]; then | |
echo "<result>${homeSize}</result>" | |
else | |
echo "<result>No Results Found</result>" | |
fi |