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
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
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
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.
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
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.
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!