mdawar.dev

A blog about programming, Web development, Open Source, Linux and DevOps.

Ansible with Terraform Inventory

Previously if you wanted to use a dynamic Ansible inventory from resources created by Terraform you had to use a Python script to create this inventory from Terraform’s output.

Now the Ansible provider can be used to create an inventory with Terraform configuration without writing any scripts.

Terraform Configuration

1. Install Ansible Provider

The Terraform Ansible Provider is required to start defining the inventory from Terraform resources.

versions.tf
hcl
1terraform {
2  required_providers {
3    ansible = {
4      source = "ansible/ansible"
5      version = "1.2.0"
6    }
7  }
8}

Then run terraform init.

2. Define Inventory Hosts and Groups

Define ansible_host resources that will be part of the inventory:

main.tf
hcl
1# EC2 instance.
2resource "aws_instance" "web1" {/* ... */}
3
4# Inventory host resource.
5resource "ansible_host" "web1" {
6  name   = "web1"
7  groups = ["aws"] # Groups this host is part of.
8
9  variables = {
10    # Connection vars.
11    ansible_user = "admin" # Default user depends on the OS.
12    ansible_host = aws_instance.web1.public_ip
13
14    # Custom vars that we might use in roles/tasks.
15    hostname = "web1"
16    fqdn     = "web1.example.com"
17  }
18}

Define groups if needed using the ansible_group resource:

main.tf
hcl
1resource "ansible_group" "web" {
2  name     = "web"
3  children = ["aws"]
4
5  # Group variables that will apply to the children hosts.
6  variables = {
7    ansible_ssh_private_key_file = "~/.ssh/id_rsa"
8  }
9}

This is equivalent to this definition in a yaml inventory file:

inventory.yml
yml
1aws:
2  hosts:
3    web1:
4      ansible_user: admin
5      ansible_host: 1.2.3.4
6      hostname: web1
7      fqdn: web1.example.com
8
9web:
10  children:
11    aws:
12  vars:
13    ansible_ssh_private_key_file: ~/.ssh/id_rsa

The variables definition is a map of string, so jsonencode() must be used for list or map values.

hcl
1resource "ansible_host" "web1" {
2  // ...
3  variables = {
4    // ...
5    list_var = jsonencode(["one", "two", "three"])
6
7    map_var = jsonencode({
8      country = "US"
9      region  = "us-east-1"
10    })
11  }
12}

Now run terraform apply to create the inventory resources.

Ansible Configuration

1. Install Terraform Collection

For Ansible to have access to the inventory defined in the Terraform configuration, the Terraform Collection for Ansible is required.

bash
$ansible-galaxy collection install cloud.terraform

Or define it in a requirements.yml file:

requirements.yml
yml
1---
2collections:
3  - name: cloud.terraform
4    version: 2.0.0

Then install it with:

bash
$ansible-galaxy install -r requirements.yml

2. Create Inventory File

Create an inventory file that uses the Terraform inventory plugin:

terraform.yml
yml
1plugin: cloud.terraform.terraform_provider
2project_path: /path/to/terraform-project
3# Terraform binary (available in the $PATH) or full path to the binary.
4binary_path: terraform

3. Use the Inventory

To use the inventory, specify it with the -i, --inventory option:

bash
$ansible -i terraform.yml ...

Or define it in the ansible.cfg file:

ansible.cfg
ini
1[defaults]
2inventory = terraform.yml

List the Inventory

Test the setup by listing the inventory hosts with their variables:

bash
$ansible-inventory -i terraform.yml --graph --vars
@all:
  |--@ungrouped:
  |--@web:
  |  |--@aws:
  |  |  |--web1
  |  |  |  |--{ansible_host = 1.2.3.4}
  |  |  |  |--{ansible_ssh_private_key_file = ~/.ssh/id_rsa}
  |  |  |  |--{ansible_user = admin}
  |  |  |  |--{fqdn = web1.example.com}
  |  |  |  |--{hostname = web1}
  |  |  |  |--{list_var = ["one","two","three"]}
  |  |  |  |--{map_var = {"country":"US","region":"us-east-1"}}
  |  |--{ansible_ssh_private_key_file = ~/.ssh/id_rsa}

Full Demo Repository

For a full demo with a dynamic Ansible inventory from Terraform using AWS and Hetzner Cloud check out this repository:

https://github.com/mdawar/ansible-terraform-example