Skip to main content

How to Pass Terraform Output to Ansible Inventory: 3 Clean Methods (2026)


Once Terraform finishes provisioning your servers, how does Ansible know where they are? Here are three clean methods to automate the handoff — from simple file generation to dynamic cloud discovery to remote state parsing.

Terraform and Ansible are built to work together, not compete. Terraform provisions the infrastructure — the virtual machines, networks, and storage — and Ansible steps in once those machines are running to install software, configure services, and deploy your application. The tools do fundamentally different jobs, and in a well-structured pipeline they hand off work to each other seamlessly.

The friction point most people hit is the handoff itself. Once Terraform finishes building your servers, Ansible needs to know where they are — their IP addresses or hostnames. The obvious fix is copying those IPs from your terminal and pasting them into a static Ansible hosts.ini file by hand. It works for a weekend project. In production, it breaks down fast: manual errors creep in, CI/CD pipelines stall, and the moment an instance restarts with a new IP address your inventory is out of date.

The cleaner solution is automating the handoff entirely, so the moment Terraform finishes, Ansible already knows exactly where to connect. Here are the three most practical ways to do that, ordered from simplest to most sophisticated.

Quick Answer: 3 Methods at a Glance

Method

Best For

Complexity

local_file resource

Local dev, fast prototyping, CI/CD testing

Low

Dynamic cloud inventory plugin

Production, auto-scaling, multi-developer teams

Medium

Remote state file parsing

High-security, private networks, compliance environments

High

Method 1: Generate the Ansible Inventory Automatically with local_file

The most direct approach is having Terraform write the Ansible inventory file for you. Terraform's local_file resource lets you generate any text file at the end of a terraform apply run — including a correctly formatted hosts.ini that Ansible can read immediately.

The Implementation

Here's a working example for a cluster of AWS EC2 instances.

Note: The local_file resource requires the hashicorp/local provider. Add the following block to your terraform.tf or versions.tf file if it is not already declared:

terraform {

  required_providers {

    local = {

      source  = "hashicorp/local"

      version = "~> 2.0"

    }

  }

}


Then your main configuration:

# Provision your compute instances

resource "aws_instance" "web_nodes" {

  count         = 3

  ami           = "ami-0c55b159cbfafe1f0"

  instance_type = "t3.micro"


  tags = {

    Name = "web-server-${count.index}"

    Role = "webserver"

  }

}


# Capture the public IP addresses as an output

output "web_node_ips" {

  value       = aws_instance.web_nodes[*].public_ip

  description = "Public IP addresses of the deployed web nodes."

}


# Write those IPs directly into an Ansible inventory file

resource "local_file" "ansible_inventory" {

  content  = <<EOT

[webservers]

${join("\n", aws_instance.web_nodes[*].public_ip)}

EOT

  filename = "${path.module}/ansible/hosts.ini"

}


How It Works

When terraform apply completes, Terraform calculates the public IPs assigned by AWS, uses the join function to format them one per line, and writes the result to ./ansible/hosts.ini. Your next pipeline step can run Ansible immediately — no intermediate script, no manual copy-paste:

terraform apply -auto-approve

ansible-playbook -i ansible/hosts.ini ansible/site.yml


Honest Verdict

Pros: Simple to set up, no external plugins or dependencies, works out of the box with any Terraform project.

Cons: Generates a static snapshot. If an instance stops and restarts with a new IP, the file is out of date until you re-run Terraform.

Best for: Local staging environments, fast prototyping, and tightly controlled short-lived CI/CD testing pipelines where infrastructure doesn't change between provisioning and deployment.

Method 2: Use Cloud Dynamic Inventory Plugins

When you're running infrastructure at scale — auto-scaling groups, multiple engineers deploying simultaneously, instances spinning up and down — a static text file on a local disk breaks down quickly. The better approach is letting Ansible query your cloud provider directly for the current live state of your infrastructure.

This is what Dynamic Inventory Plugins are for. In this pattern, Terraform's only responsibility is provisioning and tagging infrastructure cleanly. Ansible then reads the live cloud environment, grouping servers automatically based on those tags — no files, no manual steps.

