Welcome

Welcome to the MacAdmins Documentation.

The goal of this website is to assemble best-practices and useful docs found from the web. It is curated by top-notch MacAdmins.

It is dedicated to Investigate, Clarify and Resolve key issues in managing Macs.

It is independent and welcomed to every MacAdmin.

Think of a collection of pages that you would use as ultimate reference.

The goal of this repository is to:

  • Help the beginers to learn the basics
  • Help the veterants to keep current on technologies
  • Provide a quick way to answer common questions
  • Be an authoritative answer to endless discussions
  • Be a checklist to assess the state of your current work

macOS is getting more and more used by Businesses. This means more and more non-Mac Admins need to learn how to administer Macs. Unfortunately, today only a few resources are available. They might give you technical answers, but to get the full experience, you need to understand the vision, or philosophy. It’s like using open source projects and embracing the GNU philosophy.

https://i.creativecommons.org/l/by-sa/4.0/88x31.png

_MacAdmins Doc_ is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License

General Mac Knowledge

Apple Support Knowledge Base

Packaging

Forgetting a Package

Forgetting a package is a good way to troubleshoot some behaviours. It doesn’t change anything on disk, but the computer will believe the package was never installed.

Installer.app/SWU

For OS X packages, installed by Installer.app or Software update, use sudo pkgutil --forget [package_id]. You can list current installed packages with pkgutil --pkgs

This will get updated at next recon to Inventory > Package Receipts > Installer.app/SWU.

Note: According to pkgutil(1):

Discard all receipt data about package-id, but do not touch the installed files.
DO NOT use this command from an installer package script to fix broken package design.
Casper Suite

To change this (unrelated) list, you need to delete the relevant file in /Library/Application Support/JAMF/Receipts, then do a sudo jamf recon

This doesn’t do anything but change inventory.

macOS Installation

Setup Assistant

Setup Assistant is also called “MacBuddy”

Setup Assistant Panes
Name Log Short Name MDM Key MDM Skippable? File Trigger
Choose Language     No /var/db/.AppleSetupDone
Select Keyboard SelectKeyboard   No /var/db/.AppleSetupDone
Network Setup SelectWiFiNetwork   No /var/db/.AppleSetupDone
Transfer Data MigrationWelcome Restore Yes /var/db/.AppleSetupDone
Location Services EnableCoreLocation Location Yes /var/db/.AppleSetupDone
Apple ID and iCloud Sign-in iCloudLogin AppleID Yes com.apple.SetupAssistant.plist
Terms and Conditions LicenseViewer TOS Yes /var/db/.AppleSetupDone
Create User Account CreateUserAccount   Yes* /var/db/.AppleSetupDone
Automatically sending diagnostic information DiagnosticsAndUsage Diagnostics Yes com.apple.SetupAssistant.plist
Siri EnableSiri Siri Yes /var/db/.AppleSetupDone
Touch ID   Biometric Yes /var/db/.AppleSetupDone
Apple Pay   Payment Yes /var/db/.AppleSetupDone
Setting Up Your Mac SettingUpYourMac   No /var/db/.AppleSetupDone

*Initial User Creation can be skipped under certain conditions

Skipping Setup Assistant
With an MDM

Having a MDM can allow skipping some steps.

More info on MDM protocol here (search for skip_setup_items)

By messing with some files

Setup Assistant will only launch if /var/db/.AppleSetupDone is not present. Deleting this key will skip most of the steps.

/Users/[username]/Library/Preferences/com.apple.SetupAssistant.plist will store iCloud/Apple ID setup and Diagnostic Information agreement. More info on Rich’s blog

Mager Valp has an interesting script you might want to check.

macOS Configuration

System Preferences

Personal
_images/SysPrefs-10.12.3-Personal.png
Hardware
_images/SysPrefs-10.12.3-Hardware.png
Network
_images/SysPrefs-10.12.3-Network.png
System
_images/SysPrefs-10.12.3-System.png
Third Party
_images/SysPrefs-10.12.3-ThirdParty.png

Files and Storage

Storage Structure

Partition Schemes
Acronym Name Description
APM Apple Partition Map This is the traditional Apple partitioning scheme used to start up a PowerPC-based Macintosh computer, to use the disk as a non-startup disk with any Mac, or to create a multiplatform compatible startup disk.
MBR Master Boot Record This is the DOS/Windows-compatible partitioning scheme.
GPT GUID Partitioning Table This is the partitioning scheme used to start up an Intel-based Macintosh computer.

Source: diskutil(8)

Filesystems
Acronym Name Description
APFS APFS  
ExFAT ExFAT  
Free Space (or free) Free Space  
MS-DOS MS-DOS (FAT)  
MS-DOS FAT12 MS-DOS (FAT12)  
MS-DOS FAT16 MS-DOS (FAT16)  
MS-DOS FAT32 (or fat32) MS-DOS (FAT)  
HFS+ Mac OS Extended  
Case-sensitive HFS+ (or hfsx) Mac OS Extended (Case-sensitive)  
Case-sensitive Journaled HFS+ (or jhfsx) Mac OS Extended (Case-sensitive, Journaled)  
Journaled HFS+ (or jhfs+) Mac OS Extended (Journaled)  

Source: diskutil(8)

APFS

APFS is the new FileSystem that was announced at WWDC ‘16. It will be available on all Mac and iOS devices in 2017.

It features awesome new and improved features such as:

  • Clones
  • Snapshots
  • Space Sharing
  • Encryption
  • Crash Protection
  • Sparse Files
  • Fast Directory Sizing
  • Atomic Safe-Save

Rich Trouton did a very interesting talk at MacAdUk. Grab it here.

Source: APFS Guide

CoreStorage
Acronym Name Description
LVG Logical Volume Group  
PV Physical Volume  
LVF Logical Volume Family  
LV Logical Volume  

Source: diskutil(8)

Peripherals

Printing

On OS X, the printing subsystem is CUPS.

Ways to modify CUPS configuration
Options
Set default printer

sudo lpadmin -d [printer]

Enable Kerberos Authentication

sudo lpadmin -p [printer] -o auth-info-required=negotiate

You can eventually follow this article from Apple.

Change default options

To change defaults, use this command: sudo lpadmin -p [printer] -o [option]=[value]. For example: sudo lpadmin -p Follow-Me -o XRBannerSheet=None

List available options

Use lpoptions -p [printer] -l.

Notable options
Option Values Description
printer_is_shared true/false share printer
auth-info-required “none”, “username,password”, “domain,username,password”, or “negotiate” (Kerberos) Set to negotiate to allow Kerberos
media Letter A4… See here for more info
XRBannerSheet *None AtStart On Xerox, displays the coverpage with Job ID

More info here

Discovering options

This will allow you to make changes using a GUI, and find the right option.

Using GUI
  1. Open print dialog
  2. Create a preset
  3. execute defaults read ~/Library/Preferences/com.apple.print.custompresets.forprinter.[printer].plist [preset] > before.txt
  4. Make changes
  5. Create a new preset
  6. execute defaults read ~/Library/Preferences/com.apple.print.custompresets.forprinter.[printer].plist [new_preset] > after.txt
  7. See differences with diff before.txt after.txt
Using CUPS Web

I found it quite interesting to follow this:

  1. lpoptions -p [printer] -l > before.txt
  2. Make the changes on http://localhost:631/printers/ > Printer > Set default Options
  3. Run lpoptions -p [printer] -l > after.txt
  4. See differences with diff before.txt after.txt
Adding a printer
Network Printer

