Templating for fun and profit

30 Mar 2017

it begins...

The world of Mac is full of XML plist files. I've been looking for an (easy?) way to generate/manipulate some very specific ones for a while now. The problem is, the defaults command is a bit of a blunt instrument, and doesn't allow for more fine grained control of the things that you edit.

I did have a look at some python plist manipulation libraries - but as I'm not much of a coder (but, hey I'm working on it!) they left me all kinds of confused. The thing is, all I really want to do is a bulk find and replace. I know what I want my finished output files to look like, I just didn't know how to get there.

Enter the Jinja

I was explaining my woes to a colleague, who said - "what about Jinja?"

Jinja what now?

Jinja is a templating engine. You create a template, mark out the bits of the template you want to be able to change via some Python code. Point Jinja at that template, tell it what you want it to change and what/where you want it to output to, and Bob's your mother's brother.

Fuze for Rooms

For example, I've been using Apple configuration profiles to setup Fuze for Rooms. Fuze for rooms is based on the standard desktop Fuze video conferencing client, but becomes a "room" when fed a specific username and password.

The username is often (but not always) a calendar identifier string - which, where I work can be very long and hard to type in manually. There had to be a better way... Especially when one room becomes dozens, and then in short order a few hundred...

Profiles

So it was nice to discover the ability via macOS Server to import custom preference (plist) files and then turn those into Apple configuration profiles. But, when you manually import a plist, all sorts of additional information that isn't really required gets baked in. I'll blog about that some other time...

By default, these plists are generated in a binary format. If you want to edit them manually so you can remove that extra cruft, you'll need to convert them into a format you can work with - regular xml works for me!

The easiest way to fix this is to convert the .mobileconfig file from a binary plist into plain xml with plutil -convert xml1 example.plist

Bingo, you now have a text file you can edit in the text editor of your choice.

Slice and dice

Now I could mercilessly remove as much data as I dared, until all that was left was enough configuration to setup a Fuze room by name. And it worked!

Next step? I could manually edit the file I'd created, change the details to those of another Fuze room - double click to install that configuration profile - and then bingo, instant Fuze room configuration. (Well apart from the login password - that's a whole other kettle of worms)

It looks a bit like this:
In system preferences...
an Apple config profile in System Preferences

And in the Fuze client itself...
The Fuze client with a pre-populated username

But - we have over 100 Fuze rooms globally where I work. I don't want to create 100+ mobileconfig files manually. It's a lot of effort, and I don't trust my typing!

Templates

You'll recall I'd mentioned Jinja? Well, all I had to do was mark the parts of a mobileconfig template file that I wanted to change between some {{ braces }} - then I could write a loop in Python to replace those tagged values with the values for our Fuze rooms.

Some example XML...

<key>com.fuzebox.fuze.Fuze</key>
  <dict>
    <key>Forced</key>
      <array>
        <dict>
          <key>mcx_preference_settings</key>
            <dict>
              <key>Username</key>
              <string>{{ Room_username }}</string>
              <key>UsernameToJoin</key>
              <string>{{ Username_To_Join  }}</string>
            </dict>
        </dict>
      </array>
    </dict>

    [---- snip ------]

The Fuze admins were able to create a csv file containing all the Room details, and using the Python csv library, I was able to loop through that list of rooms, and generate configuration profiles for all of our rooms, ready to be deployed with Munki

UTF??

And it was all going swimmingly until I hit a meeting room by the name of The Haçienda which is (you guessed it) in our Manchester office - at which point my code choked on the ç. Python 2.7 (which is native on macOS 10.12) assumes ASCII unless told otherwise. This was resolved by explicitly forcing UTF8 in the code like so:

import sys
reload(sys)
sys.setdefaultencoding('utf8')  

Show me the code!

It's not the best code in the world, but it's saved me loads of grief. You'll find it on github, here

Published on 30 Mar 2017 Find me on Twitter!