Declarative vs Imperative Infrastructure Automation

Setting the Stage

A defining aspect of DevOps is accomplishing ops tasks via code (development) rather than through GUIs. One can automate infrastructure through writing all custom scripts that make calls to the infrastructure providers’ (examples: AWS, GCP, vSphere) APIs, and then send SSH commands to configure the resources that are brought up. However, many tools such as Terraform, Ansible, and Cloud Formation have been created to make the process easier and more powerful.

Some of these tools, such as Cloud Formation for AWS and Deployment Manager for GCP, are built by the infrastructure provider themselves, and only work on one platform. However, the most popular tools are third party and cross platform, such as Terraform, Ansible, Chef, and Puppet. These tools tend to fit into two categories: declarative and imperative. While Chef and Ansible are imperative, Terraform, Puppet, Cloud Formation, and Deployment Manager are all declarative.

Defining the Terms

Infrastructure automation, orchestration, and configuration management tools are primarily categorized as declarative or imperative because that describes the fundamental way that DevOps teams interact with each tool. When working with a declarative tool, you declare to it the desired state of your infrastructure and trust the tool to figure how to make that happen. Conversely, with an imperative tool you tell it what to do step by step by giving it a sequence of imperative commands.

If that sounds too abstract, let’s consider a simple concrete example. You have an application that you are going to run three copies of, each on their own VM, and then you’re going to load balance traffic across them. As time goes on, you scale up to 6 copies, and then later back down to 4. If you were using an imperative tool, you would run a script to first create 3 VMs, later another to add 3 more, and finally another to remove 2.

Conversely, if you were using a declarative tool, you would write a config file that describes a deployment that has 3 VMs, later you would edit that file and change the 3 to a 6, and then tell the platform (i.e. Terraform) to reapply the configuration file. Eventually, when you scale back down to 4, you would just change that number 6 to a 4, and again reapply the configuration file(s). This might sound familiar as Kubernetes is declarative and works in this way.

Imperative tools are very similar to scripting, it’s the same step-by-step procedural logic. By contrast, declarative tools are often extremely powerful and do a lot more of the work for you. They keep track of your desired state and ingest your config files to update it. Next, they query your infrastructure to see what the actual state is, and then the declarative tool decides for itself what set of actions will bring the actual state into parity with your desired state. With an imperative tool, you decide exactly what actions should be done.

Choosing a Tool

As with all technology decisions, there are no silver bullets, but rather tools must be matched to context. For example, recently I tried using Terraform to generate lab environments for students. While I generally prefer declarative tools (like Terraform), as I believe they are ideal managing the state of infrastructure over time, it didn’t fit the situation. Lab environments are generated in one go, packed up, then destroyed. They are not real ongoing infrastructures for apps that are grown while live over years or even decades. Therefore, I ended up switching to Ansible which fits the model much better. 

Another important factor is what the tool is specifically designed for. There is a lot of overlap, but most of these tools focus on orchestration, provisioning, or configuration management. While Ansible can provision, it is best known for configuration management. In my case, that was perfect because it did just enough provisioning, and would allow me to start with original source ISOs and then configure them on the fly, which was better than Terraform where I had to work with prebaked OVAs. I could have used them together, with Terraform provisioning and then triggering Ansible to handle configuration, but due to the factors listed in the previous paragraph, Ansible turned out to be better across the board.

If you use or plan to use a CICD pipeline to manage your infrastructure, you will probably find that relying on declarative tools (at least for provisioning) will work the best. That way you can simply update your desired state (as described in config files), push your Git repo, and let the CICD pipeline tell a declarative tool to ingest and apply your updated desired state. For more on that strategy, see our blog on that topic, CI/CD-Controlled Infrastructure. And if you have any questions on which tool fits your situation, drop us a line and let’s chat!