The Puppet Labs Issue Tracker has Moved:

Puppet on Mac OS X

Generally speaking, there are very few differences between Mac OS X and other Unix operating systems. OS X’s unix underpinnings are evident in the short list of special cases we must take into account when running Puppet on an OS X computer. Throughout the rest of this document, I’ll refer to Mac OS X as Darwin.


Within the Mac OS X system administration community, there are currently three major deployment strategies:

  • Monolithic Disk Image (Block Level Cloning)
  • Filesystem Synchronization (File Level Cloning)
  • Package Installation (often ad-hoc or on-demand)

Most large sites appear to deploy monolithic “master images” which contain all managed software. This is similar to Ghost in the Microsoft world, and is commonly achieved by using Apple NetBoot-Install Service, part of Mac OS X Server, or Mike Bombich’s NetRestore-Helper. The popular NetRestore product by Bombich was recently retired, and Deploy Studio has been recommended as a capable successor, one that supports advanced large-scale cloning using technologies like multicast asr, among others.

A common alternative to NetBoot-Install monolithic images are file level synchronization services, such as Radmind

Finally, some sites manage software using Apple’s PackageMaker tool, distributed as part of Xcode Tools. Often, these packages are deployed as incremental updates to managed computers through Apple Remote Desktop, or added to a slipstreamed), cloning-ready image like those created by InstaDMG or the JAMF Imaging Suite.

Puppet plays nicely with all three of these strategies. In particular, Puppet’s dependency mapping features set it apart from most other tools, particularly for package deployment. In addition, unlike the common Remote Desktop package deployment method, Puppet is able to ensure packages get installed, even if the receiving node is off line when the package is deployed to the site.



Mac OS X nodes have a slightly unique package provider available to them. The pkgdmg provider facilitates the deployment of software packaged with Package Maker, and wrapped in a disk image (DMG). The package is wrapped in order to provide easier network file copies, as a .pkg and .mpkg object are standard directories.

The pkgdmg provider also allows source packages to reside at any location Open::URI accepts. In plain terms, this means a standard HTTP server.

This example deploys Apple Remote Desktop and TextMate onto support workstations:

# Convenience component for installing pkg.dmg packages.
define pkg_deploy($sourcedir = false)
  $sourcedir_real = $sourcedir ? {
    false => "",
    default => $sourcedir
  package { $name:
    ensure => installed,
    provider => pkgdmg,
    source => "$sourcedir_real/$name"

class support-workstation {
  case $operatingsystem {
    Darwin: {
      pkg_deploy { "RemoteDesktop-3.0.0-ub.pkg.dmg":
        sourcedir => "",
        alias => ard-admin300
      pkg_deploy { "RemoteDesktopAdmin310.dmg":
        sourcedir => "",
        require => Package[ard-admin300],
        alias => ard-admin310
      pkg_deploy { "TextMate-1.5.1-01-ub.pkg.dmg": alias => textmate }

The pkg_deploy component defines standard package objects on the client, and specifies the use of pkgdmg as the provider.

Note the use of require => Package[ard-admin300] in the RemoteDesktopAdmin310.dmg definition. This dependency coupling feature of puppet sets it apart from other tools such as Apple Remote Desktop and Filewave.

The overall process this provider takes is:

  1. If necessary, download the DMG to the local file system.
  2. Mount the DMG using hdiutil
  3. Install all .pkg or .mpkg objects in the root directory of the DMG using installer
  4. Touch a control file in /var/db/, allowing future ensure => present checks
  5. Eject the DMG using hdiutil
  6. Remove source file.

The source code is short and available at [source:lib/puppet/provider/package/pkgdmg.rb]

NOTE: Currently, I believe the source file is always removed. This may bite you if you’re not installing from HTTP. This may be a bug.

Multiple Architectures

A somewhat unique issue when managing Apple hardware is dealing with two very different hardware architectures; powerpc and i386. Apple has done a remarkable job of pulling off the complete transition of all their products to intel hardware, but this poses a problem for some software which refuses to run on the wrong hardware.

I typically handle these unique cases using case statements or selectors: (Note: $hardwaremodel is automatically set by the Facter library on each client node.)

# Apple updates.
class sw-apple {
  Pkg_deploy { sourcedir => "" }
  case $hardwaremodel {
    "i386": {
      pkg_deploy { "SecUpd2006-007Intel.dmg": alias => secupd2006-007 }
      pkg_deploy { "SecUpd2006-008Univ.dmg": alias => secupd2006-008, require => Package[secupd2006-007] }
      pkg_deploy { "iChatUpdateUniv.dmg": alias => ichat10 }
      pkg_deploy { "AirPortExtremeUpdate2007001.dmg": alias => airport2007001 }
      pkg_deploy { "SecUpd2007-002Univ.dmg": alias => secupd2007-002, require => Package[secupd2007-001] }
    } # i386:
    "Power Macintosh": {
      pkg_deploy { "SecUpd2006-007Ti.dmg": alias => secupd2006-007 }
      pkg_deploy { "SecUpd2006-008Ti.dmg": alias => secupd2006-008, require => Package[secupd2006-007] }
      pkg_deploy { "iChatUpdatePPC.dmg": alias => ichat10 }
      pkg_deploy { "SecUpd2007-002Ti.dmg": alias => secupd2007-002, require => Package[secupd2007-001] }
    } # powerpc:
  } # case $hardwaremodel
  # The following object are deployed regardless of processor architecture.
  pkg_deploy { "SecUpd2007-001Ti.dmg": alias => secupd2007-001, require => Package[secupd2006-008] }
  pkg_deploy { "DSTUpdateTi-001.dmg": alias => dst_update_tiger }
  pkg_deploy { "JavaForMacOSX10.4Release5.dmg": alias => java_r5 }
  pkg_deploy { "iTunes-7.0.2-1-ub.pkg.dmg": alias => itunes }
  pkg_deploy { "RemoteDesktopClient-3.1.0-1-ub.pkg.dmg": alias => ard-client,
    sourcedir => ""

Mac OS X specifc Facts

There’s a few Mac OS X specific Facter facts which should be available in the Facter trunk or versions more recent than 1.3.8.

These facts are generated from system_profiler -xml and sw_vers

Of particular interest is the sp_serial_number, which is present on every Mac workstation.

For example:

macosx_buildversion => 9A466
macosx_productname => Mac OS X
macosx_productversion => 10.5
sp_boot_mode => normal_boot
sp_boot_rom_version => MBP11.0055.B08
sp_boot_volume => Leopard
sp_bus_speed => 667 MHz
sp_cpu_type => Intel Core Duo
sp_current_processor_speed => 2.16 GHz
sp_kernel_version => Darwin 9.0.0b1
sp_l2_cache_share => 2 MB
sp_local_host_name => nutburner
sp_machine_model => MacBookPro1,1
sp_machine_name => MacBookPro15
sp_mmm_entry => MMM_stateMMM_enabled
sp_number_processors => 2
sp_os_version => Mac OS X 10.5 (9A466)
sp_physical_memory => 2 GB
sp_serial_number => W86XXXXXXXX
sp_smc_version => 1.2f10
sp_uptime => up 0:2:30:33
sp_user_name => Jeff McCune (mccune)

Running Puppet via Launchd

Check out the Puppet With Launchd page for plist files and information about running puppetd and puppetmasterd automatically at system startup via launchd.

%flipper —, by analogy with /windows? We think this is probably sensible. Some of this info is outdated (multiple architectures). What ELSE should go in /osx? This is certainly incomplete.