MCX, dslocal, and Leopard

WGM icon

Newer posts on the same subject:

Recently on the MacEnterprise mailing list, several of us were discussing putting MCX records into the local directory service. This is an appealing idea to me, because we don’t use Open Directory, and I’ve never wanted to spend the political capital to get our LDAP schema extended to support MCX, especially since I didn’t really know if ManagedClient/MCX would actually do what we wanted.

MCX in the local directory service seemed to me a way to experiment without having to convince our LDAP admins to make schema changes.

The idea of putting MCX records in the local directory service is not new. Karl Kuehn documented a method to inject MCX settings into the local NetInfo database here. Karl’s method is a little complicated due to the complexities of working with the NetInfo database.

With the arrival of Leopard, what was an opaque database file has been transformed into simple plist files arranged in an easy-to-understand directory structure. This makes working with the local directory service much easier – in many cases, to make a directory service modification, you can just drop a file into a directory!

Additionally, Leopard’s directory service has replaced ComputerLists with ComputerGroups, which have a great new feature: computers can belong to multiple ComputerGroups. We’ll use that new feature later.

When designing my implementation – I wanted some key features:

1) Use Workgroup Manager to create ComputerGroups and set managed preferences.
2) Capture the changes quickly and easily.
3) Make the managed preferences modular, so I could mix and match sets of managed preferences on any given machine.
4) Have different sets of managed preferences apply to laptops versus desktops, and have the correct set apply to the right class of machines without having to manually associate laptop preferences to laptops, and desktop preferences to desktops.

You’ll need Workgroup Manager, which is part of Apple’s Server Admin Tools 10.5. Download those here, get them from your Leopard Server install media, or search Apple’s website for “Server Admin Tools”.

To work with the local directory service, launch Workgroup Manager on a OS X client machine. When presented with the dialog to connect to a server, type “localhost” as the server name, and enter the name and password of a local admin for the local machine.

WGM connect dialog

You’ll see a warning that you are working in a directory node that is not visible to the network. Check “Do not show this warning again” if you wish, and click OK to dismiss the panel.

Get started by selecting the Computer tab in the left pane. Click “New Computer” in the toolbar to create a new computer object. Name it “local_desktop”. Don’t add any other info to the record other than the name and short name, which should be “local_desktop” as well. Repeat the process, creating another computer object named “local_laptop”.

WGM Computer Objects

Next, move to the Computer Groups tab in the left pane. This is where it gets interesting. Create Computer Groups for each group of preferences you want to manage. I grouped them logically: the LoginWindow group contains the managed preferences for the loginwindow; NetworkProxies contains the managed settings for the network proxies, etc.

WGM Computer Groups

For each group, add local_desktop, local_laptop, or both as members; depending on whether you want the managed preferences to apply to desktop machines, laptops, or both. For example, you might want Mobility settings to apply only to laptops, so you would add only the local_laptop to the group that contained the managed preferences for Mobility.

Once you’ve created a group and added the appropriate local machines, you can then configure the preferences you wish to manage. See Apple’s documentation on User Management for more on managing preferences.

WGM computer group preferences

You now have MCX data in your local directory service. Next, we need to get that data to all our managed machines.

With Leopard, you are interested in two directories:
/private/var/db/dslocal/nodes/Default/computers/
and
/private/var/db/dslocal/nodes/Default/computergroups/

There should be two files in /private/var/db/dslocal/nodes/Default/computers/:
local_desktop.plist
local_laptop.plist

In /private/var/db/dslocal/nodes/Default/computergroups/ you should find one plist file for each Computer Group you created.

To replicate your MCX records across all your machines, you’ll need to copy the files from those directories to the same directories on all your other machines. I use radmind, which is perfectly suited for this task. You could also use FileWave, ARD, or puppet, to mention a few possibilities. Another possibility that seems pretty interesting would be to copy these files to a web server, and have a local script on each machine periodically curl them down and copy them into place.

Once you get these files to another test machine, check to see that DirectoryServices sees them:


dscl . list /Computers
dscl . list /ComputerGroups

If you don’t see them listed and you’re certain they are there, try killall DirectoryService to restart DirectoryService and check again with dscl.

There’s one more thing you must do on the local machine, though, before these files will actually do anything useful on each client.

