/

Using Terraform as the Inventory for Ansible

Terraform and Ansible are complementary tools with which you can do Infrastructure as Code. You would use Terraform to request machines from providers and then Ansible to configure them.

Using both of them together with a dynamic inventory to link them has been technically possible for years but never obvious enough for me to work out.

Until I found the Terraform provider.

Ansible provider for Terraform

By using the Ansible provider you can teach Terraform what an Ansible inventory entry would look like next to the rest of its code.

You start by defining the provider:

terraform {
  required_providers {
    ansible = {
      source  = "ansible/ansible"
      version = "1.3.0"
    }
  }
}

and then you can define the Ansible inventory entry right next to the resource:

resource "proxmox_vm_qemu" "gitea" {
  cores = 1
	...
}

resource "ansible_host" "gitea-0" {
  name   = proxmox_vm_qemu.gitea.ssh_host
  groups = ["gitea"]
  variables = {
    ansible_user = "ubuntu"
  }
}

which means you don’t need to hardcode IP addresses anywhere.

You would need to terraform apply this plan so that Terraform adds these resources to the state file so that Ansible can use them.

Terraform plugin for Ansible

On the Ansible side you use a plugin to tell it how to use Terraform as its inventory.

Add the following to your requirements.yaml file:

collections:
  - name: cloud.terraform
    version: 1.1.0

and then install the collection using ansible-galaxy install -r requirements.yaml,

then create an inventory.yaml with the following content:

plugin: cloud.terraform.terraform_provider
# project_path: ../terraform # if your Terraform code is in a different directory
# state_file: mycustomstate.tfstate # if you wanted to define which state file

and set it as the default inventory in ansible.cfg:

[defaults]
inventory = inventory.yaml

You can confirm the plugin works by doing:

ansible-inventory --graph

which should return something like the following:

@all:
  |--@ungrouped:
  |--@apps:
  |  |--192.168.1.10
  |--@database:
  |  |--192.168.1.201
  |  |--192.168.1.202
  |--@mysql:
  |  |--192.168.1.201
  |--@postgres:
  |  |--192.168.1.202
  |--@pihole:
  |  |--192.168.1.2
  |--@gitea:
  |  |--192.168.1.211
  |--@ingress:
  |  |--192.168.1.9

🎉

What’s good about this is now you can be hands-off with IP addresses.

Terraform can request a new machine, wait for it to receive an IP address and then make it available for Ansible to use, without you needing to do anything.

Remote state

The Ansible plugin will default to using whichever backend you use for the Terraform state file, whether that’s local or in an S3-compatible location.

This means Ansible is always running against the current infrastructure, which helps when colleagues and automations are updating things a hundred thousand times a day.

In Conclusion

Using Terraform and Ansible together can cover all aspects of Infrastructure as Code.

The Ansible provider lets you keep all your resource code together in Terraform and then makes it available to Ansible, which uses a plugin to let it use the state file as its inventory.