AutoPkg repo and logfile cleanup scripts for use with autopkg-conductor

As part of running autopkg-conductor over a long period of time, you may see a large percentage of disk space used on the Mac where you’re running AutoPkg and autopkg-conductor. This is because AutoPkg doesn’t remove older files from ~/Library/AutoPkg/Cache and autopkg-conductor does not remove older logfiles from ~/Library/Logs. To assist with this issue, I’ve written a couple of scripts. For more details, please see below the jump.

To assist with preserving available disk space on your AutoPkg host Mac, the following scripts are available:

  • 801.clean-autopkg-repo
  • 802.remove.autopkg.conductor.logs

Both scripts are designed to be installed into the following location:

/etc/periodic/daily

Installing them in that location will enable the periodic tool to run both scripts daily on your AutoPkg host Mac.

Permissions for both scripts should be set as follows:

root:wheel - rwxr-xr-x

The 801.clean-autopkg-repo script does the following task:

  • Determine which files in the ~/Library/AutoPkg/Cache directory are older than the number of days specified in the script (by default, 20 days)
  • Delete all files in the ~/Library/AutoPkg/Cache directory which are older than the specified number of days

This script is available below and also from GitHub at the following location:

https://github.com/rtrouton/autopkg-conductor/blob/main/cleanup_scripts/801.clean-autopkg-repo

#!/bin/bash
# Remove AutoPkg-downloaded files older than a specified number of days
# Add username of the account used to run AutoPkg
autopkg_username="autopkg"
# Age of files to retain. For example, setting the following number
# will delete anything older than 20 days
#
# autopkg_cache_age="20"
autopkg_cache_age="20"
/usr/bin/find "/Users/$autopkg_username/Library/AutoPkg/Cache" -mindepth 1 -mtime +"$autopkg_cache_age" -delete

The 802.remove.autopkg.conductor.logs script is designed to do the following:

  • Determine which autopkg-conductor log files in the ~/Library/Logs directory are older than the number of days specified in the script (by default, 20 days)
  • Delete all relevant logfiles in the ~/Library/Logs directory which are older than the specified number of days.

This script is available below and also from GitHub at the following location:

https://github.com/rtrouton/autopkg-conductor/blob/main/cleanup_scripts/802.remove.autopkg.conductor.logs

#!/bin/bash
# Remove autopkg-conductor logfiles older than a specified number of days
# Add username of the account used to run AutoPkg
autopkg_username="autopkg"
# Age of logfiles to retain. For example, setting the following number
# will delete anything older than 20 days
#
# autopkg_log_age="20"
autopkg_log_age="20"
/usr/bin/find "/Users/$autopkg_username/Library/Logs" -name "autopkg-run-for*" -mindepth 1 -mtime +"$autopkg_log_age" -delete

Using Signing Manager with autopkg-conductor

I’ve recently been working with Twocanoes Software’s Signing Manager in combination with my autopkg-conductor tool for managing AutoPkg runs. I’m happy to report it’s possible, but you may need to make some adjustments to how autopkg-conductor is being launched. For more details, please see below the jump.

As originally written, autopkg-conductor uses a LaunchDaemon to manage when the autopkg-conductor script is run. This was an idea I adopted from AutoPkgr, which also uses a LaunchDaemon to perform AutoPkg runs. The reason for using a LaunchDaemon is that the LaunchDaemon will be able to perform the scheduled AutoPkg run regardless of if the Mac is logged in at the loginwindow or not.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;
<plist version="1.0">
<dict>
<key>AbandonProcessGroup</key>
<true/>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
</dict>
<key>Label</key>
<string>com.github.autopkg-nightly-run</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/autopkg-conductor.sh</string>
</array>
<key>RunAtLoad</key>
<false/>
<key>StartCalendarInterval</key>
<array>
<dict>
<key>Hour</key>
<integer>2</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
</array>
<key>UserName</key>
<string>autopkg</string>
</dict>
</plist>

For the most part, this works fine to successfully trigger the autopkg-conductor script. However, a problem occurred once Signing Manager was added to the mix and I tried using it with the PkgSigner AutoPkg processor. The reason for this is that Signing Manager puts credentials into the login keychain and the user context was not correct to access those credentials. Instead, I was seeing errors like this when the LaunchDaemon triggered a run of autopkg-conductor:

productsign: error: Could not find appropriate signing identity for "6116291774E14F3E8A4CB6266F560C07".
productsign: error: Could not find appropriate signing identity for "6116291774E14F3E8A4CB6266F560C07".
productsign: error: Could not find appropriate signing identity for "6116291774E14F3E8A4CB6266F560C07".
productsign: error: Could not find appropriate signing identity for "6116291774E14F3E8A4CB6266F560C07".
productsign: error: Could not find appropriate signing identity for "6116291774E14F3E8A4CB6266F560C07".
productsign: error: Could not find appropriate signing identity for "6116291774E14F3E8A4CB6266F560C07".
productsign: error: Could not find appropriate signing identity for "6116291774E14F3E8A4CB6266F560C07".
productsign: error: Could not find appropriate signing identity for "6116291774E14F3E8A4CB6266F560C07".
productsign: error: Could not find appropriate signing identity for "6116291774E14F3E8A4CB6266F560C07".
productsign: error: Could not find appropriate signing identity for "6116291774E14F3E8A4CB6266F560C07".
productsign: error: Could not find appropriate signing identity for "6116291774E14F3E8A4CB6266F560C07".
JSSImporter can't find a package at '/Users/autopkg/Library/AutoPkg/Cache/local.jss.Postman-AutoUpdate/Postman_Labs_Postman_8.3.1.pkg'!
Failed.
JSSImporter can't find a package at '/Users/autopkg/Library/AutoPkg/Cache/local.jss.Atom-AutoUpdate/GitHub_Atom_1.56.0.pkg'!
Failed.
JSSImporter can't find a package at '/Users/autopkg/Library/AutoPkg/Cache/local.jss.SapMachineJDK11/SAP_SapMachine_11.0.11.pkg'!
Failed.
JSSImporter can't find a package at '/Users/autopkg/Library/AutoPkg/Cache/local.jss.FioriLaunchpad/Fiori_Launchpad_1.0.9.pkg'!
Failed.
JSSImporter can't find a package at '/Users/autopkg/Library/AutoPkg/Cache/local.jss.Code42/Code42_8.6.0.pkg'!
Failed.
JSSImporter can't find a package at '/Users/autopkg/Library/AutoPkg/Cache/local.jss.CitrixWorkspace/Citrix_Workspace_21.04.0.pkg'!
Failed.
JSSImporter can't find a package at '/Users/autopkg/Library/AutoPkg/Cache/local.jss.VMwareFusion/VMware_Fusion_12.1.1.pkg'!
Failed.
JSSImporter can't find a package at '/Users/autopkg/Library/AutoPkg/Cache/local.jss.Slack/Slack_4.16.2.pkg'!
Failed.
JSSImporter can't find a package at '/Users/autopkg/Library/AutoPkg/Cache/local.jss.SAPGUI/SAP_SAPGUI_7.70rev1.pkg'!
Failed.
JSSImporter can't find a package at '/Users/autopkg/Library/AutoPkg/Cache/local.jss.CiscoAnyConnect/Cisco_AnyConnectSecureMobilityClient_4.9.06037.pkg'!
Failed.
JSSImporter can't find a package at '/Users/autopkg/Library/AutoPkg/Cache/local.jss.MURAL/MURAL_1.0.10.pkg'!
Failed.

view raw
gistfile1.txt
hosted with ❤ by GitHub

After Twocanoes Support did some research, they recommended adding the following key to autopkg-conductor‘s LaunchDaemon:

Key: SessionCreate
Value: true

My LaunchDaemon now looked like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;
<plist version="1.0">
<dict>
<key>AbandonProcessGroup</key>
<true/>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
</dict>
<key>Label</key>
<string>com.github.autopkg-nightly-run</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/autopkg-conductor.sh</string>
</array>
<key>RunAtLoad</key>
<false/>
<key>StartCalendarInterval</key>
<array>
<dict>
<key>Hour</key>
<integer>2</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
</array>
<key>UserName</key>
<string>autopkg</string>
<key>SessionCreate</key>
<true/>
</dict>
</plist>

view raw
gistfile1.txt
hosted with ❤ by GitHub

With renewed hope, I used the LaunchDaemon to trigger a run. Same errors:

