Quantcast
Channel: rtrouton – Der Flounder
Viewing all 764 articles
Browse latest View live

Removing unwanted Time Machine backups from APFS-formatted Time Machine backup drives on macOS Monterey

$
0
0

I recently needed to prune some Time Machine backups, where I wanted to manually delete some older backups while not deleting everything on the drive. When I researched this, the guidance provided used the procedure described below:

  1. Connect your external backup drive to your Mac if needed.
  2. Launch the Time Machine app.
  3. Use the timeline on the right of the screen or the arrows to navigate to the backup date you want to delete. Alternatively, use the Finder window to navigate to the file or folder you want to delete.
  4. After selecting the date or file you want to delete, click the Action () button in Finder and choose to either Delete Backup or Delete All Backups of [Your File]

For an HFS+ formatted Time Machine backup drive, this guidance is correct. However, my Time Machine backup drive is APFS formatted. When following this guidance, I ran into the following issue:

  1. Connect your external backup drive to your Mac if needed.
  2. Launch the Time Machine app.
  3. Use the timeline on the right of the screen or the arrows to navigate to the backup date you want to delete. Alternatively, use the Finder window to navigate to the file or folder you want to delete.
  4. After selecting the date or file you want to delete, click the Action () button in Finder.

With APFS-formatted Time Machine backup drives, only the option to restore files is available. The Delete Backup or Delete All Backups options are not available.

Screen Shot 2022 07 01 at 3 17 34 PM

So how can unwanted Time Machine backups be manually deleted? For more details, please see below the jump.

You can remove unwanted backups using either the Finder, or by using the tmutil command line tool.

To remove unwanted backups using the Finder, use the procedure described below:

1. Connect your external backup drive to your Mac if needed.
2. Open a new Finder window and select the backup drive.

You should see your backups listed in the Finder window.

Screen Shot 2022 07 01 at 1 48 37 PM

3. Select the backup you want to delete and click the Action () button in Finder.

Screen Shot 2022 07 01 at 4 00 55 PM

Note: You can also control-click on the desired backup to access the same options.

Screen Shot 2022 07 01 at 1 48 50 PM

4. Select the Delete Immediately… option.

5. When prompted to confirm, click the Delete button.

Screen Shot 2022 07 01 at 1 48 58 PM

Once confirmed, the backup should be deleted and disappear from the Finder window.

Screen Shot 2022 07 01 at 1 49 21 PM

To remove unwanted backups using the tmutil command line tool, use the procedure described below:

1. Connect your external backup drive to your Mac if needed.

2. Open a Terminal window and run the following command:

tmutil listbackups

Note: You may be prompted to grant full disk access to the Terminal in order to run this command.

Screen Shot 2022 07 01 at 3 06 39 PM

Screen Shot 2022 07 01 at 3 07 32 PM

3. Identify the backup you want to delete. For this example, we’re using the following backup:

2022-07-01-154751.backup

Screen Shot 2022 07 01 at 4 26 21 PM

To delete the backup using the tmutil command line tool, you need two items of information:

  1. The path to the drive it is stored on.
  2. The time stamp of the backup.

The path to the drive is going to depend on what the drive is named. In this example, the name of the drive is Backup so the path name should be as shown below:

/Volumes/Backup

The time stamp of the backup is going to be the name of the backup prior to the .backup part of the name. In this example, the backup is named 2022-07-01-154751.backup so the time stamp should be as shown below:

2022-07-01-154751

Screen Shot 2022 07 01 at 4 26 30 PM

4. Once you have the path and time stamp, run the command shown below with root privileges to delete the backup:

tmutil delete -d /path/to/backup/drive -t timestamp_of_backup

You’ll need to provide the path and time stamp information using tmutil‘s -d flag for the path and the -t flag for the time stamp. For our example, where the path is /Volumes/Backup and the time stamp is 2022-07-01-154751, you would run the command shown below with root privileges to delete the backup:

tmutil delete -d /Volumes/Backup -t 2022-07-01-154751

The specified backup should be deleted.

Screen Shot 2022 07 01 at 4 32 51 PM

5. Run the command shown below to verify that the backup has been removed:

tmutil listbackups

Screen Shot 2022 07 01 at 4 34 48 PM

This post is focused on using Time Machine’s own tools to manage Time Machine backups, but you can also access and delete Time Machine APFS snapshots using Disk Utility on macOS Monterey. For more information on this, please see Howard Oakley‘s post linked below:

https://eclecticlight.co/2021/11/09/disk-utility-now-has-full-features-for-managing-snapshots/


Customizing Terminal behavior for documentation needs

$
0
0

As part of writing documentation today, I was given a script to follow when making some videos as part of the documentation process. The script included the following requirement:

  • Prepare the Terminal to not show the hostname or the logged-in user

By default, Terminal in macOS Monterey will show both. How to get rid of this?

Screen Shot 2022 07 14 at 3 27 15 PM

Fortunately for me, @scriptingosx had already documented how to do this as part of this post. You can use the PS1 environmental variable to set how your prompt appears in Terminal. After some experimentation, I set the following environmental variable:

PS1="\$ "

To have this prompt appear whenever I opened a new Terminal session, I added the following line to a newly-created .zshrc file in my home folder:

export PS1="\$ "

The .zshrc file is a configuration file for the zsh shell, so adding that and then opening a new Terminal window gave me a prompt which looks like this.

Screen Shot 2022 07 14 at 3 07 10 PM

As part of making the videos, I also noticed that when I copied and pasted a command into the Terminal that the pasted text was highlighted automatically. I’d seen this before and ignored it, but I thought it might be an unnecessary distraction for those watching this video later, so I went looking for how to disable it.

Screen Shot 2022 07 14 at 3 14 30 PM

After some research, I found that this was zsh’s “bracketed paste” feature, which was introduced as part of zsh 5.1. This feature can be turned off using the following command:

unset zle_bracketed_paste

Screen Shot 2022 07 14 at 3 15 20 PM

Adding entries for both the prompt and turning off bracketed paste to my .zshrc file gave me the Terminal behavior I wanted:

export PS1="\$ "
unset zle_bracketed_paste

Screen Shot 2022 07 14 at 3 19 14 PM

I also performed additional customization of my Terminal experience, but those modifications were managed using a configuration profile. For more details on that, please see this previous post:

https://derflounder.wordpress.com/2019/12/19/deploying-terminal-profile-settings-using-macos-configuration-profiles/

Specifying shell commands to run when opening new Terminal windows from macOS’s Terminal settings

$
0
0

As a follow-up to a previous post, as part of that post I had been running certain shell commands by adding them to a .zshrc file:

With some additional research, I learned that I could also run these commands using the Run command function which is available in your Terminal settings under the Shell tab.

Screen Shot 2022 07 15 at 11 17 29 AM

To replicate what I wanted, I had to enable the Run command option in the Shell tab, then also set Run inside shell. Once those were enabled, I added the following shell commands:

export PS1="\$ " && unset zle_bracketed_paste && clear
  • export PS1=”\$ “: Sets the prompt to only display “$” (no quotes) using the PS1 environmental variable.
  • unset zle_bracketed_paste: Disable the zsh shell’s bracketed paste feature.
  • clear: Removes all contents (including running the commands listed above) from the Terminal window.

The reason why this is nice is that I can now add running these commands to a macOS configuration profile using the CommandString key:


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


<key>CommandString</key>
<string>export PS1="\$ " &amp;&amp; unset zle_bracketed_paste &amp;&amp; clear</string>
view raw

gistfile1.txt

hosted with ❤ by GitHub

To see this used in context in a macOS configuration profile, please see below the jump.

The following profile sets the following settings:

  • Font: Monaco 18 point size

Additional settings:

  • Terminal prompt should not show the hostname or the logged-in user.
  • Zsh’s bracketed paste feature is disabled


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


<?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>PayloadContent</key>
<array>
<dict>
<key>PayloadContent</key>
<dict>
<key>com.apple.Terminal</key>
<dict>
<key>Forced</key>
<array>
<dict>
<key>mcx_preference_settings</key>
<dict>
<key>Default Window Settings</key>
<string>Documentation</string>
<key>Startup Window Settings</key>
<string>Documentation</string>
<key>Window Settings</key>
<dict>
<key>Documentation</key>
<dict>
<key>CommandString</key>
<string>export PS1="\$ " &amp;&amp; unset zle_bracketed_paste &amp;&amp; clear</string>
<key>Font</key>
<data>YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0
b3BYJG9iamVjdHMSAAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRy
b290gAGkCwwVFlUkbnVsbNQNDg8QERITFFZOU1NpemVYTlNmRmxh
Z3NWTlNOYW1lViRjbGFzcyNAMgAAAAAAABAQgAKAA1ZNb25hY2/S
FxgZGlokY2xhc3NuYW1lWCRjbGFzc2VzVk5TRm9udKIZG1hOU09i
amVjdAgRGiQpMjdJTFFTWF5nbnd+hY6QkpSboKu0u74AAAAAAAAB
AQAAAAAAAAAcAAAAAAAAAAAAAAAAAAAAxw==</data>
<key>FontAntialias</key>
<true/>
<key>FontWidthSpacing</key>
<real>1.004032258064516</real>
<key>Linewrap</key>
<true/>
<key>ProfileCurrentVersion</key>
<real>2.0699999999999998</real>
<key>name</key>
<string>Documentation</string>
<key>type</key>
<string>Window Settings</string>
</dict>
</dict>
</dict>
</dict>
</array>
</dict>
</dict>
<key>PayloadEnabled</key>
<true/>
<key>PayloadIdentifier</key>
<string>E7623CA6-76D7-4A3A-B35D-B1007986282A.terminal.profile.settings.40F1AB26-EAE7-4589-8101-72A4AC0C2015</string>
<key>PayloadType</key>
<string>com.apple.ManagedClient.preferences</string>
<key>PayloadUUID</key>
<string>40F1AB26-EAE7-4589-8101-72A4AC0C2015</string>
<key>PayloadVersion</key>
<integer>1</integer>
</dict>
</array>
<key>PayloadDescription</key>
<string>This configuration profile installs the Documentation Terminal profile and sets it as the default Terminal profile.</string>
<key>PayloadDisplayName</key>
<string>Sets Documentation Terminal profile</string>
<key>PayloadIdentifier</key>
<string>Documentation.41423E4C-72C8-48D1-BE24-734B62D7F77F.terminal.profile.settings.</string>
<key>PayloadOrganization</key>
<string>Company Name</string>
<key>PayloadRemovalDisallowed</key>
<false/>
<key>PayloadScope</key>
<string>System</string>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadUUID</key>
<string>E7623CA6-76D7-4A3A-B35D-B1007986282A</string>
<key>PayloadVersion</key>
<integer>1</integer>
</dict>
</plist>

Privileges.app and time-limited admin

$
0
0

Privileges is an open source tool from SAP which helps folks manage admin rights for their account. As part of its feature set, it includes an option for time-limited admin using a specific function called Toggle privileges.

Privileges dock toggleon

Privileges dock toggleon20

However, Toggle privileges’s time-limited admin feature for Privileges is its most misunderstood feature. The reason is that while the ability to set a time limit is only available if you’re using the Toggle privileges function, many users assume that this time-limited admin is available universally to all the functions used to get admin rights using the Privileges app.

