Infrastructure as Code: Terraform and Ansible for Hosting Automation

System AdminFebruary 15, 2024176 views6 min read

Clicking Through a Control Panel Is Not a Reproducible Process

Every hosting customer who has managed more than one server knows the feeling: you set up the first server perfectly, then try to replicate the setup on a second server and realize you forgot three configuration steps that you did manually the first time around. Or worse, you need to rebuild a server after a failure and discover that nobody documented the exact configuration. Infrastructure as Code eliminates this entire class of problem by treating server configuration as versioned, testable, reviewable code rather than a series of manual steps.

This guide covers two of the most practical IaC tools for hosting customers — Terraform for provisioning infrastructure and Ansible for configuring it — and shows how they fit together into a workflow that makes deployments repeatable, auditable, and safe.

The Two Layers of Infrastructure as Code

IaC operates at two distinct layers, and understanding the distinction helps you choose the right tool for each task:

Provisioning: Creating the Infrastructure

Provisioning is about creating resources: servers, load balancers, DNS records, databases, storage volumes, and network configurations. This is the "what exists" layer. Terraform excels here. You declare the desired state of your infrastructure in configuration files, and Terraform creates, modifies, or destroys resources to match that state.

Configuration: Setting Up What Runs on the Infrastructure

Configuration management is about what happens on the server after it exists: installing packages, configuring Nginx, deploying application code, setting up cron jobs, managing firewall rules, and creating user accounts. This is the "how it behaves" layer. Ansible excels here. You write playbooks that describe the desired state of the server's software and configuration, and Ansible enforces that state.

Terraform: Declarative Infrastructure Provisioning

How Terraform Works

You write .tf files that describe the infrastructure you want. Terraform compares your desired state against the current state of the world (tracked in a state file) and generates a plan showing what changes it will make. You review the plan, approve it, and Terraform applies the changes. This plan-review-apply cycle prevents surprises — you know exactly what will happen before any resources are created or destroyed.

Key Concepts

  • Providers: Plugins that interface with infrastructure APIs. Providers exist for major cloud platforms, DNS services, domain registrars, monitoring tools, and hosting providers. The provider translates your Terraform declarations into API calls.
  • Resources: The building blocks of your infrastructure — a VPS instance, a DNS record, a firewall rule. Each resource is declared with its desired configuration.
  • State: Terraform tracks the current state of your infrastructure in a state file. This file is critical — it maps your declarations to real resources. Store it in a remote backend (object storage with locking) so your team can collaborate safely.
  • Modules: Reusable groups of resources. A "web server" module might include a VPS instance, a firewall rule, and a DNS record. Modules let you standardize patterns and avoid repetition.

Practical Example: Provisioning a Hosting Stack

A typical Terraform project for hosting might define: a VPS instance with a specific OS and size, a floating IP address, DNS A records pointing to the server, firewall rules allowing HTTP, HTTPS, and SSH, and an object storage bucket for backups. All of this lives in version-controlled files. Creating a second identical environment means copying the configuration and changing a few variables.

Ansible: Agentless Configuration Management

How Ansible Works

Ansible connects to your servers over SSH (no agent installation required), executes tasks to bring the server to the desired state, and reports what changed. Playbooks are written in YAML and describe a sequence of tasks — install a package, copy a configuration file, enable a service, create a user account.

Key Concepts

  • Inventory: A list of servers organized into groups (web servers, database servers, staging, production). The inventory can be static (a file) or dynamic (generated from Terraform output or a cloud API).
  • Playbooks: YAML files that describe the desired state. Each playbook contains one or more plays, and each play targets a group of servers with a sequence of tasks.
  • Roles: Reusable collections of tasks, files, templates, and variables. A "nginx" role installs Nginx, deploys a configuration template, and enables the service. Roles are the building blocks of maintainable Ansible projects.
  • Idempotency: Ansible tasks are designed to be idempotent — running the same playbook twice produces the same result. If Nginx is already installed, the install task reports "ok" and moves on. This makes playbooks safe to run repeatedly.

Terraform + Ansible: The Complete Workflow

The most effective approach combines both tools, each handling what it does best:

  1. Terraform provisions the infrastructure: Creates the VPS, floating IP, DNS records, and firewall rules.
  2. Terraform outputs feed Ansible's inventory: Terraform outputs the server IP addresses, which Ansible uses to know where to connect.
  3. Ansible configures the servers: Installs packages, deploys application code, configures services, and sets up monitoring.
  4. Both are version-controlled: All Terraform and Ansible files live in a Git repository. Changes go through pull requests, peer review, and CI checks before being applied.

Version Control and Collaboration

The greatest benefit of IaC is that infrastructure changes follow the same workflow as application code:

  • Peer review: Before a change is applied, it goes through a pull request where teammates review the configuration diff. This catches mistakes, enforces standards, and spreads knowledge across the team.
  • Audit trail: The Git history shows who changed what, when, and why. When something goes wrong, you can trace the exact change that caused the issue.
  • Rollback: If a change causes problems, revert the commit and re-apply the previous configuration. The infrastructure returns to its known-good state.
  • Documentation: The code itself is the documentation. Instead of a wiki page that may or may not be current, the Terraform and Ansible files always describe the actual state of the infrastructure.

CI/CD for Infrastructure

Integrate IaC into your continuous integration pipeline:

  • Terraform plan on pull request: When a pull request modifies Terraform files, the CI pipeline runs terraform plan and posts the output as a comment. Reviewers can see exactly what resources will be created, modified, or destroyed.
  • Ansible lint: Run ansible-lint on playbook changes to catch syntax errors, deprecated modules, and style violations before they reach production.
  • Apply on merge: When the pull request is approved and merged, the CI pipeline applies the changes automatically (or gates behind a manual approval step for production environments).

Common Mistakes and How to Avoid Them

  • Manual changes after IaC deployment: If you modify a server manually after Ansible configures it, the next Ansible run overwrites your changes. All changes must go through the code. If you need an emergency fix, make the manual change and immediately commit the corresponding code change.
  • Storing secrets in code: Never commit passwords, API keys, or private keys to your IaC repository. Use Ansible Vault for encrypting secrets in playbooks and Terraform's integration with secrets managers for sensitive variables.
  • Monolithic configurations: A single massive Terraform file or Ansible playbook becomes unmaintainable quickly. Break configurations into modules (Terraform) and roles (Ansible) from the start.
  • Ignoring state management: Terraform's state file is critical. Losing it means Terraform loses track of what it manages. Store state in a remote backend with locking and versioning from day one.

Getting Started

You do not need to convert your entire infrastructure to code in one project. Start small:

  1. Pick one server or one environment (staging is ideal).
  2. Write a Terraform configuration that provisions it.
  3. Write an Ansible playbook that configures it.
  4. Tear it down and recreate it from code. If the result is identical, you have a reproducible infrastructure.
  5. Expand to production once the workflow is comfortable.

The Bottom Line

Infrastructure as Code transforms server management from an artisanal craft into an engineering discipline. Terraform handles provisioning. Ansible handles configuration. Version control provides audit trails and collaboration. CI/CD pipelines automate the apply process. The investment in learning these tools pays off the first time you need to rebuild a server, replicate an environment, or understand what changed last Tuesday at three in the afternoon.

DevOpsWordPressBackupLinux