productsign: error: Could not find appropriate signing identity for "6116291774E14F3E8A4CB6266F560C07".
productsign: error: Could not find appropriate signing identity for "6116291774E14F3E8A4CB6266F560C07".
productsign: error: Could not find appropriate signing identity for "6116291774E14F3E8A4CB6266F560C07".
productsign: error: Could not find appropriate signing identity for "6116291774E14F3E8A4CB6266F560C07".
productsign: error: Could not find appropriate signing identity for "6116291774E14F3E8A4CB6266F560C07".
productsign: error: Could not find appropriate signing identity for "6116291774E14F3E8A4CB6266F560C07".
productsign: error: Could not find appropriate signing identity for "6116291774E14F3E8A4CB6266F560C07".
productsign: error: Could not find appropriate signing identity for "6116291774E14F3E8A4CB6266F560C07".
productsign: error: Could not find appropriate signing identity for "6116291774E14F3E8A4CB6266F560C07".
productsign: error: Could not find appropriate signing identity for "6116291774E14F3E8A4CB6266F560C07".
productsign: error: Could not find appropriate signing identity for "6116291774E14F3E8A4CB6266F560C07".
JSSImporter can't find a package at '/Users/autopkg/Library/AutoPkg/Cache/local.jss.Postman-AutoUpdate/Postman_Labs_Postman_8.3.1.pkg'!
Failed.
JSSImporter can't find a package at '/Users/autopkg/Library/AutoPkg/Cache/local.jss.Atom-AutoUpdate/GitHub_Atom_1.56.0.pkg'!
Failed.
JSSImporter can't find a package at '/Users/autopkg/Library/AutoPkg/Cache/local.jss.SapMachineJDK11/SAP_SapMachine_11.0.11.pkg'!
Failed.
JSSImporter can't find a package at '/Users/autopkg/Library/AutoPkg/Cache/local.jss.FioriLaunchpad/Fiori_Launchpad_1.0.9.pkg'!
Failed.
JSSImporter can't find a package at '/Users/autopkg/Library/AutoPkg/Cache/local.jss.Code42/Code42_8.6.0.pkg'!
Failed.
JSSImporter can't find a package at '/Users/autopkg/Library/AutoPkg/Cache/local.jss.CitrixWorkspace/Citrix_Workspace_21.04.0.pkg'!
Failed.
JSSImporter can't find a package at '/Users/autopkg/Library/AutoPkg/Cache/local.jss.VMwareFusion/VMware_Fusion_12.1.1.pkg'!
Failed.
JSSImporter can't find a package at '/Users/autopkg/Library/AutoPkg/Cache/local.jss.Slack/Slack_4.16.2.pkg'!
Failed.
JSSImporter can't find a package at '/Users/autopkg/Library/AutoPkg/Cache/local.jss.SAPGUI/SAP_SAPGUI_7.70rev1.pkg'!
Failed.
JSSImporter can't find a package at '/Users/autopkg/Library/AutoPkg/Cache/local.jss.CiscoAnyConnect/Cisco_AnyConnectSecureMobilityClient_4.9.06037.pkg'!
Failed.
JSSImporter can't find a package at '/Users/autopkg/Library/AutoPkg/Cache/local.jss.MURAL/MURAL_1.0.10.pkg'!
Failed.

view raw
gistfile1.txt
hosted with ❤ by GitHub

This was clearly a case of context. I was running as the right user, but something about the session context wasn’t quite right to allow me access to the login keychain and access the Signing Manager credential.

After some additional research, Twocanoes Support recommended the following:

  1. Have the user account be logged in at the login window (as opposed to running logged out.)
  2. Replace the LaunchDaemon with a LaunchAgent.

I then unloaded the LaunchDaemon and replaced it with a practically identical LaunchAgent, with the two following keys removed:

Key: UserName
Value: Account which I was running AutoPkg from (in this case, the user account was named autopkg)

Key: SessionCreate
Value: true

The LaunchAgent appeared as shown below:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;
<plist version="1.0">
<dict>
<key>AbandonProcessGroup</key>
<true/>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
</dict>
<key>Label</key>
<string>com.github.autopkg-nightly-run</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/autopkg-conductor.sh</string>
</array>
<key>RunAtLoad</key>
<false/>
<key>StartCalendarInterval</key>
<array>
<dict>
<key>Hour</key>
<integer>2</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
</array>
</dict>
</plist>

view raw
gistfile1.txt
hosted with ❤ by GitHub

To test it out, I changed the following value in the LaunchAgent:

Key: RunAtLoad

From: false
To: true

After making that change, I logged out, logged in and * bang * my AutoPkg runs started being able to access the signing identity provided by Signing Manager and sign my packages using the PkgSigner AutoPkg processor.

productsign: using timestamp authority for signature
productsign: signing product with identity "Developer ID Installer: Rich Trouton (XF95CST45F)" from keychain (null)
productsign: adding certificate "Developer ID Certification Authority"
productsign: adding certificate "Apple Root CA"
productsign: Wrote signed product archive to /Users/ec2-user/Library/AutoPkg/Cache/local.jss.Postman-AutoUpdate/Postman_Labs_Postman_8.3.1.pkg
productsign: using timestamp authority for signature
productsign: signing product with identity "Developer ID Installer: Rich Trouton (XF95CST45F)" from keychain (null)
productsign: adding certificate "Developer ID Certification Authority"
productsign: adding certificate "Apple Root CA"
productsign: Wrote signed product archive to /Users/autopkg/Library/AutoPkg/Cache/local.jss.Atom-AutoUpdate/GitHub_Atom_1.56.0.pkg
productsign: using timestamp authority for signature
productsign: signing product with identity "Developer ID Installer: Rich Trouton (XF95CST45F)" from keychain (null)
productsign: adding certificate "Developer ID Certification Authority"
productsign: adding certificate "Apple Root CA"
productsign: Wrote signed product archive to /Users/autopkg/Library/AutoPkg/Cache/local.jss.SapMachineJDK11/SAP_SapMachine_11.0.11.pkg
productsign: using timestamp authority for signature
productsign: signing product with identity "Developer ID Installer: Rich Trouton (XF95CST45F)" from keychain (null)
productsign: adding certificate "Developer ID Certification Authority"
productsign: adding certificate "Apple Root CA"
productsign: Wrote signed product archive to /Users/autopkg/Library/AutoPkg/Cache/local.jss.FioriLaunchpad/Fiori_Launchpad_1.0.9.pkg
productsign: using timestamp authority for signature
productsign: signing product with identity "Developer ID Installer: Rich Trouton (XF95CST45F)" from keychain (null)
productsign: adding certificate "Developer ID Certification Authority"
productsign: adding certificate "Apple Root CA"
productsign: Wrote signed product archive to /Users/autopkg/Library/AutoPkg/Cache/local.jss.Code42/Code42_8.6.0.pkg
productsign: using timestamp authority for signature
productsign: signing product with identity "Developer ID Installer: Rich Trouton (XF95CST45F)" from keychain (null)
productsign: adding certificate "Developer ID Certification Authority"
productsign: adding certificate "Apple Root CA"
productsign: Wrote signed product archive to /Users/autopkg/Library/AutoPkg/Cache/local.jss.CitrixWorkspace/Citrix_Workspace_21.04.0.pkg
productsign: using timestamp authority for signature
productsign: signing product with identity "Developer ID Installer: Rich Trouton (XF95CST45F)" from keychain (null)
productsign: adding certificate "Developer ID Certification Authority"
productsign: adding certificate "Apple Root CA"
productsign: Wrote signed product archive to /Users/autopkg/Library/AutoPkg/Cache/local.jss.VMwareFusion/VMware_Fusion_12.1.1.pkg
productsign: using timestamp authority for signature
productsign: signing product with identity "Developer ID Installer: Rich Trouton (XF95CST45F)" from keychain (null)
productsign: adding certificate "Developer ID Certification Authority"
productsign: adding certificate "Apple Root CA"
productsign: Wrote signed product archive to /Users/autopkg/Library/AutoPkg/Cache/local.jss.Slack/Slack_4.16.2.pkg
productsign: using timestamp authority for signature
productsign: signing product with identity "Developer ID Installer: Rich Trouton (XF95CST45F)" from keychain (null)
productsign: adding certificate "Developer ID Certification Authority"
productsign: adding certificate "Apple Root CA"
productsign: Wrote signed product archive to /Users/autopkg/Library/AutoPkg/Cache/local.jss.SAPGUI/SAP_SAPGUI_7.70rev1.pkg
productsign: using timestamp authority for signature
productsign: signing product with identity "Developer ID Installer: Rich Trouton (XF95CST45F)" from keychain (null)
productsign: adding certificate "Developer ID Certification Authority"
productsign: adding certificate "Apple Root CA"
productsign: Wrote signed product archive to /Users/autopkg/Library/AutoPkg/Cache/local.jss.CiscoAnyConnect/Cisco_AnyConnectSecureMobilityClient_4.9.06037.pkg
productsign: using timestamp authority for signature
productsign: signing product with identity "Developer ID Installer: Rich Trouton (XF95CST45F)" from keychain (null)
productsign: adding certificate "Developer ID Certification Authority"
productsign: adding certificate "Apple Root CA"
productsign: Wrote signed product archive to /Users/autopkg/Library/AutoPkg/Cache/local.jss.MURAL/MURAL_1.0.10.pkg