It is not. Time limited admin is only available using the Toggle privileges function. If you’re not using the Toggle privileges function, there is no time limitation and you cannot set one from within the Privileges app.

This information is available in the Privileges FAQ:

Screen Shot 2022 07 22 at 10 05 50 AM

What does this mean?

  1. The only way time-limited admin is currently working on Privileges is by using the Toggle privileges function.
  2. If you are clicking on the icon in the dock and not selecting the Toggle privileges function, there’s no time limit.
  3. If you’re using the PrivilegesCLI command line tool, there is no time limit.

How long do you have admin if you’re not using the Toggle privileges function? Admin rights are granted until some process (like running Privileges again) takes them away. There’s no time limit.

All of the Privileges management options available for time-limited admin at this time apply only to the Toggle privileges function. If you’re using any of the management settings options listed below, they apply only and exclusively to the Toggle privileges function:

  • DockToggleTimeout
  • DockToggleMaxTimeout

They will not manage time-limited admin for any of Privileges’ functions outside of using the Toggle privileges function.

What if you want time-limited admin outside of using the Toggle privileges function? You will need to use a separate mechanism. In my case, I usually point folks towards using PrivilegesDemoter:

https://github.com/sgmills/PrivilegesDemoter

This tool uses a separate mechanism for figuring out the timing and then uses the PrivilegesCLI command line tool to take away admin when the time limit set for PrivilegesDemoter expires.

Microsoft Defender and tamper protection

$
0
0

One of the features of Microsoft Defender for macOS is tamper protection. This option is designed to prevent Defender or its settings from being removed or changed.

As of posting date, Defender’s tamper protection has three associated topics:

  • Disabled: Tamper protection is completely off.
  • Audit: Tampering operations are logged, but not blocked.
  • Blocked: Tamper protection is on, tampering operations are blocked.

Microsoft has documentation regarding Defender’s tamper protection for macOS, available via the link below:

https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/tamperprotection-macos

For more details, please see below the jump.

You can manage tamper protection via running commands via the command line, or via management profiles. The commands shown below allow tamper protection to be disabled completely, set to audit mode, or set to full tamper protection where Defender or its settings can’t be removed or changed.

To disable tamper protection, run the following command with root privileges:


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


/usr/local/bin/mdatp config tamper-protection enforcement-level –value disabled
view raw

gistfile1.txt

hosted with ❤ by GitHub

To set tamper protection to audit mode, run the following command with root privileges:


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


/usr/local/bin/mdatp config tamper-protection enforcement-level –value audit
view raw

gistfile1.txt

hosted with ❤ by GitHub

To set tamper protection to full tamper protection mode, run the following command with root privileges:


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


/usr/local/bin/mdatp config tamper-protection enforcement-level –value block
view raw

gistfile1.txt

hosted with ❤ by GitHub

If management via profiles is desired, the example profiles below will set Defender to audit mode or to full tamper protection.

Audit mode:


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


<?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">
<dict>
<key>PayloadUUID</key>
<string>05F0ADFE-8BE0-4C43-BACB-BD0DE6272306</string>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadOrganization</key>
<string>Company Name</string>
<key>PayloadIdentifier</key>
<string>com.microsoft.wdav</string>
<key>PayloadDisplayName</key>
<string>Enabling Tamper Protection for Microsoft Defender</string>
<key>PayloadDescription</key>
<string>Microsoft Defender tamper protection configuration settings</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadEnabled</key>
<true />
<key>PayloadRemovalDisallowed</key>
<true />
<key>PayloadScope</key>
<string>System</string>
<key>PayloadContent</key>
<array>
<dict>
<key>PayloadUUID</key>
<string>977D6F1E-E6C1-4BD2-96C5-D4FFCEAE92BB</string>
<key>PayloadType</key>
<string>com.microsoft.wdav</string>
<key>PayloadOrganization</key>
<string>Microsoft</string>
<key>PayloadIdentifier</key>
<string>com.microsoft.wdav</string>
<key>PayloadDisplayName</key>
<string>Microsoft Defender tamper protection configuration settings</string>
<key>PayloadDescription</key>
<string />
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadEnabled</key>
<true />
<key>tamperProtection</key>
<dict>
<key>enforcementLevel</key>
<string>audit</string>
</dict>
</dict>
</array>
</dict>
</plist>
view raw

gistfile1.txt

hosted with ❤ by GitHub

Full tamper protection:


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


<?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">
<dict>
<key>PayloadUUID</key>
<string>E21D6405-FA0F-46D2-84DE-33775762DD7E</string>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadOrganization</key>
<string>Company Name</string>
<key>PayloadIdentifier</key>
<string>com.microsoft.wdav</string>
<key>PayloadDisplayName</key>
<string>Enabling Tamper Protection for Microsoft Defender</string>
<key>PayloadDescription</key>
<string>Microsoft Defender tamper protection configuration settings</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadEnabled</key>
<true />
<key>PayloadRemovalDisallowed</key>
<true />
<key>PayloadScope</key>
<string>System</string>
<key>PayloadContent</key>
<array>
<dict>
<key>PayloadUUID</key>
<string>C3934617-C698-4A88-819D-47377A35D4F3</string>
<key>PayloadType</key>
<string>com.microsoft.wdav</string>
<key>PayloadOrganization</key>
<string>Microsoft</string>
<key>PayloadIdentifier</key>
<string>com.microsoft.wdav</string>
<key>PayloadDisplayName</key>
<string>Microsoft Defender tamper protection configuration settings</string>
<key>PayloadDescription</key>
<string />
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadEnabled</key>
<true />
<key>tamperProtection</key>
<dict>
<key>enforcementLevel</key>
<string>block</string>
</dict>
</dict>
</array>
</dict>
</plist>
view raw

gistfile1.txt

hosted with ❤ by GitHub

To check the current Defender configuration, the following command can be run:


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


/usr/local/bin/mdatp health
view raw

gistfile1.txt

hosted with ❤ by GitHub

That should return output which looks similar to what’s 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


healthy : true
health_issues : []
licensed : true
engine_version : "1.1.18900.4"
app_version : "101.78.13"
org_id : "…"
log_level : "info"
machine_guid : "…"
release_ring : "Production"
product_expiration : Feb 05, 2023 at 06:24:12 AM
cloud_enabled : true
cloud_automatic_sample_submission_consent : "safe"
cloud_diagnostic_enabled : false
passive_mode_enabled : true [managed]
real_time_protection_enabled : false [managed]
real_time_protection_available : true
real_time_protection_subsystem : "endpoint_security_extension"
network_events_subsystem : "network_filter_extension"
device_control_enforcement_level : "audit"
tamper_protection : "audit"
automatic_definition_update_enabled : true
definitions_updated : Sept 08, 2022 at 01:57:03 PM
definitions_updated_minutes_ago : 27712166
definitions_version : "1.355.2589.0"
definitions_status : "update_failed"
edr_early_preview_enabled : "enabled"
edr_device_tags : []
edr_group_ids : ""
edr_configuration_version : "20.199999.main.2022.09.08.01-10dcd7fedfed0c7a1c3bbf153ba3c9b0d0f36239"
edr_machine_id : "…"
conflicting_applications : []
network_protection_status : "stopped"
network_protection_enforcement_level : "disabled"
data_loss_prevention_status : "disabled"
view raw

gistfile1.txt

hosted with ❤ by GitHub

To check specifically for the tamper protection configuration, the following command can be run to check its status:


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


/usr/local/bin/mdatp health –field tamper_protection
view raw

gistfile1.txt

hosted with ❤ by GitHub

You should see the following output depending on the configuration:

Tamper protection disabled:


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


username@computername ~ % /usr/local/bin/mdatp health –field tamper_protection
"disabled"
username@computername ~ %
view raw

gistfile1.txt

hosted with ❤ by GitHub

Tamper protection set to audit mode:


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


username@computername ~ % /usr/local/bin/mdatp health –field tamper_protection
"audit"
username@computername ~ %
view raw

gistfile1.txt

hosted with ❤ by GitHub

Tamper protection set to full tamper protection:


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


username@computername ~ % /usr/local/bin/mdatp health –field tamper_protection
"block"
username@computername ~ %
view raw

gistfile1.txt

hosted with ❤ by GitHub

For folks with uninstall scripts for Microsoft Defender, they won’t be able to successfully uninstall Defender or its settings with tamper protection enabled. One way to address this is to add a section at the beginning of the uninstall script which can detect if full tamper protection is enabled and stop the script if it is. An example of this is 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


#!/bin/bash
ERROR=0
# Check to see if Microsoft Defender's tamper protection is enabled.
#
# If tamper protection is turned on, a message will be displayed followed
# by the script exiting before proceeding to the uninstall functions of
# the script.
# Verify that the following tool is installed and executable:
#
# /usr/local/bin/mdatp
if [[ -x "/usr/local/bin/mdatp" ]]; then
# If the mdatp is installed, Defender's Tamper protection's
# status is checked by running the following command:
#
# /usr/local/bin/mdatp" health –field tamper_protection
#
# The output of this command will then be checked against the value stored
# in the tamper_protection_enabled_keyword variable.
#
# There are three possible keywords that can be returned by this command:
#
# disabled – tamper protection is completely off.
# audit – tampering operations are logged, but not blocked.
# block – tamper protection is on, tampering operations are blocked.
#
# The tamper_protection_enabled_keyword variable will store the keyword
# currently being used by Defender, in case Microsoft chooses to change
# the keywords in future versions of Defender.
tamper_protection_enabled="$("/usr/local/bin/mdatp" health –field tamper_protection | awk '{print $1}' | tr -d '"')"
tamper_protection_enabled_keyword="block"
if [[ "$tamper_protection_enabled" == "$tamper_protection_enabled_keyword" ]]; then
/usr/bin/osascript -e 'display dialog "Tamper protection for Microsoft Defender is enabled." & "\n" & "\nDefender cannot be uninstalled while tamper protection is turned on."& "\n" & "\nFor more information, please contact the helpdesk."buttons {"Understood"} default button 1 with icon Caution'
exit "$ERROR"
fi
fi
view raw

gistfile1.txt

hosted with ❤ by GitHub

If full tamper protection is enabled, this example script will take the following actions:

1. Display the following message using osascript.

Screen Shot 2022 09 09 at 9 27 38 AM

2. Exit after displaying the message.

Jamf Pro 10.41.0 and SSL verification alerts

$
0
0

Following an upgrade to Jamf Pro 10.41.0, you may notice that you have an alert showing in the Jamf Pro admin console.

Screen Shot 2022 09 12 at 10 39 33 AM

When you click on the alert, you will see the following alert notification.

Verification of SSL certificates is disabled.

There will be a link to enable SSL certificate verification.

Screen Shot 2022 09 12 at 10 39 51 AM

If you click that link, it’ll take you to Management Settings: Computer Management – Management Framework: Security.

Screen Shot 2022 09 09 at 10 15 26 AM

So now what? For more details, please see below the jump.

The SSL certificate in question is the SSL certificate used by Tomcat. Jamf is deprecating the use of self-signed certificates for Tomcat, as mentioned in the Jamf Pro 10.41.0 release notes:

Removal of unverified SSL certificates in Jamf Pro — In a future release of Jamf Pro the option to use an unverified SSL certificate for Jamf Pro will be removed. Customers with Cloud-hosted environments and those with a verified third-party certificate will see no changes. Customers with On-Premise environments using Jamf Pro’s built-in certificate authority to issue SSL certificates need to move to a trusted third-party certificate.

