Ansible is an open-source automation tool that simplifies complex IT tasks such as configuration management, application deployment, and intra-service orchestration. Developed by Michael DeHaan and acquired by Red Hat in 2015, Ansible has become one of the most popular tools in the DevOps toolkit.
Unlike other configuration management tools, Ansible uses a push-based architecture where the control machine pushes configurations to the managed nodes. This approach eliminates the need for agents on the managed systems, making it lightweight and easy to deploy.
On This Page
Table of Contents
Introduction to Ansible
Why Use Ansible in DevOps?
In the world of DevOps, automation is key to achieving speed, reliability, and consistency. Ansible addresses these needs by:
- Simplifying complex workflows through automation
- Reducing human error by codifying infrastructure
- Enabling infrastructure as code (IaC) practices
- Facilitating continuous delivery pipelines
- Improving team collaboration through readable playbooks
Key Features and Benefits
| Feature | Benefit |
|---|---|
| Agentless | No additional software to install on managed nodes |
| Idempotent | Safe to run multiple times without unintended consequences |
| Simple YAML syntax | Human-readable playbooks that are easy to understand |
| Extensive module library | Pre-built functions for common tasks |
| Broad platform support | Works with Linux, Windows, network devices, and cloud services |
Getting Started with Ansible
Installation and Setup
Before diving into Ansible, you need to install it on your control machine. Ansible can be installed on various operating systems, but we’ll focus on Linux-based systems:
# On Ubuntu/Debian
sudo apt update
sudo apt install ansible
# On CentOS/RHEL
sudo yum install epel-release
sudo yum install ansible
# Using pip (Python package manager)
pip install ansibleAfter installation, verify that Ansible is working correctly:
ansible --versionBasic Concepts
Understanding Ansible’s core concepts is essential before moving forward:
- Control Node: The machine where Ansible is installed and from which all tasks are executed
- Managed Nodes: The target systems that Ansible configures
- Inventory: A file containing information about the managed nodes
- Modules: Small programs that Ansible executes on managed nodes
- Playbooks: YAML files containing a series of tasks to be executed
- Tasks: Individual units of work in a playbook
- Roles: Reusable collections of playbooks, templates, and other files
First Simple Ansible Command
Let’s start with a simple ad-hoc command to ping your managed nodes:
# Create a simple inventory file
echo "localhost ansible_connection=local" > inventory
# Run a ping command
ansible -i inventory all -m pingIf successful, you’ll see a response like:
localhost | SUCCESS => {
"changed": false,
"ping": "pong"
}Ansible Fundamentals
Inventory Management
The inventory is the heart of Ansible’s connection to your infrastructure. It defines the hosts and groups of hosts that Ansible will manage.
Static Inventory
A static inventory is a simple text file, typically in INI or YAML format. Here’s an example in INI format:
Webservers Group
[webservers]
web1.example.com
web2.example.comDatabases Group
[databases]
db1.example.com
db2.example.comGlobal Variables
[all:vars]
ansible_user=admin
ansible_ssh_private_key_file=~/.ssh/ansible_keyDynamic Inventory
For cloud environments or dynamic infrastructures, dynamic inventories can be used. These are scripts that generate inventory data when executed:
# Example using AWS dynamic inventory
ansible-inventory -i ./aws_ec2.yml --listAd-hoc Commands
Ad-hoc commands are simple one-line commands that allow you to perform quick tasks without writing a full playbook. They’re useful for testing, quick fixes, and repetitive tasks.
Some common examples:
# Check disk space on all web servers
ansible webservers -m command -a "df -h"
# Install a package on all database servers
ansible databases -m yum -a "name=postgresql state=present"
# Restart a service on all servers
ansible all -m service -a "name=nginx state=restarted"Modules and Their Usage
Modules are the building blocks of Ansible. They are small programs that Ansible pushes to the managed nodes to perform specific tasks.
Ansible comes with hundreds of modules categorized by functionality:
| Category | Example Modules | Use Cases |
|---|---|---|
| File | copy, fetch, file, lineinfile | Managing files and directories |
| Package | yum, apt, pip, gem | Installing and managing software packages |
| Service | service, systemd | Managing system services |
| Network | ios_config, nxos_config | Configuring network devices |
| Cloud | ec2, azure_rm, gcp | Managing cloud resources |
Here’s an example using the copy module:
ansible webservers -m copy -a "src=/path/to/local/file dest=/path/to/remote/file"Playbooks and YAML Basics
Playbooks are Ansible’s configuration, deployment, and orchestration language. They are written in YAML (YAML Ain’t Markup Language), a human-readable data serialization standard.
A simple playbook structure looks like this:
---
- name: Playbook to install and configure web server
hosts: webservers
become: yes
tasks:
- name: Install Apache web server
yum:
name: httpd
state: present
- name: Start and enable Apache service
service:
name: httpd
state: started
enabled: yesKey YAML syntax rules to remember:
- Use spaces for indentation, not tabs
- Lists are denoted by a hyphen (-)
- Key-value pairs are separated by a colon (:)
- Comments start with a hash (#)
Intermediate Ansible
Variables and Facts
Variables in Ansible allow you to parameterize your playbooks, making them more flexible and reusable. Variables can be defined in various places:
- In the playbook itself
- In separate variable files
- In the inventory
- On the command line
- Discovered from the system (facts)
Here’s an example of using variables in a playbook:
---
- name: Configure web server with variables
hosts: webservers
become: yes
vars:
http_port: 80
max_clients: 200
tasks:
- name: Ensure Apache is installed
yum:
name: httpd
state: present
- name: Configure Apache
template:
src: httpd.conf.j2
dest: /etc/httpd/conf/httpd.conf
notify: restart apache
vars:
http_port: "{{ http_port }}"
max_clients: "{{ max_clients }}"
handlers:
- name: restart apache
service:
name: httpd
state: restartedFacts are system information that Ansible automatically gathers about managed nodes. You can use these facts to make decisions in your playbooks:
---
- name: Use facts to make decisions
hosts: all
tasks:
- name: Install package based on OS family
yum:
name: "{{ 'httpd' if ansible_os_family == 'RedHat' else 'apache2' }}"
state: present
when: ansible_os_family == 'RedHat' or ansible_os_family == 'Debian'Conditionals and Loops
Conditionals allow you to control when a task should be executed based on certain conditions. The when statement is used for this purpose:
---
- name: Conditional tasks
hosts: all
tasks:
- name: Install firewalld on RHEL systems
yum:
name: firewalld
state: present
when: ansible_os_family == "RedHat"
- name: Install ufw on Ubuntu systems
apt:
name: ufw
state: present
when: ansible_os_family == "Debian"Loops allow you to repeat a task multiple times with different values. Ansible supports various loop constructs:
---
- name: Using loops
hosts: webservers
tasks:
- name: Install multiple packages
yum:
name: "{{ item }}"
state: present
loop:
- httpd
- php
- php-mysql
- php-gd
- name: Create multiple users
user:
name: "{{ item.name }}"
state: present
groups: "{{ item.groups }}"
loop:
- { name: 'alice', groups: 'wheel' }
- { name: 'bob', groups: 'users' }
- { name: 'charlie', groups: 'users' }Roles and Their Structure
Roles are a way to organize and reuse Ansible content. They provide a structured way to package related tasks, variables, files, templates, and handlers.
A typical role structure looks like this:
my_role/
├── defaults/
│ └── main.yml
├── files/
├── handlers/
│ └── main.yml
├── meta/
│ └── main.yml
├── tasks/
│ └── main.yml
├── templates/
├── tests/
│ ├── inventory
│ └── test.yml
└── vars/
└── main.ymlHere’s how to use a role in a playbook:
---
- name: Apply web server role
hosts: webservers
become: yes
roles:
- common
- webserver
- { role: database, when: ansible_os_family == 'RedHat' }Handlers
Handlers are special tasks that only run when notified by another task. They are typically used to restart services when configuration files change:
---
- name: Using handlers
hosts: webservers
become: yes
tasks:
- name: Update Apache configuration
template:
src: httpd.conf.j2
dest: /etc/httpd/conf/httpd.conf
notify: restart apache
handlers:
- name: restart apache
service:
name: httpd
state: restartedAdvanced Ansible
Ansible Vault for Secrets Management
Ansible Vault is a feature that allows you to encrypt sensitive data such as passwords, API keys, and certificates. This ensures that your secrets are not stored in plain text in your playbooks or variable files.
To create an encrypted file:
ansible-vault create secrets.ymlYou’ll be prompted to enter and confirm a password. Then, you can edit the file with your secrets:
---
db_password: "supersecretpassword"
api_key: "abcd1234efgh5678"To use the encrypted file in a playbook:
---
- name: Deploy application with secrets
hosts: app_servers
become: yes
vars_files:
- secrets.yml
tasks:
- name: Configure database connection
template:
src: database.conf.j2
dest: /etc/app/database.conf
mode: '0600'To run a playbook with encrypted files:
ansible-playbook --ask-vault-pass deploy_app.ymlDynamic Inventories
Dynamic inventories are scripts or plugins that generate inventory data at runtime. They’re particularly useful in cloud environments where infrastructure changes frequently.
For example, to use AWS EC2 instances as your inventory:
- Install the required Python packages:
pip install boto boto3- Create an AWS EC2 dynamic inventory configuration file (aws_ec2.yml):
---
plugin: aws_ec2
regions:
- us-east-1
- us-west-2
keyed_groups:
- key: tags.Environment
prefix: env
- key: tags.Role
prefix: role- Test the dynamic inventory:
ansible-inventory -i aws_ec2.yml --list- Use the dynamic inventory in a playbook:
ansible-playbook -i aws_ec2.yml configure_servers.ymlCustom Modules Development
While Ansible provides hundreds of built-in modules, sometimes you need functionality that doesn’t exist. In such cases, you can create custom modules.
Here’s a simple example of a custom module that generates a random password:
#!/usr/bin/python
# custom_password_module.py
import random
import string
import json
def generate_password(length=12):
"""Generate a random password"""
chars = string.ascii_letters + string.digits + string.punctuation
return ''.join(random.choice(chars) for _ in range(length))
def main():
module = AnsibleModule(
argument_spec=dict(
length=dict(type='int', default=12),
state=dict(type='str', default='present')
)
)
length = module.params['length']
password = generate_password(length)
module.exit_json(changed=True, password=password)
from ansible.module_utils.basic import AnsibleModule
if __name__ == '__main__':
main()To use this custom module in a playbook:
---
- name: Generate and set passwords
hosts: all
tasks:
- name: Generate random password
custom_password_module:
length: 16
register: password_result
- name: Display generated password
debug:
msg: "Generated password: {{ password_result.password }}"Ansible Tower/AWX
Ansible Tower (now called Ansible Automation Platform) is a web-based solution that makes Ansible more accessible to teams. The open-source version is called AWX.
Key features of Ansible Tower/AWX:
| Feature | Description |
|---|---|
| Web UI | Browser-based interface for managing Ansible |
| Role-Based Access Control | Fine-grained permissions for users and teams |
| Job Scheduling | Schedule playbooks to run at specific times |
| Credentials Management | Secure storage of SSH keys, passwords, etc. |
| Real-time Job Monitoring | View playbook execution in real-time |
| REST API | Integrate with other tools and systems |
| Workflow Builder | Create complex automation workflows |
Ansible Best Practices
Directory Structure
A well-organized directory structure is crucial for maintaining Ansible projects, especially as they grow in complexity. Here’s a recommended structure:
ansible_project/
├── inventories/
│ ├── production/
│ │ ├── hosts.yml
│ │ └── group_vars/
│ │ └── all.yml
│ └── staging/
│ ├── hosts.yml
│ └── group_vars/
│ └── all.yml
├── roles/
│ ├── common/
│ ├── webserver/
│ └── database/
├── playbooks/
│ ├── site.yml
│ ├── webservers.yml
│ └── databases.yml
├── library/
├── filter_plugins/
└── README.mdNaming Conventions
Consistent naming conventions improve readability and maintainability:
- Playbooks: Use descriptive names with underscores (e.g.,
deploy_web_application.yml) - Roles: Use lowercase with underscores (e.g.,
web_server) - Variables: Use lowercase with underscores (e.g.,
http_port) - Hosts: Use FQDNs or descriptive names (e.g.,
web01.example.com)
Testing Strategies
Testing is essential to ensure reliability and prevent issues in production:
- Syntax Checking:
ansible-playbook --syntax-check playbook.yml- Dry Run:
ansible-playbook --check playbook.yml- Linting:
ansible-lint playbook.yml- Testing with Molecule:
molecule testCI/CD Integration
Integrating Ansible into your CI/CD pipeline enables automated testing and deployment:
Jenkins Pipeline Example
pipeline {
agent any
stages {
stage('Lint Ansible Playbooks') {
steps {
sh 'ansible-lint playbooks/*.yml'
}
}
stage('Test Ansible Playbooks') {
steps {
sh 'ansible-playbook --check --inventory inventories/staging playbooks/site.yml'
}
}
stage('Deploy to Production') {
when {
branch 'main'
}
steps {
sh 'ansible-playbook --inventory inventories/production playbooks/site.yml'
}
}
}
}GitLab CI/CD Example
stages:
- lint
- test
- deploy
lint:
stage: lint
image: python:3.8
script:
- pip install ansible-lint
- ansible-lint playbooks/*.yml
test:
stage: test
image: python:3.8
script:
- pip install ansible
- ansible-playbook --check --inventory inventories/staging playbooks/site.yml
deploy:
stage: deploy
image: python:3.8
script:
- pip install ansible
- ansible-playbook --inventory inventories/production playbooks/site.yml
only:
- mainReal-world Ansible Use Cases
Configuration Management
Ansible excels at configuration management, ensuring that your systems are configured consistently and maintained in their desired state.
Example: Standardizing server configurations across your infrastructure:
---
- name: Standard server configuration
hosts: all
become: yes
tasks:
- name: Update all packages
yum:
name: '*'
state: latest
when: ansible_os_family == "RedHat"
- name: Update all packages
apt:
upgrade: dist
update_cache: yes
when: ansible_os_family == "Debian"
- name: Ensure common packages are installed
package:
name:
- vim
- curl
- wget
- git
state: present
- name: Configure time synchronization
service:
name: chronyd
state: started
enabled: yes
when: ansible_os_family == "RedHat"
- name: Configure time synchronization
service:
name: ntp
state: started
enabled: yes
when: ansible_os_family == "Debian"
- name: Configure firewall
firewalld:
service: ssh
permanent: yes
state: enabled
immediate: yes
when: ansible_os_family == "RedHat"Application Deployment
Ansible can automate the entire application deployment process, from code deployment to service restart.
Example: Deploying a web application:
---
- name: Deploy web application
hosts: webservers
become: yes
vars:
app_name: mywebapp
app_version: 1.2.3
app_user: www-data
app_group: www-data
tasks:
- name: Create application directory
file:
path: "/opt/{{ app_name }}"
state: directory
owner: "{{ app_user }}"
group: "{{ app_group }}"
mode: '0755'
- name: Download application package
get_url:
url: "https://releases.example.com/{{ app_name }}-{{ app_version }}.tar.gz"
dest: "/tmp/{{ app_name }}-{{ app_version }}.tar.gz"
- name: Extract application package
unarchive:
src: "/tmp/{{ app_name }}-{{ app_version }}.tar.gz"
dest: "/opt/{{ app_name }}"
remote_src: yes
owner: "{{ app_user }}"
group: "{{ app_group }}"
- name: Configure application
template:
src: config.j2
dest: "/opt/{{ app_name }}/config.ini"
owner: "{{ app_user }}"
group: "{{ app_group }}"
mode: '0640'
notify: restart application
- name: Install application dependencies
pip:
requirements: "/opt/{{ app_name }}/requirements.txt"
virtualenv: "/opt/{{ app_name }}/venv"
- name: Ensure application service is running
service:
name: "{{ app_name }}"
state: started
enabled: yes
handlers:
- name: restart application
service:
name: "{{ app_name }}"
state: restartedProvisioning
Ansible can be used for provisioning new infrastructure, whether it’s virtual machines, containers, or cloud resources.
Example: Provisioning AWS EC2 instances:
---
- name: Provision AWS EC2 instances
hosts: localhost
gather_facts: no
tasks:
- name: Create EC2 instances
ec2:
key_name: my-keypair
instance_type: t2.micro
image: ami-0c55b159cbfafe1f0
wait: yes
group: default
count: 3
vpc_subnet_id: subnet-12345678
assign_public_ip: yes
region: us-east-1
instance_tags:
Name: webserver
Environment: production
Role: web
register: ec2
- name: Add new instances to inventory
add_host:
name: "{{ item.public_ip }}"
groups: webservers
ansible_user: ec2-user
ansible_ssh_private_key_file: ~/.ssh/my-keypair.pem
loop: "{{ ec2.instances }}"
- name: Wait for SSH to be available
wait_for:
host: "{{ item.public_ip }}"
port: 22
delay: 10
timeout: 300
loop: "{{ ec2.instances }}"
- name: Configure new instances
hosts: webservers
become: yes
tasks:
- name: Install Apache
yum:
name: httpd
state: present
- name: Start and enable Apache
service:
name: httpd
state: started
enabled: yesOrchestration
Orchestration involves coordinating multiple systems to work together in a structured way. Ansible is excellent for this due to its ability to manage complex workflows.
Example: Multi-tier application orchestration:
---
- name: Deploy multi-tier application
hosts: localhost
gather_facts: no
tasks:
- name: Create database servers
ec2:
key_name: my-keypair
instance_type: t2.micro
image: ami-0c55b159cbfafe1f0
wait: yes
group: database-sg
count: 2
vpc_subnet_id: subnet-12345678
assign_public_ip: no
region: us-east-1
instance_tags:
Name: dbserver
Environment: production
Role: database
register: db_ec2
- name: Create application servers
ec2:
key_name: my-keypair
instance_type: t2.micro
image: ami-0c55b159cbfafe1f0
wait: yes
group: app-sg
count: 3
vpc_subnet_id: subnet-12345678
assign_public_ip: no
region: us-east-1
instance_tags:
Name: appserver
Environment: production
Role: application
register: app_ec2
- name: Create load balancer
ec2_elb_lb:
name: myapp-lb
state: present
region: us-east-1
zones:
- us-east-1a
- us-east-1b
listeners:
- protocol: http
load_balancer_port: 80
instance_port: 8080
instances: "{{ app_ec2.instance_ids }}"
- name: Add instances to inventory
add_host:
name: "{{ item.private_ip }}"
groups: "{{ 'databases' if 'dbserver' in item.tags.Name else 'applications' }}"
ansible_user: ec2-user
ansible_ssh_private_key_file: ~/.ssh/my-keypair.pem
loop: "{{ db_ec2.instances + app_ec2.instances }}"
- name: Configure database servers
hosts: databases
become: yes
tasks:
- name: Install PostgreSQL
yum:
name: postgresql-server
state: present
- name: Initialize PostgreSQL database
command: postgresql-setup initdb
args:
creates: /var/lib/pgsql/data/postgresql.conf
- name: Start and enable PostgreSQL
service:
name: postgresql
state: started
enabled: yes
- name: Create application database
postgresql_db:
name: myapp
state: present
- name: Create database user
postgresql_user:
db: myapp
name: myapp_user
password: secure_password
priv: "ALL"
state: present
- name: Configure application servers
hosts: applications
become: yes
tasks:
- name: Install Java
yum:
name: java-1.8.0-openjdk
state: present
- name: Download application
get_url:
url: https://releases.example.com/myapp-1.0.0.jar
dest: /opt/myapp.jar
mode: '0755'
- name: Create systemd service for application
copy:
content: |
[Unit]
Description=My Application
After=network.target
[Service]
Type=simple
User=appuser
ExecStart=/usr/bin/java -jar /opt/myapp.jar
Restart=always
[Install]
WantedBy=multi-user.target
dest: /etc/systemd/system/myapp.service
- name: Start and enable application
systemd:
name: myapp
state: started
enabled: yes
daemon_reload: yesWrapUP
In this comprehensive guide, we’ve journeyed through Ansible from the basics to advanced concepts, covering everything from simple ad-hoc commands to complex orchestration workflows. Ansible’s simplicity, power, and flexibility make it an indispensable tool in the DevOps toolkit.
Whether you’re managing a handful of servers or orchestrating complex multi-tier applications across hybrid cloud environments, Ansible provides the automation capabilities needed to streamline operations, reduce errors, and accelerate delivery.
As you continue your Ansible journey, remember that the key to success is:
- Starting small with simple playbooks and gradually increasing complexity
- Embracing idempotency to ensure predictable and repeatable results
- Organizing your code with roles and proper directory structures
- Testing thoroughly before deploying to production
- Continuously learning about new modules and best practices
With these principles in mind, you’ll be well on your way to mastering Ansible and leveraging its full potential in your DevOps practices.

FAQs
What is Ansible, in simple terms?
Think of Ansible as a remote control for all your computers and servers. Instead of manually logging into each machine to install software, update configurations, or run commands, you write simple instructions in a file, and Ansible connects to all your machines and does those tasks for you automatically. It’s a tool to automate repetitive IT work.
What makes Ansible different from other similar tools?
The biggest difference is that Ansible is agentless. This means you don’t need to install any special software on the servers you want to manage. You only need it on one “control” machine. It connects to your other servers using standard SSH (for Linux) or WinRM (for Windows), making it much simpler to set up and more secure because there are fewer extra programs to manage.
Do I need to be a programmer to learn Ansible?
Not at all! Ansible uses a language called YAML, which is designed to be easy for humans to read and write. It looks more like a structured shopping list or a set of instructions than a complex programming code. If you can write a simple outline, you can write an Ansible playbook.
What’s the difference between an “ad-hoc command” and a “playbook”?
An ad-hoc command is like a quick text message: it’s for one-off, simple tasks. For example, “Hey, check if all web servers are online.” A playbook is like a detailed recipe or project plan. It contains a series of steps to accomplish a larger goal, like deploying an entire application from scratch, including installing dependencies, configuring files, and starting services.
How does Ansible know which servers to work on?
It uses an inventory file. Think of this file as your address book for servers. You list the names or IP addresses of all the computers you want to manage, and you can even group them together (like putting all your web servers in a “webservers” group and all your database servers in a “databases” group).
How do I handle sensitive information like passwords or API keys safely?
You should never write passwords directly in your playbooks. Ansible provides a feature called Ansible Vault. Think of Vault as a digital safe. You put your secrets (passwords, keys) into an encrypted file, and Ansible can unlock and use that file when running your automation, keeping your sensitive information secure and out of plain sight.
I keep hearing the word “idempotent.” What does that mean?
Idempotency is a core principle in Ansible that means running a task multiple times will always produce the same result. For example, if a task is to “ensure a web server is installed,” running it once will install it. Running it a second time will do nothing, because the server is already there. This makes Ansible safe and predictable, as you can run your playbooks again and again without causing unintended changes.
Can Ansible manage things other than Linux servers?
Yes, absolutely! While it started with Linux, Ansible is now very versatile. It can manage Windows servers, configure network devices like switches and routers from Cisco, Juniper, and others, and even manage resources in cloud platforms like Amazon Web Services (AWS), Microsoft Azure, and Google Cloud.
What is a “role” and why would I use one?
As your automation gets more complex, playbooks can become long and messy. A role is a way to organize your work into neat, reusable packages. Think of it as a toolkit for a specific job. For example, you could have a “webserver” role that contains everything needed to set up a web server. You can then easily reuse this role in different projects, keeping your automation clean and maintainable.
Is Ansible free to use?
Yes, the core Ansible engine is open-source and completely free for anyone to download and use. There is a commercial product called Ansible Automation Platform (formerly Ansible Tower) which adds a user-friendly web interface, role-based access control, and other features for large teams, but you don’t need it to start automating with Ansible.