view raw
gistfile1.txt
hosted with ❤ by GitHub

Once my testing was completed, I changed the RunAtLoad key’s value back to false.

Transitioning from a LaunchDaemon to a LaunchAgent does mean I will need to leave the account I’m using for AutoPkg logged in at the login window, which has security implications to consider carefully.

In my particular case, AutoPkg is being run on a virtual machine where there is not a physical display attached and access to screen sharing is restricted, so for my particular case my opinion is that the trade-off is worth it.

With regards to Signing Manager’s operation, I also had some additional questions about it with regards to my AutoPkg runs which got answered during testing:

  • Question: Does the Signing Manager app need to be launched, or will productsign (used by the AutoPkg PkgSigner processor) be able to get the certificate without the app being launched?
  • Answer: After configuring Signing Manager, there’s no need to launch the app. As long as productsign is given the right signing identity (which Signing Manager refers to as a “fingerprint”), signing will work.
  • Question: If you need to reboot your Mac, do you need to do anything following the reboot in order to having signing work?
  • Answer: No

Using VLC to convert a video to play at twice normal speed

As part of preparing for an upcoming talk, I’m working on a presentation which includes a video. As part of adding the video to my Keynote slides I thought that increasing the playback speed would help with the pacing of the talk but I didn’t see a way in Keynote to have that happen as part of the video’s playback without having to manually run the video.

After some research, I found a straightforward way to use the open-source VLC video tool to double the playback speed of a video and save the changes. For more details, please see below the jump.

Pre-requisites:

To have VLC read a video file and save it as a new file which plays at twice the speed of the original file, please use the process below:

1. Open VLC
2. Under the File menu, select Convert / Stream…

Screen Shot 2021 05 09 at 4 39 01 PM

3. In the Convert & Stream window, click the Open media… button.

Screen Shot 2021 05 09 at 4 44 11 PM

4. Select the video where you want to increase the playback speed.
5. Select the video profile you want. (In this case, I’m selecting the default option: Video – H.264 + MP3 (MP4) )
6. Click the Customize… button

Screen Shot 2021 05 08 at 7 46 50 PM

7. Set the Scale option to 2.

Screen Shot 2021 05 09 at 4 47 21 PM

Screen Shot 2021 05 09 at 4 47 43 PM

Screen Shot 2021 05 09 at 4 48 28 PM

8. Click the Apply button.

Screen Shot 2021 05 09 at 4 48 29 PM

9. In the Choose Destination section of the Convert & Stream window, click the Save As File button.

Screen Shot 2021 05 08 at 7 46 51 PM

10. Choose the name and location to save the sped-up file.

Screen Shot 2021 05 08 at 7 47 12 PM

10. Click the Save button.

Screen Shot 2021 05 08 at 7 47 18 PM

VLC will then do the following:

1. List the file in its playlist.
2. Play back the original file at two times normal speed.
3. Record the original file’s playback to the new file. In this case, because I chose the Video – H.264 + MP3 (MP4) profile, the new file is saved as an .m4v file.

Screen Shot 2021 05 08 at 7 47 51 PM

Workaround for timeouts when deleting installer packages from Jamf Pro

I use AutoPkg and JSSImporter to keep my Jamf Pro server updated with the latest installers for the software used by my shop. However, this means that I usually have a large number of no-longer-needed installers stored in my Jamf Pro server’s distribution point and I need to periodically clear the obsolete packages out by deleting them. Recently, as part of removing 500+ unneeded packages from Jamf Pro using a script, I noticed the following behavior occurring:

1. Run an API command similar to the one below:

username@computername ~ % /usr/bin/curl -su username:'password' "https://jamf.pro.server.here/JSSResource/packages/id/1213" -X DELETE

2. Long pause (around 60 seconds)
3. Receive the following output:

<html>
<head><title>504 Gateway Time-out</title></head>
<body>
<center><h1>504 Gateway Time-out</h1></center>
</body>
</html>

4. Check the package and it has been deleted from Jamf Pro.

The 504 Gateway Time-out error indicated that either the load balancer in front my Jamf Pro server was timing out before the API command could report success or failure. I was seeing this behavior when running the API commands manually or as part of a script, so I decided to see if I saw the same behavior when deleting the package from the Jamf Pro admin console. When I checked, I did.

I sent in a support request to Jamf to ask about this and there is a PI open for this:

PI-009627: Having a large amount of packages uploaded to a distribution point can cause various timeouts

For others experiencing this issue, while Jamf addresses this product issue, the workaround for the timeout issue is (if possible) to increase the timeout value. In my case, increasing the load balancer timeout from 60 seconds to 120 addressed the timeout issue and allowed my API and GUI package deletions to complete successfully without timing out.

Note: This does not fix the issue of the package deletion taking a while. It just makes sure that the deletion command, either via the API or using the GUI in the admin console, doesn’t timeout before reporting success or failure.

Using the Jamf Pro API to mass-delete obsolete packages and scripts

If you’re using AutoPkg and tools like jamf-upload or JSSImporter to automate the uploading of packages and scripts to your Jamf Pro server, it may be necessary to periodically delete a large number of now-obsolete installer packages or scripts from your server. To help with this, I’ve written a couple of scripts to help automate the deletion process by using a list of Jamf IDs and the API to perform the following tasks:

  1. Delete the relevant installer packages or scripts.
  2. Generate a report of which packages or scripts were deleted.

For more details, please see below the jump.

Both scripts work with a text file of Jamf Pro IDs, and also include error checking to make sure that the text file’s entries contained only positive numbers.

To use these scripts, you will need four things:

  1. A text file containing the Jamf Pro package or script IDs you wish to delete.
  2. The address of the appropriate Jamf Pro server
  3. The username of an account on the Jamf Pro server which has the necessary privileges to delete computers and/or mobile devices.
  4. The password to that account.

The test file should contain only the relevant Jamf Pro IDs and appear similar to this:

924
1041
1079
1234
1244
1263
1269
1765
1213
1235
1253
1260
1273
1219
1334
1351
1298
1320
1394
1415
1430
1375
1464
1506
1444
1566
1585
1595
1606
1529
1542
1684
1625
1627
1654
1726
1742
1756
1705
1768
1772
1786
1527
1635
1677

Once you have the text file and the other prerequisites, the scripts can be run using the following commands:

To delete installer packages:

/path/to/delete_Jamf_Pro_Packages.sh /path/to/text_filename_here.txt

To delete scripts:

/path/to/delete_Jamf_Pro_Scripts.sh /path/to/text_filename_here.txt

For authentication, the scripts can accept manual input or values stored in a ~/Library/Preferences/com.github.jamfpro-info.plist file.

The plist file can be created by running the following commands and substituting your own values where appropriate:

To store the Jamf Pro URL in the plist file:

defaults write com.github.jamfpro-info jamfpro_url https://jamf.pro.server.goes.here:port_number_goes_here

To store the account username in the plist file:

defaults write com.github.jamfpro-info jamfpro_user account_username_goes_here

To store the account password in the plist file:

defaults write com.github.jamfpro-info jamfpro_password account_password_goes_here

It is also possible to simulate a run of the script, to make sure everything is working before running the actual deletion. To put the script into simulation mode, comment out the following line of the script.

delete_Jamf_Pro_Packages.sh

/usr/bin/curl -su ${jamfpro_user}:${jamfpro_password} "${jamfproIDURL}/$PackagesID" -X DELETE

delete_Jamf_Pro_Scripts.sh

/usr/bin/curl -su ${jamfpro_user}:${jamfpro_password} "${jamfproIDURL}/$ScriptsID" -X DELETE

To take it out of simulation mode and enable deletion, uncomment the line.

In simulation mode, you can test out if the script is reading the text file properly and the authentication method. For example, the following output should be seen in simulation mode if the text file is being read properly and manual input is being used.

username@computername ~ % /path/to/delete_Jamf_Pro_Scripts.sh ~/Desktop/home_scripts_report.txt

Please enter your Jamf Pro server URL : https://jamf.pro.server.goes.here:8443
Please enter your Jamf Pro user account : jpadmin
Please enter the password for the jpadmin account:
Deleting iscasperonline.sh - script ID 13.

Deleted iscasperonline.sh - script ID 13.

Deleting xcode_uninstall.sh - script ID 15.

Deleted xcode_uninstall.sh - script ID 15.

Report on deleted scripts available here: /var/folders/wz/mp27mjl97h505nvff787hh3c0000gn/T/tmp.IaiOiHgI.tsv
username@computername ~ %

The following output should be seen in production mode if the text file is being read properly and the needed values are being read from a ~/Library/Preferences/com.github.jamfpro-info.plist file.

username@computername ~ % /path/to/delete_Jamf_Pro_Scripts.sh ~/Desktop/home_scripts_report.txt

Deleting iscasperonline.sh - script ID 13.
<?xml version="1.0" encoding="UTF-8"?><script><id>13</id></script>
Deleted iscasperonline.sh - script ID 13.