The command to install a printer is lpadmin. You will need to specify:

  • -E to Enable the destination and accept jobs
  • -p [name]: name of the printer
  • -v [uri]: path to the queue (smb://server/queue)
  • -P [PPD]: path to PPD (usually in /Library/Printers/PPDs/Contents/Resources/)
  • -o [option]=[value]: specify options
Example
#!/bin/bash
#
# Installs printer, using Xerox Drivers (Xerox_Print_Driver_3.52.0.pkg)
#

readonly LPSTAT='/usr/bin/lpstat'
readonly LPADMIN='/usr/sbin/lpadmin'
readonly CUPSENABLE='/usr/sbin/cupsenable'
readonly CUPSACCEPT='/usr/sbin/cupsaccept'


#######################################
# Add printers using cups
# Globals:
#   LPSTAT
#   LPADMIN
#   CUPSENABLE
#   CUPSACCEPT
# Arguments:
#   name
#   uri
#   ppd
# Returns:
#   None
#######################################

add_printer() {

  local name="$1"
  local uri="$2"
  local ppd="$3"

  if ! ${LPADMIN} -E -p "${name}" \
    -v "${uri}" \
    -P "${ppd}" \
    -o printer_is_shared=false \
    -o auth-info-required=negotiate \
    -o XRBannerSheet=None \
    -o media=iso_a4_210x297mm; then
      echo "ERROR: ${name}: Unable to lpadmin (add printer)" >&2
      exit -1
  fi

  # cupsaccept and cupsenable are not needed before of '-E'. I don't remember why I included them.
  if ! ${CUPSACCEPT} "${name}"; then
    echo "ERROR: ${name}: Unable to cupsaccept." >&2
    exit -1
  fi

  if ! ${CUPSENABLE} "${name}"; then
    echo "ERROR: ${name}: Unable to cupsenable." >&2
    exit -1
  fi
}

if (! ${LPSTAT} -v "Follow-Me"); then
  add_printer "Follow-Me" \
              "smb://printserver.fti.io/Follow-Me%20Xerox%20(PCL6)" \
              "/Library/Printers/PPDs/Contents/Resources/Xerox WC 7545.gz"
fi


exit 0

Integration

Talking about integrating the Mac Computers to Enterprise environments.

Active Directory

Binding or not to Active Directory is the debate today. A couple of years ago, the general recommandation was to bind computers to Active Directory. With the change from desktop and shared computers to 1-to-1 laptop deployments, the picture has dramatically changed.

After the Kerbminder and ADPassMon scripts, we now have two alternatives:

  • Apple Enterprise Connect
  • Orchard & Grove - NoMAD

Arguments for binding or not binding to Active Directory

Topic Binding Not Binding
802.1x Wi-Fi (WPA2 Enterprise EAP-TLS) can use the machine certificate generated by AD We can also use a profile that will deploy the root certificates and request a machine certificate through SCEP NoMAD can request a 802.1x certificate
Kerberos tickets AD automatically provides Kerberos tickets, but only at login and when unlocking from screensaver. On mobile computers, users don’t logout as often and are mostly on Wi-Fi which doesn’t have time to connect before unlocking the screensaver. As a result, kerberos tickets are rarely renewed. Enterprise Connect or NoMAD handles the renewal of Kerberos tickets
AD users can log in to any bound Mac & Shared use of Mac (eg. Lab computers) As user identification and authentication resides on server, users can log in on any bound Mac. This is especially interesting for shared environments such as Labs On mobile devices, this is getting harder as Portable Home Directories (syncing user home from file share) is no longer supported. The only possibility is to use network directories which are impractical in a mobile environment
User identification and computer usage traceability Binding to AD ensures that each username and uid is used only once across the bound Mac computers MDM can better trace computer usage
Users can be admins via the directory plugin A group of users can be specified as a local admins A MDM can create a “management account” and take care of renewing the password
Password policies Password policies are handled in the AD account A Password policy can be deployed
User Password expiry Password expiry is handled in the AD account A Password policy can be deployed
Ease of setup Computer needs to have access to AD during setup No particular setup is needed For authenticated DEP, computer needs access to the MDM
Account lock Local account is locked at next login or unlock from screensaver A better way to lock the user is to issue the wipe or lock MDM command
Keychain The keychain password is not synchronized with Active Directory. When the password change is not done on the Mac, the users will get prompted to enter his old and new password Local and remote passwords are not synced Enterprise Connect or NoMAD will sync the local password when it detects a change. Change will be replicated to the Keychain
FileVault Password FileVault and remote passwords are not synced When the AD password is reset, Filevault will keep the previous password, meaning we need to also reset FileVault using the recovery key Filevault and remote passwords are not synced Enterprise Connect or NoMAD will sync the local password when it detects a change. Change will be replicated to FileVault

Choosing between NoMAD and Apple Enterprise Connect

Versions used:

  • Enterprise Connect 1.8.0
  • NoMAD 1.0.5
  • macOS 10.12
x Enterprise Connect NoMAD (Active Directory binding)
Vendor Apple Orchard & Grove Inc. Apple
Open Source x x
Support Supported by Apple PS as included in the engagement and/or AppleCare OS Support Support plans available Supported by AppleCare OS Support
OS requirement 10.10+ 10.10+ 10.3+
Single Sign-On Automatically Automatically Only at login and screensaver
Password Expiration via Notification Center via Notification Center Only at Login
Password change via menu item via menu item via System Preferences or login window
Fine Grained Password Policy support ~ (doesn’t honor password expiration time) x
Quick links to getting support and software x x
Support for changing passwords not using AD, e.g. a web-based password portal x
Password Synchronization Only when user is logged in Only when user is logged in Automatic
Home Network Share Automount x
Network Share Automount x
Change Keychain Items on AD password change x x
AD Binding required? x x
macOS native? Uses Apple Frameworks Uses Apple Frameworks macOS Native
Script on password change x
Script on connection completed x
Audit script x x
Distribution single .pkg single .pkg macOS Native
Configuration via a Configuration Profile (and .plist) via a Configuration Profile (and .plist) multiple ways
X509 Identity from CA Script provided to request it via an AD Certificate profile payload Mature
Language Support All macOS languages English, French, German, Spanish and others. All macOS languages
Maturity Mature 1.1.0 x
Installation Two-day on-site professional services engagement None None
Price $5,500 (one-time fee) Free, Support plans available ($399 to $2,500 per year) Free
Availability Contact your local Apple Sales Rep http://nomad.menu macOS Native

Jamf Pro

http://docs.jamf.com/9.98/casper-suite/jss-install-guide-linux/

Introduction

Prerequisites

Configure AWS

Configure Security

Create VPC

  • Create VPC
Create Subnets
  • Create subnet on second Availability Zone

### Create Security Groups

  • jss-lab-jss: will reference my JSS instances
  • jss-lab-jss-ports: Public ports (SSH and HTTPS/8443)
  • jss-lab-db: MySQL from jss-lab

## Create RDS Instance

  1. Choose MySQL (then Dev/Test MySQL)
  2. DB Details
  3. Advanced Settings

Create EC2 Instance

  1. RedHat 7
  2. t2.micro (not recommended in production)
  3. Select storage
  4. Configure Security Group (SSH+HTTPS)

Install the JSS

On EC2 Instance

Login to the EC2 Instance by using your newly created SSH key:

ssh -i Jamf-EC2.pem ec2-user@ec2-34-251-175-48.eu-west-1.compute.amazonaws.com

Let's update the instance!

sudo yum update

You may need to reboot (a good idea if kernel or firmware was upgraded!)

sudo reboot

Wait a few minutes until the instance is back online.

Upload the JSS Installer to the instance

scp -i Jamf-EC2.pem ~/Downloads/JSSInstallerLinux9.98.zip ec2-user@ec2-34-251-175-48.eu-west-1.compute.amazonaws.com:

Login to the EC2 Instance:

ssh -i Jamf-EC2.pem ec2-34-251-175-48.eu-west-1.compute.amazonaws.com

Install Java 1.8

sudo yum install java-1.8.0-openjdk-devel

Install unzip

sudo yum install unzip

Unzip the JSS Installer

unzip JSSInstallerLinux9.98.zip

Launch the Installation Script

sudo JSSInstallerLinux/JSS\ Installation/jssinstaller.run

[ec2-user@ip-172-30-2-85 ~]$ sudo JSSInstallerLinux/JSS\ Installation/jssinstaller.run 
Verifying archive integrity... All good.
Uncompressing JSS Installer  100%  
Starting the Linux JSS Installation
Checking installation requirements...
Checking for a 64-bit OS...OK
Checking for Java 1.7+ ...Java minor version found: 8
Verifying JCE Unlimited Strength Jurisdiction Policy files...Found required JCE support on this server.
Java JCE files verified
Checking if Tomcat is NOT installed...OK
Passed all requirements

To be installed into /usr/local/jss:
* Tomcat 8.0.41
* JSS
* JSS Database Utility
*
* NOTE: When upgrading to Tomcat 8 for the first time, the account and group named 'tomcat7' will be renamed to 'jamftomcat'

Proceed?  (y/n): y
Installing...
Starting Tomcat Installation
Adding Tomcat user and group...


Installing init.d script...
Copying Tomcat files...
Setting permissions...
Tomcat Installation Done!
Starting JSS Installation
Generating Tomcat keystore...OK
Copying JSS Webapp...
jar command found
Creating the ROOT webapp directory for extraction point
Extracting ROOT.war using jar command
ROOT.war extracted successfully
Done extracting war...
Finalizing Tomcat and Web App settings...
JSS Webapp Installation Done!
Starting Database Backup Utility Installation
Copying JSS Database Backup Utility...Error: The JSS Database Utility must be launched on a system with MySQL installed.  If MySQL is already installed, you must specify the location of MySQL using the -mysqlPath flag.
JSS Database Utility 9.98
OK
JSS Database Backup Utility and backup script are located in /usr/local/jss/bin

The JSS has been installed.
Verify that port 8443 is not blocked by a firewall.

Note: MySQL is required for the JSS to run.  If you do not have MySQL installed, download and install the latest version.
MySQL Enterprise Edition is recommended and may be found at http://www.mysql.com/downloads/mysql
Alternatively, MySQL Community Edition may be installed:
Ubuntu/Debian users can run "apt-get install mysql-server"
RedHat users can run "yum install mysql-server"
Or any Linux distribution can download and install the appropriate package at http://www.mysql.com/downloads/mysql

To complete the installation, open a web browser and navigate to https://ip-172-30-2-85.eu-west-1.compute.internal:8443.
If you are installing the JSS for the first time, you are prompted with the JSS Setup Assistant. Complete the JSS Setup Assistant to start using your JSS.
[ec2-user@ip-172-30-2-85 ~]$ 

Done!

Configure RDS

When first connecting to your instance, you should get a "Database Connection Error"

Connect to your EC2 Instance.

First, install MySQL:

sudo yum install mysql

Create the jamfsoftware database

mysql -h jamf-lab-db.c6kljkzma1b2.eu-west-1.rds.amazonaws.com -u jamfdbroot -p -e "CREATE DATABASE jamfsoftware"

Create a new user jamfsoftware who will only connect from 10.0.0.248 and have access to all tables from jamfsoftware database, with password changeme

mysql -h jamf-lab-db.c6kljkzma1b2.eu-west-1.rds.amazonaws.com -u jamfdbroot -p -e "GRANT ALL ON jamfsoftware.* TO jamfsoftware@10.0.0.248 IDENTIFIED BY 'changeme';"

You can now go to https://ec2-34-251-175-48.eu-west-1.compute.amazonaws.com:8443/

Configure the DB with the database and credentials you entered above

And… Done!! You should now see the license!

Redirecting from 8443 to 443

Install FirewallD

sudo yum install firewalld

Start the service

sudo systemctl start firewalld

Add the redirection

sudo firewall-cmd --permanent --add-masquerade sudo firewall-cmd --permanent --add-forward-port=port=443:proto=tcp:toport=8443

Allow port 8443/TCP

sudo firewall-cmd --permanent --add-port=8443/tcp

Allow port 443/TCP

sudo firewall-cmd --permanent --add-port=443/tcp

Reload the service sudo firewall-cmd --reload

Enable FirewallD at boot

sudo systemctl enable firewalld

Why not reboot to test that everything is working fine!

sudo reboot

And that's done!

Conclusion

Jamf Resources

Community

Jamf Training

See all training information here.

CCT is a great introductory-level training. Anyone remotely involved in managing, supporting and selling Mac and iOS devices at your organization should take it. It’s a 4-day course. Anyone can join, don’t be shy!

CCA is for your MacAdmins. They should all have it. Don’t go cheap on this one. They need to have 6 month of experience.

CCE is for your experienced MacAdmins who need additional challenges. It’s scenario-based. They will love it. But it’s purely optional.

CJA is for your JSS Administrators, it doesn’t cover Mac Administration at all.

I suggest you buy a Training pass:

  • Individual: one of your staff can go to any number of Jamf training for one year
  • Organizational: any of your staff cxan go to any number of Jamf trainings for one year, provided they don’t attend to the same one at the same time.

Open Source and Jamf

This list contains some open source projects that are related to managing JAMF Pro (formerly Casper Suite). They may help you to do some things that are not possible with the Web UI alone.

Repositories

These are some notable repositories that are dedicated to supporting JAMF Pro or have a significant number of projects related to JAMF.

API

  • python-jss Python wrapper for the Jamf JSS Rest API
  • ruby-jss Access to the Casper Suite from Ruby

Command Line Tools

  • jss_helper jss_helper is a powerful commandline interface for managing and auditing your Casper JSS.

Configuration Management Modules

  • jss_chef This cookbook installs and configures Jamf Pro.
  • puppet-jss Puppet manifests for deploying and configuring JAMF Software JSS, MySQL, and Distribution Points.

Migration

Onboarding

Packaging

  • JSSImporter Upload packages made with AutoPkg to your JSS with customizable policies.
  • Spruce Identify unused packages and scripts on a JAMF Casper JSS and optionally remove them.
  • Depot3 Command line package and patch management for Casper
  • jamJAR jamJAR: Jamf, AutoPKG & Munki combined by dataJAR.
  • patchoo Patchoo somewhat emulates munki workflows and user experience for JAMF Software’s Casper Suite.

Scripts (Misc)

Troubleshooting

  • Deadpool JAMF check-ins with healing factor and a mouth

JSS Tuning

Warning

Do not attempt to make any of these changes without a Backup of your JSS Database.

Java & Tomcat

Database

optimizer_search_depth

For some reason this is recommended to be set to 3. I believe this is to stop MariaDB/MySQL from coming up with too many execution plans.

key_buffer_size

As per this article, key buffer size should be set to about 25% or more of the available server RAM.

query_cache_size

See article, Set to 0.

query_cache_type

Set to 0 to disable the query cache.

(ignore) Table Storage: InnoDB

Changing the table storage might have far reaching implications for you so I don’t recommend this now, but these settings were recommended:

innodb_log_file_size=512M
innodb_flush_log_at_trx_commit=1
innodb_file_per_table=1
innodb_buffer_pool_size=amount_of_RAMG # 60% or more of your total ram
innodb_buffer_pool_instances=8

Configuring Cloud Distribution Points

Concept

A Cloud Distribution Point (CDP) is a glorified FTP service. Currently, the JSS supports:

  • Amazon Web Services (S3 + CloudFront)
  • Rackspace Cloud Files
  • Akamai

I will currently focus on Amazon S3. Feel free to fork and add more info.

Amazon S3 + CloudFront

As told earlier, Amazon S3 is a glorified FTP service. It’s available from Amazon Web Services, and permit to store your files online. On top of that, we use CloudFront, that replicates your Amazon S3 data to all Amazon datacenters worldwide. Quite neat! But all great things come with a cost. Be careful with what you store. Perhaps it’s time to leverage your applications autoupdate mechanisms!

Note

CloudFront automatically replicates your files accross all Amazon datacenters. That means there will be a few minutes delay until your files are available everywhere.

Common setups

A single Cloud Distribution Point
  • Few users or few packages
  • small, spread-out websites
CDP acting as ‘backup’ DP
  • Provide data wherever the user is (= outside of the network)
  • Provide data to small sites that don’t need/want a local server
CDP for some files

Setup a CDP and select which files you want to provide from it.

  • Provide a fail-safe Distribution Point for small packages

Setting up

Amazon S3

You need to create a IAM (Identity Access Management) group, user, and assign a profile to limit its access rights.

  1. Navigate to IAM
  2. Create a group jamf-group
  3. Create a user jamf-user
  4. Assign the user jamf-user to the group jamf-group
  5. Create and assign policy jamf-policy (see below) to the group jamf-group

With AWS CLI:

# Create group
aws iam create-group --group-name jamf-group --output table

# Create user
aws iam create-user --user-name jamf-user --output table

# Create an access key for this user (will dump AcessKeyId and SecretAccessKey)
aws iam create-access-key --user-name jamf-user

# Add user to group
aws iam add-user-to-group --user-name jamf-user --group-name jamf-group

# Assign policy to group
aws iam put-group-policy --group-name jamf-group --policy-document file://./jamf-group-policy.json --policy-name jamf-policy

# Check if it's working well:
aws iam get-group --group-name jamf-group --output table
aws iam get-group-policy --group-name jamf-group --policy-name jamf-policy --output table

Your jamf-policy file:

{
    "Version": "2016-01-05",
    "Statement": [
        {

            "Effect": "Allow",
            "Action": [
                "s3:*"
            ],
            "Resource": [
                "*"
            ]
        },
        {

            "Effect": "Allow",
            "Action": [
                "cloudfront:CreateCloudFrontOriginAccessIdentity",
                "cloudfront:CreateDistribution",
                "cloudfront:CreateInvalidation",
                "cloudfront:CreateStreamingDistribution",
                "cloudfront:GetCloudFrontOriginAccessIdentity",
                "cloudfront:GetCloudFrontOriginAccessIdentityConfig",
                "cloudfront:GetDistribution",
                "cloudfront:GetDistributionConfig",
                "cloudfront:GetInvalidation",
                "cloudfront:GetStreamingDistribution",
                "cloudfront:GetStreamingDistributionConfig",
                "cloudfront:ListCloudFrontOriginAccessIdentities",
                "cloudfront:ListDistributions",
                "cloudfront:ListInvalidations",
                "cloudfront:ListStreamingDistributions",
                "cloudfront:UpdateCloudFrontOriginAccessIdentity",
                "cloudfront:UpdateDistribution",
                "cloudfront:UpdateStreamingDistribution"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

Note

TODO: need to try to use “jamf*” as the ressource.

JSS
$ java -version
java version "1.7.0_91"
OpenJDK Runtime Environment (rhel-2.6.2.3.el7-x86_64 u91-b00)
OpenJDK 64-Bit Server VM (build 24.91-b01, mixed mode)

Tip

Centos users: if you installed 1.8 by mistake, try sudo yum swap java-1.8.0-openjdk.x86_64 java-1.7.0-openjdk.x86_64 then sudo systemctl restart jamf.tomcat7.service to restart the JSS.

  1. Navigate to Settings > Computer Management > Cloud Distribution Point
  2. Enter all the relevant info you provided earlier.
_images/jss-cdp.png
AutoPkg

The JSS is acting as a dispatcher. You simply need to provide the necessary information.

Example:

# Check the current settings:
defaults read ~/Library/Preferences/com.github.autopkg.plist

# Setup the array (optional if you have something already)
/usr/libexec/PlistBuddy -c "Add :JSS_REPOS array" ~/Library/Preferences/com.github.autopkg.plist

# Setup a new dict (make sure to change index number if you have some already)
/usr/libexec/PlistBuddy -c "Add :JSS_REPOS:0 dict" ~/Library/Preferences/com.github.autopkg.plist
/usr/libexec/PlistBuddy -c "Add :JSS_REPOS:0:type string CDP" ~/Library/Preferences/com.github.autopkg.plist

Testing

Listing the files on Amazon S3 with Cyberduck

I found out the easiest way to see what’s actually store on Amazon S3 is to use Cyberduck. Simply add a profile with “S3 (Amazon Simple Storage Service).

Note

your file may take several minutes to show up here.

_images/cyberduck-s3-add.png _images/cyberduck-s3.png

AutoPkg: Word Example

Make an Override

$ autopkg make-override com.github.jss-recipes.jss.MicrosoftWord
Override file saved to /Users/admin/Library/AutoPkg/RecipeOverrides/Microsoft Word.jss.recipe
$ mv ~/Library/AutoPkg/RecipeOverrides/Microsoft\ Word.jss.recipe ~/Library/AutoPkg/RecipeRepos/com.github.ftiff.mac-autopkg/MicrosoftOffice2016/
$ cd ~/Library/AutoPkg/RecipeRepos/com.github.ftiff.mac-autopkg/MicrosoftOffice2016/

Recipe

Delete all the keys you will not override.

  1. Change the Identifier
  2. Category (for Package)
  3. Policy_Category (for Policy)
<?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"> 
<plist version="1.0">
<dict> 
        <key>Identifier</key>                             
        <string>com.github.ftiff.mac-autopkg.jss.Microsoft Word</string>
        <key>Input</key>
        <dict>
                <key>CATEGORY</key>                       
                <string>Productivity</string>             
                <key>GROUP_NAME</key>                     
                <string>%NAME%-update-smart</string>      
                <key>GROUP_TEMPLATE</key>                 
                <string>SmartGroupTemplateCasper.xml</string>              
                <key>POLICY_CATEGORY</key>                
                <string>Productivity</string>
                <key>POLICY_TEMPLATE</key>                
                <string>PolicyTemplate.xml</string>       
        </dict> 
        <key>ParentRecipe</key>
        <string>com.github.jss-recipes.jss.MicrosoftWord</string>          
</dict> 
</plist>

Smart Group Template

<computer_group>
    <name>%group_name%</name>
    <is_smart>true</is_smart>
    <criteria>
        <criterion>
            <name>Packages Installed By Casper</name>
            <priority>0</priority>
            <and_or>and</and_or>
            <search_type>does not have</search_type>
            <value>%NAME%-%VERSION%.pkg</value>
        </criterion>
    </criteria>
</computer_group>

## Policy Template

<policy>
    <general>
        <name>%PROD_NAME%</name>
        <enabled>true</enabled>
        <frequency>Ongoing</frequency>
        <category>
            <name>%POLICY_CATEGORY%</name>
        </category>
    </general>
        <scope>
                <!--Scope added by JSSImporter-->
        </scope>
        <package_configuration>
                <!--Package added by JSSImporter-->
    </package_configuration>
    <scripts>
                <!--Scripts added by JSSImporter-->
    </scripts>
    <self_service>
        <use_for_self_service>true</use_for_self_service>
        <feature_on_main_page>true</feature_on_main_page>
                <install_button_text>Install %VERSION%</install_button_text>
                <self_service_description>%SELF_SERVICE_DESCRIPTION%</self_service_description>
    </self_service>
    <maintenance>
        <recon>true</recon>
    </maintenance>
</policy>

Build the Recipe

$ autopkg run Microsoft\ Word.jss.recipe

Results

Package

Package

Policy

Package

Package

Package

Smart Group

Package

Infrastructure Manager

Architecture

_images/JAMF-IM-Infra.png

Your JAMF Infrastructure Manager server should sit in the DMZ and should only be able to talk with the correct port of your LDAP server.

It should be reachable from Internet at the port you’ll specify in the JSS.

Prerequisites

Please read the Administrator Manual first.

Ubuntu 14.04 LTS

You’ll need to download and setup Ubuntu 14.04 LTS. I won’t cover this here.

I use Microsoft Azure for test purposes.

Firewall settings
Internal Network <-> DMZ

Infrastructure Manager Server should be able to reach:

  • the LDAP server (389, 636 or 3268)
  • your internal DNS server
  • your internal NTP server if available
DMZ <-> Internet

Infrastructure manager should be able to reach

  • your JSS (port 8443 usually)

Infrastructure manager needs to be reached on the port you’ll set later. Choose a port > 1024.

Consult Permitting Inbound/Outbound Traffic with JAMF Cloud for more information.

Java 1.8

In an ideal world, you should download Java from Oracle. Or better, you shouldn’t use Java.

I found the best way to install Oracle Java 1.8 is doing the following:

sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update
sudo apt-get install oracle-java8-installer

You’ll have to access Oracle’s License to proceed.

As this is a third-party repository, use it with caution, and only on test instances. If you found a better way, please contribute<

Test with java -version to see if it works.

$ java -version
java version "1.8.0_101"
Java(TM) SE Runtime Environment (build 1.8.0_101-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.101-b13, mixed mode)
JAMF Infrastructure Manager package
  1. Connect to JAMF Nation
  2. Go to My Assets
  3. Download Infrastructure Manager
  4. Upload it to your Linux server

I use scp to upload the binary to my server:

cd ~/Downloads
scp jamf-im_1.0.0-0_all.deb fti@13.93.87.150:

Configuring

Create the correct user in JSS

Create a Standard User that has only “Infrastructure Manager” role.

_images/jamf-im-1.png _images/jamf-im-2.png
Launch the setup assistant

By installing the package, you’ll launch the setup assistant. sudo dpkg -i jamf-im_1.0.0-0_all.deb

Enter the host for your cloud instance: .. image:: images/jamf-im-sa-5.png

Enter the login and password you created earlier: .. image:: images/jamf-im-sa-4.png .. image:: images/jamf-im-sa-3.png

Enter the public IP Address of your Infrastructure Manager server: .. image:: images/jamf-im-sa-2.png

Setup the frequency. I use the default value of 30 seconds: .. image:: images/jamf-im-sa-1.png

If everything goes well, you should see the following: ` Enrollment invitation stored. Successfully obtained enrollment invitation from https://ftiff.jamfcloud.com `

Create a LDAP Server

It’s all downhill for now. Just kidding. This is the tricky part, as LDAP can be difficult to configure. We won’t cover LDAP configuration here.

  1. Open JSS
  2. Go to System Settings > LDAP Servers
  3. Add a LDAP Server
  4. Choose “Configure Manually”
  5. Choose “Enable LDAP Proxy Server”

Make sure you choose the right port number. It should be >1024 and be reachable from internet to your public IP address.

_images/jamf-im-ldap.png

Troubleshooting

Log files

Log files are located here:

  • /var/log/jamf-im-launcher.log
  • /var/log/jamf-im.log
LDAP Server needs authentication
Error: javax.naming.NamingException: [LDAP: error code 1 - 000004DC: LdapErr: DSID-0C0906E8, comment: In order to perform this operation a successful bind must be completed on the connection., data 0, v1db1]; remaining name 'OU=Org,DC=fti,DC=io'
Suggestion: No suggestion available
Infrastructure Manager cannot bind to address
2016-08-30 14:43:16,834 INFO c.j.j.l.LpsServerSocketListener [lps: /13.93.87.150:31337 (ssl)] Sleeping for 5000 ms before retry of server socket bind for address /13.93.87.150:31337
2016-08-30 14:43:21,835 INFO c.j.j.l.LpsServerSocketListener [lps: /13.93.87.150:31337 (ssl)] Waking for retry of server socket bind for address /13.93.87.150:31337
2016-08-30 14:43:21,839 ERROR c.j.j.l.LpsServerSocketListener [lps: /13.93.87.150:31337 (ssl)] Failed to obtain server socket for address /13.93.87.150:31337
com.jamfsoftware.jsam.lps.LpsException: Failed to bind server socket to [/13.93.87.150:31337]
        at com.jamfsoftware.jsam.lps.socket.LpsSocketSupplier.bindServerSocket(LpsSocketSupplier.java:136) ~[11:ldap-proxy:0.0.1.20160714202842]
        at com.jamfsoftware.jsam.lps.socket.LpsSocketSupplier.createSslServerSocket(LpsSocketSupplier.java:61) ~[11:ldap-proxy:0.0.1.20160714202842]
        at com.jamfsoftware.jsam.lps.LpsServerSocketListener.safeCreateServerSocket(LpsServerSocketListener.java:150) [11:ldap-proxy:0.0.1.20160714202842]
        at com.jamfsoftware.jsam.lps.LpsServerSocketListener.bindServerSocket(LpsServerSocketListener.java:114) [11:ldap-proxy:0.0.1.20160714202842]
        at com.jamfsoftware.jsam.lps.LpsServerSocketListener.execute(LpsServerSocketListener.java:93) [11:ldap-proxy:0.0.1.20160714202842]
        at com.jamfsoftware.jsam.lps.LpsServerSocketListener.run(LpsServerSocketListener.java:72) [11:ldap-proxy:0.0.1.20160714202842]
Caused by: java.net.BindException: Cannot assign requested address
        at java.net.PlainSocketImpl.socketBind(Native Method) ~[?:?]
        at java.net.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:387) ~[?:?]
        at java.net.ServerSocket.bind(ServerSocket.java:375) ~[?:?]
        at java.net.ServerSocket.bind(ServerSocket.java:329) ~[?:?]
        at com.jamfsoftware.jsam.lps.socket.LpsSocketSupplier.bindServerSocket(LpsSocketSupplier.java:132) ~[?:?]
        ... 5 more
2016-08-30 14:43:21,840 INFO c.j.j.l.LpsServerSocketListener [lps: /13.93.87.150:31337 (ssl)] Sleeping for 5000 ms before retry of server socket bind for address /13.93.87.150:31337

This error might happen if your server is not bound to a public address (if ip show add doesn’t show the public address). This happens if you create a NAT to map a public IP address to a private IP.

I haven’t found the solution for this one yet. It seems that JAMF haven’t thought of this use case.

Upgrade JSS, the Manual way

Disclaimer

I'm just listing what I usually do to upgrade JSS. If you have a better idea, please contribute!

Please first test on a test JSS.

Backup, Backup, Backup!

Make sure you have a working backup before doing anything. I use the following command: java -jar /usr/local/jss/bin/JSSDatabaseUtil.jar backup -saveBackupTo ~/ -server jamf-mysql1.sdfsfsaa111.eu-west-1.rds.amazonaws.com -pass This will save the backup in your user home folder. Send it to another computer.

In general, please follow this article: Preparing to Upgrade the JSS

Prepare the JSS Installer

Download the JSS Installer from JAMF Nation.

  1. Connect to JAMF Nation
  2. Go to My Assets
  3. Click "Show JSS installer downloads"
  4. Download JSS Manual Installation
  5. Upload it to your Linux box
  6. Unzip it, and you're ready to go!

Note: I usually upload it to my Distribution Point, and get it from my Ubuntu server using:

curl https://login:password@dp-1.fti.io/JSS_Installers/JSSInstallation9.93.zip --digest -k -O

Upgrade JSS

If you have a Clustered JSS, please read Upgrading the JSS in a Clustered Environment.

First, let's stop the JSS:

service jamf.tomcat7 stop

Then archive the current install to ~/ROOT-war-20160830.tgz:

tar czf ~/ROOT-war-20160830.tgz /usr/local/jss/tomcat/webapps/
rm -rf /usr/local/jss/tomcat/webapps/*

Copy the new ROOT.war and restart tomcat:

mv JSSInstallation/JSS\ Components/ROOT.war /usr/local/jss/tomcat/webapps/
service jamf.tomcat7 start

Just reconfigure the database, and everything should be working again!

If something goes wrong

Read the logs in /usr/local/jss/tomcat/logs/. Worst case, restore from backup and use the Linux automatic updater.

Restrict installation of macOS Sierra

Apple has yet to provide a way to prevent the update to a major OS release.

But, I believe it's for the greater good. Everyone should work toward supporting macOS on the day it is released. Apple gives us plenty of time to do this, thanks to the Developer, Apple Seed and Public betas.

If something goes wrong and you want to make sure your user don't upgrade to the newest macOS, follow these steps.

Restrict Beta Version

If your goal is to restrict the Beta version, Apple provides the following kbase: https://support.apple.com/en-us/HT203018

On Casper Suite, simply create a Configuration profile with a "Software Update" payload and deselect "Allow installation of OS X beta releases".

Restrict Retail Version

As stated above, Apple doesn't provide a way to disable a major OS upgrade.

We'll use JSS built-in "Restricted Software" mechanism to kill the Installation app as soon as it's launched by the user.

It is not super user-friendly, so make sure you communicate to the users first.

Restricted Software Records

  1. Open your JSS
  2. Go to Computers > Restricted Software
  3. Click + "New"

Add Restricted Software Record

macOS Sierra installer uses the process "osinstallersetupd" to setup installation.

Blocking this process will ensure that no user will be able to launch the installation, even if renaming "Install macOS Sierra.app".

Scope Restricted Software Record

Choose the right Scope. "All Managed Clients" is usually a good choice.

I exclude from this Smart Group my test machines and my BYOD clients.

Restricted Software Records

Our Record is now ready.

Computer Inventory Collection

  1. Navigate to Computers > Management Settings
  2. Click on "Inventory Collection"

Edit Computer Inventory Collection

Check "Collect active services".

Note: I couldn't find relevant ressources to confirm this was needed, but my tests indicate so

On the client

You may want to try a "jamf manage" and a "jamf policy" to refresh the management framework.

If you launch "Install macOS Sierra.app", you'll get the following screen.

AirWatch

Using AirWatch API

Initial Setup

According to the "AirWatch REST API Guide" PDF document that you can get in https://my.air-watch.com, you need:

  • the URL: https:///API/v1/help
  • the Token: aw-tenant-code (or API Key)
  • Authorization: Basic base64.b64encode("username:password")
Enable Basic Authentication and get the Token
  1. Select the right Organization Group (eg. Root)
  2. Enable Basic Authentication from Groups > Groups & Settings > System > Advanced > API > REST > Authentication
  3. Go to Group & Settings > System > Advanced > API > REST > General
  4. Tick “Enable API Access” & add a service. Entering a service name will generate an API Key, which we’ll need for API calls.
NOTE: This was called "Tenant Code" or "aw-tenant-code" previously & in the current (8.2) API documentation & will be referred as such within this post.^accessing-airwatchs-rest-api-with-python
Authorization

The easiest way is to use Basic authentication.

  1. Make sure your admin has the correct role. In production, you should create a custom Role, but for test, Console Administrator is fine. Make sure he's in the correct OG, of course.
  2. The form should be "username:password", encoded using Base64. You can do this on OS X terminal (see below)
$ python -c "import base64; print base64.b64encode('login:password')"
bG9naW46cGFzc3dvcmQ=
Testing
Testing with Curl
$ curl -X "GET" "https://host.awmdm.com/API/v1/help" \ -H "Authorization: Basic bG9naW46cGFzc3dvcmQ=" \ -H "aw-tenant-code: bG9naW46cGFzc3dvcmFzZG/2FmYXNkZmFkc2Zhc2Zk="
Testing with Python
# Install the Python Requests library:
# from bash: pip install requests

import requests


def send_request():
    # My API
    # GET https://host.awmdm.com/API/v1/help

    try:
        response = requests.get(
            url="https://host.awmdm.com/API/v1/help",
            headers={
                "Authorization": "Basic bG9naW46cGFzc3dvcmQ=",
                "aw-tenant-code": "bG9naW46cGFzc3dvcmFzZGZ/2FmYXNkZmFkc2Zhc2Zk=",
            },
        )
        print('Response HTTP Status Code: {status_code}'.format(
            status_code=response.status_code))
        print('Response HTTP Response Body: {content}'.format(
            content=response.content))
    except requests.exceptions.RequestException:
        print('HTTP Request failed')

Queries (using Python)

I suggest using a REST editor to test your parameters, such as Paw. It can also automatically generates python code.

Opening Request
import requests

# Set your console URL (eg. 'http://cn23.awmdm.com')
consoleURL = 'http://cn23.awmdm.com'

# Maximum set of values (1-10000 - default: 500)
lookupLimit = '500'

# Base64 encoded 'login:password' -- discouraged in production
b64EncodedAuth = 'bG9naW46cGFzc3dvcmQ='

# Your tenant code (see above)
tenantCode = 'bG9naW46cGFzc3dvcmFzZG/2FmYXNkZmFkc2Zhc2Zk='

# Your request. See API documentation.
request = '/API/v1/mdm/devices/search'

# It's a good idea to enclose the following in a try-except format.
try:
    # API call, pulling in all Employee Owned devices from the OG "All Peoples Devices"
    request = requests.get(consoleURL + request + "?pagesize=" + lookupLimit, 
              headers={"Authorization": "Basic " + b64EncodedAuth,
                       "aw-tenant-code": tenantCode,
                       "Accept": "application/json"},
              timeout=30)

    # If the above gives a 4XX or 5XX error
    request.raise_for_status()
    
    # Insert your code here

except requests.exceptions.RequestException as e:
    print 'Get request failed with %s' % e
Getting all devices

request = '/API/v1/mdm/devices/search'

    # Get the JSON from the above
    deviceDetails = request.json()
    
    # Pull in the "Devices' dict only
    deviceDetails = deviceDetails['Devices']
    
    # For each device in deviceDetails
    for device in deviceDetails:
    
        # Log each devices one by one
        print device

Using with OS X Clients

Unfortunately, the API doesn't -yet- support all the features from OS X Clients:

<AirWatchFaultContract xmlns="http://www.air-watch.com/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <ActivityId>56b6ed75-30a2-418e-84fa-f8e04d35506a</ActivityId>
  <ErrorCode>501</ErrorCode>
  <Message>Functionality not supported for device type : AppleOsX</Message>
</AirWatchFaultContract>

Puppet

Additional Resources

Here are some additional resources created by the MacAdmin Community regarding Puppet that I'm aware of.

Michael Holt

Graham Gilbert

Samuel Keeley

Brian Warsing

  • ManagedMac - A Comprehensive Puppet module for OS X.

Additional Information

This is not intended to be a comprehensive list but I'll do my best to add as I find additional resources. Feel free to contribute more as well with a pull request (See the Contributing section under 00 About for more details).

Basic Configuration

This tutorial goes over how to add some basic configuration to your Puppetserver for managing your macs.

Originally Posted at: www.mholt.tech/blog/2015/12/07/basic-puppet-configuration/

Configuration Overview

So this is the second post about Puppet. I'm going to go through a brief overview deploying some configuration to your computer using Puppet.

If you don't already have Puppetserver running, please go back to my previous post Here to get up and running with Puppet. If you aren't following from my previous post some of this will be different depending on your Environment but I will be continuing with the setup on the Docker Image that I've created. We're going to start with some basic configuration so you can understand how the basics work. Next year I'll be providing a repository with more detailed configuration options that won't necessarily be covered here.

There are multiple parts to applying configurations to your computer. Inside of the Core Repo folder that you cloned previously you'll see a folder named Hiera.

First off, you have Hiera. These files are used to apply configuration to your machine.

  • machine/c02n5heug3qj.yaml (You may have renamed this in the previous post)
  • role/test.yaml
  • common.yaml

The files inside of machine are optional and used if you want to apply a special configuration option to a specific machine. These files should be named after the serial number of the machine, always in lower case.

The files in role are used to create a configuration file that is applied to multiple machines and is defined as a custom fact as done in the previous post.

The final file, common.yaml is a master configuration that is applied to all machines.

When defining specific configuration data, you can have the same variable in multiple files and the one that is seen first in the order of files above is what is applied.

Lets start with opening common.yaml. In this file you'll see a few lines of code. Classes are configuration functions defined in manifests either from Modules that are included in Puppetfile, or additional custom manifests defined in site/(profiles or roles)/manifests.

The first line you see under classes is "puppet_run". This is calling a function inside of a Puppet Module by Graham Gilbert called Puppet Run. This module configures puppet agent on the machine and configures puppet to automatically run every 30 minutes along with a random delay of anywhere between 0 and 20 minutes to prevent all of your machines from checking in at the same time.

The next line you'll see under Classes is roles::default. This calls a custom configuration file inside of site/roles/manifests/default.pp and simply run an echo command outputting "Default Role" when running puppet agent -t

The final line is a variable, puppet_run::server_name. This variable is what tells the Puppet Run module what your puppet servers name is. When it comes to variables you can override them on a per role or per machine basis by also including the variable along with the corresponding class inside of the respective role or machine yaml file.

Lets add some custom configuration

Your needs and environment are going to vary from mine but i'm going to go over some basic configuration options using ManagedMac by dayglojesus.

Add some text to Login Window.

We're going to start off with configuring puppet to display a message on the login window.

We'll start off with adding a message that will be applied to everyone. To do this, lets open up common.yaml and add these lines.

Under classes add:

  - managedmac::loginwindow

now at the bottom of the file lets add the variable to define the message.

managedmac::loginwindow::loginwindow_text: "This is a global message"

Once this is done go ahead and save, commit, and push the file to your git repository. Once this is done you need to log into your Docker server and run

docker exec -it puppetserver r10k deploy environment -pv

Once your puppetserver has been updated lets manually run Puppet on your test machine

puppet agent -t

Now go ahead and log out and you should see a message on the login window saying "This is a global message".

Now lets go ahead and define a machine specific message on the login window.

Create a file inside of hiera/machine/<serial-number>.yaml (Ensure that you use all lower case). Inside of this file go ahead and populate with:

---
classes:
  - managedmac::loginwindow
managedmac::loginwindow::loginwindow_text: "This is a machine message"

Go ahead and commit and push this to your git repository, then once again run this on the server:

docker exec -it puppetserver r10k deploy environment -pv

Afterwards, run on your machine

puppet agent -t

Log out and you will now see your login window saying "This is a machine message" instead of "This is a global message".

There are a lot more configuration options for ManagedMac and they can all be found Here.

Hide Puppet User

Lets go ahead and do one more thing before we wrap up this session. We're going to hide the annoying "Puppet" user that shows up on the login window.

Go ahead and navigate to site/profiles/manifests and create a file called hidepuppetuser.pp.

Inside of this file, insert

class profiles::hidepuppetuser {
  exec {
    'Hide Puppet User':
    command => "/usr/bin/defaults write /Library/Preferences/com.apple.loginwindow HiddenUsersList -array-add puppet",
  }
}

Now go ahead and save this file and close it. The next step is to tell the machines to go ahead and run this manifest. We want to apply this to ALL machines, so go ahead and edit hiera/common.yaml and under classes insert

  - profiles::hidepuppetuser

Save this file, then commit and push both files to your git repository. After that, run r10k to update your Puppetserver and then run puppet on your test machine. After this is done, you should no longer see the user "Puppet" when you are at the login window.

This concludes this blog post and gives you an idea of how to use Puppet to configure your machines. As I mentioned previously, i'll be posting a number of my configurations up on Github when I get back from Christmas Vacation.

UPDATE: An Example Core Repository can be found HERE

Getting Started

This tutorial goes over how to get started with running Puppet in a Docker Container to manage your mac configuration.

Originally Posted at: www.mholt.tech/blog/2015/12/04/getting-started-with-puppet/

Installing Docker

I'm going to assume you already have a working Ubuntu 14.04 Server. If not, I Highly recommend using Linode, which is what we use to run our Management Platform. You can sign up Here.

Our first job after SSH'ing into the server is to get Docker installed. This is a very easy process.

First lets makes sure we have wget installed:

which wget

If nothing is returned, we need to install wget:

sudo apt-get update
sudo apt-get install wget

And now we can install Docker.

wget -qO- https://get.docker.com/ | sh

Enter your password when asked and then you're done.

If you aren't running as root (which isn't secure anyways so I hope you aren't), you can give a user access to Docker without having to grant sudo and preface all docker commands with sudo. This can be done by running

sudo usermod -aG docker <username>

Clone the Core Repo

Now before we continue with docker we need to clone the base configuration that our Puppetserver will be using. I'm going to go with the assumption that you are familiar with git. If this is you're first time there are a lot of tutorials on the internet, personally I like to use a GUI and recommend SourceTree.

This is also going to be based on using Bitbucket for storing your configurations privately. Bitbucket gives you unlimited PRIVATE repositories for free which I highly recommend doing to keep your configuration data private. This is also compatible with GitHub as well. You can get a BitBucket account here.

Here's how to get started with your own copy of the Core Repository:

  1. To start off, go to Bitbucket's website and log in.
  2. Go to Repositories -> Import Repository
  3. For URL, enter: https://github.com/MichaelHoltTech/puppet-core_repo.git
  4. For Name, you may keep the name of the imported repository or change this to anything you want.
  5. For Access Level, make sure to check "This is a private repository"
  6. Click Import Repository to import the base repository into your Bitbucket account. It'll take a moment for the code to import and then you can continue.

Start setting up Puppetserver

Now we're ready to go back to Docker and start setting up Puppetserver. This is a very simple process.

We'll start off with creating what is called a Volume Container. This will store the SSL Certificates used by Puppet so that the container can be updated as needed without worrying about losing some important configuration.

On your Ubuntu server, start by running this command. Note: If you are not logged in with root then preface all commands from here on out with sudo

docker pull busybox
docker run -d --name data_puppet \
  -v /root/.ssh \
  -v /var/lib/puppet/ssl \
  busybox

Now we have to create a file in order for the Puppetserver to know how to get your Core Repository. If you skip this step you'll run into some issues when we get to restarting the container. I prefer nano, but you use whatever editor you prefer on the Linux Server.

To begin, lets create some directories and grant all users inside of the Docker user group access.

sudo mkdir -p /usr/local/docker/puppetserver
sudo chgrp -R docker /usr/local/docker
sudo chmod -R 770 /usr/local/docker
cd /usr/local/docker/puppetserver
nano custom.yaml

Inside of custom.yaml insert the following contents, replacing the repo url in single quotes with repo's SSH URL found by clicking: ... -> Clone -> Change HTTPS to SSH.

---
repo_url: 'git@github.com:MichaelHoltTech/puppet-core_repo.git'

Now lets go ahead and close and save this file

Now that we have that out of the way we can get started with the Puppetserver. Make sure you replace puppet.example.com with the url/hostname you intend to use for your puppet server.

docker pull michaelholttech/puppetserver
docker run -d --name=puppetserver \
  --volumes-from data_puppet \
  -v /root/.ssh \
  -v /var/lib/puppet/ssl \
  -v /usr/local/docker/puppetserver/custom.yaml:/root/bootstrap/hiera/data/custom.yaml \
  -e PUPPETSERVER_JAVA_ARGS="-Xms384m -Xmx384m -XX:MaxPermSize=256m" \
  -p 8140:8140 \
  -h puppet.example.com \
  --restart="always" \
  michaelholttech/puppetserver

After you have run those commands we need to monitor the logs for some important information that will be provided. This can be done by running:

docker logs -f puppetserver

Once the initial scripts have run you'll see Public Key displayed in the logs. You need to take this and enter it as a Deployment Key for your Repository. This can be done by browsing to your repository on the Bitbucket Website, and then going to Setttings -> Deployment Keys -> Add Key. Copy/Paste the Publickey starting with ssh-rsa and ending with R10K Deployment Key

Now that we've gotten that done we're ready to let Puppet finish bootstraping itself. This can be done by copy/pasting the commands after the Publickey in the logs that were looking at in the last step. You can also run:

docker stop puppetserver
docker start puppetserver

Now if you watch the logs again you can see puppet preparing itself

docker logs -f puppetserver

This will take several minutes to complete. When it is done you will see a line saying [p.s.m.master-service] Puppet Server has successfully started and is now ready to handle requests

Now that your puppetserver is running there's only one last command to run. This command is only needed if there isn't already data existing inside of data_puppet. This command is also set up to automatically run ever 30 minutes inside of the container.

docker exec -it puppetserver puppet agent -t

Set up you're first client!

Whew we're almost there. Not much longer until you will have your first client checking into your brand new Puppetserver.

Let's start with a fresh Mac OS environment, be it a VM or spare computer. We're going to have to start off by installing two packages on the machine... Puppet & Facter.

Puppet v3.8.4 can be downloaded HERE.

Facter v2.4.4 can be downloaded HERE.

Once downloaded go ahead and install these onto your test machine.

At this point all that's left is to get your machine configured. This is extremly simple and can be done with running one command in terminal, replacing puppet.example.com with your puppetserver's URL. If you don't have a DNS record for it, make sure you add a manual entry inside of /etc/hosts on your test machine!

sudo puppet agent -t --certname $(ioreg -l | awk '/IOPlatformSerialNumber/ { split($0, line, "\""); printf("%s\n", line[4]); }' | tr '[:upper:]' '[:lower:]') --waitforcert 20 --server puppet.example.com

You now have your first machine up and running on Puppet! Congratulations!

There's plenty of information online if you want to begin playing with some configuration settings inside of the Core Repository.

We'll go over this more in a future post, but to configure a role we need to create a fact on the local machine. This can be done by running:

sudo mkdir -p /etc/facter/facts.d
sudo nano /etc/facter/facts.d/computer_role.yaml

Paste the following inside of computer_role.yaml

---
computer_role: "test"

Since the machine is now configured with puppet, you can trigger puppet by running a much simpler command:

sudo puppet agent -t

If you have added the computer_role fact, you should get an output similar to the following when you run puppet agent -t:

Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Loading facts
Info: Caching catalog for c02n5heug3qj
Info: Applying configuration version '1449305286'
Notice: Test Role
Notice: /Stage[main]/Roles::Test/Notify[Test Role]/message: defined 'message' as 'Test Role'
Notice: Default Role
Notice: /Stage[main]/Roles::Default/Notify[Default Role]/message: defined 'message' as 'Default Role'
Notice: Common Profile
Notice: /Stage[main]/Profiles::Common/Notify[Common Profile]/message: defined 'message' as 'Common Profile'
Notice: Test Profile
Notice: /Stage[main]/Profiles::Test/Notify[Test Profile]/message: defined 'message' as 'Test Profile'
Notice: Finished catalog run in 13.84 seconds

Additional Notes

Whenever you make a change to your Core Repo, you also need to manually tell your Puppetserver to pull in the changes. This can be done by running this on the server:

docker exec -it puppetserver r10k deploy environment -pv

Yay! We've made it to the end and we now have a functional Puppetserver! If you've made it this far give yourself a pat on the back, it took me much longer to get up and running with Puppet when I first started.

Here's a few quick notes:

  1. This is a new Docker image and could have some bugs and issues. I'm relying on the community to help identify these issues.
  2. I'm not an expert at this, I just started using puppet a few months ago myself. If you see areas that could be improved feel free to submit a pull request.
    1. The Puppetserver code can be found here.
    2. The Core Repo code can be found here.
    3. An Example Repository with more code can be found here.
    4. The Base Image code can be found here. It is based off of work done by phusion, located here.
  3. I'm currently not running this Image in Production. I plan on moving over to it after the Christmas Holidays as I continue to document our Management Platform.

Stay Tuned for the next post! No promises but i'll see if I can at least get one more post up documenting how to begin programming some configuration options. If I don't get to it, I'll definitely have time in January! (I'll do my best not to keep you waiting 2 months this time)

Profiles and Settings

macOS Updates

com.apple.commerce

  • AutoUpdate → Install app updates
  • AutoUpdateRestartRequired → Install OS X updates

com.apple.SoftwareUpdate

Deploying

As of 2017.07.27, deploying com.apple.commerce settings via profile is unpredictable. Forcing the settings to “false” appears to work, but forcing the settings to “true” does not. For maximum reliablity, use a script.

With Casper Suite
  1. Create a script from “Computer Management > Scripts”
  2. Create a Policy “Once per Computer” to execute this script
  3. Create a Policy “Once per Week” with Software Updates > Install Software Updates from “Each computer’s default software update server”. Don’t forget to set restart options.

Applications

Google Chrome

Configure Suggested Preferences

To do that, we’ll create a file: /Library/Google/Google Chrome Master Preferences

Use these pages as a reference: * <https://support.google.com/chrome/a/answer/187948> * <https://www.chromium.org/administrators/configuring-other-preferences> (may not be up to date)

For example:

{
  "homepage" : "http://www.maclovin.org",
  "homepage_is_newtabpage" : true,
  "browser" : {
    "show_home_button" : true,
    "check_default_browser" : false
  },
  "bookmark_bar" : {
    "show_on_all_tabs" : true
  },
  "distribution" : {
    "skip_first_run_ui" : true,
    "show_welcome_page" : false,
    "import_search_engine" : true,
    "import_history" : false,
    "create_all_shortcuts" : true,
    "do_not_launch_chrome" : true,
    "make_chrome_default" : false
  },
  "first_run_tabs" : [
    "http://www.maclovin.org",
    "welcome_page",
    "new_tab_page"
  ]
}

If you want to delete every user Preferences and Cache, and launch Chrome as if it was its first run, use the following commands:

rm ~/Library/Preferences/com.google.Chrome.plist
rm -rf ~/Library/Caches/Google/
rm -rf ~/Library/Application\ Support/Google/Chrome/
Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --force-first-run

-> Good ressource on Google Chrome’s Command Line options

Microsoft Office 2016

Changing the Name and Initials

If you deployed Office 2016 with a volume license, chances are your user will complain that the name used for reviews (or Auto-Track changes) is “Microsoft Office User” with initials “MO” (or any localized variation).

It is stored here : ~/Library/Group Containers/UBF8T346G9.Office/MeContact.plist

$ defaults read "/Users/fti/Library/Group Containers/UBF8T346G9.Office/MeContact.plist"
{
    Initials = FTI;
    Name = "Francois Levaux-Tiffreau";
}
How to script it

A simple script that sets both the Office 2016 Name and Initials values in the MeContact.plist for the currently logged in user.

#!/bin/bash

PATH=/bin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/libexec
export PATH

FullScriptName=$(basename "$0") # Variable used to store the file name of this script

DsclSearchPath="/Local/Default" # Variable used to store the search path used by the dscl command.

# Get the username of the person currently running the script.
username=$(id -un)

echo "$FullScriptName -- Personalizing Office 2016 for $username"

# Lookup the user's name from the local directory
firstname=$(dscl "$DsclSearchPath" -read /Users/$username RealName | tr -d '\n' | awk '{print $2}')
lastname=$(dscl "$DsclSearchPath" -read /Users/$username RealName | tr -d '\n' | awk '{print $3}')

# Get the first letter for the initial
firstInitial=${firstname:0:1}

# Get the first letter for the initial
lastInitial=${lastname:0:1}

# Concatenate the initials together into one variable.
UserInitials="$(echo $firstInitial$lastInitial)"

# Concatenate the full name together into one variable.
UserFullName="$(echo $firstname $lastname)"

# Remove any leading or trailing whitepace
UserFullName="$(echo -e "${UserFullName}" | sed -e 's/^[[:space:]]//' -e 's/[[:space:]]$//')"
UserInitials="$(echo -e "${UserInitials}" | sed -e 's/^[[:space:]]//' -e 's/[[:space:]]$//')"

defaults write "/Users/$username/Library/Group Containers/UBF8T346G9.Office/MeContact.plist" Name "$UserFullName"

defaults write "/Users/$username/Library/Group Containers/UBF8T346G9.Office/MeContact.plist" Initials "$UserInitials"

echo "$FullScriptName -- Completed personalizing Office 2016 for $username"

# Quit the script without errors.
exit 0

Deploying Office Templates

It’s technically possible to deploy your templates in ~/Library/Group Containers/UBF8T346G9.Office/User Content.localized/Templates.localized, but unfortunately this container won’t exist until the user launches an Office application. There’s a better way.

Simply drop your templates in /Library/Application Support/Microsoft/Office365/User Content.localized/Templates.localized to get them avaiable for all users at any time. They will be available to the user in File > New from Template…. You can also create subfolders (won’t change display). As they’re directly referenced, any change to this folder will be reflected in Office (they’re not copied).

How to script it

You can either create a package to deploy the templates at the right place, or use this script to create the directories:

# This script checks for and creates if needed the directories for Office 2016 templates for Word, PowerPoint and Excel
# Made by Rich-the-Great

function test_command {
    "$@"
    local status=$?
    /bin/echo -n "Executing '$@'… "
    if [ $status -ne 0 ]; then
        echo "ERROR: $@" >&2
        exit $status
    fi
    echo "OK"

}

if [[ ! -d "/Library/Application Support/Microsoft/Office365/User Content.localized/Templates.localized" ]]; then
   /bin/echo "Necessary support directories for Office 2016 templates not found."
   /bin/echo "Creating necessary support directories for Office 2016 templates."

   test_command /bin/mkdir -p "/Library/Application Support/Microsoft/Office365/User Content.localized/Templates.localized"

   # We don't use -R to make sure we don't step on anybody's shoe
   # On a fresh 15.19.1 install, perms are root:wheel 755
   test_command /usr/sbin/chown root:wheel "/Library/Application Support/Microsoft/Office365"
   test_command /usr/sbin/chown root:wheel "/Library/Application Support/Microsoft/Office365/User Content.localized"
   test_command /usr/sbin/chown root:wheel "/Library/Application Support/Microsoft/Office365/User Content.localized/Templates.localized"
   test_command /bin/chmod 755 "/Library/Application Support/Microsoft/Office365"
   test_command /bin/chmod 755 "/Library/Application Support/Microsoft/Office365/User Content.localized"
   test_command /bin/chmod 755 "/Library/Application Support/Microsoft/Office365/User Content.localized/Templates.localized"
fi

SQL Clients

IBMi DB2 Databases

You could use the IBM System i Navigator, but how about a tool you can use on all DB (PostgreSQL, MySQL, SQL Server, DB2…). I must warn you: it’s Java from the nineties.

Components needed
JTOpen

The IBM Toolbox for Java is a library of Java classes supporting the client/server and internet programming models to a system running IBM i (or i5/OS or OS/400). The classes are used by SQuirreL to easily access IBM i data and resources.

  1. Download and install Java 1.8 JDK

  2. Download JTOpen

  3. Copy lib/ where you’d like. I believe the right path is ‘/usr/local/lib/jtopen/’:

    ``sudo ditto ~/Downloads/jtopen_9_0/lib /usr/local/lib/jtopen_9_0``
    
SQuirreL
  1. Download `SQuirrel <http://www.squirrelsql.org/#installation>>`_
  2. Open squirrel-sql-3.7-MACOSX-install.jar
  3. Make sure you select “Optional Plugin - DB2”
  4. Click on Drivers > JTOpen(AS/400)
  5. Click on Extra Class Path
  6. Click Add, then choose /usr/local/lib/jtopen_9_0/java8/jt400.jar and click OK.
  7. You should get a green message “Driver class com.ibm.as400.access.AS400JDBCDriver successfully registered for driver definition: JTOpen(AS/400)”
  8. Click on Aliases > + (blue cross)
  9. Select Driver: JTOpen(AS/400) (it should have a blue check mark)
  10. Enter your info, click OK. It should now be working!
_images/SQuirreL-conf.gif

More info…

Security

Antivirus

Microsoft SCEP

title

placeholder

About MacAdminsDoc

Contributing

Community Structure

  • Owner is currently @ftiff
  • Maintainers and Collaborators are all the members of [Shufflepuck](http://github.com/Shufflepuck)
  • Contributors is everyone who has had a pull request merged into this project
  • Community Members are all the MacAdmins. You’re part of it if you professionaly administer Macs

We welcome everyone interested in sharing to the MacAdmin community at large.

Getting Started

If you’re new to the MacAdmin community, please read the doc Getting Started.

Best practices?

This site is about things that have a large consensus in the MacAdmin Community. For example:

  • thin imaging is better than thick imaging (not always).
  • Apple maintains only the last three major OS releases (well… we had a security update for 10.6.8 after 10.11 was released[#]_).
[1]https://support.apple.com/kb/HT205267

So yes, defining “best practice” is not that easy. Think of what you will tell to your apprentice.

What is a good doc?

A good documentation:

  • uses International English - Keep the sentences clear, simple and straight to the point;
  • is neutral - Show multiple point-of-views. Be accurate by citing reliable and authoritative sources. Debate is welcomed, but only on Issues and Pull Requests;
  • uses Free content - As per the [license](http://creativecommons.org/licenses/by-sa/4.0/), no one owns the content;
  • is respectful - Follows the [Contributor Code of Conduct v1.3.0](http://contributor-covenant.org/version/1/3/0/).
  • is honest, but positive.

Contributing docs

We use Github to manage the whole content. You should be familiar with how to contribute on GitHub. I personally use TextMate to edit the reStructuredText documents.

The easiest way for quick edits is to click on the “Edit on GitHub” on upper right of every page.

_images/edit_on_github.png

For more substantial edits, please fork the project and open a pull request.

Note

You can always Create an issue. Please do it if you don’t have time!

License

License is Creative Common Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)

This means:

You are free to:

  • Share — copy and redistribute the material in any medium or format
  • Adapt — remix, transform, and build upon the material

for any purpose, even commercially.

The licensor cannot revoke these freedoms as long as you follow the license terms.

under the following terms:

  • Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made.
  • ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.

Read more…

MacAdmins Resources

Discussion

  • `MacAdmins Slack <https://macadmins.herokuapp.com>`_ - if you like instant communication;
  • `##osx-server <http://webchat.freenode.net/?channels=#%23osx-server>`_ - this was the go-to before Slack existed. Try the Field Guide to IRC from AFP548;
  • `JAMF Nation <http://jamfnation.jamfsoftware.com/>`_ - Primarily focused on JAMF Product, this is nonetheless a great resource;
  • `Munki Google Group <https://groups.google.com/d/forum/munki-dev>`_
  • `Mac Enterprise Mailing List <http://www.macenterprise.org/mailing-list>`_

Tools for MacAdmins

Editors

TextEdit

Being free and available on every Mac, this is a no-brainer.

  • Make sure you edit in text-only (CMD+SHIFT+T)
  • Add it to your Dock for quick drag ‘n drop
  • in CLI, use open -e [file] to open in TextEdit
Sublime Text

Download for free here

TextMate

Download for free here

  • My preferred editor. Thanks to @lolopb for pointing this out!

Notable Contributors

ftiff

Real Name François Levaux-Tiffreau
Employer Amaris
Slack @ftiff
Twitter @ftiff
Linkedin ftiff
Blog https://maclovin.org

mactroll

Real Name Joel Rennich
Employer Trusource Labs
Slack @mactroll

mosen

Real Name Unknown
Slack @mosen