Terraform and Ansible, Best Buddies

Bruce LBruce L
3 min read

You might be already know that Terraform and Ansible are both champs in their own domains. Terraform is the master of provisioning infrastructure on cloud. Ansible, meanwhile, is expert at configuring those systems, ensuring everything's set up and running perfectly.

But did you know they can team up to create something unstoppable? Let’s dive into how these two can work together seamlessly. In this post, I’ll walk you through integrating Terraform and Ansible in a GitHub-driven CI/CD workflow for for automatically deploying and configuring EC2 instances on AWS. No manual fiddling required, pure automation magic!

The Setup: Terraform Provisions, Ansible Configures

Picture this: Terraform kicks things off by creating a fleet of EC2 instances on AWS, then Ansible takes over to handle the configuration, like adding users or dropping files. Here's a simple Terraform snippet to launch those instances:

resource "aws_instance" "my_vms" {
  ami                         = var.instance_ami
  instance_type               = var.instance_type
  count                       = var.instance_count
  key_name                    = var.key_name
  subnet_id                   = var.subnet_id
  associate_public_ip_address = true
  vpc_security_group_ids = [var.security_group_id]
  tags = {
    Name = "vm${count.index + 1}"
  }
}

But here’s the catch! Ansible needs an inventory (a list of EC2 VM with their IP addresses or hostnames) to know where to connect and do its thing.

In fact, Terraform can easily generate that inventory after the instances are up. Check out this resource that grabs the public DNS names and dumps them into a file called inventory.txt.

resource "local_file" "public_dns_file" {
  content  = "[all]\n${join("\n", aws_instance.my_vms[*].public_dns)}"
  filename = "${path.module}/inventory.txt"
}

Once Terraform runs apply and creates that file, Ansible can take over. Here's a basic playbook (demo_ec2.yaml) to configure the hosts — adding an Ansible user and creating a welcome text file:

---
- name: Quick Demo Playbook
  hosts: all
  become: yes

  tasks:
    - name: add Ansible user
      user:
        name: ansible
        state: present
      tags:
        - user

    - name: Create a text file in home dir
      copy:
        content: "This server is managed by Ansible. Hello from Terraform!"
        dest: /home/ec2-user/welcome.txt
        owner: ec2-user
        group: ec2-user
        mode: '0644'
      tags:
        - file

But how can Terraform pass that inventory file to Ansible seamlessly? So there’s one more piece to the puzzle, who’s going to coordinate this handoff? That’s where GitHub steps in!

With GitHub Actions (or other CI/CD tool), we can actually automate the whole process. Here is an example of Github workflows:

name: CI TEST

on:
  workflow_dispatch:
    inputs:
      action:
        description: 'Select action'
        required: true
        default: 'plan'
        type: choice
        options:
          - plan
          - apply
          - play

jobs:
  plan:
    if: inputs.action == 'plan'
    runs-on: self-hosted
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4

      - name: Set up Terraform
        uses: hashicorp/setup-terraform@v2
        with:
          terraform_version: 1.11.4
          terraform_wrapper: false

      - name: Terraform Init
        run: terraform init

      - name: Terraform Plan
        run: terraform plan

  apply:
    if: inputs.action == 'apply'
    runs-on: self-hosted
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4

      - name: Set up Terraform
        uses: hashicorp/setup-terraform@v2
        with:
          terraform_version: 1.11.4
          terraform_wrapper: false

      - name: Terraform Init
        run: terraform init

      - name: Terraform Apply
        run: terraform apply -auto-approve

  play:
    runs-on: self-hosted
    needs: apply
    steps:
      - name: display ansible inventory
        run: cat inventory.txt

      - name: run ansible play
        run: ansible-playbook -i inventory.txt -u ec2-user demo_ec2.yaml

In this setup, Terraform generates Inventory file on my self-host runner, then the job play executes the playbook together the inventory file ansible-playbook -i inventory.txt -u ec2-user demo_ec2.yaml.

💡
If you don’t run on a self-hosted runner with persistent storage, you can persist the inventory file using artifacts. Upload it after Terraform apply, download it before Ansible plays. For example:
apply:
...
  - name: Upload Inventory Artifact
    uses: actions/upload-artifact@v4
    with:
      name: inventory
      path: inventory.txt

Wrapping it Up

This is just a PoC showing how Terraform and Ansible can collaborate like best buds in a GitHub workflow, no need for fancy enterprise editions, no complex integrations, no hidden costs. It's all open-source goodness! Tweak it for your setup, add more security, or scale it up. If you've got questions or your own twists, drop a comment.

0
Subscribe to my newsletter

Read articles from Bruce L directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Bruce L
Bruce L

I’ve been rocking the DevOps journey for a decade, starting with building Cisco’s software-defined datacenters for multi-region OpenStack infrastructures. I then shifted to serverless and container deployments for finance institutions. Now, I’m deep into service meshes like Consul, automating with Ansible and Terraform, and running workloads on Kubernetes and Nomad. Stick around for some new tech and DevOps adventures!