You’ll recall that when we created the local_desktop and local_laptop computer objects, we only named them – we did not enter the Ethernet ID information. In order to actually work, the appropriate object (local_desktop or local_laptop) must have its ENetAddress field set to the MAC layer address of the en0 interface of the local machine. To do this, I run a script at startup (I’ve added a bunch of line breaks [\] to make the text fit inside WordPress’s textwidth):


#!/bin/sh

changedMCX=false

macAddress=`/sbin/ifconfig en0 | \
 /usr/bin/grep 'ether' | \
 /usr/bin/sed \
 "s/^[[:space:]]ether //"  | \
 cut -f1 -d " "`

IS_LAPTOP=`/usr/sbin/system_profiler \
 SPHardwareDataType | \
 grep "Model Identifier" | grep "Book"`

if [ "$IS_LAPTOP" != "" ]; then
        computerRecordName=local_laptop
        otherRecordName=local_desktop
else
	computerRecordName=local_desktop
        otherRecordName=local_laptop
fi
	
storedMacAddress=`/usr/bin/dscl . -read \
 /Computers/$computerRecordName \
 ENetAddress | cut -f2 -d " "`

if [ "$storedMacAddress" != "$macAddress" ] ; then
	echo "Updating MAC address \
 for /Computers/$computerRecordName..."

	echo "was: $storedMacAddress"
	echo "now: $macAddress"

	/usr/bin/dscl . -create \
 /Computers/$computerRecordName \
 ENetAddress $macAddress

	/usr/bin/dscl . -delete \
 /Computers/$otherRecordName \
 ENetAddress

	changedMCX=true
fi

if [ "$changedMCX" = "true" ] ; then
	currentuser=`/usr/bin/who | \
 grep console`

	if [ "$currentuser" = "" ]; then
		echo "Restarting loginwindow..."
		`/usr/bin/killall loginwindow`
	fi
fi

This script uses system_profiler to tell if the machine is a laptop. If it is, it adds its en0 MAC layer address to local_laptop, otherwise it adds it to local_desktop.

Once local_laptop or local_desktop point to the local machine by way of the MAC layer address, all the policies you’ve defined in the various ComputerGroup objects will now apply.

You can deliver all the ComputerGroup objects to each client, or if you have a client with special settings, you can deliver only some of the ComputerGroup objects, or deliver different ComputerGroup objects.

The local_laptop and local_desktop Computer objects allow you to specify that some policies apply only to laptops, only to desktops, or both, and the local machine automatically places itself in the correct Computer object. This could be extended to have a machine place itself in some other Computer object, but it’s important to note that a machine’s MAC layer address can be in only ONE Computer object. The Computer object can be in multiple ComputerGroups.

In my implementation, local_laptop is in all the ComputerGroups, but local_desktop is not in the MobileAccounts, ScreenSaver, or SecureVM groups as we don’t enforce those settings on desktop machines. No-one has to remember to add the laptop-only policies to the right machines, or remember to add the laptops to the correct ComputerGroups – the machines do this themselves.

Fully implemented, we now have a solution that allows us to use Apple’s tools (Workgroup Manager, ManagedClient.app, MCX preference manifests, etc) to manage client preferences without having to extend the LDAP schema or build a “Magic Triangle” solution. This might serve as a test platform, or a learning environment to allow Mac admins to experiment with Client Management, or as an actual deployed method of managing OS X clients in your environment.

Nigel Kersten has also written on this topic. His article on afp548.com has a lot of command line examples, including examples of the new MCX support in dscl in Leopard.

MCX, dslocal, and Leopard