Deleting xcode_uninstall.sh - script ID 15.
<?xml version="1.0" encoding="UTF-8"?><script><id>15</id></script>
Deleted xcode_uninstall.sh - script ID 15.

Report on deleted scripts available here: /var/folders/wz/mp27mjl97h505nvff787hh3c0000gn/T/tmp.vZgL8WOk.tsv
username@computername ~ %

Once the script has completed its run, it will generate a report on the deleted items in tab-separated format and display the .tsv file’s location.

Screen Shot 2021 04 16 at 2 43 29 PM

The scripts are available below, and at the following addresses on GitHub:

https://github.com/rtrouton/rtrouton_scripts/tree/master/rtrouton_scripts/Casper_Scripts/delete_Jamf_Pro_Packages

https://github.com/rtrouton/rtrouton_scripts/tree/master/rtrouton_scripts/Casper_Scripts/delete_Jamf_Pro_Scripts

 

delete_Jamf_Pro_Packages.sh:

#!/bin/bash
##########################################################################################
# Packages Delete Script for Jamf Pro
#
#
# Usage: Call script with the following four parameters
# – a text file of the Jamf Pro package IDs you wish to delete
#
# You will be prompted for:
# – The URL of the appropriate Jamf Pro server
# – Username for an account on the Jamf Pro server with sufficient API privileges
# – Password for the account on the Jamf Pro server
#
# The script will:
# – Delete the specified packages using their Jamf Pro package IDs
# – Generate a report of all successfully deleted packages in TSV format
#
# Example: ./delete_Jamf_Pro_Packages.sh jamf_pro_id_numbers.txt
#
##########################################################################################
filename="$1"
ERROR=0
report_file="$(mktemp).tsv"
if [[ -n $filename && -r $filename ]]; then
# If you choose to hardcode API information into the script, uncomment the lines below
# and set one or more of the following values:
#
# The username for an account on the Jamf Pro server with sufficient API privileges
# The password for the account
# The Jamf Pro URL
#jamfpro_url="" ## Set the Jamf Pro URL here if you want it hardcoded.
#jamfpro_user="" ## Set the username here if you want it hardcoded.
#jamfpro_password="" ## Set the password here if you want it hardcoded.
# If you do not want to hardcode API information into the script, you can also store
# these values in a ~/Library/Preferences/com.github.jamfpro-info.plist file.
#
# To create the file and set the values, run the following commands and substitute
# your own values where appropriate:
#
# To store the Jamf Pro URL in the plist file:
# defaults write com.github.jamfpro-info jamfpro_url https://jamf.pro.server.goes.here:port_number_goes_here
#
# To store the account username in the plist file:
# defaults write com.github.jamfpro-info jamfpro_user account_username_goes_here
#
# To store the account password in the plist file:
# defaults write com.github.jamfpro-info jamfpro_password account_password_goes_here
#
# If the com.github.jamfpro-info.plist file is available, the script will read in the
# relevant information from the plist file.
if [[ -f "$HOME/Library/Preferences/com.github.jamfpro-info.plist" ]]; then
if [[ -z "$jamfpro_url" ]]; then
jamfpro_url=$(defaults read $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_url)
fi
if [[ -z "$jamfpro_user" ]]; then
jamfpro_user=$(defaults read $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_user)
fi
if [[ -z "$jamfpro_password" ]]; then
jamfpro_password=$(defaults read $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_password)
fi
fi
# If the Jamf Pro URL, the account username or the account password aren't available
# otherwise, you will be prompted to enter the requested URL or account credentials.
if [[ -z "$jamfpro_url" ]]; then
read -p "Please enter your Jamf Pro server URL : " jamfpro_url
fi
if [[ -z "$jamfpro_user" ]]; then
read -p "Please enter your Jamf Pro user account : " jamfpro_user
fi
if [[ -z "$jamfpro_password" ]]; then
read -p "Please enter the password for the $jamfpro_user account: " -s jamfpro_password
fi
echo ""
# Remove the trailing slash from the Jamf Pro URL if needed.
jamfpro_url=${jamfpro_url%%/}
# Set up the Jamf Pro Computer ID URL
jamfproIDURL="${jamfpro_url}/JSSResource/packages/id"
while read -r PackagesID
do
# Verify that the input is a number. All Jamf Pro
# IDs are positive numbers, so any other input will
# not be a valid Jamf Pro ID.
if [[ "$PackagesID" =~ ^[0-9]+$ ]]; then
if [[ ! -f "$report_file" ]]; then
/usr/bin/touch "$report_file"
printf "Deleted Package ID Number\tDeleted Package Name\n" > "$report_file"
fi
# Get package display name
PackagesName=$(/usr/bin/curl -su "${jamfpro_user}:${jamfpro_password}" -H "Accept: application/xml" "${jamfpro_url}/JSSResource/packages/id/$PackagesID" | xmllint –xpath '//package/name/text()'2>/dev/null)
# Remove comment from line below to preview
# the results of the deletion command.
echo -e "Deleting $PackagesName – package ID $PackagesID."
# Remove comment from line below to actually run
# the deletion command.
/usr/bin/curl -su ${jamfpro_user}:${jamfpro_password} "${jamfproIDURL}/$PackagesID" -X DELETE
if [[ $? -eq 0 ]]; then
printf "$PackagesID\t %s\n" "$PackagesName" >> "$report_file"
echo -e "\nDeleted $PackagesName – package ID $PackagesID.\n"
else
echo -e "\nERROR! Failed to delete $PackagesName – package ID $PackagesID.\n"
fi
else
echo "All Jamf Pro IDs are expressed as numbers. The following input is not a number: $PackagesID"
fi
done < "$filename"
else
echo "Input file does not exist or is not readable"
ERROR=1
fi
if [[ -f "$report_file" ]]; then
echo "Report on deleted installer packages available here: $report_file"
fi
exit "$ERROR"

delete_Jamf_Pro_Scripts.sh:

#!/bin/bash
##########################################################################################
# Scripts Delete Script for Jamf Pro
#
#
# Usage: Call script with the following four parameters
# – a text file of the Jamf Pro script IDs you wish to delete
#
# You will be prompted for:
# – The URL of the appropriate Jamf Pro server
# – Username for an account on the Jamf Pro server with sufficient API privileges
# – Password for the account on the Jamf Pro server
#
# The script will:
# – Delete the specified scripts using their Jamf Pro script IDs
# – Generate a report of all successfully deleted scripts in TSV format
#
# Example: ./delete_Jamf_Pro_Scripts.sh jamf_pro_id_numbers.txt
#
##########################################################################################
filename="$1"
ERROR=0
report_file="$(mktemp).tsv"
if [[ -n $filename && -r $filename ]]; then
# If you choose to hardcode API information into the script, uncomment the lines below
# and set one or more of the following values:
#
# The username for an account on the Jamf Pro server with sufficient API privileges
# The password for the account
# The Jamf Pro URL
#jamfpro_url="" ## Set the Jamf Pro URL here if you want it hardcoded.
#jamfpro_user="" ## Set the username here if you want it hardcoded.
#jamfpro_password="" ## Set the password here if you want it hardcoded.
# If you do not want to hardcode API information into the script, you can also store
# these values in a ~/Library/Preferences/com.github.jamfpro-info.plist file.
#
# To create the file and set the values, run the following commands and substitute
# your own values where appropriate:
#
# To store the Jamf Pro URL in the plist file:
# defaults write com.github.jamfpro-info jamfpro_url https://jamf.pro.server.goes.here:port_number_goes_here
#
# To store the account username in the plist file:
# defaults write com.github.jamfpro-info jamfpro_user account_username_goes_here
#
# To store the account password in the plist file:
# defaults write com.github.jamfpro-info jamfpro_password account_password_goes_here
#
# If the com.github.jamfpro-info.plist file is available, the script will read in the
# relevant information from the plist file.
if [[ -f "$HOME/Library/Preferences/com.github.jamfpro-info.plist" ]]; then
if [[ -z "$jamfpro_url" ]]; then
jamfpro_url=$(defaults read $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_url)
fi
if [[ -z "$jamfpro_user" ]]; then
jamfpro_user=$(defaults read $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_user)
fi
if [[ -z "$jamfpro_password" ]]; then
jamfpro_password=$(defaults read $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_password)
fi
fi
# If the Jamf Pro URL, the account username or the account password aren't available
# otherwise, you will be prompted to enter the requested URL or account credentials.
if [[ -z "$jamfpro_url" ]]; then
read -p "Please enter your Jamf Pro server URL : " jamfpro_url
fi
if [[ -z "$jamfpro_user" ]]; then
read -p "Please enter your Jamf Pro user account : " jamfpro_user
fi
if [[ -z "$jamfpro_password" ]]; then
read -p "Please enter the password for the $jamfpro_user account: " -s jamfpro_password
fi
echo ""
# Remove the trailing slash from the Jamf Pro URL if needed.
jamfpro_url=${jamfpro_url%%/}
# Set up the Jamf Pro Computer ID URL
jamfproIDURL="${jamfpro_url}/JSSResource/scripts/id"
while read -r ScriptsID
do
# Verify that the input is a number. All Jamf Pro
# IDs are positive numbers, so any other input will
# not be a valid Jamf Pro ID.
if [[ "$ScriptsID" =~ ^[0-9]+$ ]]; then
if [[ ! -f "$report_file" ]]; then
/usr/bin/touch "$report_file"
printf "Deleted Script ID Number\tDeleted Script Name\n" > "$report_file"
fi
# Get script display name
ScriptsName=$(/usr/bin/curl -su "${jamfpro_user}:${jamfpro_password}" -H "Accept: application/xml" "${jamfpro_url}/JSSResource/scripts/id/$ScriptsID" | xmllint –xpath '//script/name/text()'2>/dev/null)
# Remove comment from line below to preview
# the results of the deletion command.
echo -e "Deleting $ScriptsName – script ID $ScriptsID."
# Remove comment from line below to actually run
# the deletion command.
#/usr/bin/curl -su ${jamfpro_user}:${jamfpro_password} "${jamfproIDURL}/$ScriptsID" -X DELETE
if [[ $? -eq 0 ]]; then
printf "$ScriptsID\t %s\n" "$ScriptsName" >> "$report_file"
echo -e "\nDeleted $ScriptsName – script ID $ScriptsID.\n"
else
echo -e "\nERROR! Failed to delete $ScriptsName – script ID $ScriptsID.\n"
fi
else
echo "All Jamf Pro IDs are expressed as numbers. The following input is not a number: $ScriptsID"
fi
done < "$filename"
else
echo "Input file does not exist or is not readable"
ERROR=1
fi
if [[ -f "$report_file" ]]; then
echo "Report on deleted scripts available here: $report_file"
fi
exit "$ERROR"

