Big Bubbles (no troubles)

What sucks, who sucks and you suck

Puppet to Ansible

I’ve just completed the move of the configuration management for my very small home network from an obsolete Puppet 2.x setup to Ansible. Total time = approximately half a day. Showstoppers - none.

There are only two nodes, a fileserver running OpenIndiana and a Fedora desktop. I had some very limited Puppet manifests to install my preferred packages, configure the automounter and apply some local fixes - the bare minimum for recovery if I ever needed to recreate systems. This was based on the Puppet 2.6 rulebase I used in my day job, with most of it hacked or commented out to disable the irrelevant parts.

It was way overkill for what I needed, and the problem was that the Fedora repository tracked the mainstream Puppet releases much more closely than staid old EPEL at work, so my 2.6 rules were suddenly running under a 3.7 agent. This worked, but led to voluminous log entries such as:

puppet-agent[6818]: Using less secure serialization of reports and query parameters for compatibility
puppet-agent[6818]: with older puppet master. To remove this notice, please upgrade your master(s)
puppet-agent[6818]: to Puppet 3.3 or newer.

puppet-agent[6818]: The package type's allow_virtual parameter will be
changing its default value from false to true in a future release. If you
do not want to allow virtual packages, please explicitly set allow_virtual
to false.

The entries are harmless, although they tend to indicate that things are going to break at some point in the future. And honestly, “less secure serialization”? It doesn’t really answer the four key questions of a lazy sysadmin: Yeah? And? So? What?

More importantly, I just didn’t fancy delving into an aging Puppet codebase that had been thrown together to begin with, and refactoring to some unknown degree to bring it up to 3.x standards. Puppet’s problem here is that the language has changed across releases, and practices that were previously recommended or common are now deprecated or even unsupported. In particular, the scoping changes looked likely to mean some extensive recoding, as I relied a lot on inheritance previously. Or maybe not - it was working now. But would it still work when Fedora 30 brought Puppet 7.0 with it?

So, as I was currently using Ansible more in my day job, I opted to rewrite the whole thing in Ansible anyway. It’s a lighter weight tool, it’s very quick to grasp and, so far, it hasn’t suffered the kind of semantic upheavals that Puppet has gone through. As mentioned, it took about half a day to convert the few Puppet classes I had into playbooks and tasks. In fact, I was so confident that the Ansible tasks were doing what I intended that I was able to extend more of them to cover my fileserver (which under Puppet, only had a couple of simple permission changes applied).

For example, here’s the Puppet code that updates my automounter configs:

file { autofs_conf:
  path   => $confdir,
  ensure => directory,
  owner  => "root",
  group  => "root",
  mode   => 0644,
  source => "puppet:///modules/$module_name/$operatingsystem",
  recurse => remote,
  ignore => "CVS",
  notify => Service[$svc_list],
  require => Package[$pkg_list],

And here’s the Ansible equivalent:

- name: copy configs
    src: "{{ item }}"
    dest: /etc/
    owner: root
    group: root
    mode: 0644
  with_fileglob: auto.*
  notify: restart autofs

The Puppet code was in a module, and the Ansible task is in a role - potay-to, potah-to. (However, Ansible’s with_fileglob loop doesn’t currently allow you to exclude particular files. I can live with that.) Beyond that, it was pretty much a one-to-one syntactic conversion of the Puppet semantics to Ansible. (Plus of course, I lost all the legacy code that I had never needed in the first place.)

As I only have two nodes, I opted to forego the idea of a control node and just wrote local playbooks for each host (95% similar) that would be checked out using ansible-pull in a cron job. This was actually the element of Puppet I miss most in Ansible: the overall structure and control for regular running of the manifests against hosts. Ansible is much more aimed at one-off or adhoc tasks, and if you want to schedule it regularly, you’re left to patch your own solution together. (Ansible creator Michael DeHaan apparently doesn’t like the concept of ‘convergence’.) In this case, I want regular runs of the tasks because there are some … um … recurring issues whose reversion I want to enforce. (Yeah, I really should get to the bottom of those but meh, the cobbler’s children have no shoes.) Using ansible-pull does mean installing Ansible on each node, but as there are only two this is a negligible overhead.

By the way, installing Ansible on legacy OpenIndiana releases is a little complicated:

  • Install gcc-illumos, python-setuptools and pyyaml from the repository.
  • Use easy_install to install PIP.
  • Use PIP to install Ansible. It will fail while building pycrypto, but you can manually fix this in the build tree:
    # pip install --no-clean ansible
    Change to the /tmp/pip-build-* temporary directory.
    Edit /usr/lib/python2.6/config/Makefile, change KPIC to fPIC.
    Edit pycrypto, change ‘/usr/include/’ to ‘/usr/include/gmp’.
    # env CC=/opt/gcc/4.4.4/bin/gcc python ./ install
  • Then rerun the Ansible pip install.

Incidentally, I’d still happily use Puppet-latest-release for a greenfield deployment of a large number of fairly similar systems. But I’d be more likely to reach for Ansible if I was in a hurry, if the scope was somewhat open-ended or if the requirements were more limited or random. (The latter applies here.)