I know I’m late to this party, but I just have to say it: I absolutely love Ansible.
For many years, I have been a Puppet wrangler. It was always a real struggle. I mean, the basics are pretty straightforward, and the framework provides everything you reasonably need to achieve some exceptionally sophisticated automation. If you embrace the “Puppet Way”, there’s really nothing you can’t do. I’ve helped write and maintain modules that are many tens-of-thousands of lines of code that pretty well ran our application infrastructure and was the foundation of the application’s software delivery machinery. But it was a pain. It was never just right. There were always caveats and mental hoops to jump through. It made me feel inadequate as a developer. It broke my brain. It was such a chore. I came to dread wading into module code for any reason.
About three months ago, I changed jobs. At the new job, we have a mix of Puppet (legacy) and Ansible (new). My first meaningful assignment was to implement static code analysis services for our internal CI/CD pipeline. Initially, I was handed an in-progress project that was “almost finished”, which I inherited from a departing team member. I was the third owner of the project. It was a hodge-podge of puppetforge modules, custom internal modules, and internal forks of puppetforge modules. Oh, and did I mention that this “almost finished” project didn’t even have a working proof-of-concept?
We elected to burn it down and start over from scratch with Ansible. That was a great decision. The project has been a joy from day one. I was able to bring up a functioning proof-of-concept stack in just a couple of days. I’ve continued to iterate on the project with another teammate and we’ve created a production-grade, ready-for-general-availability stack in just a couple of weeks.
So, what do I love, specifically? Can I say everything? I prefer the “Ansible Way” to the “Puppet Way” in every respect.
Ansible has features for which Puppet does not have equivalents, such as orchestration. Puppet is purely configuration management. If you want to add orchestration to your puppet infrastructure, you might use something like MCollective. Or Ansible (ha!). That makes it sound like Puppet is a more single-purpose tool, which is good, right? Not exactly. Puppet isn’t just a framework. It’s, confusingly, also a language. More correctly, it’s a DSL used to declare your configuration, which the framework can realize.
Ansible is agentless. It is true that you can run Puppet on a local client without being required to set up a puppetmaster, but you still have to install Puppet to be able to execute on the client host. By contrast, you can install Ansible on a control box, and nowhere else. You *can* install Ansible on a host and execute playbooks locally, but you can also execute those playbooks from a remote host onto a host that *doesn’t* have Ansible installed. This has benefits in many scenarios. I particularly appreciate this feature when performing initial host provisioning and configuration. Another place where I find this feature very helpful is in utilizing my setup and deployment playbooks on a local Vagrant VM exactly the same way I would on a production host. Again, if you jump through enough hoops, this is (mostly) possible with Puppet, but definitely not as easy – even after you work out how to do it.
Ansible uses SSH. I cannot overstate how useful this turns out to be. SSH is installable on everything including Windows, and is usually there by default. This makes it an ideal transport for managing systems. No extra software required. Puppet uses SSL certs and requires all the affiliated service machinery (CA, etc). To their credit, Puppet has done a great job of reducing the burden of managing that infrastructure to the point of it being nearly invisible. But it’s there. And occasionally bites. SSH and all its whacky features is fully supported by Ansible. It is possible to manage a publicly inaccessible remote box through an intermediate jump box, for example. Puppet clients have to be able to talk to a puppetmaster (excepting the local puppet-apply); this might require setting up a puppetmaster on the remote network, which requires you to devise a synchronization scheme, unless you work out network access controls to allow puppet clients to talk to remote (i.e. non-local) puppetmasters. Possible, but notably more complicated than allowing SSH. In the equivalent worst-case with Ansible, you ‘pip install ansible’ and ‘git clone’ your playbooks. Boom. Done.
Ansible’s order of execution is deterministic to humans. I admit, when I first started with Ansible, having come from Puppetland, it seemed like I was simply describing shell scripts in YAML. As I’ve gone further with Ansible, I’ve come to see that as one of its great advantages. The order in which you write tasks in a playbook is the order in which they will execute. Not so in Puppet. Yes, you can declare dependencies and relationships between resources to guarantee certain things happen in roughly the right order in Puppet; but, you have to declare relationships and dependencies between resources to guarantee things happen in roughly the right order in Puppet. This added overhead led me to spend way too much time thinking long and hard about how to write lots of extra stuff in Puppet to ensure everything executed in a sane order; because sometimes I actually needed things to happen in a certain order. Puppet is not well suited to scenarios where execution order matters, or where state needs to be maintained between steps. Building CI/CD software delivery automation on top of Puppet is suboptimal for precisely these reasons. It is possible, but it is unnecessarily difficult and potentially fragile.
I’ve noticed another thing about Ansible playbooks, too: the amount of “code” required to express my goal is dramatically less than equivalent Puppet code. It’s easier to understand. It’s easier to create. It’s more succinct. I spent a lot of time in Puppetland feeling like I wasn’t good at the puppets because my code was too easy to follow compared to others’. Whenever I endeavored to understand the inner workings of a module I didn’t write, or refresh my memory on a module I did write, I knew I’d need to set aside hours of uninterrupted time. By contrast, Ansible is so much more straightforward. It doesn’t take hours to comprehend what’s going on. It’s philosophically closer to the Zen of Python than Puppet, for sure.
Perhaps I had an easier time picking up Ansible because I’ve spent years training myself to think in configuration management; but I find that I can reason quicker and more completely about playbooks than I can about Puppet code. I find that I can create playbooks that solve my problems faster. I find that Ansible is unobtrusive enough that I focus on my goal rather than the tool. That last point is a fundamental shift from my years with Puppet, and something I wasn’t even aware of until it wasn’t the case anymore. I have not yet found anything that I can do in Puppet but not in Ansible – or that is easier in Puppet. Ansible is superior in every way.