Combining Terraform & Ansible

Terraform and Ansible are powerful tools that are used to deploy infrastructures and configure applications.

Terraform

Terraform is an infrastructure as code tool that lets you build, change, and version infrastructure safely and efficiently. This includes low-level components like compute instances, storage, and networking; and high-level components like DNS entries and SaaS features.

Ansible

Ansible is an open source, command-line IT automation software application written in Python. It can configure systems, deploy software, and orchestrate advanced workflows to support application deployment, system updates, and more. 

Ansible’s main strengths are simplicity and ease of use. It also has a strong focus on security and reliability, featuring minimal moving parts. It uses OpenSSH for transport and a human-readable language that is designed for getting started quickly without a lot of training.

The differences and similarities

Terraform’s main focus is on deploying a (cloud) infrastructure and Ansible’s main focus is configuring an already deployed system. That being said, Ansible is also capable of deploying infrastructures and Terraform is also capable of configuring systems and application stacks.

This raises the question, which tool is the best to do what we want. And the answer to that question depends, as always, on the use case, the environment and also the capabilities and knowledge of the people that maintain the “stack”. It is also possible to combine those and use the strengths of both. There are (3rd party) Terraform resources that make it possible to use Ansible in a Terraform plan and for Ansible there is a Terraform module that provides support for deploying resources with Terraform and pulling resource information back into Ansible.

In this blog we will explain how the Terraform module can be used with Ansible to deploy (virtual) systems and configure them after deployment.

Our use case

Our use case is a very simple one, we want to deploy a web application. For the sake of simplicity, we will deploy a web server (nginx) on a virtual server that is deployed using Terraform.

Setup

Terraform uses “providers” to provision systems on a specific (cloud)provider, for example AWS (Amazon) or GKE (Google). In this case we will use the “libvirt” provider which is able to provision virtual machines on a Linux hypervisor. The virtual machine will be created using a Rocky Linux cloud image.

We will separate the ‘infrastructure’ part which is done using Terraform from the configuration part that is executed using Ansible. This layered setup ensures that moving to another infrastructure provider can be done without significant investment.

Requirements

Both Ansible and Terraform need to be installed and the Ansible collection community.general, which contains the Terraform module. The Terraform provider libvirt can be pre-installed on the controller host, else it will be fetched during the ‘terraform init’ phase.

Ansible playbook

Our Ansible playbook contains two plays, one to install the virtual machine and the other to configure the web server. The main trick that is used, is that in the first part the inventory is created (in memory) for the second part.

playbook.yml

Copy to Clipboard

This file (playbook.yml), can be applied using the following command:
ansible-playbook -i localhost, playbook.yml
Specifying the inventory like we do here, with the hostname localhost and a comma at the end, tells Ansible that all hosts are specified on the command line and should not be read from an inventory file, as you would normally do. The infrastructure role executes the terraform binary on the controller host, that’s why the hostname “localhost” is used.

Ansible infrastructure role

The infrastructure role installs the virtual machine and uses a standard Rocky Linux cloud image to deploy the operating system.

roles/infrastructure/tasks/main.yml

Copy to Clipboard

Most cloud images use a component called cloud-init to inject extra configuration like a user and secure shell key into the pre backed image. The first task in this role generates a cloud-init configuration file which will be picked up and executed during the first boot of the system. The cloud-init file used here is generated from a template. That template gathers information from the user executing this Ansible playbook and uses it to generate the file.

cloud-init.cfg.j2

Copy to Clipboard

This specific configuration ensures that the current user is able to connect to the web server in the second part and is able to use the sudo command to execute privileged commands. The second task in the infrastructure role generates a Terraform main.tf file that specifies the configuration that is used to deploy the virtual machine.

Copy to Clipboard

The third task in the infrastructure role creates the web server with the provided cloud-init configuration. It also expects two variables, domain and installation image. You could specify those on the command line or using the preferred way, in the group_vars or host_vars. I used ‘example.com’ as domain and ‘Rocky-9-GenericCloud-Base.latest.x86_64.qcow2’ as installation_image.
The last two tasks in the infrastructure role create the inventory file in memory and wait until the web server is reachable over SSH.

Ansible webserver role

The role to install the web server is very simple, just install the NGINX web server and start the service. In a real world scenario, this role will contain all the specifics to set up the web application.

roles/webserver/task/main.yml

Copy to Clipboard

Final result

Running the playbook results in the following output. The output shows clearly the two separate Ansible plays and that the first play is executed on “localhost” and the second play is executed on “webserver”
Since both Ansible and Terraform are idempotent, a second run does not re-create the virtual machine, but just checks the state. If the state is okay, it gathers the information from
the running machine without touching it. This way it is completely safe to modify the configuration of the web server and just rerun the complete playbook.

Copy to Clipboard

Conclusion

In this blog we have shown that combining Terraform with Ansible is possible. This is ‘a way’ to do it and could be a good fit for your company. Here we have only shown a simple configuration, but more complex setups with for example multiple web servers behind a load balancer, where the load balancer is automatically configured with the newly created machine, or a complete high available identity management cluster, all is possible.

Connect with us to explore more about this topic and discover related solutions tailored to your needs!

Get in touch

Share This Story, Choose Your Platform!