Using Markdown comments to add search keywords to Self Service descriptions

For those using Jamf Pro’s Self Service, one of the handier features can be the Search function built into the app. This search is able to examine Self Service policies and use the information in the policy and Self Service description to populate its search results. For the most part, just the displayed information in the policy should allow Self Service’s search to display relevant policies.

However, you may have a need to force the search process to include policies that would otherwise fall outside of the search parameters. For those who need this ability, thanks to Self Service’s support of Markdown it’s possible to invisibly add search keywords to a Self Service policy description. For more details, please see below the jump.

Markdown is a markup language which uses specific syntax to produce formatted text. In the image below, the left side shows what appears in the Markdown editor and the right side shows how the formatted document would appear.

Screen Shot 2021 04 02 at 4 30 53 PM

Among the syntax options is the ability to add comments using the HTML comment tag. These show up in the Markdown editor, but they don’t show up in the displayed document.

Screen Shot 2021 04 02 at 4 31 54 PM

 

Since Self Service descriptions only display the formatted text, but the search function can search everything in the description, we can use the comment syntax to add search keywords which Self Service’s search can use but which are invisible to someone looking at the Self Service description via the Self Service app.

To add search keywords, add the syntax tags for Markdown commenting to your Self Service description and add the search keywords you want to use within the tags. An example is shown below:

<!-- Search Keywords Go Here -->

For example, if you wanted to add search keywords like Java and JDK to a Java install policy, you could add the following to your Self Service description:

<!--Java, JDK-->

 

Screen Shot 2021 04 02 at 4 54 28 PM

Screen Shot 2021 04 02 at 9 07 36 AM

 

Even if the displayed text in the policy or the Self Service description never mentions either Java or JDK, Self Service’s search will include this policy when you search for those terms.

Screen Shot 2021 04 02 at 9 07 48 AM

 

Here’s another example, using Backup as a search keyword:

<!--Backup-->

 

Screen Shot 2021 04 02 at 12 04 38 PM

 

Screen Shot 2021 04 02 at 9 08 17 AM

 

Screen Shot 2021 04 02 at 9 08 03 AM

 

Note: Self Service’s search is case-insensitive so searching for backup or BACKUP when you have Backup as the search keyword should also display the relevant policy or policies.

Connecting to AWS EC2 instances via Session Manager

When folks have needed command line access to instances running in Amazon Web Service’s EC2 service, SSH has been the usual method used. However, in addition to using SSH to connect to EC2 instances in AWS, it is also possible to connect remotely via Session Manager, one of the services provided by AWS’s Systems Manager tool.

Session Manager uses the Systems Manager agent to provide secure remote access to the Mac’s command line interface without needing to change security groups and allow SSH access to the instance. In fact, Session Manager allows remote access to EC2 instances which have security groups configured to allow no inbound access at all. For more details, please see below the jump.

To access EC2 instances via Session Manager, please use the procedure shown below:

1. Verify that the Systems Manager agent is installed and configured properly.
2. Select the desired EC2 instance.

Screen Shot 2021 04 01 at 3 04 54 PM

3. Click the Connect button.

Screen Shot 2021 04 01 at 3 04 55 PM

4. In the Connect to instance window, select the Session Manager tab then click the Connect button.

Screen Shot 2021 04 01 at 3 03 22 PM

5. A new browser window will open up.

Screen Shot 2021 04 01 at 3 03 50 PM

Note: The active user at this point is the Systems Manager agent’s user account, which is ssm-user.

Screen Shot 2021-04-01 at 3.03.59 PM

To get access to the ec2-user account (the default account used on most EC2 instances running macOS or Linux), you’ll need to switch accounts. To do this, run the command shown below to change to the ec2-user account:

sudo su ec2-user

Screen Shot 2021 04 01 at 3 04 19 PM

6. You should now be logged in as the ec2-user account.

Screen Shot 2021 04 01 at 3 04 27 PM

To close the remote session, use the procedure shown below.

1. Verify that all work has been completed.
2. Click the Terminate button.

Screen Shot 2021 04 01 at 3 04 28 PM

3. When prompted for confirmation, click the Terminate button.

Screen Shot 2021 04 01 at 3 04 41 PM

Jamf Pro server installer for macOS being retired

As part of the release notes for Jamf Pro 10.28, there is this note in the Deprecations and Removals section:

Support ending for the Jamf Pro Server Installer for macOS — Support for using the Jamf Pro Installer for macOS will be discontinued in a future release. Mac computers with Apple silicon are not supported by the Jamf Pro Installer for macOS. If you want to migrate your Jamf Pro server from macOS to Jamf Cloud, contact Jamf Support. If you want to keep your server on premise, you can migrate your Jamf Pro server from macOS to one of the following servers: Red Hat Enterprise Linux, Ubuntu, or Windows. For more information, see the Migrating to Another Server Knowledge Base article.

Screen Shot 2021 03 17 at 1 55 31 PM

For those folks who are running on-premise Jamf Pro servers on Macs, it looks like it’s time to contact Jamf Support and plan a migration if you haven’t already. As of March 17th, 2021, Jamf’s published support for running Jamf Pro includes the following OS, database and Java versions:

Recommended Configuration:
Operating Systems:
Windows Server 2019
Ubuntu Server 20.04 LTS
Red Hat Enterprise Linux 7.x
macOS 10.15.5
Database software versions:
MySQL 8.0 – InnoDB
Amazon Aurora (MySQL 5.7 compatible)
MySQL 5.7.8 or later – InnoDB
Java version:
OpenJDK 11
Minimum Supported:
Operating Systems:
Windows Server 2016
Windows Server 2012 R2
Ubuntu Server 18.04 LTS
macOS 10.14.5
Database software versions:
MySQL 5.7.8 – InnoDB
MySQL 5.7.8 on Amazon RDS – InnoDB
Java version:
Oracle Java 11

view raw
gistfile1.txt
hosted with ❤ by GitHub

Using Twocanoes’ Signing Manager to sign AutoPkg-built installer packages

As part of many application or package building workflows, there is a requirement to sign the end result to guarantee that the app or package has not been tampered with. With the advent of Apple’s notarization process, this has become even more important because an app or installer package must be signed before it can be notarized.

However, in order to sign apps or packages, you must have the signing certificate available. This has often meant putting copies of Apple signing certificates, complete with the certificate’s private key, onto the Mac or Macs used to build the application and/or installer package. This has security concerns because if the signing certificate’s private key is compromised, you must now revoke the existing certificate, get a new one from Apple and re-sign everything that used that now-revoked signing certificate.

To assist with the security concerns, Twocanoes Software has developed Signing Manager. This tool provides a way to centralize hosting of signing certificates and make their signing capabilities securely available to Macs which need them. In my own case, I’m investigating Signing Manager in the context of signing AutoPkg-built installer packages. For more details, please see below the jump.

Signing Manager consists of a server which hosts certificates and a client which logs into the server using an API key. Let’s take a look at how you set up a certificate to be shared. In this example, I’ll be using Twocanoes’ own signing server and a sample Package Signing certificate.

