

Most teams don't debate this for long. They start with Terraform to provision infrastructure, hit a server that needs post-deployment configuration, and reach for Ansible. The tools end up running together.
The question worth asking isn't "which is better?" It's "where does one stop being the right tool?" That boundary is less obvious than it sounds. Ansible can provision cloud resources. Terraform has a provisioner block for running commands on instances. Teams use each tool outside its intended domain and eventually hit hard edges.
This post covers where those edges are, how to combine the tools without creating an unmaintainable tangle, and what's changed in 2026: Terraform's BSL license change, IBM's completed acquisition of HashiCorp, and OpenTofu as a production-ready alternative gaining real traction.
Last updated: May 2026
At a glance Ansible is an open source automation platform maintained by Red Hat, licensed under GPL-3.0-or-later. Current stable release: ansible-core 2.20.5 / community package 13.5.0 (PyPI, April 2026). Terraform is HashiCorp's infrastructure provisioning tool, now under the Business Source License 1.1 since August 2023. Current stable release: 1.15.1 (GitHub, May 2026). OpenTofu is the open source (MPL-2.0) community fork under Linux Foundation governance. Current stable release: 1.11.6 (GitHub, April 2026).
Requirements for the demo in this post:
- A free GitLab account
- An AWS account (the demo runs within the 12-month free tier)
- A free env0 account
TL;DR: The complete repository is on GitLab.
Video walkthrough
The video below shows the full setup in env0: creating a project, template, and environment that provisions the Jenkins machine using both Terraform and Ansible. Skip ahead to the demo section if you want the code walkthrough first.
What is Ansible?
Ansible is a configuration management tool that runs over SSH, needs no agent on the target host, and describes what should happen to a system in YAML playbooks: install packages, copy files, start services, apply OS hardening. It was built for machines that already exist.
Ansible's strength (and its constraint) is the SSH model. Any machine you can reach over SSH is a valid target with no bootstrapping ceremony. At scale, managing SSH key distribution and encrypting secrets with Ansible Vault becomes its own problem, but for most teams starting out, the agentless approach is what makes Ansible worth reaching for first.
Red Hat acquired Ansible in 2015 and maintains it today. The community distribution ships as the ansible package on PyPI; the core engine ships separately as ansible-core. Both are open source under the GPL-3.0-or-later license.
In 2026, Ansible distributes plugins, modules, and roles through a collections model, a shift from the older monolithic structure worth knowing if you're picking up Ansible for the first time. Collections install via ansible-galaxy collection install <namespace.collection> and are referenced by their fully qualified name (for example, community.docker.docker_container). The demo in this post uses community.docker for container management.
One feature that matters for developer confidence: ansible-playbook --check --diff runs a playbook in dry-run mode, showing what would change without making changes. It's Ansible's equivalent of terraform plan and should be part of any testing workflow before applying to production. For role development, ansible-lint catches style and correctness issues the same way terraform validate catches HCL errors.
In enterprise environments, teams often run Ansible through AWX (the open-source upstream) or Red Hat Ansible Automation Platform (AAP) rather than the community CLI directly. env zero orchestrates Ansible regardless of how it's invoked; Custom Flow hooks execute any shell command, including AAP job templates via the awx CLI.
Related reading: The ultimate Ansible tutorial. A step-by-step guide covering installation, inventory, playbooks, and roles.
What is Terraform?
Terraform is a provisioning tool built around declarative Infrastructure as Code (IaC). You describe what infrastructure should exist (VPCs, subnets, EC2 instances, RDS clusters) in HashiCorp Configuration Language (HCL), and Terraform calculates what to create, modify, or destroy to reach that state.
The tool maintains a state file that represents everything it manages. When you run terraform plan, it compares the live state against your configuration and shows you exactly what would change before anything runs. When you run terraform apply, it makes those changes. The state file is what makes terraform destroy safe: it knows precisely what it created. For teams managing many resources, this explicitness is the reason Terraform is the default choice for provisioning, not just a preference.
Related reading: Terraform tutorial: a complete guide. Covers state management, providers, modules, and the apply lifecycle in depth.
A note on the BSL license and IBM. Terraform moved from the open source MPL 2.0 license to the Business Source License 1.1 (BSL) in August 2023. Then, in February 2025, IBM completed a $6.4B acquisition of HashiCorp. For teams evaluating long-term Terraform dependency, these two events together (BSL and IBM ownership) are what's driving OpenTofu adoption in organizations with vendor lock-in concerns.
BSL is not closed source. The code is publicly visible on GitHub and free to use internally. Per the LICENSE file, the Change License is MPL 2.0 and applies four years from the date each version is published, so early BSL versions begin converting to open source around 2027. The restriction applies specifically to companies building competing hosted services on top of Terraform. If you're using Terraform to manage your own infrastructure, the license change doesn't affect your day-to-day work.
If your organization requires an OSI-approved open source license, OpenTofu is the community fork under Linux Foundation governance, licensed under MPL-2.0, actively maintained at version 1.11.6, and a drop-in replacement for Terraform 1.6 and earlier configurations. For many teams, the combination of BSL and IBM ownership is reason enough to evaluate it.
Related reading: OpenTofu vs Terraform: a practical guide for enterprise teams. Decision framework for teams evaluating a migration.
Ansible vs Terraform: core differences
The fundamental difference is philosophy, not feature overlap.
Terraform is declarative and immutable. You say what should exist; Terraform makes it so. When something changes, its preferred pattern is to rebuild from scratch rather than patch a running system. This makes it the right tool for infrastructure provisioning (networks, VMs, databases, load balancers) where reproducibility matters.
Ansible is procedural and mutable. You describe what steps to run, in what order, on a target host. It operates on existing systems: installing software, applying patches, restarting services. It's designed for Day 1+ operations where you're working with infrastructure that already exists.
Comparing them side by side makes the right choice clearer for most tasks:
| Feature | Ansible | Terraform |
|---|---|---|
| Primary use case | Configuration management, app deployment | Infrastructure provisioning |
| Approach | Procedural (imperative) | Declarative |
| Infrastructure mutability | Mutable; modifies existing systems | Immutable; prefers rebuild over patch |
| License | Open source (GPL-3.0-or-later) | Source-available (BSL 1.1 since Aug. 2023); converts to MPL 2.0 four years per version |
| Cloud support | All major clouds | All major clouds |
| Agent requirement | Agentless (SSH/WinRM) | Agentless |
| State management | No persistent state file | Maintains state file for all managed resources |
| Language | YAML playbooks | HCL (HashiCorp Configuration Language) |
| Resource ordering | Manual; you define execution order | Automatic; manages dependency graph |
| Idempotency | Supported; depends on module implementation | Built into the declarative model |
| Dry run / preview | --check --diff mode | terraform plan |
| Change detection | Partial; may miss tag changes or external modifications | Detects drift against state; continuous detection requires tooling like env0 |
| Infrastructure teardown | Requires a separate playbook; reverse order is manual | Single command (terraform destroy) |
The "Change detection" row deserves extra attention. Terraform detects drift at plan time by comparing live state to your configuration. But if resources change between applies (manual console edits, for example), Terraform only catches that drift the next time someone runs plan. Continuous drift detection, which surfaces changes as they happen rather than at next run time, is what tools like env0 add on top. The env0 drift detection docs cover how that works.
Related reading: Infrastructure as Code 101. If you're newer to IaC and want the foundational context before comparing specific tools.
What we see in customer environments
The clean boundary
Terraform owns everything the provider Application Programming Interface (API) touches: VPCs, subnets, security groups, RDS instances, EKS clusters. Ansible handles the layer above: application deployment, OS hardening, compliance enforcement across a fleet of EC2 nodes or on-prem servers. The tools have a clean boundary when kept to these domains. When they drift into each other's territory (Ansible managing cloud resources, Terraform running provisioners to configure software), the maintenance burden compounds quickly.
The bootstrapping gap
Terraform provisions a server. Something needs to run Ansible against it before the server is truly ready. In a CI/CD pipeline, that something is a shell script: it waits for SSH to become available, writes the inventory file with the newly provisioned IP, and fires the playbook. When that script is the only thing holding the two tools together, every engineer who touches it introduces a new failure mode. The script works until it doesn't, and when it breaks, it breaks silently.
Drift at the Ansible layer
If Terraform provisions an instance and Ansible patches it over time, the running server gradually diverges from the original image. When Terraform replaces that instance (after an AMI update, a resize, or a destroy-and-recreate), the Ansible run needs to happen again immediately, or the replacement server is missing six months of configuration. Teams that don't account for this end up with infrastructure that can't be reproduced cleanly from code alone.
When to use Ansible, when to use Terraform
The industry shorthand for infrastructure lifecycle phases is Day 0, Day 1, and Day 2. It maps reasonably well to where each tool belongs, at least for traditional VM-based infrastructure. If your stack is fully containerized, Day 2 looks nothing like fleet SSH management.
| Phase | What happens | Right tool | |---|---|---| | Day 0 | Initial provisioning: networks, VMs, databases, cloud services | Terraform / OpenTofu | | Day 1 | Initial configuration: software install, OS hardening, app deployment | Ansible | | Day 2+ | Ongoing operations: patching, fleet compliance, scaling, updates | Ansible (+ purpose-built tools at scale) |
Terraform's declarative model and state tracking make it the right choice for Day 0: defining what infrastructure should exist. Terraform modules are the standard pattern for packaging reusable infrastructure here.
Day 1 is where Ansible takes over. You're describing what to do to a running system, not what the system should be. Installing Docker, deploying application code, applying OS hardening. These are mutable operations on existing hosts.
Day 2+ is ongoing operations: patching, scaling, compliance enforcement across a fleet. Ansible conditionals and variable layering become important here as the number of managed hosts grows. For policy enforcement before infrastructure changes apply, blocking non-compliant resources before they're created. env zero's built-in Checkov, OPA, and TFSec integrations enforce policies as part of the apply gate.
The practical rule: reach for Terraform when you're creating cloud resources managed by a provider API (EC2, RDS, GCP networks, Azure VNETs) or any infrastructure that needs clean teardown semantics. Use Ansible when you're working with software running on existing servers, or when you need compliance enforcement, patch management, or application deployment across a fleet.
Ansible can provision cloud resources through its amazon.aws collection. It works until you need to track what was created across multiple runs or destroy cleanly. Ansible has no state file, so there's no equivalent to terraform destroy. For cloud infrastructure provisioning, Terraform or OpenTofu is the right tool.
For teams managing network devices, Ansible has a significant advantage: its cisco.ios, arista.eos, and ansible.netcommon collections handle Day 2 network device configuration in a way Terraform's cloud provider model was never designed to address. If your infrastructure includes managed switches, routers, or firewall rules on physical appliances, Ansible is the clear choice for that layer regardless of what provisions the surrounding cloud resources.
Related reading: The four stages of Terraform automation. How infrastructure automation matures from manual CLI runs to a fully managed platform, and where the Day 0/1/2 model fits each stage.
Using Ansible and Terraform together
Most production setups use both. The tools operate at different layers and don't conflict.
The standard pattern:
- Terraform provisions infrastructure (VPC, subnets, EC2 instance, security groups, SSH keys)
- Terraform exposes values needed by the next step (public IP, private key) as outputs
- Ansible consumes those outputs to configure the provisioned instance (install Docker, deploy an application)
The orchestration challenge is the handoff: Terraform's outputs need to populate Ansible's inventory before the playbook runs. In a basic CI/CD pipeline, you handle this with shell scripts. That holds for one or two environments. Scale it to 15 environments across three teams and the problems are different in kind, not just degree. The shell script breaks silently: the Ansible step just doesn't run, nobody knows until a host is missing its configuration, and there's no audit trail to trace when or why. A junior engineer applies to the wrong environment and there's no approval gate. The Jenkins test server from two Fridays ago is still running because destroying it meant someone had to remember to run terraform destroy. These aren't edge cases; they're what happens when the coordination logic lives in a bash file.
Related reading: Terraform and Ansible: better together. More detail on the integration patterns that hold up in production.
How env0 handles the orchestration
env zero's Custom Flows handle this natively. An env0.yml file at the root of your repository defines hooks that run before and after each Terraform stage: init, plan, apply, output. The Ansible playbook runs as a post-apply hook, using Terraform's outputs as inputs, all within the same deployment environment.
The value here isn't just execution order. Approval workflows, audit logs, Role-Based Access Control (RBAC), and Time-to-Live (TTL) policies apply to the combined Terraform + Ansible run as a single unit. You're not managing pipeline permissions and IaC permissions separately.
The env0 Custom Flows documentation covers all available hooks for Terraform, OpenTofu, and other supported frameworks. The env0 Ansible integration docs cover first-class Ansible support: when Ansible is your primary IaC framework rather than a post-apply step, env zero provides dedicated ansibleGalaxy, ansibleCheck, and ansiblePlaybook pipeline stages instead of generic bash hooks.
Demo: provisioning an EC2 instance and configuring Jenkins
We'll use Terraform to provision an AWS EC2 instance and Ansible to deploy Docker and Jenkins on it, with env0 orchestrating the handoff.
The scenario: a short-lived Jenkins server for CI testing, automatically torn down when it reaches a TTL limit, saving cost if someone forgets to shut it down manually. env0's TTL policy handles the teardown; Terraform handles the infrastructure; Ansible handles Jenkins. If team leads need to see cost impact before approving the environment request, env zero's Infracost integration surfaces estimated spend before any apply runs.
The complete repository is on GitLab.
Related reading: Using Jenkins for Terraform management. A full walkthrough of using Jenkins as the CI layer for Terraform and Ansible, including the Ansible stage, Azure provisioning, and how RBAC fits in.
The env0.yml Custom Flow
deploy:
steps:
terraformOutput:
after:
- ansible-galaxy collection install community.docker
- terraform output -raw private_key > /tmp/myKey.pem
- chmod 400 /tmp/myKey.pem
- sed -i "s/\[placeholder_app\]/$(terraform output -raw public_ip)/g" Ansible/inventory
- pip3 install ansible
- cd Ansible && ansible-playbook --private-key /tmp/myKey.pem -i inventory jenkinsPlaybook.yaml
After Terraform generates its output, six steps run in sequence. First, the community.docker Ansible collection installs. This is required because the playbook uses community.docker.docker_image and community.docker.docker_container; skipping this step causes a module-not-found error at playbook start. Then the private key is extracted to a temp file and locked to read-only (chmod 400 is the minimum permission SSH requires; looser permissions cause ssh to refuse the key entirely).
The sed command replaces the [placeholder_app] marker in the inventory file with the EC2 instance's public IP. Note the escaped brackets in the pattern: \[placeholder_app\]. Without the backslashes, sed interprets the brackets as a regex character class matching any single character in that set, not the literal string.
For a cleaner approach in production, env zero's $ENV0_ENV mechanism exports variables between hook steps: adding echo "ANSIBLE_HOST=$(terraform output -raw public_ip)" >> $ENV0_ENV makes $ANSIBLE_HOST available to every subsequent hook step in the same deployment, which avoids in-place file mutation entirely.
One thing worth flagging: terraform output -raw private_key prints the sensitive private key in plaintext so Ansible can use it for SSH. This is intentional and acceptable in a controlled CI environment, but it means the key appears in the execution log. In production, use a secrets manager instead.
The inventory file keeps the marker pattern so it works across every environment without modification:
[all:children]
jenkins
[all:vars]
ansible_user=ubuntu
ansible_python_interpreter=/usr/bin/python3
[jenkins]
jenkinsvm ansible_host=[placeholder_app]
For production fleets with more than a handful of nodes, replace this static inventory with the aws_ec2 dynamic inventory plugin, which discovers running instances by tag rather than requiring hardcoded IPs.
Terraform configuration
The main.tf provisions a full network stack: VPC, subnet, internet gateway, security group (ports 22 for SSH, 8080 for the Jenkins web interface, and 50000 for Jenkins agent connections), an Elastic IP, and an EC2 instance.
The AMI lookup targets Ubuntu 22.04 LTS. Ubuntu 20.04 standard support ended in May 2025; use jammy (22.04) or noble (24.04) for new infrastructure. The owners value 099720109477 is Canonical's official AWS account ID:
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
owners = ["099720109477"] # Canonical's official AWS account
}
The TLS private key for SSH access is generated by Terraform and exposed as a sensitive output. Terraform marks sensitive = true outputs to suppress them in CLI output (displaying (sensitive value) in terraform plan), though terraform output -raw will still print them in plaintext. env0 surfaces sensitive outputs as masked environment variables within the Custom Flow context.
output "url" {
value = "http://${aws_eip.env0.public_dns}"
}
output "public_ip" {
value = aws_eip.env0.public_ip
}
output "private_key" {
value = tls_private_key.env0.private_key_pem
sensitive = true
}
The tls_private_key resource attribute private_key_pem is documented in the Terraform Registry and outputs the private key in PEM format, which is what SSH and Ansible expect.
For production use, generating SSH keys through Terraform state isn't ideal. The private key sits in the state file, which is a secret management problem. Pull keys from a secrets manager (HashiCorp Vault or AWS Secrets Manager) rather than generating them in Terraform. env zero integrates natively with both, so credentials can be pulled at apply time without touching state. This demo uses the TLS provider for simplicity.
OpenTofu 1.11 note: If you're running OpenTofu rather than Terraform, the private key problem has a cleaner answer. OpenTofu 1.11 introduced ephemeral outputs: values that exist only in memory during an operation and are never written to state files or plan output. Marking
private_keyas an ephemeral output means the key is passed to the Custom Flow hook but never persisted anywhere. This is one concrete technical reason some security-conscious teams are choosing OpenTofu for workflows that handle credentials. Terraform's BSL version does not have this feature.
Related reading: Protecting secrets with Ansible Vault. The Ansible side of secrets management when combining these tools.
Ansible playbook
The jenkinsPlaybook.yaml configures the provisioned instance in six logical stages: install prerequisites, add the Docker GPG key and repository, install Docker, install the Python Docker SDK, pull the Jenkins image, and start the container. It targets ansible-core 2.16+, which is required for the retries without until syntax used on the apt task.
- name: Set up Jenkins with Docker
hosts: jenkins
become: true
tasks:
- name: Install pip3 and unzip
apt:
name: ["python3-pip", "unzip"]
state: present
update_cache: yes
retries: 5
delay: 5
- name: Add Docker GPG key
# apt_key is deprecated on Ubuntu 22.04+; for production use
# ansible.builtin.deb822_repository (ansible-core 2.15+) instead
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
state: present
- name: Add Docker repository
apt_repository:
repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
state: present
- name: Install Docker
apt:
name: docker-ce
state: present
update_cache: yes
- name: Install Python Docker SDK
ansible.builtin.pip:
name: docker
state: present
- name: Pull Jenkins image
community.docker.docker_image:
name: samgabrail/jenkins-tf-vault-ansible:latest
source: pull
- name: Prepare Jenkins data directory
file:
path: /home/ubuntu/jenkins_data
state: directory
owner: "1000"
group: "1000"
mode: "0755"
- name: Start Jenkins container
community.docker.docker_container:
name: jenkins
image: samgabrail/jenkins-tf-vault-ansible:latest
state: started
restart_policy: unless-stopped
ports:
- "8080:8080"
- "50000:50000"
volumes:
- /home/ubuntu/jenkins_data:/var/jenkins_home
A few things worth flagging in this playbook:
The apt_key module works but is deprecated on Ubuntu 22.04 and later. For production playbooks, use ansible.builtin.deb822_repository (available in ansible-core 2.15+), which handles both the GPG key and repository in a single task using the modern /etc/apt/sources.list.d/*.sources format.
The retry logic on the initial apt install matters: Ubuntu package mirrors can be temporarily unavailable when a fresh instance first connects. Five retries with a five-second delay covers most transient failures without aborting the run. This syntax (retries without an until clause) is valid in ansible-core 2.16+ and will retry until the task succeeds.
The Jenkins image (samgabrail/jenkins-tf-vault-ansible:latest) is a community image pre-configured with Terraform and Vault tooling. It's available on Docker Hub and last updated in May 2024. For a production deployment, build and maintain your own Jenkins image.
Related reading: Ansible playbooks: a step-by-step guide. How to structure playbooks for repeatable, idempotent runs.
Common pitfalls when combining these tools
Race conditions on Terraform outputs
The Custom Flow above runs Ansible after terraformOutput. If you're building this pattern in a basic CI/CD pipeline, Ansible must not start before Terraform's apply completes and outputs are populated. The failure mode is a cryptic SSH error because the IP address was never written to the inventory. Make the dependency explicit in whatever orchestration layer you're using; do not rely on timing.
Using Ansible to provision cloud resources
Ansible's amazon.aws collection can create EC2 instances and S3 buckets. The problem surfaces when you need to track what was created across multiple runs or tear down cleanly. Ansible has no state file, so there's no equivalent to terraform destroy. Cloud provisioning belongs in Terraform or OpenTofu; reserve Ansible for the configuration layer.
Drift accumulates at the Ansible layer
If Terraform provisions an instance and Ansible patches it over time, the running instance gradually diverges from your baseline image. When Terraform replaces the instance (after an AMI update, for example), Ansible needs to re-run to reconfigure it. Plan for this: your pipeline should include the Ansible configuration step, not just the Terraform apply. Otherwise you end up with infrastructure that can't be reproduced from code alone.
SSH key management at scale
Generating SSH keys through the TLS provider and storing them in Terraform state is fine for demos. In production with multiple environments, the private keys accumulate in state files. Move key management to a dedicated secrets store early; retrofitting it after you have 20 or more environments is harder than it looks. env zero's native integrations with HashiCorp Vault and AWS Secrets Manager make this straightforward to wire in at the platform level.
Sensitive outputs in CI logs
terraform output -raw on a sensitive value prints it in plaintext. If your CI system logs all shell output, that's where the key lands. Use masked variables or environment-scoped secret management before using this pattern at scale.
What env0 adds to this stack
The pitfalls above share a root cause: coordination logic that lives in scripts outside your IaC. The handoff, the inventory mutation, the key extraction: each one is a single point of failure with no visibility, no approval gate, and no audit trail.
env zero replaces them with something that has operational awareness. The env0.yml defines the same Terraform-to-Ansible handoff, but wrapped in approval workflows, a full audit log, and Role-Based Access Control (RBAC). TTL policies automatically destroy ephemeral environments when they exceed their configured age. The Jenkins demo uses one specifically because a test server provisioned on a Friday will still be running Monday morning if nothing tears it down automatically. Drift detection runs continuously and surfaces when live infrastructure diverges from what Terraform last applied, which matters when Ansible is making ongoing changes between Terraform runs.
For teams already using HashiCorp Vault or AWS Secrets Manager, env zero integrates with both natively, so credentials can be pulled at apply time without ever touching Terraform state. That same integrations page covers Datadog, Splunk, and Grafana Loki for observability, and OPA, Checkov, and TFSec for policy enforcement at the apply gate: the entire compliance and audit story, not just the orchestration layer.
None of this requires rewriting your existing Terraform or Ansible code. env zero wraps the execution environment; your IaC stays as-is.
Related reading: What is Checkov and how to use it for IaC security. env zero integrates Checkov as another Custom Flow hook for policy enforcement before apply.
Try it with env zero
Fork the GitLab repository, add it as a template in env0, and you'll have a working Terraform + Ansible deployment running in under 15 minutes. Free account, no credit card, stays within the AWS free tier.
Start a free trial or book a demo if you'd rather see how Custom Flows fit your existing stack before setting anything up.
References
- Ansible documentation: official Ansible docs
- ansible-core 2.20.5 on PyPI: current stable release and license
- Ansible collections guide: official collections docs
- Ansible COPYING (GPL-3.0-or-later): license file
- ansible.builtin.deb822_repository module: recommended replacement for apt_key on Ubuntu 22.04+
- community.docker.docker_image module: official docs
- aws_ec2 dynamic inventory plugin: tag-based dynamic inventory for AWS
- Terraform documentation: HashiCorp Developer
- Terraform 1.15.1 release: GitHub
- Terraform LICENSE (BSL 1.1): official license file with Change Date terms
- HashiCorp BSL license FAQ: HashiCorp
- IBM completes HashiCorp acquisition: IBM Newsroom, February 2025
- tls_private_key resource docs: Terraform Registry
- OpenTofu 1.11.6 release: GitHub
- OpenTofu ephemeral outputs: OpenTofu 1.11 ephemerality docs
- Ubuntu release cycle and EOL dates: Canonical
- Find Ubuntu AMIs on AWS: confirms Canonical account ID 099720109477
- env0 Custom Flows documentation: env0
- env0 TTL policy documentation: env0
- env0 Ansible integration: env0
- env0 drift detection: env0
- env0 integrations: full list of supported frameworks, cloud providers, security plugins, and secret managers
Frequently asked questions
What is the difference between Ansible and Terraform?
Ansible is a configuration management tool that operates on existing infrastructure, installing software, applying configuration, and running commands over SSH. Terraform is an infrastructure provisioning tool that defines what cloud resources should exist and manages their lifecycle through a state file. They operate at different layers. Most production setups use both: Terraform provisions the infrastructure, Ansible configures it.
Is Terraform open source in 2026?
Not exactly. Terraform moved from the open source MPL 2.0 license to the Business Source License 1.1 (BSL) in August 2023. In February 2025, IBM completed its $6.4B acquisition of HashiCorp. BSL is source-available; the code is publicly visible on GitHub and free to use internally. Per the Terraform LICENSE file, each published version converts to MPL 2.0 four years after its release. The restriction applies to companies offering Terraform as a competing hosted service. If you're using Terraform to manage your own cloud infrastructure, the license change doesn't affect you. OpenTofu is the fully open source (MPL-2.0) community fork if your organization requires an OSI-approved license. For many teams, the combination of BSL and IBM ownership is reason enough to evaluate it.
Can I use Ansible instead of Terraform?
Technically yes, but you'll run into limits quickly. Ansible has cloud provisioning modules for AWS, GCP, and Azure. The problem: Ansible has no state file, so it can't track what it created, detect drift, or perform clean teardowns across runs. For cloud infrastructure provisioning, Terraform or OpenTofu is the better choice. Ansible belongs in the configuration management layer.
Does Terraform work with Ansible?
Yes. The two tools complement each other at different layers. The standard pattern: Terraform provisions infrastructure and exposes outputs (IP addresses, credentials), Ansible consumes those outputs to configure the provisioned systems. The orchestration (making sure Ansible runs after Terraform with the right inputs) can be handled by your CI/CD pipeline or by a platform like env0, which provides Custom Flows specifically for this pattern.
What are Day 0, Day 1, and Day 2 operations?
Day 0 is initial infrastructure provisioning: standing up networks, VMs, databases, and cloud services. Terraform's domain. Day 1 is initial configuration of what was provisioned, including installing software, configuring services, and deploying applications. Ansible takes over here. Day 2+ is ongoing operations: patching, scaling, updates, compliance enforcement across a fleet. Ansible handles this layer well, though teams often add purpose-built tools depending on complexity and scale.
What is the Ansible equivalent of terraform plan?
ansible-playbook --check --diff runs a playbook in dry-run mode, showing what would change without making any changes to the target systems. Adding --diff alongside --check shows the exact line-by-line changes to files and configuration. Not every Ansible module supports check mode (modules that call external APIs may not), but for most configuration management tasks, it's a reliable way to preview changes before applying. For static analysis on playbooks and roles before running them at all, ansible-lint catches style and correctness issues early.
What is new in Ansible and Terraform in 2026?
ansible-core 2.20.5 is the current stable release (April 2026), with the community package at version 13.5.0. One critical upgrade consideration: ansible-core 2.20 raises the minimum Python version on the control node to 3.12. Teams running Ubuntu 20.04 (Python 3.8) on their CI runner will hit a hard failure on upgrade. Update Python or the runner OS first. New playbook capabilities in 2.20 include Register Projections and a technology preview of Play Argument Specs. Terraform 1.15.1 is the current stable release (May 2026), adding Windows ARM64 support. IBM completed its $6.4B acquisition of HashiCorp in February 2025, accelerating OpenTofu adoption among organizations evaluating vendor risk. OpenTofu 1.11.6 (April 2026) introduced ephemeral outputs: values that exist only in memory and are never written to state. This is a significant security improvement for workflows handling credentials.
Can env0 orchestrate both Ansible and Terraform?
Yes. env zero's Custom Flows support pre- and post-hooks at each stage of a Terraform or OpenTofu run. You can install Ansible, build an auto-generated inventory from Terraform outputs, and execute a playbook as a post-apply step, all within the same env0 environment. Approval workflows, audit logging, and TTL-based teardowns apply to the combined run as a single unit, not as separate CI/CD jobs. env zero also supports Ansible as a first-class IaC framework with dedicated ansibleGalaxy, ansibleCheck, and ansiblePlaybook pipeline stages. See the env0 Ansible integration docs or start a free trial. The full demo in this post runs on a free account.

.webp)

![Using Open Policy Agent (OPA) with Terraform: Tutorial and Examples [2026]](https://cdn.prod.website-files.com/63eb9bf7fa9e2724829607c1/69d6a3bde2ffe415812d9782_post_th.png)