Generating printer configurations using payload-free_package_printer_generator.sh
As part of a recent discussion, a colleague posted in the MacAdmins Slack that they needed to deploy printers as part of a DeployStudio workflow. DeployStudio doesn’t natively include this functionality, so that meant developing a way to deploy the desired printers to the appropriate Macs via one of the following methods:
- Using a script
- Using a management profile
As part of the conversation, I pointed to Nick McSpadden‘s PrinterGenerator tool:
https://github.com/nmcspadden/PrinterGenerator
Nick’s tool is designed to create printer configurations for deployment via Munki. However, my colleague wasn’t using Munki in this case and didn’t plan to deploy it. So even though there was a tool that could have solved the problem, adapting it to work for my DeployStudio-using colleague’s needs was going to take some time and effort.
The discussion got me started thinking about the problem of printer deployments and ways to solve it that could work for the vast majority of deployment solutions. After some research and testing, I’ve developed a solution that may work for most deployment needs. For more details, see below the jump.
My solution was to build a script named payload-free_package_printer_generator.sh, which does three tasks:
- Accept printer configuration input.
- Use that input to generate a script which uses the lpadmin tool to create printers with the provided configuration information.
- Generate a payload-free package to run the script.
Running the script
Usage
/path/to/payload-free_package_printer_generator.sh -n -l -d -a -p -1 -2 -3 -4 -5 -6 -7 -8 -9 -c
Options:
- -n: Name of the print queue. May not contain spaces, tabs, # or / characters. (required)
- -l: The physical location of the printer. Examples may include Reception Desk, Librarian’s Office or Second Floor, Room 2C456 (optional)
- -d: The printer name which is displayed in the Printers & Scanners pane of System Preferences, as well as in the print dialogue boxes. (required)
- -a: The IP or DNS address of the printer. Protocol must be specified as part of the address (for example, use lpd://ip.address.goes.here or lpd://dns.address.goes.here for LPR printing.) (required)
- -p: Name of the driver file in /Library/Printers/PPDs/Contents/Resources/. This must use the full path to the drive (starting with /Library). (required)
- -1: Specify first printer option. (optional)
- -2: Specify second printer option. (optional)
- -3: Specify third printer option. (optional)
- -4: Specify fourth printer option. (optional)
- -5: Specify fifth printer option. (optional)
- -6: Specify sixth printer option. (optional)
- -7: Specify seventh printer option. (optional)
- -8: Specify eighth printer option. (optional)
- -9: Specify ninth printer option. (optional)
- -c: Name of the Apple Developer ID Installer certificate being used to sign the payload-free package. Certificate name should be formatted like Developer ID Installer: Your Name or Developer ID Installer: Your Name (F487797D). (optional)
Note: This script needs to be run with root privileges.
Examples
To create an unsigned payload-free package using only the required printer configuration options, run the command shown below with root privileges:
/path/to/payload-free_package_printer_generator.sh -n PrinterQueueGoesHere -d PrinterNameGoesHere -a lpd://ip.address.goes.here -p /Library/Printers/PPDs/Contents/Resources/PrinterDriverPPDHere.gz
The script’s output should appear similar to that shown below:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
computername:~ username$ sudo /path/to/payload-free_package_printer_generator.sh -n PrinterQueueGoesHere -d PrinterNameGoesHere -a lpd://ip.address.goes.here -p /Library/Printers/PPDs/Contents/Resources/PrinterDriverPPDHere.gz | |
Password: | |
You have chosen the following printer configuration options: | |
Printer Queue Name: PrinterQueueGoesHere | |
Printer Display Name: PrinterNameGoesHere | |
Printer Address: lpd://ip.address.goes.here | |
Printer Driver: /Library/Printers/PPDs/Contents/Resources/PrinterDriverPPDHere.gz | |
Printer Sharing: Disabled | |
pkgbuild: Inferring bundle components from contents of /tmp/PrinterQueueGoesHere/nopayload | |
pkgbuild: Adding top-level postinstall script | |
pkgbuild: Wrote package to /tmp/PrinterQueueGoesHere/Create PrinterNameGoesHere Printer.pkg | |
computername:~ username$ |
The script will generate a postinstall script that looks like this:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# This payload-free package will configure a printer with the following options: | |
# Printer Queue Name: PrinterQueueGoesHere | |
# Printer Display Name: Printer Name Goes Here | |
# Printer Address: lpd://ip.address.goes.here | |
# Printer Driver: /Library/Printers/PPDs/Contents/Resources/PrinterDriverPPDHere.gz | |
# Printer Sharing: Disabled | |
# Remove existing printer queue if one exists. | |
/usr/sbin/lpadmin -x PrinterQueueGoesHere | |
# Add new printer queue with the specified options | |
/usr/sbin/lpadmin -p "PrinterQueueGoesHere" -L "" -D "Printer Name Goes Here" -v "lpd://ip.address.goes.here" -P "/Library/Printers/PPDs/Contents/Resources/PrinterDriverPPDHere.gz" -o "printer-is-shared=false" -E |
Once the postinstall script has been created, a payload-free package will be generated using the script. Once the package has been generated, a new Finder window will appear to show the completed package’s location.
If you need to sign the payload-free package, that is possible using the -c option. For example, you may need to create a signed package that sets up a printer with the following settings:
Printer Queue Name: ReceptionDeskBrotherLaserPrinter Printer Location: Reception Desk Printer Display Name: Reception Desk Brother Laser Printer Printer Address: lpd://192.168.1.186 Printer Driver: /Library/Printers/PPDs/Contents/Resources/Brother HL-2170W series CUPS.gz
To create an signed payload-free package which can create this printer, run the command shown below with root privileges:
/path/to/payload-free_package_printer_generator.sh -n ReceptionDeskBrotherLaserPrinter -l "Reception Desk" -d "Reception Desk Brother Laser Printer" -a lpd://192.168.1.186 -p "/Library/Printers/PPDs/Contents/Resources/Brother HL-2170W series CUPS.gz" -c "Developer ID Installer: Rich Trouton (XF95CST45F)"
You may be prompted to authorize access to your Developer ID certificate.
Once authorized, the package will be generated.
You can verify that the package is signed by clicking on the padlock icon in the Installer window.
Testing
To test a payload-free package generated by this script, take it to a Mac which does not already have the desired printer set up on it and run the installer package.
Note: The appropriate printer drivers must be installed prior to setting up the printer. If not, the lpadmin tool which is used by the payload-free package to set up the printer will generate an error and the installer package will report a failed install.
The printer should be set up as specified.
The script is available below. It is also available from GitHub at the following address:
https://github.com/rtrouton/payload-free_package_printer_generator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Parameters: | |
# | |
# Name of the print queue (required) | |
# Location of the printer (optional) | |
# The printer's displayed name (required) | |
# The IP or DNS address of the printer (required) | |
# Printer driver name (required) | |
# Up to nine additional printer options, like setting printer to print double-sided or to set specified trays to have certain paper sizes (optional) | |
# Apple Developer certificate, used to sign the payload-free package (optional) | |
# | |
# By default, printer sharing is disabled. | |
# | |
# Usage: | |
# When using the script to create payload-free packages, command should look something like this: | |
# | |
# /path/to/payload-free_package_printer_generator.sh -n PrinterQueueGoesHere -d PrinterNameGoesHere -a lpd://ip.address.goes.here -p /Library/Printers/PPDs/Contents/Resources/PrinterDriverPPDHere.gz | |
# | |
# To create a signed payload-free package using a Developer ID Installer certificate: | |
# | |
# /path/to/payload-free_package_printer_generator.sh -n PrinterQueueGoesHere -d PrinterNameGoesHere -a lpd://ip.address.goes.here -p /Library/Printers/PPDs/Contents/Resources/PrinterDriverPPDHere.gz -c "Developer ID Installer: Your Name (F487797D)" | |
# | |
# To create a signed payload-free package which creates a printer using a displayed name with spaces, add quotation marks to the displayed printer name: | |
# | |
# /path/to/payload-free_package_printer_generator.sh -n PrinterQueueGoesHere -d "Printer Name Goes Here" -a lpd://ip.address.goes.here -p /Library/Printers/PPDs/Contents/Resources/PrinterDriverPPDHere.gz -c "Developer ID Installer: Your Name (F487797D)" | |
# | |
# Other flags can be added as needed: | |
# | |
# To add one additional option to the printer configuration (in this example, a custom tray for an HP printer): | |
# | |
# /path/to/payload-free_package_printer_generator.sh -n PrinterQueueGoesHere -d PrinterNameGoesHere -a lpd://ip.address.goes.here -p /Library/Printers/PPDs/Contents/Resources/PrinterDriverPPDHere.gz -1 HPOptionDuplexer=True | |
# | |
# To add two additional options to the printer configuration (in this example, a custom tray for an HP printer and a specified output mode) and sign the payload-free package: | |
# | |
# /path/to/payload-free_package_printer_generator.sh -n PrinterQueueGoesHere -d PrinterNameGoesHere -a lpd://ip.address.goes.here -p /Library/Printers/PPDs/Contents/Resources/PrinterDriverPPDHere.gz -1 HPOptionDuplexer=True -2 OutputMode=normal -c "Developer ID Installer: Your Name (F487797D)" | |
phelp() { | |
echo "" | |
echo "Usage: ./payload-free_package_printer_generator.sh options: -n -l -d -a -p -1 -2 -3 -4 -5 -6 -7 -8 -9 -c" | |
echo "" | |
echo "n: Name of the print queue. May not contain spaces, tabs, # or / characters. >> REQUIRED <<" | |
echo "l: The physical location of the printer. Examples may include 'Reception Desk', 'Librarian's Office' or 'Second Floor, Room 2C456'. (optional)" | |
echo "d: The printer name which is displayed in the Printers & Scanners pane of System Preferences, as well as in the print dialogue boxes. >> REQUIRED <<" | |
echo "a: The IP or DNS address of the printer. Protocol must be specified as the address (for example, use 'lpd://ip.address.goes.here' or 'lpd://dns.address.goes.here' for LPR printing. >> REQUIRED <<" | |
echo "p: Name of the driver file in /Library/Printers/PPDs/Contents/Resources/. This must use the full path to the drive (starting with /Library). >> REQUIRED <<" | |
echo "1: Specify first printer option. (optional)" | |
echo "2: Specify second printer option. (optional)" | |
echo "3: Specify third printer option. (optional)" | |
echo "4: Specify fourth printer option. (optional)" | |
echo "5: Specify fifth printer option. (optional)" | |
echo "6: Specify sixth printer option. (optional)" | |
echo "7: Specify seventh printer option. (optional)" | |
echo "8: Specify eighth printer option. (optional)" | |
echo "9: Specify ninth printer option. (optional)" | |
echo "c: Name of the Apple Developer ID Installer certificate being used to sign the payload-free package. Certificate name should be formatted like 'Developer ID Installer: Your Name' or 'Developer ID Installer: Your Name (F487797D)'. (optional)" | |
echo "" | |
} | |
while getopts n:l:d:a:p:1:2:3:4:5:6:7:8:9:c: option | |
do | |
case $option in | |
n) | |
printerqueue=${OPTARG}; | |
FLAG=false; | |
if [ -z "${printerqueue}" ]; then | |
ERR=true | |
MSG="$MSG | Please make sure to enter a printer queue name. Queue name cannot have spaces." | |
fi | |
;; | |
l) | |
location=${OPTARG} | |
if [ -z "${location}" ]; then | |
ERR=true | |
MSG="$MSG | Please make sure to enter the location of the printer." | |
fi | |
;; | |
d) | |
gui_display_name=${OPTARG} | |
FLAG=false; | |
if [ -z "${gui_display_name}" ]; then | |
ERR=true | |
MSG="$MSG | Please make sure to enter a display name for the printer." | |
fi | |
;; | |
a) | |
address=${OPTARG} | |
FLAG=false; | |
if [ -z "${address}" ]; then | |
ERR=true | |
MSG="$MSG | Please make sure to enter the address URL for the printer. " | |
fi | |
;; | |
p) | |
driver_ppd=${OPTARG} | |
FLAG=false; | |
if [ -z "${driver_ppd}" ]; then | |
ERR=true | |
MSG="$MSG | Please make sure to enter the driver file in /Library/Printers/PPDs/Contents/Resources/. This can be either a relative path or the full path (starting with /Library). " | |
fi | |
;; | |
1) | |
option_1=${OPTARG} | |
if [ -z "${option_1}" ]; then | |
ERR=true | |
MSG="$MSG | Please enter the desired printer option." | |
fi | |
;; | |
2) | |
option_2=${OPTARG} | |
if [ -z "${option_2}" ]; then | |
ERR=true | |
MSG="$MSG | Please enter the desired printer option." | |
fi | |
;; | |
3) | |
option_3=${OPTARG} | |
if [ -z "${option_3}" ]; then | |
ERR=true | |
MSG="$MSG | Please enter the desired printer option." | |
fi | |
;; | |
4) | |
option_4=${OPTARG} | |
if [ -z "${option_4}" ]; then | |
ERR=true | |
MSG="$MSG | Please enter the desired printer option." | |
fi | |
;; | |
5) | |
option_5=${OPTARG} | |
if [ -z "${option_5}" ]; then | |
ERR=true | |
MSG="$MSG | Please enter the desired printer option" | |
fi | |
;; | |
6) | |
option_6=${OPTARG} | |
if [ -z "${option_6}" ]; then | |
ERR=true | |
MSG="$MSG | Please enter the desired printer option" | |
fi | |
;; | |
7) | |
option_7=${OPTARG} | |
if [ -z "${option_7}" ]; then | |
ERR=true | |
MSG="$MSG | Please enter the desired printer option" | |
fi | |
;; | |
8) | |
option_8=${OPTARG} | |
if [ -z "${option_8}" ]; then | |
ERR=true | |
MSG="$MSG | Please enter the desired printer option" | |
fi | |
;; | |
9) | |
option_9=${OPTARG} | |
if [ -z "${option_9}" ]; then | |
ERR=true | |
MSG="$MSG | Please enter the desired printer option" | |
fi | |
;; | |
c) | |
signing_cert=${OPTARG} | |
if [ -z "${signing_cert}" ]; then | |
ERR=true | |
MSG="$MSG | Please make sure to enter the name of your Apple Developer ID Installer certificate. Certificate name should be formatted like 'Developer ID Installer: Your Name' or 'Developer ID Installer: Your Name (F487797D)'" | |
fi | |
;; | |
\?) echo "Unknown option: –$OPTARG" >&2; phelp; exit 1;; | |
🙂 echo "Missing option argument for –$OPTARG" >&2; phelp; exit 1;; | |
*) echo "Unimplemented option: –$OPTARG" >&2; phelp; exit 1;; | |
esac | |
done | |
# Display warning if the printer queue setting hasn't been entered. | |
if [ -z "${printerqueue}" ]; then | |
echo "" | |
echo ">> PROBLEM << : Please make sure to enter a printer queue name using the -n option. Displaying script options below." | |
echo "" | |
phelp | |
exit 1 | |
fi | |
# Display warning if the printer's display name setting hasn't been entered. | |
if [ -z "${gui_display_name}" ]; then | |
echo "" | |
echo ">> PROBLEM << : Please make sure to enter a display name for the printer with the -d parameter. Displaying script options below." | |
echo "" | |
phelp | |
exit 1 | |
fi | |
# Display warning if the IP or DNS URL setting hasn't been entered. | |
if [ -z "${address}" ]; then | |
echo "" | |
echo ">> PROBLEM << : Please make sure to enter the address URL for the printer with the -a parameter. Displaying script options below." | |
echo "" | |
phelp | |
exit 1 | |
fi | |
# Display warning if the printer driver location setting hasn't been entered. | |
if [ -z "${driver_ppd}" ]; then | |
echo "" | |
echo ">> PROBLEM << : Please make sure to enter the driver file for the printer with the -p parameter. Displaying script options below." | |
echo "" | |
phelp | |
exit 1 | |
fi | |
# Display the options which have been selected for creating the printer | |
echo "" | |
echo "You have chosen the following printer configuration options:" | |
echo "" | |
if [[ "${printerqueue}" != "" ]]; then | |
echo "Printer Queue Name: ${printerqueue}" | |
fi | |
if [[ "${location}" != "" ]]; then | |
echo "Printer Location: ${location}" | |
fi | |
if [[ "${gui_display_name}" != "" ]]; then | |
echo "Printer Display Name: ${gui_display_name}" | |
fi | |
if [[ "${address}" != "" ]]; then | |
echo "Printer Address: ${address}" | |
fi | |
if [[ "${driver_ppd}" != "" ]]; then | |
echo "Printer Driver: ${driver_ppd}" | |
fi | |
if [[ "${option_1}" != "" ]]; then | |
echo "Additional Printer Option: ${option_1}" | |
fi | |
if [[ "${option_2}" != "" ]]; then | |
echo "Additional Printer Option: ${option_2}" | |
fi | |
if [[ "${option_3}" != "" ]]; then | |
echo "Additional Printer Option: ${option_3}" | |
fi | |
if [[ "${option_4}" != "" ]]; then | |
echo "Additional Printer Option: ${option_4}" | |
fi | |
if [[ "${option_5}" != "" ]]; then | |
echo "Additional Printer Option: ${option_5}" | |
fi | |
if [[ "${option_6}" != "" ]]; then | |
echo "Additional Printer Option: ${option_6}" | |
fi | |
if [[ "${option_7}" != "" ]]; then | |
echo "Additional Printer Option: ${option_7}" | |
fi | |
if [[ "${option_8}" != "" ]]; then | |
echo "Additional Printer Option: ${option_8}" | |
fi | |
if [[ "${option_9}" != "" ]]; then | |
echo "Additional Printer Option: ${option_9}" | |
fi | |
echo "Printer Sharing: Disabled" | |
if [[ "${signing_cert}" != "" ]]; then | |
echo "The payload-free package will be signed with the following certificate: ${signing_cert}" | |
fi | |
echo "" | |
# Creating the payload-free package script | |
# Remove any previous build directories whose names match | |
# the build directory which is about to be created. | |
if [[ -d /tmp/"${printerqueue}" ]]; then | |
rm -rf /tmp/"${printerqueue}" | |
fi | |
# Create build directories for the payload-free package | |
mkdir -p /tmp/"${printerqueue}"/scripts | |
mkdir -p /tmp/"${printerqueue}"/nopayload | |
# Take any entered printer options and set them into the | |
# variable format used by the payload-free package script. | |
if [[ "${option_1}" != "" ]]; then | |
add_option1="-o \"${option_1}\" " | |
fi | |
if [[ "${option_2}" != "" ]]; then | |
add_option2="-o \"${option_2}\" " | |
fi | |
if [[ "${option_3}" != "" ]]; then | |
add_option3="-o \"${option_3}\" " | |
fi | |
if [[ "${option_4}" != "" ]]; then | |
add_option4="-o \"${option_4}\" " | |
fi | |
if [[ "${option_5}" != "" ]]; then | |
add_option5="-o \"${option_5}\" " | |
fi | |
if [[ "${option_6}" != "" ]]; then | |
add_option5="-o \"${option_6}\" " | |
fi | |
if [[ "${option_7}" != "" ]]; then | |
add_option5="-o \"${option_7}\" " | |
fi | |
if [[ "${option_8}" != "" ]]; then | |
add_option5="-o \"${option_8}\" " | |
fi | |
if [[ "${option_9}" != "" ]]; then | |
add_option5="-o \"${option_9}\" " | |
fi | |
# Create postinstall script | |
postinstall=/tmp/"${printerqueue}"/scripts/postinstall | |
touch "$postinstall" | |
echo "#!/bin/bash" >> "$postinstall" | |
echo "" >> "$postinstall" | |
echo "# This payload-free package will configure a printer with the following options:" >> "$postinstall" | |
echo "" >> "$postinstall" | |
if [[ "${printerqueue}" != "" ]]; then | |
echo "# Printer Queue Name: ${printerqueue}" >> "$postinstall" | |
fi | |
if [[ "${location}" != "" ]]; then | |
echo "# Printer Location: ${location}" >> "$postinstall" | |
fi | |
if [[ "${gui_display_name}" != "" ]]; then | |
echo "# Printer Display Name: ${gui_display_name}" >> "$postinstall" | |
fi | |
if [[ "${address}" != "" ]]; then | |
echo "# Printer Address: ${address}" >> "$postinstall" | |
fi | |
if [[ "${driver_ppd}" != "" ]]; then | |
echo "# Printer Driver: ${driver_ppd}" >> "$postinstall" | |
fi | |
if [[ "${option_1}" != "" ]]; then | |
echo "# Additional Printer Option: ${option_1}" >> "$postinstall" | |
fi | |
if [[ "${option_2}" != "" ]]; then | |
echo "# Additional Printer Option: ${option_2}" >> "$postinstall" | |
fi | |
if [[ "${option_3}" != "" ]]; then | |
echo "# Additional Printer Option: ${option_3}" >> "$postinstall" | |
fi | |
if [[ "${option_4}" != "" ]]; then | |
echo "# Additional Printer Option: ${option_4}" >> "$postinstall" | |
fi | |
if [[ "${option_5}" != "" ]]; then | |
echo "# Additional Printer Option: ${option_5}" >> "$postinstall" | |
fi | |
if [[ "${option_6}" != "" ]]; then | |
echo "# Additional Printer Option: ${option_6}" >> "$postinstall" | |
fi | |
if [[ "${option_7}" != "" ]]; then | |
echo "# Additional Printer Option: ${option_7}" >> "$postinstall" | |
fi | |
if [[ "${option_8}" != "" ]]; then | |
echo "# Additional Printer Option: ${option_8}" >> "$postinstall" | |
fi | |
if [[ "${option_9}" != "" ]]; then | |
echo "# Additional Printer Option: ${option_9}" >> "$postinstall" | |
fi | |
echo "# Printer Sharing: Disabled" >> "$postinstall" | |
echo "" >> "$postinstall" | |
echo "# Remove existing printer queue if one exists." >> "$postinstall" | |
echo "/usr/sbin/lpadmin -x ${printerqueue}" >> "$postinstall" | |
echo "" >> "$postinstall" | |
echo "# Add new printer queue with the specified options" >> "$postinstall" | |
echo "/usr/sbin/lpadmin -p \"${printerqueue}\" -L \"${location}\" -D \"${gui_display_name}\" -v \"${address}\" -P \"${driver_ppd}\" -o \"printer-is-shared=false\" ${add_option1}${add_option2}${add_option3}${add_option4}${add_option5}${add_option6}${add_option7}${add_option8}${add_option9}-E" >> "$postinstall" | |
# Set postinstall script to be executable | |
/bin/chmod a+x "$postinstall" | |
pkgid="com.github.create_${printerqueue}_printer" | |
pkgvers=$(date +"%Y.%j.%H.%s") | |
# Depending if the option for signing the payload-free package has been set, one of the following actions is performed: | |
# | |
# – If no certificate option has been set, an unsigned payload-free package is created. | |
# – If a signing certificate has been specified, a signed payload-free package is created | |
# using the specified signing certificate. | |
if [[ -z "${signing_cert}" ]]; then | |
pkgbuild –identifier "${pkgid}" –version "${pkgvers}" –root /tmp/"${printerqueue}"/nopayload –scripts /tmp/"${printerqueue}"/scripts /tmp/"${printerqueue}"/"Create ${gui_display_name} Printer.pkg" | |
else | |
pkgbuild –identifier "${pkgid}" –version "${pkgvers}" –root /tmp/"${printerqueue}"/nopayload –scripts /tmp/"${printerqueue}"/scripts –sign "${signing_cert}" /tmp/"${printerqueue}"/"Create ${gui_display_name} Printer.pkg" | |
fi | |
# Once the payload-free package has been created, a new Finder window opens | |
# to display the new payload-free package in its build location. | |
open /tmp/"${printerqueue}" |
Could this be implemented with a payload of drivers to install first and then run this script to install the printers? Is it possible to specify a default printer from this script as well?
Hi Vince , Did you get an answer to the question
I have the same scenario, whereby I need to have the PPD packaged in the installer