Screen Shot 2022 09 12 at 10 44 05 AM

The alert is being triggered if you have the SSL Certificate Verification setting set to one of the following:

  • Disabled
  • Always except during enrollment

The Disabled setting means the Jamf Pro agent installed on a Mac isn’t verifying certificate trust at all for the SSL certificate that Tomcat is using.

The Always except during enrollment setting means that the Jamf Pro agent installed on a Mac isn’t verifying certificate trust for the SSL certificate that Tomcat is using at enrollment, but does verify that the SSL certificate is trusted for all subsequent communication.

Note: The Always except during enrollment setting was meant to ensure that Jamf Pro could install a root certificate for a self-signed certificate and establish certificate trust that way.

Screen Shot 2022 09 09 at 10 16 14 AM

 

If your Jamf Pro service is using a publicly trusted SSL certificate, the fix is to set the SSL Certificate Verification setting to the following:

  • Always

Screen Shot 2022 09 09 at 10 16 24 AM

Selecting that setting and clicking the Save button will result in the following warning being displayed. If you’re certain you have a publicly trusted certificate, click OK. Otherwise, click the Cancel button to back the change out.

Screen Shot 2022 09 12 at 10 40 55 AM

As long as you have a publicly-trusted SSL certificate for Tomcat, changing the SSL Certificate Verification setting to Always should have no impact. 

If you’re hosted in Jamf Cloud, you should already be using a publicly trusted SSL certificate. If you’re hosting Jamf Pro yourself, I recommend verifying that you’re using a publicly trusted certificate before making that change.

If you are hosting Jamf Pro yourself and don’t have a publicly trusted SSL certificate for Tomcat, I strongly recommend getting one as soon as possible. As Jamf’s release notes mention, the option to not use a trusted certificate will be removed from a future version of Jamf Pro.

Microsoft Defender tamper protection status detection for Jamf Pro

$
0
0

As a follow-up to my earlier post about working with Microsoft Defender’s tamper protection, I’ve written an Extension Attribute for Jamf Pro which detects and reports on Defender’s tamper protection status. For more details, please see below the jump.

The Extension Attribute uses Defender’s mdatp command line tool to report on Defender’s tamper protection status. Once the mdatp tool is verified to be installed and executable, it’s used to check the tamper protection status. The EA will return one of the following values:

  • 000
  • 001
  • 010
  • 100

The returned values indicate the following:

  • 000 = The /usr/local/bin/mdatp command-line tool cannot be found or is not executable.
  • 001 = Tamper protection is fully disabled.
  • 010 = Tamper protection is set to audit mode.
  • 100 = Tamper protection is fully enabled.

The Extension Attribute is available below. It’s also available from GitHub using the following link:

https://github.com/rtrouton/rtrouton_scripts/blob/main/rtrouton_scripts/Casper_Extension_Attributes/check_microsoft_defender_tamper_protection_status


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
# Check to see if Microsoft Defender's tamper protection is enabled.
# This Jamf Pro Extension Attribute will return one of four statuses
#
# 000 = The /usr/local/bin/mdatp command-line tool cannot be found or is not executable.
# 001 = Tamper protection is fully disabled.
# 010 = Tamper protection is set to audit mode.
# 100 = Tamper protection is fully enabled.
mdatpPath="/usr/local/bin/mdatp"
# Set default result for the Extension Attribute to be the following:
#
# 000 = The /usr/local/bin/mdatp command-line tool cannot be found or is not executable.
eaResult="000"
# Verify that the following tool is installed and executable:
#
# /usr/local/bin/mdatp
if [[ -x "$mdatpPath" ]]; then
# If the mdatp tool is installed, Defender's tamper protection
# status is checked by running the following command:
#
# /usr/local/bin/mdatp" health –field tamper_protection
#
# There are three possible keywords that can be returned by this command:
#
# disabled – tamper protection is completely off.
# audit – tampering operations are logged, but not blocked.
# block – tamper protection is on, tampering operations are blocked.
tamper_protection_enabled="$("$mdatpPath" health –field tamper_protection | awk -F'"' '{print $2}')"
if [[ "$tamper_protection_enabled" = "disabled" ]]; then
eaResult="001"
elif [[ "$tamper_protection_enabled" = "audit" ]]; then
eaResult="010"
elif [[ "$tamper_protection_enabled" = "block" ]]; then
eaResult="100"
fi
fi
echo "<result>$eaResult</result>"
exit 0
view raw

gistfile1.txt

hosted with ❤ by GitHub

Slides from the “Running Jamf Pro at Scale, from SAP with ❤️” session at Jamf Nation User Conference 2022


Slides from the “Leveling Up – Managing admin rights in the enterprise” session at MacSysAdmin 2022

Running Jamf Pro inventory updates at startup time

$
0
0

With the release of macOS Ventura expected this month, an important topic to many Mac admins is having their systems management tools detect as quickly as possible which of their Macs have upgraded to macOS Ventura. The reasons for this are varied, but one particular reason is to get configuration profiles deployed as soon as possible to manage new features and functionality in macOS Ventura.

One way to ensure quick detection if you’re using Jamf Pro is to have your managed Macs submit an inventory update to the Jamf Pro server when the Mac starts up. For one way to do this, please see below the jump.

For Macs managed by Jamf Pro, it’s possible to trigger the Jamf agent from the command line to do the following tasks:

  1. Verify that the Jamf agent on the Mac can contact the Jamf Pro server.
  2. Collect an inventory update from the Mac and submit it to the Jamf Pro server

The commands to do so are the following:

Verify connection to the Jamf Pro server:


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


/usr/local/jamf/bin/jamf checkJSSConnection
view raw

gistfile1.txt

hosted with ❤ by GitHub

Collect and submit an inventory update to the Jamf Pro server:


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


/usr/local/jamf/bin/jamf recon
view raw

gistfile1.txt

hosted with ❤ by GitHub

The following command should do the following:

  1. Try for 60 seconds to verify the connection to the Jamf Pro server
  2. If connection is successfully verified, collect and submit an inventory update to the Jamf Pro server.


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


/usr/local/jamf/bin/jamf checkJSSConnection -retry 60 && /usr/local/jamf/bin/jamf recon
view raw

gistfile1.txt

hosted with ❤ by GitHub

Note: The && in the command will ensure that the second command (the inventory update) will only run if the previous command runs without errors. If the connection can’t be verified, the jamf checkJSSConnection command will exit with an error status. The error status will mean that the subsequent inventory update command won’t be executed.

The command above can be added to a LaunchDaemon like the one shown below. Installing this LaunchDaemon will ensure that the two commands (connection verification and inventory update) are run every time the Mac starts.


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


<?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>Label</key>
<string>com.github.runjamfproinventoryupdate</string>
<key>ProgramArguments</key>
<array>
<string>sh</string>
<string>-c</string>
<string>/usr/local/jamf/bin/jamf checkJSSConnection -retry 60 && /usr/local/jamf/bin/jamf recon</string>
</array>
</dict>
</plist>

You can deploy this LaunchDaemon using a script like the one shown below. The example script shown below will do the following:

  1. Create the LaunchDaemon file on the Mac in question.
  2. Set the correct permissions on the LaunchDaemon file
  3. Install the LaunchDaemon file into /Library/LaunchDaemons
  4. Load the LaunchDaemon
  5. Verify that the LaunchDaemon is in place and loaded.

Note: Once the LaunchDaemon is loaded, the Jamf agent on Mac will immediately perform the following actions:

  1. Try for 60 seconds to verify the connection to the Jamf Pro server
  2. If connection is successfully verified, collect and submit an inventory update to the Jamf Pro server.

The LaunchDaemon will also be loaded by the Mac at startup, so the same actions will also performed any time the Mac starts up.


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
# Script which installs a LaunchDaemon which runs a Jamf inventory update at startup time.
#
# The LaunchDaemon takes the following actions:
#
# * Verifies that the Mac can communicate with the Jamf Pro server.
# * Sends an updated inventory to the Jamf Pro server
#
# Create the jamf_inventory_update_at_boot LaunchDaemon by using cat input redirection
# to write the XML contained below to a new file.
#
# The LaunchDaemon will run when when loaded and also when the Mac boots up.
# Set the identifier for the LaunchDaemon
LaunchDaemonName="com.github.runjamfproinventoryupdate"
# Set exit code
ERROR=0
# Create temp directory to store LaunchDaemon file inside at file creation time.
temp_directory=$(mktemp -d)
# Create the LaunchDaemon file
/bin/cat > "$temp_directory/$LaunchDaemonName.plist" << JAMF_PRO_INVENTORY_UPDATE_LAUNCHDAEMON
<?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>Label</key>
<string>$LaunchDaemonName</string>
<key>ProgramArguments</key>
<array>
<string>sh</string>
<string>-c</string>
<string>/usr/local/jamf/bin/jamf checkJSSConnection -retry 60 && /usr/local/jamf/bin/jamf recon</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
JAMF_PRO_INVENTORY_UPDATE_LAUNCHDAEMON
# Once the LaunchDaemon file has been created, fix the permissions
# so that the file is owned by root:wheel and set to not be executable
# After the permissions have been updated, move the LaunchDaemon into
# place in /Library/LaunchDaemons.
/usr/sbin/chown root:wheel "${temp_directory}/${LaunchDaemonName}.plist"
/bin/chmod 644 "${temp_directory}/${LaunchDaemonName}.plist"
/bin/chmod a-x "${temp_directory}/${LaunchDaemonName}.plist"
/bin/mv "${temp_directory}/${LaunchDaemonName}.plist" "/Library/LaunchDaemons/${LaunchDaemonName}.plist"
# After the LaunchDaemon is place with proper permissions, load the LaunchDaemon.
# Loading the launchdaemon will trigger an Jamf Pro inventory update to be run.
if [[ -f "/Library/LaunchDaemons/${LaunchDaemonName}.plist" ]]; then
/bin/launchctl bootstrap system "/Library/LaunchDaemons/${LaunchDaemonName}.plist"
fi
# Remove temp directory
/bin/rm -rf "$temp_directory"
if [[ -f "/Library/LaunchDaemons/${LaunchDaemonName}.plist" ]]; then
LaunchDaemonLoaded=$(/bin/launchctl list | grep -o "$LaunchDaemonName")
if [[ -n "$LaunchDaemonLoaded" ]]; then
echo "$LaunchDaemonName LaunchDaemon is loaded. Jamf Pro inventory updates will run when the Mac boots."
else
echo "ERROR: $LaunchDaemonName LaunchDaemon is not loaded."
ERROR=1
fi
else
echo "ERROR: $LaunchDaemonName.plist LaunchDaemon file was not created successfully."
ERROR=1
fi
exit "$ERROR"

Running Jamf Pro inventory updates at startup time using a Jamf Pro policy

$
0
0

As a follow-up to my previous post on running Jamf Pro inventory updates at startup, several folks have asked if the approach I showed was better or more efficient than using a Jamf Pro policy to run the inventory update. I thought about it and I can’t say for certain if the LaunchDaemon-driven approach I described is better than using a Jamf Pro policy.

The advantage of the LaunchDaemon-driven approach has is that the Mac admin has control of the options being used. In my example solution’s case, I have jamf checkJSSConnection checking for up to 60 seconds before giving up. Depending on your network setup, it may take that long before your Mac can verify it can talk to the Jamf Pro server.

