Why we use Terraform and not Chef, Puppet, Ansible, SaltStack, or CloudFormation
Yevgeniy Brikman

I guess I’m trying to figure out how you would use terraform to install services and configurations on resources — example: an EC2?

I guess you’d say that “It’s perfectly reasonable to use Terraform and Ansible together.” The grounds for your position are that using Ansible to make changes to an existing piece of infrastructure AFTER the fact of its instantiation fundamentally violates the idea of what makes “immutable” infrastructure immutable. Immutability strictly dictates that one shouldn’t alter (in any way) the state of a resource once that resource has been created. And I am a major proponent of this idea, for the record.

But I think, in this article, there’s a misleading conflation between what Ansible and Terraform do, fundamentally. I won’t even bring Chef, Puppet, or Salt into this conversation. You argued in a different response that it *IS* useful to compare apples to oranges — but not when the premise for your comparison is itself conflated.

Terraform’s job is to provide a DSL and a set of abstractions whereby you can declaratively express and describe infrastructure which you intend to create. Terraform will go and do exactly that. Unlike Chef, it doesn’t spend time in a “compiler” phase where *it* decides the best course of actions and sequences to achieve the desired end. It simply goes and does the thing. True, you can leave out lots of settings which will default to “computed,” but the level of inference on Terraform’s part isn’t actually inference at all. It references a default or defers to whatever AWS will respond back with as a resource attribute (like an auto-assigned public DNS and IP). That’s it. No default, no further assumptions or inference. It will tell you, “Hey, I need this thing you haven’t given me. There’s no default for it. For anything that’s generated as a result of creating the resource from Amazon’s side (IP,DNS), here’s the output.”

Ansible’s job is to provide a tasty little YAML-based DSL that essentially abstracts a friendly version of a shell provisioner script. Ansible *IS* an orchestration tool, in that you ORCHESTRATE the sequence of instructions by which you install and configure the system and services, and those are executed in line, top-down. Again, a tool like Chef takes all the recipes you put into your playbook, and then shuffles them together into what it believes is its best course of action, based on its “compile phase” rules engine. It infers a great many things — and those are based on its internal rule engine, not some set of clearly defined defaults.

Additionally, It is true that Ansible has some in-built imperative-ness if you’re using upstream roles from galaxy inside your playbooks. But those, too, are just inline, simple scripts. The order in which you call those roles is the order in which they’re executed. It won’t move on to the next service until the last one’s specified steps haven’t completed. With Chef, it is not so. In Chef, you can pull in a recipe for MySQL and another recipe for nginx. You can put the MySQL recipe one line before nginx, and you’d think that Chef will install MySQL, configure it, then restart it for it for you, before moving on to nginx…but it won’t. Its internal “compiler” will install MySQL, configure it, then install nginx, configure that, then reload both services at the end of the run. Ansible will do as it’s told. As you wrote it. If your MySQL role is first, it will install that, configure it, restart it, check that it’s okay, then move to nginx and do the same there. In *this* sense, it’s *more* declarative than Chef — it doesn’t presume to infer the best sequence of steps.

BECAUSE of this innate characteristic, Ansible is not only a configuration tool, but also an orchestration tool, since your instructions to it will orchestrate the sequence of events.

If you wanna talk about immutability along the lines of Ansible VS Terraform (or the not-conflated version of that: Ansible + Terraform), Ansible doesn’t necessarily violate immutability.

You can use the AWS EC2 Provider’s “user_data” to pass in a script that will spin up Ansible, pull down your playbooks, and run them on the instance. It will bootstrap itself, configure itself, then remove Ansible and the cloned repos from the system. It will then depend on sane Ops and SysAdmin practices to prevent engineers from having SSH access to those systems, so that they cannot mutate that instance’s state. Now you’ve got immutable systems.

Updated some configs for a service running one one of those “immutable instances” and need to run Ansible with the new configs? Easy. Taint the instances w/ Terraform “taint” and, on your next terraform apply, they’ll be nuked and recreated. When they’re recreated, the user_data script will take care of pulling down the new configs and running them against the EC2.

I think the article is a bit misleading, albeit very thoughtful and informative. Just my 2 cents. Thanks!

A single golf clap? Or a long standing ovation?

By clapping more or less, you can signal to us which stories really stand out.