Ansible Infrastructure as Code
Introduction
Infrastructure as Code (IaC) is a modern approach to infrastructure management where infrastructure configurations are defined using code instead of manual processes. Ansible, a powerful automation tool, excels at implementing IaC principles by allowing you to describe your infrastructure using simple, human-readable YAML files.
This guide will introduce you to using Ansible as an Infrastructure as Code tool, explaining the core concepts and providing practical examples to get you started on your automation journey.
What is Infrastructure as Code?
Infrastructure as Code treats infrastructure configuration as software code that can be:
- Version controlled: Track changes over time
- Tested: Validate before deployment
- Reused: Apply the same configuration to multiple environments
- Automated: Eliminate manual, error-prone processes
IaC ensures consistent, reproducible environments and reduces the risk of configuration drift—when manually configured systems gradually become inconsistent due to ad-hoc changes.
Ansible as an IaC Tool
Ansible stands out among IaC tools for several key reasons:
- Agentless architecture: No need to install software on managed nodes
- YAML-based syntax: Easy to read and write, even for beginners
- Idempotent operations: Running the same playbook multiple times produces the same result
- Extensive module library: Built-in support for most infrastructure components
Core Components of Ansible IaC
Inventory Files
Inventory files define the hosts and groups that Ansible manages. These can be static files or dynamic scripts that pull host information from external sources.
# inventory.yml
webservers:
hosts:
web1.example.com:
http_port: 80
web2.example.com:
http_port: 8080
vars:
ansible_user: deploy
databases:
hosts:
db1.example.com:
db2.example.com:
Variables
Variables allow you to customize your infrastructure configuration for different environments or use cases.
# group_vars/webservers.yml
http_port: 80
deploy_path: /var/www/html
app_version: 1.2.3
# host_vars/web1.example.com.yml
http_port: 8080 # Override group variable
Playbooks
Playbooks are the heart of Ansible's IaC approach. They define a series of tasks to configure systems to the desired state.
# webserver-setup.yml
---
- name: Configure webservers
hosts: webservers
become: true
vars:
doc_root: /var/www/html
tasks:
- name: Install Apache
apt:
name: apache2
state: present
update_cache: yes
- name: Start and enable Apache
service:
name: apache2
state: started
enabled: yes
- name: Deploy application
copy:
src: files/index.html
dest: "{{ doc_root }}/index.html"
owner: www-data
group: www-data
mode: '0644'
Roles
Roles provide a framework for fully independent or interdependent collections of variables, tasks, files, templates, and modules.
roles/
webserver/
tasks/
main.yml
handlers/
main.yml
files/
index.html
templates/
httpd.conf.j2
vars/
main.yml
defaults/
main.yml
meta/
main.yml
Example usage in a playbook:
# site.yml
---
- name: Configure webservers
hosts: webservers
become: true
roles:
- webserver
- { role: monitoring, when: monitoring_enabled }
Practical Example: Complete Infrastructure Deployment
Let's walk through a practical example of using Ansible to define and deploy a complete web application infrastructure.
Step 1: Define Your Inventory
# inventory.yml
---
all:
children:
webservers:
hosts:
web1:
ansible_host: 192.168.1.10
web2:
ansible_host: 192.168.1.11
loadbalancers:
hosts:
lb1:
ansible_host: 192.168.1.5
databases:
hosts:
db1:
ansible_host: 192.168.1.20
vars:
ansible_user: deploy
ansible_ssh_private_key_file: ~/.ssh/id_rsa
Step 2: Create Roles for Each Component
Let's create a role for our web server configuration:
# roles/webserver/tasks/main.yml
---
- name: Install necessary packages
apt:
name:
- nginx
- python3
- python3-pip
state: present
update_cache: yes
- name: Configure Nginx
template:
src: nginx.conf.j2
dest: /etc/nginx/sites-available/default
owner: root
group: root
mode: '0644'
notify: Restart Nginx
- name: Deploy application code
git:
repo: https://github.com/example/webapp.git
dest: /var/www/html
version: master
notify: Restart Application
And handlers to respond to changes:
# roles/webserver/handlers/main.yml
---
- name: Restart Nginx
service:
name: nginx
state: restarted
- name: Restart Application
command: /var/www/html/scripts/restart.sh
Step 3: Create a Main Playbook
# site.yml
---
- name: Configure databases
hosts: databases
become: true
roles:
- database
- name: Configure web servers
hosts: webservers
become: true
roles:
- webserver
- app_config
- name: Configure load balancers
hosts: loadbalancers
become: true
roles:
- loadbalancer
Step 4: Run Your Infrastructure Deployment
# Command line execution
ansible-playbook -i inventory.yml site.yml
Output example:
PLAY [Configure databases] *************************
TASK [database : Install MySQL] ********************
ok: [db1]
TASK [database : Configure MySQL] ******************
changed: [db1]
PLAY [Configure web servers] **********************
TASK [webserver : Install necessary packages] ******
ok: [web1]
ok: [web2]
TASK [webserver : Configure Nginx] ****************
changed: [web1]
changed: [web2]
TASK [webserver : Deploy application code] *********
changed: [web1]
changed: [web2]
PLAY [Configure load balancers] *******************
TASK [loadbalancer : Install HAProxy] *************
ok: [lb1]
TASK [loadbalancer : Configure HAProxy] ***********
changed: [lb1]
PLAY RECAP ****************************************
db1 : ok=2 changed=1 unreachable=0 failed=0
web1 : ok=3 changed=2 unreachable=0 failed=0
web2 : ok=3 changed=2 unreachable=0 failed=0
lb1 : ok=2 changed=1 unreachable=0 failed=0
Advanced IaC Techniques with Ansible
Infrastructure Visualization
You can create visualizations of your infrastructure using Mermaid diagrams:
Orchestration
Ansible can orchestrate complex deployments with fine-grained control:
# orchestration.yml
---
- name: Database migration
hosts: databases
become: true
tasks:
- name: Backup database
command: /usr/local/bin/backup.sh
- name: Run migrations
command: /usr/local/bin/migrate.sh
- name: Deploy new application version
hosts: webservers
serial: 1 # One server at a time
become: true
tasks:
- name: Pull new code
git:
repo: https://github.com/example/webapp.git
dest: /var/www/html
version: "{{ app_version }}"
- name: Run tests
command: /var/www/html/test.sh
register: test_result
- name: Restart application
service:
name: webapp
state: restarted
when: test_result.rc == 0
- name: Verify deployment
uri:
url: http://localhost/health
return_content: yes
register: health_check
failed_when: "'OK' not in health_check.content"
- name: Rollback if necessary
command: /usr/local/bin/rollback.sh
when: health_check is failed
Environment Templating
Use templates to maintain different configurations for different environments:
# templates/nginx.conf.j2
server {
listen {{ http_port }};
server_name {{ server_name }};
root {{ doc_root }};
{% if env == "production" %}
ssl on;
ssl_certificate {{ ssl_cert_path }};
ssl_certificate_key {{ ssl_key_path }};
{% endif %}
location / {
try_files $uri $uri/ /index.html;
}
}
Best Practices for Ansible IaC
- Version Control: Store all your Ansible code in a git repository
- Modular Design: Use roles to create reusable components
- Environment Separation: Maintain separate variables for dev, test, and production
- Secret Management: Use Ansible Vault to encrypt sensitive data:
# Encrypting sensitive data
ansible-vault encrypt group_vars/all/secrets.yml
# Using encrypted files
ansible-playbook site.yml --ask-vault-pass
- Testing: Test your playbooks in a development environment before production
- Documentation: Document your infrastructure code with comments and README files
- Continuous Integration: Validate your Ansible code with automated testing
Implementing CI/CD for Infrastructure
A powerful IaC approach is to implement CI/CD for your infrastructure changes:
# .gitlab-ci.yml example
stages:
- lint
- test
- deploy
lint:
stage: lint
script:
- ansible-lint *.yml
test:
stage: test
script:
- ansible-playbook --check -i inventory.dev.yml site.yml
deploy_staging:
stage: deploy
script:
- ansible-playbook -i inventory.staging.yml site.yml
environment: staging
only:
- main
deploy_production:
stage: deploy
script:
- ansible-playbook -i inventory.production.yml site.yml
environment: production
when: manual
only:
- tags
Summary
Ansible's approach to Infrastructure as Code provides a powerful yet accessible way to automate your infrastructure management. By defining your infrastructure using YAML playbooks and roles, you can ensure consistency, track changes, and deploy with confidence.
The key benefits of using Ansible for IaC include:
- Simple, human-readable syntax
- Agentless architecture
- Powerful templating and variable system
- Extensive module library
- Community support and plugins
As you continue your journey with Ansible IaC, focus on building modular, reusable components that can be composed to create complete infrastructure deployments.
Additional Resources
-
Practice exercises:
- Create a playbook to deploy a simple web application
- Modify your playbook to support multiple environments
- Convert your playbook into reusable roles
- Implement a CI/CD pipeline for your infrastructure code
-
Books and documentation:
- Ansible official documentation
- "Ansible for DevOps" by Jeff Geerling
- "Infrastructure as Code" by Kief Morris
Remember: The key to successful Infrastructure as Code with Ansible is starting simple and iteratively improving your automation. Begin with automating a single, well-understood process, and gradually expand to more complex infrastructure components.
If you spot any mistakes on this website, please let me know at feedback@compilenrun.com. I’d greatly appreciate your feedback! :)