If you’re running an inventory update via a Jamf policy’s startup trigger, you’re using whatever configuration Jamf has chosen for making sure the policy is triggered when you want it to be. Jamf’s choices may be the right ones, but those choices are being made by Jamf and not the individual Mac admin.

That said, collecting and submitting inventory updates to Jamf Pro is a problem which can be solved multiple ways and what I presented in my previous blog post was a solution, but not the only solution. With that in mind, please see below the jump for details on how to solve the problem of collecting and submitting inventory updates at startup using a Jamf Pro policy.

You can set up a Jamf Pro policy with the following options to collecting and submit inventory updates to Jamf Pro when a Mac starts up:

1. Under the General policy options, set the following trigger and execution frequency:

  • Trigger: Startup
  • Execution Frequency: Ongoing

Screen Shot 2022 10 09 at 4 51 56 PM

2. Under the Maintenance policy options, set the following option:

  • Update Inventory: Check the box to select this option

Screen Shot 2022 10 09 at 4 52 00 PM

 

All other options (scoping, category, user interaction, etc.) can be set according to the individual Mac admins’s preferences.

Building Jamf Pro smart groups for Ventura-compatible and Ventura-incompatible Mac models

$
0
0

As part of preparing for macOS Ventura, it may be useful to have a way to easily distinguish between the Macs in your fleet which can run macOS Ventura and those which can’t. Apple has published the following list of Macs which are compatible with Ventura, which will help with both identitying the compatible Mac models as well as the incompatible Mac models.

  • iMac: 2017 and later models
  • iMac Pro: All models
  • MacBook: 2017 and later models
  • MacBook Pro: 2017 and later models
  • MacBook Air: 2018 and later models
  • Mac Mini: 2018 or later models
  • Mac Pro: 2019 or later models
  • Mac Studio: All models

From there, here’s the list of Mac models which are compatible with macOS Ventura:


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


Mac13,1
Mac13,2
Mac14,2
Mac14,7
MacBook10,1
MacBookAir10,1
MacBookAir8,1
MacBookAir8,2
MacBookAir9,1
MacBookPro14,1
MacBookPro14,2
MacBookPro14,3
MacBookPro15,1
MacBookPro15,2
MacBookPro15,3
MacBookPro15,4
MacBookPro16,1
MacBookPro16,2
MacBookPro16,3
MacBookPro16,4
MacBookPro17,1
MacBookPro18,1
MacBookPro18,2
MacBookPro18,3
MacBookPro18,4
MacPro7,1
Macmini8,1
Macmini9,1
VirtualMac2,1
iMac18,1
iMac18,2
iMac18,3
iMac19,1
iMac19,2
iMac20,1
iMac20,2
iMac21,1
iMac21,2
iMacPro1,1
iSim1,1

We can use this information to build smart groups which can help identify which Macs are compatible with Ventura and which are not. For more details, see below the jump:

Using the information mentioned above, I was able to build two smart groups, one which displays compatible Macs and the other which displays incompatible Macs.

The compatible Macs’ smart group checks for if the Mac in question’s model identifier is any of the model identifiers which are compatible with Ventura:


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


<?xml version="1.0" encoding="UTF-8"?>
<computer_group>
<name>Macs compatible with macOS Ventura</name>
<is_smart>true</is_smart>
<criteria>
<size>40</size>
<criterion>
<name>Model Identifier</name>
<priority>0</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>Mac13,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>1</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>Mac13,2</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>2</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>Mac14,2</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>3</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>Mac14,7</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>4</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>MacBook10,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>5</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>MacBookAir10,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>6</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>MacBookAir8,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>7</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>MacBookAir8,2</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>8</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>MacBookAir9,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>9</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>MacBookPro14,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>10</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>MacBookPro14,2</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>11</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>MacBookPro14,3</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>12</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>MacBookPro15,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>13</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>MacBookPro15,2</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>14</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>MacBookPro15,3</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>15</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>MacBookPro15,4</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>16</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>MacBookPro16,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>17</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>MacBookPro16,2</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>18</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>MacBookPro16,3</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>19</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>MacBookPro16,4</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>20</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>MacBookPro17,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>21</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>MacBookPro18,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>22</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>MacBookPro18,2</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>23</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>MacBookPro18,3</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>24</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>MacBookPro18,4</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>25</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>MacPro7,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>26</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>Macmini8,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>27</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>Macmini9,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>28</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>VirtualMac2,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>29</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>iMac18,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>30</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>iMac18,2</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>31</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>iMac18,3</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>32</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>iMac19,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>33</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>iMac19,2</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>34</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>iMac20,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>35</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>iMac20,2</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>36</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>iMac21,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>37</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>iMac21,2</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>38</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>iMacPro1,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>39</priority>
<and_or>or</and_or>
<search_type>is</search_type>
<value>iSim1,1</value>
</criterion>
</criteria>
<computers/>
</computer_group>

Screen Shot 2022 10 12 at 1 20 52 PM

The incompatible Macs’ smart group checks for if the Mac in question’s model identifier is not any of the model identifiers which are compatible with Ventura:


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


<?xml version="1.0" encoding="UTF-8"?>
<computer_group>
<name>Macs incompatible with macOS Ventura</name>
<is_smart>true</is_smart>
<criteria>
<size>40</size>
<criterion>
<name>Model Identifier</name>
<priority>0</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>Mac13,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>1</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>Mac13,2</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>2</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>Mac14,2</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>3</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>Mac14,7</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>4</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>MacBook10,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>5</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>MacBookAir10,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>6</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>MacBookAir8,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>7</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>MacBookAir8,2</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>8</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>MacBookAir9,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>9</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>MacBookPro14,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>10</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>MacBookPro14,2</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>11</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>MacBookPro14,3</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>12</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>MacBookPro15,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>13</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>MacBookPro15,2</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>14</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>MacBookPro15,3</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>15</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>MacBookPro15,4</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>16</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>MacBookPro16,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>17</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>MacBookPro16,2</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>18</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>MacBookPro16,3</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>19</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>MacBookPro16,4</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>20</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>MacBookPro17,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>21</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>MacBookPro18,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>22</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>MacBookPro18,2</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>23</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>MacBookPro18,3</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>24</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>MacBookPro18,4</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>25</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>MacPro7,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>26</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>Macmini8,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>27</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>Macmini9,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>28</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>VirtualMac2,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>29</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>iMac18,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>30</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>iMac18,2</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>31</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>iMac18,3</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>32</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>iMac19,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>33</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>iMac19,2</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>34</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>iMac20,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>35</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>iMac20,2</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>36</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>iMac21,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>37</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>iMac21,2</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>38</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>iMacPro1,1</value>
</criterion>
<criterion>
<name>Model Identifier</name>
<priority>39</priority>
<and_or>and</and_or>
<search_type>is not</search_type>
<value>iSim1,1</value>
</criterion>
</criteria>
<computers/>
</computer_group>

Screen Shot 2022 10 12 at 1 21 30 PM

To upload these smart group XML files to a Jamf Pro server server using the API, download the XML file to a convenient location, then run the commands shown below (substituting your Jamf Pro server and Jamf Pro user account information as appropriate):

Note: You will first need to get an API bearer token for authentication. Use the commands below (depending on which OS you’re using) to obtain a bearer token from your JAMF Pro server:

Get API bearer token on macOS Monterey and later:


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


curl -X POST -u username:password -s https://server.name.here/api/v1/auth/token | plutil -extract token raw –
view raw

gistfile1.txt

hosted with ❤ by GitHub

Get API bearer token on macOS Big Sur and earlier:


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


curl -X POST -u username:password -s https://server.name.here/api/v1/auth/token | python -c 'import sys, json; print json.load(sys.stdin)["token"]'
view raw

gistfile1.txt

hosted with ❤ by GitHub

The bearer token should look something 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


eyJhbGciOiJIUzI1NiJ9.eyJhdXRoZW50aWNhdGVkLWFwcCI6IkdFTkVSSUMiLCJhdXRoZW50aWNhdGlvbi10eXBlIjoiSlNTIiwiZ3JvdXBzIjpbXSwic3ViamVjdC10eXBlIjoiSlNTX1VTRVJfSUQiLCJ0b2tlbi11dWlkIjoiODQwNzBjZjctOGYwNS00N2NhLTliNWItZjU3YzYwYTY2ZGIwIiwibGRhcC1zZXJ2ZXItaWQiOi0xLCJzdWIiOiIxMSIsImV4cCI6MTY2NTYwMTUzMX0.R8grAtlzG1raZw95HJqiLyxZavf03SwFqbgfb3eVSgg
view raw

gistfile1.txt

hosted with ❤ by GitHub

Once you have the bearer token, use the token to authenticate the API command as 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


curl -sf https://jamfpro.server.here/JSSResource/computergroups/id/0 -T /path/to/filename.xml -X POST -H "Authorization: Bearer API_Bearer_Token_Goes_Here";
view raw

gistfile1.txt

hosted with ❤ by GitHub

If the API bearer token is the one shown above, the API command should look something 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


curl -sf https://jamfpro.server.here/JSSResource/computergroups/id/0 -T /path/to/filename.xml -X POST -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdXRoZW50aWNhdGVkLWFwcCI6IkdFTkVSSUMiLCJhdXRoZW50aWNhdGlvbi10eXBlIjoiSlNTIiwiZ3JvdXBzIjpbXSwic3ViamVjdC10eXBlIjoiSlNTX1VTRVJfSUQiLCJ0b2tlbi11dWlkIjoiODQwNzBjZjctOGYwNS00N2NhLTliNWItZjU3YzYwYTY2ZGIwIiwibGRhcC1zZXJ2ZXItaWQiOi0xLCJzdWIiOiIxMSIsImV4cCI6MTY2NTYwMTUzMX0.R8grAtlzG1raZw95HJqiLyxZavf03SwFqbgfb3eVSgg";
view raw

gistfile1.txt

hosted with ❤ by GitHub

If the smart group was successfully uploaded, you should next see output 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


<?xml version="1.0" encoding="UTF-8"?><computer_group><id>95</id></computer_group>
view raw

gistfile1.txt

hosted with ❤ by GitHub

The new smart group should now be present on the Jamf Pro server.

Screen Shot 2022 10 12 at 1 20 52 PM

Using the Jamf Pro API to report on Self Service policies

$
0
0

Every so often, it may be necessary to generate a report from Jamf Pro of which policies are available in Self Service. To assist with this task, I’ve written a script which uses the Jamf Pro Classic API to search through the policy records and generate a report in .tsv format.

For more details, please see below the jump.

Pre-requisites:

If setting up a specific Jamf Pro user account for this purpose with limited rights, here are the required API privileges for the account on the Jamf Pro server:

Jamf Pro Server Objects:

  • Policies: Read

For authentication, the script 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

Usage:

./Generate_Self_Service_Policy_Report.sh

Screen Shot 2022 10 14 at 5 29 53 PM

The script takes the following actions:

  1. Uses the Jamf Pro Classic API to download the Jamf Pro IDs of all computer policies.
  2. Checks which policies are Self Service policies.
  3. Uses the Jamf Pro Classic API to download all information about matching Self Service policies.
  4. Pulls the following information out of the policy record data:
  • Jamf Pro ID
  • If the policy is enabled in the Jamf Pro admin console
  • The policy’s name in the Jamf Pro admin console
  • The name of the policy’s category
  • The name displayed in Self Service for the policy