The Implementation

First, tag your Terraform resources consistently so Ansible can find them:

resource "aws_instance" "app_nodes" {

  count         = 2

  ami           = "ami-0c55b159cbfafe1f0"

  instance_type = "t3.medium"


  tags = {

    Environment  = "production"

    Project      = "vortex-momentum"

    AnsibleGroup = "appservers"

  }

}


Next, create an Ansible dynamic inventory config file named aws_ec2.yml in your Ansible project directory:

---

plugin: aws_ec2

regions:

  - us-east-1

filters:

  tag:Project: vortex-momentum

  instance-state-name: [ 'running' ]

hostnames:

  - dns-name

  - public-ip-address

keyed_groups:

  - key: tags.AnsibleGroup

    prefix: group


Running the Pipeline

Make sure boto3 is installed and your AWS credentials are configured, then run:

ansible-playbook -i aws_ec2.yml ansible/deploy.yml


How It Works

When the playbook runs, Ansible calls the AWS API, filters for running instances tagged Project=vortex-momentum, and automatically groups them into internal Ansible groups based on your keyed_groups definition — in this case group_appservers. The inventory is always current because it reflects the live state of your cloud account, not a file generated at a previous point in time.

Honest Verdict

Pros: True production scalability. Handles auto-scaling, variable IP addresses, and multi-developer environments cleanly. No local files to go stale. The live cloud state is the single source of truth.

Cons: Slightly slower startup since Ansible makes an API call before beginning any playbook tasks. Requires correct IAM permissions on whatever machine is running the playbook.

Best for: Production cloud environments, infrastructure using auto-scaling groups, and any setup where multiple people are deploying changes across a shared environment.

Method 3: Read Terraform State Directly from a Remote Backend

There are scenarios where neither local file generation nor cloud tagging works well — particularly in high-security environments where servers live behind jump boxes, public tags aren't used, or compliance requirements restrict how infrastructure metadata is exposed. In these cases, you can configure Ansible to read your Terraform state file directly from a secure remote backend.

This approach skips the cloud API entirely. Ansible connects to your state backend — an S3 bucket, Terraform Cloud, HashiCorp Consul — reads the state JSON, and builds an in-memory inventory from the output values Terraform recorded.

The Implementation

Configure your Terraform backend and outputs explicitly:

terraform {

  backend "s3" {

    bucket = "my-company-terraform-states"

    key    = "prod/infrastructure.tfstate"

    region = "us-east-1"

  }

}


output "database_private_ips" {

  value = aws_instance.db_nodes[*].private_ip

}


Then write an Ansible playbook that reads the state before configuring any servers:

---

- name: "Bootstrap inventory from remote Terraform state"

  hosts: localhost

  gather_facts: false

  tasks:

    - name: "Fetch state file from S3"

      amazon.aws.aws_s3:

        bucket: "my-company-terraform-states"

        object: "prod/infrastructure.tfstate"

        mode: getstr

      register: tf_state_string


    - name: "Parse state and build in-memory inventory"

      vars:

        state_json: "{{ tf_state_string.contents | from_json }}"

        db_ips: "{{ state_json.outputs.database_private_ips.value }}"

      add_host:

        name: "{{ item }}"

        groups: dynamic_db_group

      loop: "{{ db_ips }}"


- name: "Configure database servers"

  hosts: dynamic_db_group

  become: true

  tasks:

    - name: "Ensure PostgreSQL is installed"

      ansible.builtin.apt:

        name: postgresql

        state: present


How It Works

The playbook runs in two distinct phases. Phase one runs entirely on your local control node — it pulls the raw state JSON from S3, parses the database_private_ips output, and uses add_host to build an in-memory inventory group called dynamic_db_group. Phase two immediately targets that group to complete the configuration. Nothing is written to disk, and nothing is exposed via public cloud tags.

Honest Verdict

Pros: The most secure option. Reads directly from your source-of-truth state file without public tagging or local file exposure. Works well in air-gapped or highly restricted network environments.

Cons: Requires solid understanding of Ansible filters and state file structure. Demands tightly controlled access permissions over your backend storage. More moving parts to debug when something goes wrong.

