How to Use Terraform and Ansible With The Ansible Provider
Automate provisioning, configuration with the Terraform Ansible provider
Terraform is an essential tool for managing infrastructure as code. It allows you to create and manage resources on cloud platforms and others. Its declarative and reproducible approach simplifies infrastructure deployment.
However, Terraform is limited when it comes to managing detailed configurations and deploying applications. This is where Ansible comes in. It provides a powerful solution for automating configurations and handling repetitive tasks.
Ansible is often used with Packer to create immutable images. However, these machine images may require “live” adjustments after their instantiations. Why not use Ansible for these adjustments to ensure tool consistency?
In the past, the Terraform local-exec provisioner was used to run an Ansible playbook. While this works, it loses centralized management of the Ansible inventory and is not ideal for managing infrastructure at scale.
This is where the Terraform Ansible provider becomes useful. It allows you to integrate Ansible with the Terraform workflow. In this article, we will see how to use the Terraform Ansible provider to deploy an NGINX server on DigitalOcean.
Pre-Requisites
A DigitalOcean account and a configured token. If you are new to DigitalOcean, you can use this referral link to get a $200, 60-day credit to try products.
Install the
cloud.terraform
Ansible collection :ansible-galaxy collection install cloud.terraform
Configure the Ansible Inventory With The Terraform Ansible Provider
The cloud.terraform.terraform_provider
plugin integrates Terraform with Ansible. It allows Ansible to interact with resources managed by Terraform. The plugin pulls data from the Terraform state to generate an inventory of provisioned resources to ensure consistency between both tools.
Here is the content of the inventory.yml
file :
---
plugin: cloud.terraform.terraform_provider
Write The Terraform Code
This Terraform code demonstrates how to provision infrastructure on DigitalOcean and configure it using the Terraform Ansible provider. It defines the necessary providers, the DigitalOcean API for provisioning resources, and Ansible for configuration management :
terraform {
required_providers {
ansible = {
source = "ansible/ansible"
version = "1.3.0"
}
digitalocean = {
source = "digitalocean/digitalocean"
version = "2.45.0"
}
}
}
variable "do_token" {
description = "Token to authenticate to DigitalOcean"
type = string
sensitive = true
}
resource "digitalocean_ssh_key" "default" {
name = "Terraform Example"
public_key = file(pathexpand("~/.ssh/id_rsa.pub"))
}
resource "digitalocean_droplet" "web1" {
name = "web1"
image = "ubuntu-24-10-x64"
region = "nyc2"
size = "s-1vcpu-1gb"
backups = false
ssh_keys = [digitalocean_ssh_key.default.fingerprint]
}
resource "ansible_host" "web1" {
name = digitalocean_droplet.web1.ipv4_address
groups = ["webservers"]
variables = {
ansible_user = "root"
ansible_ssh_private_key_file = "~/.ssh/id_rsa"
ansible_python_interpreter = "/usr/bin/python3"
ansible_ssh_common_args = "-o StrictHostKeyChecking=no"
}
}
output "ipv4_address" {
value = digitalocean_droplet.web1.ipv4_address
}
Explanation :
First, the required_providers
block specifies the necessary providers: ansible
(for Ansible integration) and digitalocean
(for interacting with the DigitalOcean API). The code also includes the versions for both providers to ensure compatibility.
The variable do_token
block defines a sensitive variable for the DigitalOcean API token, which is used for authentication to DigitalOcean.
The digitalocean_ssh_key
resource uploads an SSH public key to DigitalOcean, allowing secure SSH access to provisioned machines.
The ansible_host
resource creates an Ansible inventory entry for the provisioned droplet. It assigns the droplet’s IP address to the web1 host group and sets necessary Ansible variables, such as the SSH private key path and Python interpreter for the Ansible playbooks.
Finally, the output block outputs the IP address of the provisioned droplet, allowing easy access to the newly created server.
Apply the Terraform code with your DigitalOcean token :
$ $ read -s DO_TOKEN
$ terraform apply -var "do_token=$DO_TOKEN"
Ansible Dynamic Inventory With Terraform
The command ansible-inventory -i inventory.yml --graph --vars
is used to display a graphical representation of your Ansible inventory along with any associated variable values for the hosts.
@all: This represents all the hosts in the inventory.
@ungrouped: This group contains any hosts that are not part of a specific group
@webservers: This group contains all hosts defined under the “webservers” group, in this case, the IP address 162.243.103.221.
Under the @webservers group, the host 162.243.103.221 has several associated variables:
{ansible_python_interpreter = /usr/bin/python3}: Specifies the path to the Python interpreter on the target machine.
{ansible_ssh_common_args = -o StrictHostKeyChecking=no}: This is an SSH configuration option to bypass the host key check when connecting to the server.
{ansible_ssh_private_key_file = ~/.ssh/id_rsa}: Specifies the path to the SSH private key for authentication.
{ansible_user = root}: Specifies the user to log in when SSHing into the server (in this case, root).
This output helps you visualize the inventory structure and confirm that the necessary configurations are applied to the hosts, which is useful when debugging or reviewing your setup.
$ ansible-inventory -i inventory.yml --graph --vars
@all:
|--@ungrouped:
|--@webservers:
| |--162.243.103.221
| | |--{ansible_python_interpreter = /usr/bin/python3}
| | |--{ansible_ssh_common_args = -o StrictHostKeyChecking=no}
| | |--{ansible_ssh_private_key_file = ~/.ssh/id_rsa}
| | |--{ansible_user = root}
Apply the Ansible Playbook using the Terraform Dynamic Inventory
This command runs the Ansible playbook playbook.yml using the inventory.yml file with Terraform to define the target hosts. Ansible first gathers facts about the system, then ensures the NGINX package is installed, and finally makes sure NGINX is started and enabled.
$ ansible-playbook playbook.yml -i inventory.yml
PLAY [Webservers] *************************************************************************************************
TASK [Gathering Facts] ********************************************************************************************
ok: [162.243.103.221]
TASK [Ensure NGINX package is present.] ***************************************************************************
changed: [162.243.103.221]
TASK [Ensure NGINX is started and enabled.] ***********************************************************************
ok: [162.243.103.221]
PLAY RECAP ********************************************************************************************************
162.243.103.221 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
$ HOSTNAME=$(terraform output -json | jq -r .ipv4_address.value)
$ ssh root@$HOSTNAME
# systemctl status nginx
● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled)
Active: active (running) since Wed 2024-12-04 10:51:51 UTC; 1min 29s ago
Invocation: 6c7c64b422404a549fdea09bb90f3520
Docs: man:nginx(8)
Process: 2438 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCE>
Process: 2439 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
Main PID: 2441 (nginx)
Tasks: 2 (limit: 1110)
Memory: 2.1M (peak: 2.3M)
CPU: 26ms
CGroup: /system.slice/nginx.service
├─2441 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"
└─2442 "nginx: worker process"
Conclusion
In this article, we’ve shown how to combine Terraform and Ansible to automate both the provisioning and configuration of your infrastructure. By using the Terraform Ansible provider, you can avoid the hassle of managing inventories manually, making everything more streamlined and consistent.
With Terraform handling resource creation and Ansible managing configuration, this integration helps you automate tasks more efficiently. It simplifies your workflow, ensures consistency, and makes managing infrastructure at scale much easier.
You can retrieve the presented code here: https://github.com/guivin/terraform-ansible-integration-101