Create a report in tab-separated value (.tsv) format which contains the following information about the Self Service policies.

  • Jamf Pro ID
  • If it’s a Self Service policy
  • If the policy is enabled in the Jamf Pro admin console
  • The policy’s name in the Jamf Pro admin console
  • The name of the policy’s category
  • The name displayed in Self Service for the policy
  • Jamf Pro admin console URL for the Self Service policy

The report generated by script should appear similar to what is shown below:



Jamf Pro ID Number Self Service Policy Policy Enabled Policy Name Category Self Service Display Name Jamf Pro URL
105 TRUE TRUE Check for Apple Software Updates First Aid Check for Apple Software Updates https://server.name.here/policies.html?id=105
107 TRUE FALSE Get Logs Utilities Get Logs https://server.name.here/policies.html?id=107
94 TRUE TRUE Microsoft Office 365 Microsoft Microsoft Office 16.47.0 https://server.name.here/policies.html?id=94
23 TRUE TRUE Microsoft OneNote Microsoft Microsoft OneNote https://server.name.here/policies.html?id=23
22 TRUE TRUE Microsoft Remote Desktop Remote Access Microsoft Remote Desktop https://server.name.here/policies.html?id=22
104 TRUE TRUE Slack Communication Slack https://server.name.here/policies.html?id=104

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

https://github.com/rtrouton/rtrouton_scripts/tree/main/rtrouton_scripts/Casper_Scripts/Generate_Self_Service_Policy_Report


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 script uses the Jamf Pro Classic API to detect Jamf Pro policies are
# Self Service policies and generates a report with information about those
# policies.
# Set default exit code
exitCode=0
# Create report file
report_file="$(mktemp).tsv"
# If you're on Jamf Pro 10.34.2 or earlier, which doesn't support using Bearer Tokens
# for Classic API authentication, set the NoBearerToken variable to the following value
# as shown below:
#
# yes
#
# NoBearerToken="yes"
#
# If you're on Jamf Pro 10.35.0 or later, which does support using Bearer Tokens
# for Classic API authentication, set the NoBearerToken variable to the following value
# as shown below:
#
# NoBearerToken=""
NoBearerToken=""
GetJamfProAPIToken() {
# This function uses Basic Authentication to get a new bearer token for API authentication.
# Use user account's username and password credentials with Basic Authorization to request a bearer token.
if [[ $(/usr/bin/sw_vers -productVersion | awk -F . '{print $1}') -lt 12 ]]; then
api_token=$(/usr/bin/curl -X POST –silent -u "${jamfpro_user}:${jamfpro_password}" "${jamfpro_url}/api/v1/auth/token" | python -c 'import sys, json; print json.load(sys.stdin)["token"]')
else
api_token=$(/usr/bin/curl -X POST –silent -u "${jamfpro_user}:${jamfpro_password}" "${jamfpro_url}/api/v1/auth/token" | plutil -extract token raw –)
fi
}
APITokenValidCheck() {
# Verify that API authentication is using a valid token by running an API command
# which displays the authorization details associated with the current API user.
# The API call will only return the HTTP status code.
api_authentication_check=$(/usr/bin/curl –write-out %{http_code} –silent –output /dev/null "${jamfpro_url}/api/v1/auth" –request GET –header "Authorization: Bearer ${api_token}")
}
CheckAndRenewAPIToken() {
# Verify that API authentication is using a valid token by running an API command
# which displays the authorization details associated with the current API user.
# The API call will only return the HTTP status code.
APITokenValidCheck
# If the api_authentication_check has a value of 200, that means that the current
# bearer token is valid and can be used to authenticate an API call.
if [[ ${api_authentication_check} == 200 ]]; then
# If the current bearer token is valid, it is used to connect to the keep-alive endpoint. This will
# trigger the issuing of a new bearer token and the invalidation of the previous one.
if [[ $(/usr/bin/sw_vers -productVersion | awk -F . '{print $1}') -lt 12 ]]; then
api_token=$(/usr/bin/curl "${jamfpro_url}/api/v1/auth/keep-alive" –silent –request POST –header "Authorization: Bearer ${api_token}" | python -c 'import sys, json; print json.load(sys.stdin)["token"]')
else
api_token=$(/usr/bin/curl "${jamfpro_url}/api/v1/auth/keep-alive" –silent –request POST –header "Authorization: Bearer ${api_token}" | plutil -extract token raw –)
fi
else
# If the current bearer token is not valid, this will trigger the issuing of a new bearer token
# using Basic Authentication.
GetJamfProAPIToken
fi
}
# If you choose to hardcode API information into the script, 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
# Set the Jamf Pro URL here if you want it hardcoded.
jamfpro_url=""
# Set the username here if you want it hardcoded.
jamfpro_user=""
# Set the password here if you want it hardcoded.
jamfpro_password=""
# Read the appropriate values from ~/Library/Preferences/com.github.jamfpro-info.plist
# if the file is available. To create the file, run the following commands:
#
# defaults write $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_url https://jamf.pro.server.here
# defaults write $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_user API_account_username_goes_here
# defaults write $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_password API_account_password_goes_here
#
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%%/}
# If configured to get one, get a Jamf Pro API Bearer Token
if [[ -z "$NoBearerToken" ]]; then
GetJamfProAPIToken
fi
# The following function downloads individual Jamf Pro policy as XML data
# then mines the policy data for the relevant information.
CheckSelfServicePolicies(){
local PolicyId="$1"
if [[ -n "$PolicyId" ]]; then
if [[ -z "$NoBearerToken" ]]; then
CheckAndRenewAPIToken
local DownloadedXMLData=$(/usr/bin/curl -s –header "Authorization: Bearer ${api_token}" -H "Accept: application/xml" "${jamfpro_url}/JSSResource/policies/id/$PolicyId")
else
local DownloadedXMLData=$(/usr/bin/curl -su "${jamfpro_user}:${jamfpro_password}" -H "Accept: application/xml" "${jamfpro_url}/JSSResource/policies/id/$PolicyId")
fi
local PolicyName=$( echo "$DownloadedXMLData" | xmllint –xpath '/policy/general/name/text()'2>/dev/null)
local SelfServicePolicyCheck=$(echo "$DownloadedXMLData" | xmllint –xpath '/policy/self_service/use_for_self_service/text()'2>/dev/null)
# If a policy is detected as being a Self Service policy, specified information is extracted from the downloaded data
# and added to a report in .tsv format.
if [[ "$SelfServicePolicyCheck" = "true" ]]; then
if [[ ! -f "$report_file" ]]; then
touch "$report_file"
printf "Jamf Pro ID Number\tSelf Service Policy\tPolicy Enabled\tPolicy Name\tCategory\tSelf Service Display Name\tJamf Pro URL\n" > "$report_file"
fi
JamfProID=$(echo "$DownloadedXMLData" | xmllint –xpath '//policy/general/id/text()'2>/dev/null)
PolicyEnabled=$(echo "$DownloadedXMLData" | xmllint –xpath '//policy/general/enabled/text()'2>/dev/null)
PolicyName=$(echo "$DownloadedXMLData" | xmllint –xpath '//policy/general/name/text()'2>/dev/null)
PolicyCategory=$(echo "$DownloadedXMLData" | xmllint –xpath '//policy/general/category/name/text()'2>/dev/null)
SelfServiceDisplayName=$(echo "$DownloadedXMLData" | xmllint –xpath '//policy/self_service/self_service_display_name/text()'2>/dev/null)
JamfProURL=$(echo "$jamfpro_url"/policies.html?id="$JamfProID")
if [[ $? -eq 0 ]]; then
printf "$JamfProID\t$SelfServicePolicyCheck\t$PolicyEnabled\t$PolicyName\t$PolicyCategory\t$SelfServiceDisplayName\t${JamfProURL}\n" >> "$report_file"
else
echo "ERROR! Failed to read policy record with ID $JamfProID"
fi
fi
fi
}
progress_indicator() {
spinner="⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"
while :
do
for i in $(seq 0 7)
do
echo -n "${spinner:$i:1}"
echo -en "\010"
sleep 0.10
done
done
}
echo "Report being generated. File location will appear below once ready."
progress_indicator &
SPIN_PID=$!
trap "kill -9 $SPIN_PID" $(seq 0 15)
# Download all Jamf Pro policy ID numbers
if [[ -z "$NoBearerToken" ]]; then
CheckAndRenewAPIToken
PolicyIDList=$(/usr/bin/curl -s –header "Authorization: Bearer ${api_token}" -H "Accept: application/xml" "${jamfpro_url}/JSSResource/policies" | xmllint –xpath '//id'2>/dev/null)
else
PolicyIDList=$(/usr/bin/curl -su "${jamfpro_user}:${jamfpro_password}" -H "Accept: application/xml" "${jamfpro_url}/JSSResource/policies" | xmllint –xpath '//id'2>/dev/null)
fi
PolicyIDs=$(echo "$PolicyIDList" | grep -Eo "[0-9]+")
PoliciesCount=$(echo "$PolicyIDs" | grep -c ^)
echo "Checking $PoliciesCount policies for Self Service policies …"
echo
# Generate report of Self Service policies.
for anID in ${PolicyIDs}; do
CheckSelfServicePolicies $anID
done
kill -9 "$SPIN_PID"
if [[ -f "$report_file" ]]; then
echo "Report on Self Service policies available here: $report_file"
else
echo "ERROR! Report on Self Service policies not found."
exitCode=1
fi
exit $exitCode

Creating AWS S3 buckets for webpage redirection

$
0
0

I recently had an issue where I needed to solve a particular problem:

1. I had a DNS domain name

dns.name.here

2. I needed to point it to a HTTPS URL hosted on another domain:

https://other.dns.name.here/path/to/site/goes/here

3. The DNS server for dns.name.here does not support HTTP Redirect records.

To address this, I decided to use S3 buckets hosted on Amazon Web Services to handle the redirection to the HTTPS URL. In this scenario, what I’m doing is pointing the relevant dns.name.here domain name at the S3 bucket’s AWS domain name. The S3 bucket is performing a HTTP 301 redirect, which sends the requesting web browser the URL of the site I want to connect to. For those interested, Amazon’s documentation of how to use an S3 bucket for URL redirection is linked below:

https://docs.aws.amazon.com/AmazonS3/latest/userguide/how-to-page-redirect.html

After doing it the first time manually, I decided to see if anyone had scripted this task. It turns out the answer is “no”, at least for what I wanted to do, so I’ve written a script which handles this task. For more details, please see below the jump.

The script I’ve developed sets up S3 buckets in Amazon Web Services for use with redirecting DNS or URL requests to an alternative HTTP or HTTPS URL.

Pre-requisites:

The AWS CLI tool must be installed.
The AWS CLI tool must be configured to use AWS programmatic user credentials with the permissions to do the following:

  • Create an S3 bucket
  • Set permissions on the newly-created S3 bucket
  • Apply an S3 bucket policy to the newly-created S3 bucket
  • Apply a website configuration to the newly-created S3 bucket

Usage:

./s3_website_redirection_creator.sh

Once the pre-requisites are met, this script performs the following actions:

  1. Requests a name for an S3 bucket, which should be the DNS name that you want to set up a redirection for.
  2. Requests the AWS region that the S3 bucket should be created in.
  3. Requests the HTTP or HTTPS URL of the website that the redirection is being set up for.