34 thoughts on “MCX, dslocal, and Leopard

  1. Greg,
    Thanks for getting me on the right track. I moved our network from AFP to NFS and PHDs stopped syncing. I couldn’t find anything on the web to lead me in the right direction until I came across your blog. It was when you mentioned:

    “With Leopard, you are interested in two directories:
    /private/var/db/dslocal/nodes/Default/computers/
    and
    /private/var/db/dslocal/nodes/Default/computergroups/”

    It got me thinking there was something in there regarding users, and sure enough, there the golden plist lie. All I had to do was delete the array for “original_home_loc”, and I was good to go.

    Thanks again, I’ll be checking back often.

    Jason

  2. Jerry says:

    Ok, I’ve done pretty much all of the above steps but didn’t see any change in my MCX settings on the local client. I performed all of the dscl checks and even restarted the computer.

    The only thing I suspect is the script I copied the script and then attempted executing it with ARD to the local client. Bad idea? If so where should I place the script in order for it to execute? Also, for MCX settings to take would existing user accounts see the new MCX settings?

    Thanks

    Jerry

  3. If the script did not execute properly, then the MCX settings won’t be applied.

    Forgetting remote machines for a moment – does the script run correctly on your local machine?
    Is the stored MAClayer address in /var/db/dslocal/nodes/Default/computers/local_desktop.plist (or local_laptop.plist) correct?

    If so, check a remote machine – the ENetAddress in the local_desktop.plist (or local_laptop.plist) must be the same as the MAC layer address of en0.

  4. Mark says:

    Really useful post, unfortunately when I follow the above I get the following error when running the script:

    DS Error: -14136 (eDSRecordNotFound)
    Updating MAC address for /Computers/local_laptop…
    was:
    now: 00:17:f2:2f:30:d7
    create: Invalid Path
    DS Error: -14009 (eDSUnknownNodeName)
    delete: Invalid Path
    DS Error: -14009 (eDSUnknownNodeName)

    The mac address is being picked up, but it’s not writing it to the local-laptop file. Can anyone help please?

  5. Is there a valid plist at //var/db/dslocal/nodes/Default/computers/local_laptop.plist? The script posted _modifies_ that entry, but does not create it if it’s missing. The DS Error you’re getting seems to imply the record isn’t there.

  6. Shane Palmer says:

    Greg,

    I am currently rethinking my monolithic imaging process as I prepare for Snow Leopards release and as I move away from Mike Bombich’s awesome but decommissioned NetRestore utility. I came across your articles as a possible way to deal with settings on my images rather than dealing with the User Template folder.

    I am learning the hard way that there are some gotchas as you mentioned. For instance I found that most settings in Date & Time can be managed, but the Time Server setting cannot because it is stored in a simple text file at /etc/ntp.conf. I have also come across a possible Apple bug (at least in 10.5.7) in the Energy Saver settings in that they can be managed Always, but are not grayed out to prevent users from changing them.

    Before coming across your tips on using MCX locally I started using a shell script for some of the defaults settings that I have figured out, especially those wonderful hidden settings that cannot be set via any GUI (like DSDontWriteNetworkStores) and had high hopes that MCX could provide a single point for controlling all of the custom settings I put on my image, but it doesn’t appear to be the case.

    I know you are a busy person (aren’t we all), but it would be nice to see a followup post listing all of the gotchas you have come across and all of the specific settings you manage, especially settings that you have added since the original posting. Obviously you are or have thought about Office 2008 and iTunes since you have written about those, but have you tried managing many other third party apps?

    Thanks for all of the great info on your site.

  7. Absent a followup post, please accept this followup comment:

    The only third-party apps I currently manage via MCX are the Office 2008 apps.

    I can’t think of any reason you couldn’t use MCX to manage DSDontWriteNetworkStores, just import ~/Library/Prefrences/com.apple.desktopservices.plist and prune away any unwanted keys.

    As for everything else: my approach is to use MCX to manage what it can manage, and script the rest. Nothing’s perfect.

  8. Hey Greg,

    You tried this with 10.6 yet? I poked around MacEnterprise and didn’t see anyone talking about it. We implemented it with 10.5 last spring/summer and it’s been working fantastic once we finally got it working.

    We’re gearing up to start deploying 10.6 to our users and so this is what I choose to tackle today but it has been giving me some fits.

    1) The local_computer.plist computer entry in the local directory disappears sometimes on reboot.
    2) If it does stick around which sometimes it does my managed preferences kick in like they should but DirectoryService starts crashing continuously causing the machine to pinwheel every 5 seconds or so.

    Anyways wondering if you’ve had any luck and if you’re still using it the same fashion you were using it before?

    I just finished perusing “Mac OS X Server v10.6 – User Management” hoping to find some clues in there but no luck.

  9. It seems to work for us under Snow Leopard. I did see some oddity where the MAC layer address disappeared from local_desktop.plist, but I haven’t yet been able to reproduce the issue. I certainly haven’t seen DirectoryService crashing from this…

    How are you populating your local_computer.plist?

  10. I’ve been able to figure out how to keep the local_computer.plist entry from disappearing.

    If the plist is created and it doesn’t have any MCX settings associated with it would disappear on reboot. While if it has MCX settings in it, it sticks around.

    I’m guessing that there may be some sort of redundancy check in dslocal that notices that there is essentially two localhost entries (the system default being /var/db/dslocal/nodes/Default/computers/localhost.plist) and the one we are creating. We call it local_computer.plist just since we were afraid naming it local_host could conflict with something that may be reserved. Probably an unfounded fear.

    On 10.5 we didn’t associate any MCX settings with this entry and instead add computergroups containing this local_computer entry that have our MCX settings. But on 10.6 since it kept disappearing I wasn’t having any MCX policy being applied.

    Workgroup Manager doesn’t let you add localhost.plist to computergroups, but I haven’t tried adding it directly to the plist with a script which I could check out. Then I wouldn’t need the script that creates local_host.plist and sets the EN0 address in it.

    So now I just added an MCX setting we want all computers to have to our local_computer.plist and its sticking around just fine.

    As far as DS crashing it has something to do with enforcing mobile accounts creation and even more specifically with me mapping the mobile account to a non-startup volume. Because I can get mobile accounts working on the startup volume fine.

    I’m going to mess around with it again today. I noticed there are new Server Admin Tools out that include an updated Workgroup Manager. So I’ll give it a try with that, double check what the mobile accounts MCX plist looks like, compare it to my 10.5 plist that works just fine.

    I’m glad to hear that it is still working for you. Once we got it working its been so great for managing staff! And with the mobile accounts and password caching our staff can use their campus password so it gets us out of the password business.

    We could certainly stop being lazy and set up a golden triangle with the campus AD but this worked so well in the past that we’ve never quite gotten to the point we actually wanted to tackle that option. And the only people we use it for is staff and we only just started supporting staff this year. Previously it was just labs.

  11. Michael Linde says:

    I’m having a headache with this, and willing for ideas on where to look. I just started working on migration to SL (10.6.2) and my 10.5.x Local MCX settings don’t work (which is fine, I guess).

    So I did the clean break setup, put a machine up with nothing but a clean install of SL, patched fully to 10.6.2, downloaded 10.6.2 Server Admin, created a new local computer record, added the MAC address, and applied login window settings. They show up in the local node in WGM, but don’t apply. I checked /Library and there is no Managed Preferences folder being created?

  12. Odd. My Local MCX computergroups (which include a “local_desktop” and/or “local_laptop”) were brought over from my 10.5 setup without change and generally work as expected. I’ve seen no real issues.

    Have you used mcxquery and/or mcxrefresh? Note that loginwindow managed preference changes require at least a logout to take effect.

    1. Michael Linde says:

      I did it a couple ways before trying a clean image, but even with the clean image (reboots and logouts tried) I can’t get it going. Now I’m SURE it’s something simple. Just what, is the question…

  13. Cyrus says:

    Am I correct to assume the script that determines the MAC Address runs every time the computer starts up or it only needs to run once? If once, can I assume it won’t matter which MAC Address it grabs, en0 or en1? If it runs at every startup how does the script pick up the correct MAC Address, I only see references to en0.

    Great solution and thank you for sharing.

    1. The MAC layer address for en0 is the correct value for the computer record no matter which interface is actually in use. You could run it once or at every startup; running it at every startup would fix the record if it were to become broken for whatever reason.

      -Greg

  14. escobar says:

    This is awesome!.. great job!
    I have a question.. I created the computer with no Mac Address and created a computer group (one group only “local_desktop”) ..and also created all the mcx settings.

    now i’m trying to deploy the image and run the script to replace the mac address with the new one, is it possible to run the script at login using launchd ?

    and do i need to copy any files afterwords like /private/var/db/dslocal/nodes/Default/computers/
    or /computergroups ?

  15. Irv says:

    I spent some time working on getting this script working on Snow Leopard. I modified it so that injects the en_address (MAC Address) and the UUID into the local computer group. This took me a long time since I don’t know much UNIX. Feel free to tell me where you would like to see changes done. Also, I can’t take credit for the script as I only modified to work with SL.

    #!/bin/sh

    changedMCX=false

    UUID=`system_profiler SPHardwareDataType | grep “Hardware UUID” | awk -F’: ‘ ‘{print $2}’`

    IS_LAPTOP=`/usr/sbin/system_profiler SPHardwareDataType | grep “Model Identifier” | grep “Book”`

    if [ “$IS_LAPTOP” != “” ]; then
    computerRecordName=local_laptop
    otherRecordName=local_desktop
    else
    computerRecordName=local_desktop
    otherRecordName=local_laptop
    fi

    storedUUID=`/usr/bin/dscl . -read /Computers/$computerRecordName hardwareuuid | cut -f2 -d ” “`

    if [ “$storedUUID” != “$UUID” ] ; then
    echo “Updating UUID for /Computers/$computerRecordName…”

    echo “was: $storedUUID”
    echo “now: $UUID”

    /usr/bin/dscl . -create /Computers/$computerRecordName hardwareuuid $UUID

    /usr/bin/dscl . -delete /Computers/$otherRecordName hardwareuuid

    changedMCX=true
    fi

    if [ “$changedMCX” = “true” ] ; then
    currentuser=`/usr/bin/who | grep console`

    if [ “$currentuser” = “” ]; then
    echo “Restarting loginwindow…”
    `/usr/bin/killall loginwindow`
    fi
    fi

    changedMCX=false

    changedMCX=false

    macAddress=`/sbin/ifconfig en0 | /usr/bin/grep ‘ether’ | /usr/bin/sed “s/^[[:space:]]ether //” | cut -f1 -d ” “`

    IS_LAPTOP=`/usr/sbin/system_profiler SPHardwareDataType | grep “Model Identifier” | grep “Book”`

    if [ “$IS_LAPTOP” != “” ]; then
    computerRecordName=local_laptop
    otherRecordName=local_desktop
    else
    computerRecordName=local_desktop
    otherRecordName=local_laptop
    fi

    storedMacAddress=`/usr/bin/dscl . -read /Computers/$computerRecordName en_address | cut -f2 -d ” “`

    if [ “$storedMacAddress” != “$macAddress” ] ; then
    echo “Updating MAC address for /Computers/$computerRecordName…”

    echo “was: $storedMacAddress”
    echo “now: $macAddress”

    /usr/bin/dscl . -create /Computers/$computerRecordName en_address $macAddress

    /usr/bin/dscl . -delete /Computers/$otherRecordName en_address

    changedMCX=true
    fi

    if [ “$changedMCX” = “true” ] ; then
    currentuser=`/usr/bin/who | grep console`

    if [ “$currentuser” = “” ]; then
    echo “Restarting loginwindow…”
    `/usr/bin/killall loginwindow`
    fi
    fi

    1. Thanks, Irv. I haven’t found it necessary to add the hardware UUID to the computer records, but maybe it’s needed in some environments. Did this solve an issue you saw?

      1. Irv says:

        I didn’t know you didn’t the the Hardware UUID in Snow Leopard. I just always assumed they were needed. Not sure if it solved any issue I was having because i thought I needed the Hardware UUID in SL. Since I was only checking the record for it and not if MCX was actually working I don’t know if it worked otherwise.

        Question: Not sure what the en_address is for a macbook AIR since it doesn’t have a built in ethernet. If anyone can answer that it would great.

  16. macosx says:

    I only have one managed computergroup, can i just keep it within the deployed master image and then run the macaddress script to add the machine to that computergroup??

    or do i still need to copy that computergroup via ARD ?

  17. Hi Everyone,

    I just wanted to say thanks for keeping this record public. I am a recruiter for contract and permanent positions and this string of Q&A has helped me better understand what my client is in need of for their Active Directory/ Migration work.Ideally they are looking for someone who can come in and implement, setup new procedures. Not looking for support personnel rather looking for someone to managed AD containers and setup policies, manage the deployment of Altiris. Again, really appreciate this post and look forward to any feedback this can bring.

    Regards,

    Mike McGonigle

  18. software reviews says:

    It is the best time to make some plans for the future and it’s time to be happy. I have read this post and if I could I want to suggest you few interesting things or advice. Perhaps you can write next articles referring to this article. I want to read even more things about it!

Comments are closed.