Best for: Financial services, healthcare, or other compliance-heavy environments with strict data handling requirements. Also useful for private infrastructure that isn't tagged or queryable through a cloud API.

Choosing the Right Method

The right method depends on where you are in the infrastructure lifecycle:

  • Starting out or building a prototype? Method 1 — the local_file approach gets you running in an afternoon with no additional setup.

  • Running production cloud infrastructure? Method 2 — dynamic inventory plugins are the standard approach for this environment. The extra setup pays off immediately in reliability.

  • Operating in a regulated or high-security environment? Method 3 — reading remote state directly gives you the control and auditability compliance requirements typically demand.

Most real teams eventually end up using Method 2 for most of their infrastructure, with Method 3 reserved for specific sensitive workloads like databases or secrets management systems where tagging metadata publicly isn't appropriate.

Official Documentation References

These are the primary sources for each tool used in this guide:

What to Read Next

If you're building out a full DevOps or cloud engineering skillset, the Terraform/Ansible integration pattern is one piece of a wider toolkit Terraform vs Ansible for Beginners: What They Actually Do and Which to Learn First. Our  covers the foundational difference between provisioning and configuration management if you want the conceptual layer before diving into pipeline integration patterns.

About the Author

Jakpa Desmond Igho is a remote infrastructure analyst and workspace optimization writer. Over the past five years, he has followed workspace hardware trends and reliability discussions across the tech sector. Find more breakdowns at VortexMomentum.tech.


Comments

Popular posts from this blog

Best Standing Desk for Multi Monitors: What Sysadmins and Programmers Actually Buy (2026)

  Meta description: Looking for the best standing desk for multi monitors? We dug through years of sysadmin and programmer forum discussions to find which desks actually survive heavy multi-monitor, multi-PC setups — and which ones wobble apart. <!-- TODO: Once approved for Uplift / Secretlab / FlexiSpot / Amazon Associates affiliate programs, restore this disclosure line above the fold and replace it with the real one: "Affiliate Disclosure: This post contains affiliate links. If you buy through them, we may earn a small commission at no extra cost to you. We only recommend gear that holds up under real, long-term technical use." Do NOT publish this disclosure until affiliate links below are live — an affiliate disclosure with no actual affiliate links is misleading and can violate program terms. --> Most standing desks look the same in a product photo. Smooth motorized lift, a digital keypad, a clean minimalist top. The marketing la...

Privacy Policy and Affiliate Disclosure

Last Updated: June 2026 Welcome to VortexMomentum.tech. We value your privacy and are committed to transparency regarding how our website operates, handles data, and generates revenue. 1. Affiliate Disclosure VortexMomentum.tech is a participant in various affiliate marketing programs designed to provide a means for sites to earn advertising fees by advertising and linking to commercial retailers. This means that certain links on this website are affiliate links. If you click on one of these links and make a purchase, we may receive a small commission from the retailer at absolutely zero extra cost to you. Our Commitment to Integrity We only recommend products, hardware, and services that we believe add genuine value to our readers. All reviews, guides, and recommendations are based on rigorous research, technical specifications, and community data. The compensation received through affiliate links helps maintain and support the operational costs of running this platform. 2. Informatio...

How to Build a Local NAS Setup for Engineering Workspaces (2026)

  Cloud storage is expensive and slow when your internet drops. Here's how to build a reliable local NAS setup for engineers — covering enclosure selection, RAID configuration, drive choice, and network setup.  TODO: Add Amazon affiliate disclosure once tracking tag is confirmed in all 3 links below. Standard FTC-compliant wording: "As an Amazon Associate, we earn from qualifying purchases made through links on this page, at no extra cost to you." --> The "put everything in the cloud" approach made sense for a while. It felt simple, scalable, and low-maintenance. But for engineers and developers working with large Docker containers, virtual machine images, and continuous system backups, the cracks in that approach show up quickly. Pull a 50GB database backup over a standard home internet connection and you're watching a progress bar instead of writing code. Your internet goes down during a deployment and your entire local workflow stalls...