Importing the signing certificate into the Signing Manager server

Pre-requisites:

  • Signing certificate’s public and private keys stored in a .p12 file
  • The password to unlock the .p12 file

Screen Shot 2021 03 05 at 1 17 20 PM

1. Log into the Signing Manager server.
2. Make note of the Signing Server Domain URL and API key. You’ll need them later with the Signing Manager client software.

Screen Shot 2021 03 05 at 11 50 12 AM

3. Click on the Identities link.

Screen Shot 2021 03 05 at 11 50 13 AM

3. Click on the Import Identity button.

Screen Shot 2021 03 05 at 11 57 52 AM

4. Select your signing certificate’s .p12 file and enter the password for it into the password blank.

Screen Shot 2021 03 05 at 11 58 20 AM

5. Click the Import button.

Screen Shot 2021 03 05 at 11 58 21 AM

Your certificate should now be imported.

Screen Shot 2021 03 05 at 11 58 49 AM

Enabling the signing certificate for access on a client Mac

1. Install the Signing Manager client software.
2. In the Signing Manager, set the following:

  • In the Signing Server blank, enter the Signing Server Domain URL.
  • In the API Key blank, enter the API key

Screen Shot 2021 03 05 at 8 51 00 AM

Once entered and verified, click the OK button.

3. You should now see the certificate appear in the Signing Manager app window, along with a notification that a smartcard has been inserted.

Screen Shot 2021 03 05 at 4 33 45 PM

Signing Manager sets up a virtual smart card with the signing certificate’s information stored inside. Your Mac should be able to work with the certificate information on this virtual smart card like it can with certificates stored in your Mac’s own keychain files.

The Signing Manager client software also includes several useful features:

1. Copying the Common Name from the certificate:

Clicking the Copy CN button will add the Common Name of the selected certificate to the clipboard.

2. Copying the Fingerprint, or SHA1 hash of the certificate to the clipboard.

Clicking the Copy Fingerprint button will add the SHA1 hash of the selected certificate to the clipboard.

Note: The Fingerprint name is what will be used as the certificate name when signing.

3. Copying an example codesign command with certificate name to clipboard.

Clicking the Copy codesign command button will copy the example codesign command to the clipboard.

4. Copying an example productsign command with certificate name to clipboard.

Clicking the Copy productsign command button will copy the example productsign command to the clipboard.

5. Displaying certificate information

Clicking the Show Certificate button will display information about the selected certificate.

Signing an AutoPkg-built installer package

In most ways, using a Signing Manager-hosted certificate for signing is identical to using a certificate stored in a Mac’s keychain. The main difference will be the name of the certificate, as Signing Manager will use the Fingerprint identifier for the certificate. Specifically, you can use Apple’s codesign and productsign tools with a Signing Manager-hosted certificate just like you would a certificate stored locally on your Mac inside a keychain.

This similarity allows it to be easily integrated into an AutoPkg workflow which uses the PkgSigner processor. This AutoPkg processor uses productsign to do the following:

  1. Identify an unsigned package built by an AutoPkg recipe.
  2. Rename the unsigned package from /path/to/package_name_here.pkg to /path/to/package_name_here-unsigned.pkg.
  3. Sign the package.
  4. Save the signed package as /path/to/package_name_here.pkg, so that the name matches the original package. Renaming the signed package to match the original unsigned package’s name allows AutoPkg to continue to work with the now-signed installer package.

The main difference should be that a keychain-stored certificate would be named something like this:

Developer ID Installer: Rich Trouton (XF95CST45F)

The Signing Manager-hosted certificate would instead be identified by the Fingerprint value, which may look something like this:

4A72196F535A51A98FF2480132F024222B65060C

With that in mind, let’s take a look at how a Signing Manager-hosted certificate could be integrated into an AutoPkg workflow, using a process I’ve written about previously.

For this example, a .pkg recipe for Postman which includes the PkgSigner processor is being used:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;
<plist version="1.0">
<dict>
<key>Description</key>
<string>Downloads the latest version of Postman and creates a signed installer package.</string>
<key>Identifier</key>
<string>net.trouton.pkg.postman</string>
<key>Input</key>
<dict>
<key>NAME</key>
<string>Postman</string>
<key>VENDOR</key>
<string>Postman</string>
<key>SOFTWARETITLE</key>
<string>Labs</string>
<key>SOFTWARETYPE</key>
<string>Postman</string>
<key>SIGNINGCERTIFICATE</key>
<string>Put_Signing_Certificate_into_AutoPkg_recipe_override</string>
</dict>
<key>MinimumVersion</key>
<string>1.0.0</string>
<key>ParentRecipe</key>
<string>com.github.dataJAR-recipes.download.postman</string>
<key>Process</key>
<array>
<dict>
<key>Arguments</key>
<dict>
<key>predicate</key>
<string>SIGNINGCERTIFICATE == "Put_Signing_Certificate_into_AutoPkg_recipe_override"</string>
</dict>
<key>Processor</key>
<string>StopProcessingIf</string>
</dict>
<dict>
<key>Processor</key>
<string>PkgRootCreator</string>
<key>Arguments</key>
<dict>
<key>pkgroot</key>
<string>%RECIPE_CACHE_DIR%/%SOFTWARETYPE%</string>
<key>pkgdirs</key>
<dict>
<key>Applications</key>
<string>0755</string>
</dict>
</dict>
</dict>
<dict>
<key>Processor</key>
<string>Unarchiver</string>
<key>Arguments</key>
<dict>
<key>archive_path</key>
<string>%pathname%</string>
<key>destination_path</key>
<string>%pkgroot%/Applications</string>
<key>purge_destination</key>
<true />
</dict>
</dict>
<dict>
<key>Processor</key>
<string>Versioner</string>
<key>Arguments</key>
<dict>
<key>input_plist_path</key>
<string>%pkgroot%/Applications/%SOFTWARETYPE%.app/Contents/Info.plist</string>
<key>plist_version_key</key>
<string>CFBundleShortVersionString</string>
</dict>
</dict>
<dict>
<key>Processor</key>
<string>PkgCreator</string>
<key>Arguments</key>
<dict>
<key>pkgname</key>
<string>%VENDOR%_%SOFTWARETITLE%_%SOFTWARETYPE%_%version%</string>
<key>pkg_request</key>
<dict>
<key>version</key>
<string>%version%</string>
<key>id</key>
<string>com.postmanlabs.mac</string>
<key>options</key>
<string>purge_ds_store</string>
<key>chown</key>
<array>
<dict>
<key>path</key>
<string>Applications</string>
<key>user</key>
<string>root</string>
<key>group</key>
<string>wheel</string>
</dict>
</array>
</dict>
</dict>
</dict>
<dict>
<key>Processor</key>
<string>PkgSigner</string>
<key>Arguments</key>
<dict>
<key>pkg_path</key>
<string>%RECIPE_CACHE_DIR%/%VENDOR%_%SOFTWARETITLE%_%SOFTWARETYPE%_%version%.pkg</string>
<key>signing_cert</key>
<string>%SIGNINGCERTIFICATE%</string>
</dict>
</dict>
<dict>
<key>Arguments</key>
<dict>
<key>path_list</key>
<array>
<string>%RECIPE_CACHE_DIR%/%SOFTWARETYPE%</string>
</array>
</dict>
<key>Processor</key>
<string>PathDeleter</string>
</dict>
</array>
</dict>
</plist>

view raw
Postman.pkg.recipe
hosted with ❤ by GitHub

An override of the recipe would be needed, in order to include the Fingerprint value from Signing Manager into the SIGNINGCERTIFICATE key’s value in the recipe override.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;
<plist version="1.0">
<dict>
<key>Identifier</key>
<string>local.pkg.Postman</string>
<key>Input</key>
<dict>
<key>DOWNLOAD_URL</key>
<string>https://dl.pstmn.io/download/latest/osx</string>
<key>NAME</key>
<string>Postman</string>
<key>SIGNINGCERTIFICATE</key>
<string>4A72196F535A51A98FF2480132F024222B65060C</string>
<key>SOFTWARETITLE</key>
<string>Labs</string>
<key>SOFTWARETYPE</key>
<string>Postman</string>
<key>VENDOR</key>
<string>Postman</string>
</dict>
<key>ParentRecipe</key>
<string>net.trouton.pkg.postman</string>
<key>ParentRecipeTrustInfo</key>
<dict>
<key>non_core_processors</key>
<dict>
<key>PkgSigner</key>
<dict>
<key>git_hash</key>
<string>3c6c09b14d63dfa4b40f737ee079bf1d3842aef5</string>
<key>path</key>
<string>~/Library/AutoPkg/RecipeRepos/com.github.rtrouton.signing_manager_autopkg_recipes/Postman/PkgSigner.py</string>
<key>sha256_hash</key>
<string>464d1756f190a0161a6a00358cd42042bcdb5213802624d9ac58d3e723877a56</string>
</dict>
</dict>
<key>parent_recipes</key>
<dict>
<key>com.github.dataJAR-recipes.download.postman</key>
<dict>
<key>git_hash</key>
<string>ec4d1a926dbec4bb4e1d44dbe425e2c771d18f37</string>
<key>path</key>
<string>~/Library/AutoPkg/RecipeRepos/com.github.autopkg.dataJAR-recipes/Postman/Postman.download.recipe</string>
<key>sha256_hash</key>
<string>6ce36a3ad1b99cd4804cd9acfbd16e1763d757c8d6d1aae44c10f4a992c7ba6b</string>
</dict>
<key>net.trouton.pkg.postman</key>
<dict>
<key>git_hash</key>
<string>3c6c09b14d63dfa4b40f737ee079bf1d3842aef5</string>
<key>path</key>
<string>~/Library/AutoPkg/RecipeRepos/com.github.rtrouton.signing_manager_autopkg_recipes/Postman/Postman.pkg.recipe</string>
<key>sha256_hash</key>
<string>3aa55bc6b5af7417409b6a40e9ccdeb83e8c2e59ce4fe88bfbe35c044b235632</string>
</dict>
</dict>
</dict>
</dict>
</plist>