Once the user-requested information is provided, this script performs the following actions:

  1. Creates an S3 bucket using the name supplied by user input
  2. Set permissions on the newly-created S3 bucket so that no public access is permitted.
  3. Set the default encryption behavior for the newly-created S3 bucket to be enabled and to use Amazon S3-managed encryption keys.
  4. Sets an S3 bucket policy which blocks non-SSL connections to the contents of the newly-created S3 bucket.
  5. Set the website configuration for the desired URL redirection for the newly-created bucket.

Screen Shot 2022 10 18 at 4 41 10 PM

Once the S3 bucket is created, you should be able to verify that accessing the HTTP address of the S3 bucket results in your browser being automatically forwarded to the desired website address.

Screen Shot 2022 10 18 at 4 39 25 PM

Screen Shot 2022 10 18 at 4 39 38 PM

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

https://github.com/rtrouton/aws_scripts/tree/main/s3_website_redirection_creator


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 script sets up S3 buckets in Amazon Web Services for use with redirecting
# DNS or URL requests to an alterative HTTP or HTTPS URL.
#
# The following pre-requisites are needed:
#
# * The AWS CLI tool must be installed
# * The AWS CLI tool must have access to AWS programmatic user credentials with the
# permissions to do the following:
#
# * Create an S3 bucket
# * Set permissions on the newly-created S3 bucket
# * Apply an S3 bucket policy to the newly-created S3 bucket
# * Apply a website configuration to the newly-created S3 bucket
#
#
# Once the pre-requisites are met, this script performs the following actions:
#
# A. Requests a name for an S3 bucket, which should be the DNS name that you want to set up a redirection for.
# B. Requests the AWS region that the S3 bucket should be created in.
# C. Requests the HTTP or HTTPS URL of the website that the redirection is being set up for.
#
# Once the user-requested information is provided, this script performs the following actions:
#
# 1. Creates an S3 bucket using the name supplied by user input
# 2. Set permissions on the newly-created S3 bucket so that no public access is permitted.
# 3. Set the default encryption behavior for the newly-created S3 bucket to be enabled and to use Amazon S3-managed encryption keys.
# 4. Sets an S3 bucket policy which blocks non-SSL connections to the contents of the newly-created S3 bucket.
# 5. Set the website configuration for the desired URL redirection for the newly-created bucket.
# Set exit code
exitCode=0
clear
echo "This script sets up S3 buckets in Amazon Web Services for website redirection."
echo "You will need to enter the following information:"
echo ""
echo "A. The name of the new S3 bucket, which should be the DNS name that you want to set up a redirection for."
echo "B. The Amazon Web Services region that you want to create the S3 bucket in."
echo "C. The address of the website address you want to redirect to."
echo ""
read -p "Please enter the name of the new S3 bucket: " s3_bucket_name
read -p "Please enter the AWS region that the new S3 bucket should be created in: " s3_bucket_region
read -p "Please enter the website address you want to redirect to : " website_url
# Figure out if an HTTP or HTTPS URL is being used.
http_protocol=${website_url%://*}
# Get the website URL and split it as necessary for use with the redirection rules.
site=${website_url#*//}
if [[ "$site" == *\/* ]]; then
site=${site%%/*}
site_path=${website_url#*//}
site_path=${site_path#*/}
else
site=${site%%/*}
site_path=""
fi
# Verify that the URL begins with either 'http' or 'https'. If it doesn't, the script
# will display an error message and exit.
if [[ ${http_protocol} != "http" ]] && [[ ${http_protocol} != "https" ]] && [[ ${http_protocol} != "HTTP" ]] && [[ ${http_protocol} != "HTTPS" ]]; then
echo "ERROR – ${website_url} URL begins with $http_protocol, which means it is not a valid HTTP or HTTPS URL. Script will now exit."
exitCode=1
exit "$exitCode"
fi
# The redirection rule Protocol entries need to be lower-case, so
# set HTTP and HTTPS entries to lower-case if needed.
if [[ ${http_protocol} == "HTTP" ]]; then
http_protocol="http"
elif [[ ${http_protocol} == "HTTPS" ]]; then
http_protocol="https"
fi
# Create an S3 website configuration for the desired URL redirection.
if [[ -n ${site_path} ]]; then
read -r -d '' redirectionJSON <<AWS_S3_REDIRECTION_POLICY
{
"IndexDocument": {
"Suffix": "index.html"
},
"ErrorDocument": {
"Key": "error.html"
},
"RoutingRules": [
{
"Redirect": {
"HostName": "$site",
"HttpRedirectCode": "301",
"Protocol": "$http_protocol",
"ReplaceKeyPrefixWith": "$site_path"
}
}
]
}
AWS_S3_REDIRECTION_POLICY
else
read -r -d '' redirectionJSON <<AWS_S3_REDIRECTION_POLICY
{
"IndexDocument": {
"Suffix": "index.html"
},
"ErrorDocument": {
"Key": "error.html"
},
"RoutingRules": [
{
"Redirect": {
"HostName": "$site",
"HttpRedirectCode": "301",
"Protocol": "$http_protocol"
}
}
]
}
AWS_S3_REDIRECTION_POLICY
fi
redirectionJSON_file=$(mktemp)
echo "$redirectionJSON" > "$redirectionJSON_file"
# Create an S3 bucket policy which blocks non-SSL connections to the contents
# of the S3 bucket.
read -r -d '' bucketpolicyJSON <<AWS_S3_BUCKET_POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowSSLRequestsOnly",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::$s3_bucket_name",
"arn:aws:s3:::$s3_bucket_name/*"
],
"Condition": {
"Bool": {
"aws:SecureTransport": "false"
}
}
}
]
}
AWS_S3_BUCKET_POLICY
bucketpolicyJSON_file=$(mktemp)
echo "$bucketpolicyJSON" > "$bucketpolicyJSON_file"
# Create the S3 bucket.
if [[ ${s3_bucket_region} = "us-east-1" ]]; then
aws s3api create-bucket –bucket ${s3_bucket_name} –region ${s3_bucket_region} 2>&1 > /dev/null
else
aws s3api create-bucket –bucket ${s3_bucket_name} –region ${s3_bucket_region} –create-bucket-configuration LocationConstraint=${s3_bucket_region} 2>&1 > /dev/null
fi
# Set permissions on the newly-created S3 bucket so that no public access is permitted.
aws s3api put-public-access-block –bucket ${s3_bucket_name} –public-access-block-configuration "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true" 2>&1 > /dev/null
# Set the default encryption behavior for the newly-created S3 bucket to be enabled and to use Amazon S3-managed encryption keys.
aws s3api put-bucket-encryption –bucket ${s3_bucket_name} –server-side-encryption-configuration '{"Rules": [{"ApplyServerSideEncryptionByDefault": {"SSEAlgorithm": "AES256"}}]}' 2>&1 > /dev/null
# Sets an S3 bucket policy which blocks non-SSL connections to the contents of the newly-created S3 bucket.
aws s3api put-bucket-policy –bucket ${s3_bucket_name} –policy file://$bucketpolicyJSON_file 2>&1 > /dev/null
# Set the website configuration for the desired URL redirection for the newly-created bucket.
aws s3api put-bucket-website –bucket ${s3_bucket_name} –website-configuration file://$redirectionJSON_file 2>&1 > /dev/null
echo "New S3 bucket is available from the address below:"
echo ""
echo "S3 bucket name: ${s3_bucket_name}"
echo "S3 bucket location: ${s3_bucket_region}"
echo "S3 bucket website URL: http://${s3_bucket_name}.s3-website-${s3_bucket_region}.amazonaws.com"
echo ""
echo "Going to http://${s3_bucket_name}.s3-website-${s3_bucket_region}.amazonaws.com in a web browser should automatically redirect the browser to the address below: "
echo ""
echo "${website_url}"
exit "$exitCode"

Opening macOS Ventura’s System Settings to desired locations via the command line

$
0
0

With the release of macOS Ventura, the System Preferences application has been replaced with the System Settings application.

macOS Monterey System Preferences:

Screen Shot 2022 10 25 at 3 08 11 PM

macOS Ventura System Settings:

Screenshot 2022 10 25 at 3 10 04 PM

Along with this change, a number of previously-known commands for opening individual System Preferences preference panes from the command line no longer work with System Settings.

However, it looks like the underlying command line functionality wasn’t changed by Apple. You just need to know what the new options are to enter. For more details, please see below the jump.

I’ve put together a list of the ones I’ve found to work, which is available below. Find any more? Please let me know in the comments:


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


Open Storage, in System Settings: General:
open x-apple.systempreferences:com.apple.settings.Storage
Open Software Update, in System Settings: General:
open x-apple.systempreferences:com.apple.Software-Update-Settings.extension
Open General, in System Settings:
open x-apple.systempreferences:com.apple.systempreferences.GeneralSettings
Open Privacy & Security, in System Settings:
open x-apple.systempreferences:com.apple.preference.security
Open Privacy & Security, in System Settings:
open x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension
Open Startup Disk, in System Settings: General:
open x-apple.systempreferences:com.apple.preference.startupdisk
Open Startup Disk, in System Settings: General:
open x-apple.systempreferences:com.apple.Startup-Disk-Settings.extension
Open Displays, in System Settings:
open x-apple.systempreferences:com.apple.preference.displays
Open Wallpaper, in System Settings:
open x-apple.systempreferences:com.apple.Wallpaper-Settings.extension
Open Network, in System Settings:
open x-apple.systempreferences:com.apple.preference.network
Open Network, in System Settings:
open x-apple.systempreferences:com.apple.Network-Settings.extension
Open Profiles, in System Settings: Privacy & Security:
open x-apple.systempreferences:com.apple.Profiles-Settings.extension
Open Transfer or Reset, in System Settings: General:
open x-apple.systempreferences:com.apple.Transfer-Reset-Settings.extension
Open Date & Time, in System Settings: General:
open x-apple.systempreferences:com.apple.Date-Time-Settings.extension
Open About, in System Settings: General:
open x-apple.systempreferences:com.apple.SystemProfiler.AboutExtension
Open Language & Region, in System Settings: General:
open x-apple.systempreferences:com.apple.Localization-Settings.extension
Open Login Items, in System Settings: General:
open x-apple.systempreferences:com.apple.LoginItems-Settings.extension
Open Sharing, in System Settings: General:
open x-apple.systempreferences:com.apple.Sharing-Settings.extension
Open AirDrop & Handoff, in System Settings: General
open x-apple.systempreferences:com.apple.AirDrop-Handoff-Settings.extension
Open Time Machine, in System Settings: General
open x-apple.systempreferences:com.apple.Time-Machine-Settings.extension
Open Appearance, in System Settings:
open x-apple.systempreferences:com.apple.Appearance-Settings.extension
Open Apple ID, in System Settings:
open x-apple.systempreferences:com.apple.preferences.AppleIDPrefPane

Adding hidden Login Items on macOS Ventura

$
0
0

One of the changes made between macOS Monterey’s System Preferences and macOS Ventura’s System Settings is that the Hide checkbox in System Preferences’ Login Items has disappeared from System Settings’ Login Items.

Login Items in System Preferences

Screen Shot 2022 10 27 at 2 25 28 PM

Login Items in System Settings

Screenshot 2022 10 27 at 2 40 18 PM

Fortunately for those who want to continue being able to launch applications on login and automatically hide them, it’s still possible to do so on macOS Ventura from the command line using osascript.

To do this, run a command similar to the one shown below using the logged-in user’s privileges:

/usr/bin/osascript -e 'tell application "System Events" to make login item at end with properties {path:"/path/to/itemname", hidden:true}'

For example, if you want Safari to launch at login with its windows automatically hidden, run the command below using the logged-in user’s privileges:

/usr/bin/osascript -e 'tell application "System Events" to make login item at end with properties {path:"/Applications/Safari.app", hidden:true}'

Safari will appear in the Login Items list without any sign that it’s launching as hidden, but the application behavior on login will be just like it would be on earlier versions of macOS where the Hide checkbox was checked.

Downloading macOS Monterey from the App Store

$
0
0

Now that macOS Ventura has been released, it’s become more difficult to access the macOS Monterey installer for those who still need it. Fortunately, macOS Monterey has not been removed from the App Store and it is still available for download. Apple has a KBase article that shows how to access the macOS Monterey page in the App Store, available via the link below:

https://support.apple.com/HT211683

Screenshot 2022 11 11 at 5 24 32 PM

To access the macOS Monterey page directly, please click on the link below:

https://apps.apple.com/us/app/macos-monterey/id1576738294?mt=12

That link should open the App Store and take you to the macOS Monterey download page.

Screenshot 2022 11 11 at 5 26 18 PM

 

In the event that you’re blocked from downloading macOS Monterey, you should be able to download it in a virtual machine. I have a post on how to do this, available via the link below:

https://derflounder.wordpress.com/2017/02/21/downloading-older-os-installers-on-incompatible-hardware-using-vms/

Apple Device Management Second Edition book coming soon

$
0
0

Three years back, both Charles Edge and I wrote a book together:

We decided to put the band back together for a Second Edition, which I’m pleased to say is far enough along in the publishing process that it’s been assigned an ISBN number and a listing on Amazon.

While it’s not yet available for pre-order, hopefully you’ll be able to add a pre-order for it to your Christmas shopping list!

Charles and I were also on the Mac Admins Podcast to discuss the new book so if you’re interested in learning more, please see the link below:

https://podcast.macadmins.org/2022/09/27/episode-284-rich-trouton-on-apple-device-management-2nd-edition/

Downloading macOS IPSW files for use with Mac virtual machines on Apple Silicon Macs

$
0
0

A change between creating Mac virtual machines on Intel Macs and creating them on Apple Silicon Macs is that virtualization on Apple Silicon Macs often assumes that the virtual machine is built using a macOS restore image . These restore images are files with an .ipsw file extension and are commonly referred to as IPSW files.

Apple publishes the download links for macOS restore images via the following URL:

https://mesu.apple.com/assets/macos/com_apple_macOSIPSW/com_apple_macOSIPSW.xml

If you look at the XML file from the link above, it provides download links for the current version of macOS for the various Mac models which support running that version of macOS.

Among the various models listed is the model identifier for Mac virtual machines (VirtualMac2,1) created using Apple’s Virtualization framework. This means that we should be able to identify and download the appropriate IPSW file for use when building Mac virtual machines.

Screenshot 2022-11-16 at 7.33.48 PM

Using this information, I’ve written a script to download the appropriate IPSW file for building macOS virtual machines by checking the file linked above for the download URL associated with the VirtualMac2,1 Mac model. For more details, please see below the jump.

The script checks Apple’s IPSW feed to get the appropriate IPSW file for the current release of macOS used by the VirtualMac2,1 Mac model. If it finds a matching IPSW download URL, it will take the following actions:

  1. Download the IPSW file to a temp directory.
  2. If the download succeeds, a message is displayed notifying the user that the download has completed and where the IPSW file is stored.
  3. If the download fails, a message is displayed notifying the user that the download failed and the script exits with an error.

Usage:

./download_latest_macOS_ipsw_for_virtualization.sh

Screenshot 2022 11 16 at 10 39 10 PM

Screenshot 2022 11 16 at 10 42 14 PM

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

https://github.com/rtrouton/rtrouton_scripts/tree/main/rtrouton_scripts/download_latest_macOS_ipsw_for_virtualization


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 script checks Apple's IPSW feed to get the appropriate IPSW file
# for the current release of macOS used by the VirtualMac2,1 virtualization
# Mac model.
clear
exitCode=0
Apple_macOS_IPSW_Download_Directory=$(mktemp -d)
Apple_macOS_IPSW_Feed="https://mesu.apple.com/assets/macos/com_apple_macOSIPSW/com_apple_macOSIPSW.xml"
Apple_macOS_IPSW_XML=$(/usr/bin/curl -s "$Apple_macOS_IPSW_Feed" | xmllint –format –)
Apple_macOS_IPSW_Download_URL=$(/usr/libexec/PlistBuddy -c 'print ":MobileDeviceSoftwareVersionsByVersion:1:MobileDeviceSoftwareVersions:VirtualMac2,1"' /dev/stdin <<< "$Apple_macOS_IPSW_XML" | awk '/FirmwareURL/ {print $3}')
Apple_macOS_IPSW_Filename=$(echo "$Apple_macOS_IPSW_Download_URL" | awk -F / '{print $NF}')
# Verify that the IPSW download URL contains a filename which ends in .ipsw
if [[ -n $(echo "$Apple_macOS_IPSW_Download_URL" | grep -o ".ipsw") ]]; then
# If the IPSW download URL contains a filename which ends in .ipsw,
# download the IPSW file and store it in a temp directory.
echo "Downloading $Apple_macOS_IPSW_Filename"
echo "From: $Apple_macOS_IPSW_Download_URL"
echo "To: $Apple_macOS_IPSW_Download_Directory/$Apple_macOS_IPSW_Filename"
echo ""
/usr/bin/curl -L "$Apple_macOS_IPSW_Download_URL" -o "$Apple_macOS_IPSW_Download_Directory"/"$Apple_macOS_IPSW_Filename" && download_success=1
# If the download succeeds, display a message notifying the user that the
# download has completed and where the IPSW file is stored.
#
# If the download fails, display a message notifying the user that the download failed
# and exit with an error.
if [[ -n "$download_success" ]] && [[ -f "$Apple_macOS_IPSW_Download_Directory"/"$Apple_macOS_IPSW_Filename" ]]; then
echo ""
echo "$Apple_macOS_IPSW_Filename has been downloaded to the following location:"
echo "$Apple_macOS_IPSW_Download_Directory/$Apple_macOS_IPSW_Filename"
else
echo "Download of $Apple_macOS_IPSW_Filename from $Apple_macOS_IPSW_Download_URL has failed. Exiting."
exitCode=1
fi
else
# If the IPSW download URL does not contain a filename which ends in .ipsw,
# display a message notifying the user that an IPSW file was not found and
# exit with an error.
echo "Unable to detect macOS IPSW file to download. Exiting."
exitCode=1
fi
exit "$exitCode"

autopkg-conductor updated to support reporting to Slack and Microsoft Teams

$
0
0

When the autopkg-conductor tool was first written, one of its primary functions was to send the output of JSSImporter to a Slack channel. With JSSImporter being deprecated in favor of JamfUploader, I’ve decided to do the following:

  1. Drop support for JSSImporter.
  2. Add additional reporting options for JamfUploader.

As of the current version to the tool, autopkg-conductor can send output from JamfUploader to the following:

For more details, please see below the jump.

autopkg-conductor can now be configured to send output to Slack, Teams or to both Slack and Teams. The following AutoPkg processors are being leveraged for this:

To configure autopkg-conductor to send to Slack using the JamfUploaderSlacker AutoPkg processor, the following variables need to be configured:

  • slack_post_processor
  • slack_webhook

To configure autopkg-conductor to send to Teams using the JamfUploaderTeamsNotifier AutoPkg processor, the following variables need to be configured:

  • teams_post_processor
  • teams_webhook

To configure autopkg-conductor to send to both Slack and Teams, all four variables need to be configured:

  • slack_post_processor
  • slack_webhook
  • teams_post_processor
  • teams_webhook

Screenshot 2022 11 19 at 3 35 15 PM

Both the JamfUploaderSlacker and the JamfUploaderTeamsNotifier AutoPkg processors should be included with JamfUploader. The message which appears in Slack should look similar to what is shown below:

Screenshot 2022 11 19 at 2 50 14 PM

The message which appears in Teams should look similar to what is shown below:

Screenshot 2022 11 19 at 2 46 31 PM

Error logs should also be sent to Slack and/or Teams. There will be differences in appearance, as the script sends the error log one line at a time. Slack and Teams handle this differently in terms of formatting, so the error logs should appear similar to what’s shown below:

Slack:

Screenshot 2022 11 19 at 2 54 09 PM

Teams:

Screenshot 2022 11 19 at 2 53 37 PM

The autopkg-conductor script is available below. It’s also available from GitHub using the following link:

https://github.com/rtrouton/autopkg-conductor


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
# AutoPkg automation script
# Adjust the following variables for your particular configuration.
#
# autopkg_user_account – This should be the user account you're running AutoPkg in.
# autopkg_user_account_home – This should be the home folder location of the AutoPkg user account
#
# Note: The home folder location is currently set to be automatically discovered
# using the autopkg_user_account variable.
#
# recipe_list – This is the location of the plain text file being used to store
# your list of AutoPkg recipes. For more information about this list, please see
# the link below:
#
# https://github.com/autopkg/autopkg/wiki/Running-Multiple-Recipes
#
# log_location – This should be the location and name of the AutoPkg run logs.
#
# Note: The location is currently set to be automatically discovered
# using the autopkg_user_account_home variable.
autopkg_user_account="username_goes_here"
autopkg_user_account_home=$(/usr/bin/dscl . -read /Users/"$autopkg_user_account" NFSHomeDirectory | awk '{print $2}')
recipe_list="/path/to/recipe_list.txt"
log_location="$autopkg_user_account_home/Library/Logs/autopkg-run-for-$(date +%Y-%m-%d-%H%M%S).log"
# If you're using Jamf Upload, the URL of your Jamf Pro server should be populated into the jamfpro_server variable automatically.
#
# If you're not using Jamf Upload, this variable will return nothing and that's OK.
jamfpro_server=$(/usr/bin/defaults read "$autopkg_user_account_home"/Library/Preferences/com.github.autopkg JSS_URL)
# Optional variables
# This script supports using either Jamf Upload's JamfUploaderSlacker or Jamf Upload's JamfUploaderTeamsNotifier processors
# JamfUploaderSlacker – used with Jamf Upload
#
# To use the JamfUploaderSlacker post-processor, you'll need to use add Graham Pugh's
# Autopkg repo by running the command below:
#
# autopkg repo-add grahampugh-recipes
#
# The slack_post_processor variable should look like this:
# slack_post_processor="com.github.grahampugh.jamf-upload.processors/JamfUploaderSlacker"
slack_post_processor=""
# JamfUploaderTeamsNotifier – used with Jamf Upload
#
# To use the JamfUploaderTeamsNotifier post-processor, you'll need to use add Graham Pugh's
# Autopkg repo by running the command below:
#
# autopkg repo-add grahampugh-recipes
#
# The teams_post_processor variable should look like this:
# teams_post_processor="com.github.grahampugh.jamf-upload.processors/JamfUploaderTeamsNotifier"
teams_post_processor=""
# If you're sending the results of your AutoPkg run to Slack, you'll need to set up
# a Slack webhook to receive the information being sent by the script.
# If you need help with configuring a Slack webhook, please see the links below:
#
# https://api.slack.com/incoming-webhooks
# https://get.slack.help/hc/en-us/articles/115005265063-Incoming-WebHooks-for-Slack
#
# Once a Slack webhook is available, the slack_webhook variable should look similar
# to this:
# slack_webhook="https://hooks.slack.com/services/XXXXXXXXX/YYYYYYYYY/ZZZZZZZZZZ&quot;
slack_webhook=""
# If you're sending the results of your AutoPkg run to Teams, you'll need to set up
# a Teams webhook to receive the information being sent by the script.
# If you need help with configuring a Slack webhook, please see the links below:
#
# https://learn.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook
#
# Once a Teams webhook is available, the teams_webhook variable should look similar
# to this:
# teams_webhook="https://companyname.webhook.office.com/webhookb2/7ce853bd-a9e1-462f-ae32-d3d35ed5295d@7c155bae-5207-4bb5-8b58-c43228bc1bb7/IncomingWebhook/8155d8581864479287b68b93f89556ae/651e63f8-2d96-42ab-bb51-65cb05fc62aa&quot;
teams_webhook=""
# don't change anything below this line
# Set script exit status
exit_error=0
# Define logger behavior
ScriptLogging(){
DATE=$(date +%Y-%m-%d\ %H:%M:%S)
LOG="$log_location"
echo "$DATE" " $1" >> $LOG
}
# Function for sending multi-line output to a Slack webhook. Original script from here:
#
# http://blog.getpostman.com/2015/12/23/stream-any-log-file-to-slack-using-curl/
SendToSlack(){
cat "$1" | while read LINE; do
(echo "$LINE" | grep -e "$3") && curl -X POST –silent –data-urlencode "payload={\"text\": \"$(echo $LINE | sed "s/\"/'/g")\"}" "$2";
done
}
# Function for sending multi-line output to a Teams webhook.
SendToTeams(){
while read LINE; do
(echo "${LINE}" | grep -e "$3") && curl -X POST -H 'Content-Type: application/json' –silent -d "{\"text\": \"${LINE//\"/\'/}\"}" "$2";
done < "$1"
}
# Function for AutoPkg runs
RunAutoPkg(){
if [[ ! -z "$slack_autopkg_report" ]] && [[ -z "$teams_autopkg_report" ]]; then
/usr/local/bin/autopkg run –recipe-list="${recipe_list}" –post=${slack_post_processor} –key ${slack_autopkg_postprocessor_key}=${slack_webhook} >> /tmp/autopkg.out 2>>/tmp/autopkg_error.out
elif [[ -z "$slack_autopkg_report" ]] && [[ ! -z "$teams_autopkg_report" ]]; then
/usr/local/bin/autopkg run –recipe-list="${recipe_list}" –post=${teams_post_processor} –key ${teams_autopkg_postprocessor_key}=${teams_webhook} >> /tmp/autopkg.out 2>>/tmp/autopkg_error.out
elif [[ ! -z "$slack_autopkg_report" ]] && [[ ! -z "$teams_autopkg_report" ]]; then
/usr/local/bin/autopkg run –recipe-list="${recipe_list}" –post=${slack_post_processor} –key ${slack_autopkg_postprocessor_key}=${slack_webhook} –post=${teams_post_processor} –key ${teams_autopkg_postprocessor_key}=${teams_webhook} >> /tmp/autopkg.out 2>>/tmp/autopkg_error.out
else
/usr/local/bin/autopkg run –recipe-list="${recipe_list}" >> /tmp/autopkg.out 2>>/tmp/autopkg_error.out
fi
}
# The key used by the JamfUploaderSlacker and JamfUploaderTeamsNotifier AutoPkg processors is slightly different
# so the right one needs to be used when running AutoPkg.
#
# JamfUploaderSlacker: slack_webhook_url
#
# JamfUploaderTeamsNotifier: teams_webhook_url
#
# The slack_autopkg_postprocessor and teams_autopkg_postprocessor variables will enable
# the script to identify the correct key for the processor.
slack_autopkg_postprocessor=${slack_post_processor#*/}
teams_autopkg_postprocessor=${teams_post_processor#*/}
# If the AutoPkg run's log file is not available, create it
if [[ ! -r "$log_location" ]]; then
touch "$log_location"
fi
# If the AutoPkg recipe list is missing or unreadable, stop the script with an error.
if [[ ! -r "$recipe_list" ]]; then
ScriptLogging "Error Detected. Unable to start AutoPkg run."
echo "" > /tmp/autopkg_error.out
if [[ "$jamfpro_server" = "" ]]; then
echo "AutoPkg run failed" >> /tmp/autopkg_error.out
else
echo "AutoPkg run for $jamfpro_server failed" >> /tmp/autopkg_error.out
fi
echo "$recipe_list is missing or unreadable. Fix immediately." >> /tmp/autopkg_error.out
echo "" > /tmp/autopkg.out
# If a Slack webhook is configured, send the error log to Slack.
if [[ ! -z "$slack_webhook" ]]; then
SendToSlack /tmp/autopkg_error.out ${slack_webhook}
fi
cat /tmp/autopkg_error.out >> "$log_location"
ScriptLogging "Finished AutoPkg run"
exit_error=1
fi
# If the the AutoPkg recipe list is readable and AutoPkg is installed,
# run the recipes stored in the recipe list.
if [[ -x /usr/local/bin/autopkg ]] && [[ -r "$recipe_list" ]]; then
ScriptLogging "AutoPkg installed at $(which autopkg)"
ScriptLogging "Recipe list located at $recipe_list and is readable."
echo "" > /tmp/autopkg.out
if [[ "$jamfpro_server" = "" ]]; then
echo "Starting AutoPkg run" >> /tmp/autopkg_error.out
else
echo "Starting AutoPkg run for $jamfpro_server" >> /tmp/autopkg.out
fi
echo "" >> /tmp/autopkg.out
echo "" > /tmp/autopkg_error.out
if [[ "$jamfpro_server" = "" ]]; then
echo "Error log for AutoPkg run" >> /tmp/autopkg_error.out
else
echo "Error log for AutoPkg run to $jamfpro_server" >> /tmp/autopkg_error.out
fi
echo "" >> /tmp/autopkg_error.out
/usr/local/bin/autopkg repo-update all 2>&1 >> /tmp/autopkg.out 2>>/tmp/autopkg_error.out
cat /tmp/autopkg.out >> "$log_location" && cat /tmp/autopkg_error.out >> "$log_location"
# If a webhook for Slack is configured, send output to Slack
if [[ ! -z "$slack_webhook" ]]; then
if [[ ! -z "$slack_post_processor" ]] && [[ ! -z "$slack_autopkg_postprocessor" ]]; then
if [[ ${slack_autopkg_postprocessor} = "JamfUploaderSlacker" ]]; then
# If both a post-processor to post to Slack and a Slack webhook are configured and nothing is configured for Teams,
# the Jamf Upload recipes should have their outputs posted to Slack using the post-processor, while all other
# output should go to /tmp/autopkg.out. All standard error output should go to /tmp/autopkg_error.out
if [[ ${slack_autopkg_postprocessor} = "JamfUploaderSlacker" ]]; then
slack_autopkg_postprocessor_key="slack_webhook_url"
slack_autopkg_report=1
fi
fi
fi
fi
# If a webhook for Teams is configured, send output to Teams
if [[ ! -z "$teams_webhook" ]]; then
if [[ ! -z "$teams_post_processor" ]] && [[ ! -z "$teams_autopkg_postprocessor" ]]; then
if [[ ${teams_autopkg_postprocessor} = "JamfUploaderTeamsNotifier" ]]; then
# If both a post-processor to post to Teams and a Teams webhook are configured and nothing is configured for Slack,
# the Jamf Upload recipes should have their outputs posted to Teams using the post-processor, while all other
# output should go to /tmp/autopkg.out. All standard error output should go to /tmp/autopkg_error.out
if [[ ${teams_autopkg_postprocessor} = "JamfUploaderTeamsNotifier" ]]; then
teams_autopkg_postprocessor_key="teams_webhook_url"
teams_autopkg_report=1
fi
fi
fi
fi
# Run AutoPkg with the configured reporting options for Slack and/or Teams
RunAutoPkg
if [[ "$jamfpro_server" = "" ]]; then
echo "Finished with AutoPkg run" >> /tmp/autopkg.out
else
echo "Finished with AutoPkg run for $jamfpro_server" >> /tmp/autopkg.out
fi
echo "" >> /tmp/autopkg.out && echo "" >> /tmp/autopkg_error.out
cat /tmp/autopkg.out >> "$log_location"
cat /tmp/autopkg_error.out >> "$log_location"
ScriptLogging "Finished AutoPkg run"
echo "" >> /tmp/autopkg_error.out
echo "End of error log for AutoPkg run" >> /tmp/autopkg_error.out
echo "" >> /tmp/autopkg_error.out
if [[ -z "$slack_post_processor" ]] && [[ ! -z "$slack_webhook" ]]; then
# If the AutoPkg post-processor for posting to Slack is
# not configured but we do have a Slack webhook set up,
# all standard output should be sent to Slack.
ScriptLogging "Sending AutoPkg output log to Slack"
SendToSlack /tmp/autopkg.out ${slack_webhook}
ScriptLogging "Sent AutoPkg output log to $slack_webhook."
fi
if [[ -z "$teams_post_processor" ]] && [[ ! -z "$teams_webhook" ]]; then
# If the AutoPkg post-processor for posting to Teams is
# not configured but we do have a Teams webhook set up,
# all standard output should be sent to Teams.
ScriptLogging "Sending AutoPkg output log to Teams"
SendToTeams /tmp/autopkg.out ${teams_webhook}
ScriptLogging "Sent AutoPkg output log to $teams_webhook."
fi
if [[ ! -z "$slack_webhook" ]]; then
# If using a Slack webhook, at the end of the AutoPkg run all standard
# error output logged to /tmp/autopkg_error.out should be output to Slack,
# using the SendToSlack function.
if [[ $(wc -l </tmp/autopkg_error.out) -gt 7 ]]; then
ScriptLogging "Sending AutoPkg error log to Slack"
SendToSlack /tmp/autopkg_error.out ${slack_webhook}
ScriptLogging "Sent autopkg log to $slack_webhook. Ending run."
else
ScriptLogging "Error log was empty. Nothing to send to Slack."
fi
fi
if [[ ! -z "$teams_webhook" ]]; then
# If using a Teams webhook, at the end of the AutoPkg run all standard
# error output logged to /tmp/autopkg_error.out should be output to Teams,
# using the SendToTeams function.
if [[ $(wc -l </tmp/autopkg_error.out) -gt 7 ]]; then
ScriptLogging "Sending AutoPkg error log to Teams"
SendToTeams /tmp/autopkg_error.out ${teams_webhook}
ScriptLogging "Sent autopkg log to $teams_webhook. Ending run."
else
ScriptLogging "Error log was empty. Nothing to send to Teams."
fi
fi
fi
exit "$exit_error"
Viewing all 764 articles
Browse latest View live