view raw
Postman.pkg.recipe
hosted with ❤ by GitHub

When the recipe override is run in verbose mode, the Fingerprint value shows up as the signing certificate used to successfully sign the certificate using the PkgSigner processor:

Screen Shot 2021 03 05 at 6 13 20 PM

Processing local.pkg.Postman…
{'AUTOPKG_VERSION': '2.3.1',
'DOWNLOAD_URL': 'https://dl.pstmn.io/download/latest/osx&#39;,
'NAME': 'Postman',
'PARENT_RECIPES': ['/Users/username/Library/AutoPkg/RecipeRepos/com.github.rtrouton.signing_manager_autopkg_recipes/Postman/Postman.pkg.recipe',
'/Users/username/Library/AutoPkg/RecipeRepos/com.github.autopkg.dataJAR-recipes/Postman/Postman.download.recipe'],
'RECIPE_CACHE_DIR': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman',
'RECIPE_DIR': '/Users/username/Library/AutoPkg/RecipeOverrides',
'RECIPE_OVERRIDE_DIRS': ['~/Library/AutoPkg/RecipeOverrides'],
'RECIPE_PATH': '/Users/username/Library/AutoPkg/RecipeOverrides/Postman.pkg.recipe',
'RECIPE_REPOS': {'/Users/username/Library/AutoPkg/RecipeRepos/com.github.autopkg.dataJAR-recipes': {'URL': 'https://github.com/autopkg/dataJAR-recipes&#39;},
'/Users/username/Library/AutoPkg/RecipeRepos/com.github.autopkg.hjuutilainen-recipes': {'URL': 'https://github.com/autopkg/hjuutilainen-recipes&#39;},
'/Users/username/Library/AutoPkg/RecipeRepos/com.github.rtrouton.signing_manager_autopkg_recipes': {'URL': 'https://github.com/rtrouton/signing_manager_autopkg_recipes&#39;}},
'RECIPE_SEARCH_DIRS': ['.',
'~/Library/AutoPkg/Recipes',
'/Library/AutoPkg/Recipes',
'/Users/username/Library/AutoPkg/RecipeRepos/com.github.rtrouton.signing_manager_autopkg_recipes',
'/Users/username/Library/AutoPkg/RecipeRepos/com.github.autopkg.hjuutilainen-recipes',
'/Users/username/Library/AutoPkg/RecipeRepos/com.github.autopkg.dataJAR-recipes',
'/Users/username/Library/AutoPkg/RecipeOverrides',
'/Users/username/Library/AutoPkg/RecipeRepos/com.github.rtrouton.signing_manager_autopkg_recipes/Postman',
'/Users/username/Library/AutoPkg/RecipeRepos/com.github.rtrouton.signing_manager_autopkg_recipes/Postman',
'/Users/username/Library/AutoPkg/RecipeRepos/com.github.rtrouton.signing_manager_autopkg_recipes/Postman'],
'SIGNINGCERTIFICATE': '4A72196F535A51A98FF2480132F024222B65060C',
'SOFTWARETITLE': 'Labs',
'SOFTWARETYPE': 'Postman',
'VENDOR': 'Postman',
'verbose': 4}
URLDownloader
{'Input': {'filename': 'Postman.zip',
'url': 'https://dl.pstmn.io/download/latest/osx&#39;}}
URLDownloader: No value supplied for prefetch_filename, setting default value of: False
URLDownloader: No value supplied for CHECK_FILESIZE_ONLY, setting default value of: False
URLDownloader: Curl command: ['/usr/bin/curl', '–silent', '–show-error', '–no-buffer', '–dump-header', '-', '–speed-time', '30', '–location', '–url', 'https://dl.pstmn.io/download/latest/osx&#39;, '–fail', '–output', '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/downloads/tmpgnvvfjc8']
URLDownloader: Downloaded /Users/username/Library/AutoPkg/Cache/local.pkg.Postman/downloads/Postman.zip
{'Output': {'download_changed': True,
'pathname': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/downloads/Postman.zip',
'url_downloader_summary_result': {'data': {'download_path': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/downloads/Postman.zip'},
'summary_text': 'The following '
'new items were '
'downloaded:'}}}
EndOfCheckPhase
{'Input': {}}
{'Output': {}}
Unarchiver
{'Input': {'archive_path': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/downloads/Postman.zip',
'destination_path': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman',
'purge_destination': True}}
Unarchiver: No value supplied for USE_PYTHON_NATIVE_EXTRACTOR, setting default value of: False
Unarchiver: Guessed archive format 'zip' from filename Postman.zip
Unarchiver: Unarchived /Users/username/Library/AutoPkg/Cache/local.pkg.Postman/downloads/Postman.zip to /Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman
{'Output': {}}
CodeSignatureVerifier
{'Input': {'input_path': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman/Postman.app',
'requirement': 'identifier "com.postmanlabs.mac" and anchor apple '
'generic and certificate '
'1[field.1.2.840.113635.100.6.2.6] /* exists */ and '
'certificate leaf[field.1.2.840.113635.100.6.1.13] '
'/* exists */ and certificate leaf[subject.OU] = '
'H7H8Q7M5CK'}}
CodeSignatureVerifier: Verifying code signature…
CodeSignatureVerifier: Deep verification enabled…
CodeSignatureVerifier: Strict verification not defined. Using codesign defaults…
CodeSignatureVerifier: /Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman/Postman.app: valid on disk
CodeSignatureVerifier: /Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman/Postman.app: satisfies its Designated Requirement
CodeSignatureVerifier: /Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman/Postman.app: explicit requirement satisfied
CodeSignatureVerifier: Signature is valid
{'Output': {}}
Versioner
{'Input': {'input_plist_path': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman/Postman.app/Contents/Info.plist',
'plist_version_key': 'CFBundleShortVersionString'}}
Versioner: No value supplied for skip_single_root_dir, setting default value of: False
Versioner: Found version 8.0.6 in file /Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman/Postman.app/Contents/Info.plist
{'Output': {'version': '8.0.6'}}
StopProcessingIf
{'Input': {'predicate': 'SIGNINGCERTIFICATE == '
'"Put_Signing_Certificate_into_AutoPkg_recipe_override"'}}
StopProcessingIf: (SIGNINGCERTIFICATE == "Put_Signing_Certificate_into_AutoPkg_recipe_override") is False
{'Output': {}}
PkgRootCreator
{'Input': {'pkgdirs': {'Applications': '0755'},
'pkgroot': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman'}}
PkgRootCreator: Created /Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman
PkgRootCreator: Creating Applications
PkgRootCreator: Created /Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman/Applications
{'Output': {}}
Unarchiver
{'Input': {'USE_PYTHON_NATIVE_EXTRACTOR': False,
'archive_path': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/downloads/Postman.zip',
'destination_path': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman/Applications',
'purge_destination': True}}
Unarchiver: Guessed archive format 'zip' from filename Postman.zip
Unarchiver: Unarchived /Users/username/Library/AutoPkg/Cache/local.pkg.Postman/downloads/Postman.zip to /Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman/Applications
{'Output': {}}
Versioner
{'Input': {'input_plist_path': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman/Applications/Postman.app/Contents/Info.plist',
'plist_version_key': 'CFBundleShortVersionString',
'skip_single_root_dir': False}}
Versioner: Found version 8.0.6 in file /Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman/Applications/Postman.app/Contents/Info.plist
{'Output': {'version': '8.0.6'}}
PkgCreator
{'Input': {'pkg_request': {'chown': [{'group': 'wheel',
'path': 'Applications',
'user': 'root'}],
'id': 'com.postmanlabs.mac',
'options': 'purge_ds_store',
'version': '8.0.6'}}}
PkgCreator: Connecting
PkgCreator: Sending packaging request
PkgCreator: Disconnecting
PkgCreator: Failed to close socket: [Errno 9] Bad file descriptor
{'Output': {'new_package_request': True,
'pkg_creator_summary_result': {'data': {'identifier': 'com.postmanlabs.mac',
'pkg_path': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman_Labs_Postman_8.0.6.pkg',
'version': '8.0.6'},
'report_fields': ['identifier',
'version',
'pkg_path'],
'summary_text': 'The following '
'packages were '
'built:'},
'pkg_path': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman_Labs_Postman_8.0.6.pkg'}}
PkgSigner
{'Input': {'pkg_path': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman_Labs_Postman_8.0.6.pkg',
'signing_cert': '4A72196F535A51A98FF2480132F024222B65060C'}}
['/usr/bin/productsign', '–sign', '4A72196F535A51A98FF2480132F024222B65060C', '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman_Labs_Postman_8.0.6-unsigned.pkg', '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman_Labs_Postman_8.0.6.pkg']
productsign: signing product with identity "Package Signing" from keychain (null)
productsign: adding certificate "ca.twocanoes.com"
productsign: Wrote signed product archive to /Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman_Labs_Postman_8.0.6.pkg
{'Output': {'pkg_path': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman_Labs_Postman_8.0.6.pkg'}}
PathDeleter
{'Input': {'path_list': ['/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman']}}
PathDeleter: Deleted /Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman
{'Output': {}}
{'AUTOPKG_VERSION': '2.3.1',
'CHECK_FILESIZE_ONLY': False,
'DOWNLOAD_URL': 'https://dl.pstmn.io/download/latest/osx&#39;,
'NAME': 'Postman',
'PARENT_RECIPES': ['/Users/username/Library/AutoPkg/RecipeRepos/com.github.rtrouton.signing_manager_autopkg_recipes/Postman/Postman.pkg.recipe',
'/Users/username/Library/AutoPkg/RecipeRepos/com.github.autopkg.dataJAR-recipes/Postman/Postman.download.recipe'],
'RECIPE_CACHE_DIR': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman',
'RECIPE_DIR': '/Users/username/Library/AutoPkg/RecipeOverrides',
'RECIPE_OVERRIDE_DIRS': ['~/Library/AutoPkg/RecipeOverrides'],
'RECIPE_PATH': '/Users/username/Library/AutoPkg/RecipeOverrides/Postman.pkg.recipe',
'RECIPE_REPOS': {'/Users/username/Library/AutoPkg/RecipeRepos/com.github.autopkg.dataJAR-recipes': {'URL': 'https://github.com/autopkg/dataJAR-recipes&#39;},
'/Users/username/Library/AutoPkg/RecipeRepos/com.github.autopkg.hjuutilainen-recipes': {'URL': 'https://github.com/autopkg/hjuutilainen-recipes&#39;},
'/Users/username/Library/AutoPkg/RecipeRepos/com.github.rtrouton.signing_manager_autopkg_recipes': {'URL': 'https://github.com/rtrouton/signing_manager_autopkg_recipes&#39;}},
'RECIPE_SEARCH_DIRS': ['.',
'~/Library/AutoPkg/Recipes',
'/Library/AutoPkg/Recipes',
'/Users/username/Library/AutoPkg/RecipeRepos/com.github.rtrouton.signing_manager_autopkg_recipes',
'/Users/username/Library/AutoPkg/RecipeRepos/com.github.autopkg.hjuutilainen-recipes',
'/Users/username/Library/AutoPkg/RecipeRepos/com.github.autopkg.dataJAR-recipes',
'/Users/username/Library/AutoPkg/RecipeOverrides',
'/Users/username/Library/AutoPkg/RecipeRepos/com.github.rtrouton.signing_manager_autopkg_recipes/Postman',
'/Users/username/Library/AutoPkg/RecipeRepos/com.github.rtrouton.signing_manager_autopkg_recipes/Postman',
'/Users/username/Library/AutoPkg/RecipeRepos/com.github.rtrouton.signing_manager_autopkg_recipes/Postman'],
'SIGNINGCERTIFICATE': '4A72196F535A51A98FF2480132F024222B65060C',
'SOFTWARETITLE': 'Labs',
'SOFTWARETYPE': 'Postman',
'USE_PYTHON_NATIVE_EXTRACTOR': False,
'VENDOR': 'Postman',
'archive_path': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/downloads/Postman.zip',
'destination_path': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman/Applications',
'download_changed': True,
'etag': '',
'filename': 'Postman.zip',
'input_path': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman/Postman.app',
'input_plist_path': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman/Applications/Postman.app/Contents/Info.plist',
'last_modified': '',
'new_package_request': True,
'path_list': ['/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman'],
'pathname': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/downloads/Postman.zip',
'pkg_creator_summary_result': {'data': {'identifier': 'com.postmanlabs.mac',
'pkg_path': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman_Labs_Postman_8.0.6.pkg',
'version': '8.0.6'},
'report_fields': ['identifier',
'version',
'pkg_path'],
'summary_text': 'The following packages were '
'built:'},
'pkg_path': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman_Labs_Postman_8.0.6.pkg',
'pkg_request': {'chown': [{'group': 'wheel',
'path': 'Applications',
'user': 'root'}],
'id': 'com.postmanlabs.mac',
'infofile': '',
'options': 'purge_ds_store',
'pkgdir': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman',
'pkgname': 'Postman_Labs_Postman_8.0.6',
'pkgroot': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman',
'pkgtype': 'flat',
'resources': '',
'scripts': '',
'version': '8.0.6'},
'pkgdirs': {'Applications': '0755'},
'pkgname': 'Postman_Labs_Postman_8.0.6',
'pkgroot': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman',
'plist_version_key': 'CFBundleShortVersionString',
'predicate': 'SIGNINGCERTIFICATE == '
'"Put_Signing_Certificate_into_AutoPkg_recipe_override"',
'prefetch_filename': False,
'purge_destination': True,
'requirement': 'identifier "com.postmanlabs.mac" and anchor apple generic and '
'certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ '
'and certificate leaf[field.1.2.840.113635.100.6.1.13] /* '
'exists */ and certificate leaf[subject.OU] = H7H8Q7M5CK',
'signing_cert': '4A72196F535A51A98FF2480132F024222B65060C',
'skip_single_root_dir': False,
'stop_processing_recipe': False,
'url': 'https://dl.pstmn.io/download/latest/osx&#39;,
'url_downloader_summary_result': {'data': {'download_path': '/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/downloads/Postman.zip'},
'summary_text': 'The following new items '
'were downloaded:'},
'verbose': 4,
'version': '8.0.6'}
Receipt written to /Users/username/Library/AutoPkg/Cache/local.pkg.Postman/receipts/local.pkg-receipt-20210305-130108.plist
The following new items were downloaded:
Download Path
————-
/Users/username/Library/AutoPkg/Cache/local.pkg.Postman/downloads/Postman.zip
The following packages were built:
Identifier Version Pkg Path
———- ——- ——–
com.postmanlabs.mac 8.0.6 /Users/username/Library/AutoPkg/Cache/local.pkg.Postman/Postman_Labs_Postman_8.0.6.pkg

By itself, Signing Manager is an amazing tool. For those interested in using AutoPkg on a cloud service or as part of a continuous integration workflow, it opens up all kinds of possibilities because it means it’s no longer necessary to have one or multiple copies of your signing certificates on the same Macs where you’re running AutoPkg. Now you can have your signing certificate stored in a secured central place and also have it available on-demand to remote clients in a secure manner.

Selectively removing the drop shadow from screenshots on macOS Big Sur

One of my personal preferences with macOS is removing the drop shadow from screenshots. On macOS Catalina and earlier, I was able to to turn off drop shadows on screenshots by running the following commands:

defaults write com.apple.screencapture disable-shadow true
killall SystemUIServer

This appears to not work on fresh installs of macOS Big Sur, though it appears to still work on Big Sur Macs who had the setting applied prior to upgrading to Big Sur. However, when using keyboard shortcuts to make screenshots, it looks like there’s a way to selectively add or remove the drop shadow at the time of making the screenshot. For more details, please see below the jump.

To take a screenshot of window or menu with a drop shadow:

1. Click Command + Shift + 4 + Spacebar
2. Wait for the mouse pointer to change into a camera icon.
3. Click on the window to take the screenshot.

The screenshot should be taken and have a drop shadow.

Screen Shot 2021 03 03 at 9 30 59 AM

 

 

To take a screenshot of window or menu without a drop shadow:

1. Click Command + Shift + 4 + Spacebar
2. Wait for the mouse pointer to change into a camera icon.
3. Hold down the Option key
4. Click on the window to take the screenshot.
5. Release the Option key

The screenshot should be taken and not have a drop shadow.

Screen Shot 2021 03 03 at 9 31 14 AM