What it is
Ansible is an open-source automation tool that simplifies IT infrastructure provisioning, configuration management, and application deployment through agentless, human-readable playbooks.
Installation
Linux (Debian/Ubuntu)
sudo apt update
sudo apt install ansible
Linux (RHEL/CentOS/Fedora)
sudo yum install ansible
# or
sudo dnf install ansible
macOS
brew install ansible
Windows
Ansible is designed to manage remote systems, typically Linux. While you can install Ansible on Windows, it’s primarily for managing other machines. For managing Windows hosts, you’ll need to ensure WinRM is configured on the target machines.
Install using pip (requires Python):
python -m pip install ansible
Ensure Python is in your PATH.
Core Concepts
- Inventory: A list of hosts that Ansible manages. Can be static (a file) or dynamic (generated by cloud providers or scripts).
- Playbook: A YAML file that describes a series of tasks to be executed on managed hosts.
- Task: An action to be performed on a managed host, defined by a module.
- Module: A unit of code that Ansible executes on the target host (e.g.,
apt,copy,service). - Role: A way to organize playbooks, variables, files, templates, and handlers into reusable units.
- Handler: A task that runs only when notified by another task (e.g., restarting a service only if a configuration file changes).
- Variable: A named value that can be used in playbooks to customize behavior.
- Fact: Information gathered by Ansible about the managed hosts (e.g., operating system, IP address).
- Connection: How Ansible connects to managed hosts (e.g., SSH for Linux, WinRM for Windows).
- Vault: Ansible’s tool for encrypting sensitive data like passwords and private keys.
Commands / Usage
Running Ad-Hoc Commands
Execute a single task across one or more hosts without writing a playbook.
-
Ping a host:
ansible webservers -m pingCheck if Ansible can connect to and execute modules on hosts in the
webserversgroup. -
Get system facts:
ansible all -m setupGather detailed information about all hosts.
-
Install a package:
ansible webservers -m apt -a "name=nginx state=present" --becomeEnsure the
nginxpackage is installed onwebservers.--becomeruns the task with elevated privileges (likesudo). -
Copy a file:
ansible dbservers -m copy -a "src=/path/to/local/file.conf dest=/etc/app.conf" --becomeCopy a local file to the destination on
dbservers. -
Start a service:
ansible appservers -m service -a "name=apache2 state=started" --becomeEnsure the
apache2service is running onappservers. -
Run a command:
ansible monitoring -m command -a "df -h"Execute the
df -hcommand on hosts in themonitoringgroup. Useshellmodule for commands requiring shell features like pipes or redirection.ansible webservers -m shell -a "grep 'error' /var/log/nginx/error.log | tail -n 5"
Running Playbooks
Execute a defined set of tasks from a YAML playbook file.
-
Run a playbook:
ansible-playbook deploy_app.ymlExecute the tasks defined in
deploy_app.yml. -
Specify an inventory file:
ansible-playbook -i hosts.ini deploy_app.ymlUse
hosts.inias the inventory source. -
Limit execution to specific hosts or groups:
ansible-playbook -i hosts.ini deploy_app.yml --limit webserversRun the playbook only on hosts in the
webserversgroup. -
Ask for variables:
ansible-playbook -i hosts.ini deploy_app.yml --ask-varsPrompt the user to enter values for variables defined in the playbook.
-
Check mode (dry run):
ansible-playbook -i hosts.ini deploy_app.yml --checkSimulate playbook execution without making any changes. Shows what would happen.
-
Become (escalate privileges):
ansible-playbook -i hosts.ini deploy_app.yml --becomeExecute tasks with escalated privileges (e.g.,
sudo). -
Specify become method:
ansible-playbook -i hosts.ini deploy_app.yml --become --become-method sudoUse
sudoas the privilege escalation method. Other common methods includesuandpbrun. -
Specify become user:
ansible-playbook -i hosts.ini deploy_app.yml --become --become-user rootExecute tasks as the
rootuser. -
Skip tags:
ansible-playbook -i hosts.ini deploy_app.yml --skip-tags "configure_nginx"Run the playbook but skip any tasks tagged with
configure_nginx. -
Start at task:
ansible-playbook -i hosts.ini deploy_app.yml --start-at-task "Install packages"Begin playbook execution at the task named "Install packages".
-
List hosts:
ansible-playbook --list-hosts deploy_app.ymlShow all hosts that the playbook will run on.
-
List tasks:
ansible-playbook --list-tasks deploy_app.ymlShow all tasks that the playbook will execute.
Managing Inventory
Ansible needs to know which hosts to manage.
-
Static inventory file (INI format):
[webservers] web1.example.com web2.example.com [dbservers] db1.example.com ansible_user=postgres [monitoring:vars] ansible_user=monitorGroup hosts, define host-specific variables, and group variables.
-
Static inventory file (YAML format):
all: children: webservers: hosts: web1.example.com: web2.example.com: dbservers: hosts: db1.example.com: ansible_user: postgres monitoring: hosts: monitor.example.com: vars: ansible_user: monitorA more structured way to define inventory.
-
Dynamic inventory: Ansible can use scripts or plugins to generate inventory from cloud providers (AWS, Azure, GCP), virtualization platforms (VMware), or CMDBs. Example using AWS EC2 dynamic inventory script:
ansible-playbook -i ec2.py deploy_app.ymlRequires
ec2.py(or similar) script and necessary AWS credentials.
Managing Variables
Variables make playbooks flexible and reusable.
- Defining variables in playbooks (using
varskeyword):--- - name: Deploy web application hosts: webservers vars: app_version: "1.2.3" deploy_dir: "/var/www/myapp" tasks: - name: Deploy code copy:
{% raw %} src: "files/myapp-{{ app_version }}.tar.gz" {% endraw %} {% raw %} dest: "{{ deploy_dir }}/" {% endraw %} ```
-
Defining variables in inventory: (See Static Inventory examples above)
-
Defining variables in group_vars or host_vars: Create directories
group_vars/andhost_vars/in the same directory as your playbook or inventory.group_vars/webservers.yml:app_port: 8080host_vars/web1.example.com.yml:app_specific_setting: "enabled" -
Defining variables on the command line:
ansible-playbook -i hosts.ini deploy_app.yml -e "app_version=1.2.4 deploy_dir=/opt/myapp"Use the
-eflag to pass extra variables. -
Using facts as variables:
- name: Configure firewall for app port firewalld:
{% raw %}
port: "{{ app_port }}/tcp"
{% endraw %}
permanent: true
state: enabled
when: "'webservers' in group_names"
vars:
app_port: 8080 # Example: variable defined elsewhere
```
Access gathered facts like ansible_os_family, ansible_default_ipv4.address.
Using Roles
Organize playbook content into reusable units.
-
Directory structure:
roles/ webserver/ tasks/ main.yml handlers/ main.yml templates/ nginx.conf.j2 files/ app.conf vars/ main.yml defaults/ main.yml meta/ main.yml -
Include role in playbook:
--- - name: Deploy web application using roles hosts: webservers roles: - webserver - common -
Passing variables to roles:
--- - name: Deploy web application with custom role vars hosts: webservers roles: - role: webserver app_port: 8081 nginx_worker_processes: 4
Ansible Vault
Encrypt sensitive data.
-
Create an encrypted file:
ansible-vault create secrets.ymlYou’ll be prompted to set a vault password.
-
Edit an encrypted file:
ansible-vault edit secrets.yml -
Encrypt an existing file:
ansible-vault encrypt existing_vars.yml -
Decrypt a file:
ansible-vault decrypt secrets.yml -
View an encrypted file:
ansible-vault view secrets.yml -
Run a playbook using vault:
ansible-playbook deploy_app.yml --ask-vault-passPrompt for the vault password.
ansible-playbook deploy_app.yml --vault-password-file ~/.vault_pass.txtUse a file containing the vault password.
Configuration
Ansible’s behavior can be customized via ansible.cfg.
-
Default config file locations:
ANSIBLE_CONFIGenvironment variableansible.cfgin the current directory~/.ansible.cfg/etc/ansible/ansible.cfg
-
Common
ansible.cfgsettings:[defaults] inventory = /etc/ansible/hosts remote_user = ansible_user private_key_file = ~/.ssh/id_rsa host_key_checking = False retry_files_enabled = False deprecation_warnings = False [privilege_escalation] become = True become_method = sudo become_user = root
Common Patterns
-
Deploying an application and restarting a service only on change:
--- - name: Deploy application hosts: appservers become: yes tasks: - name: Copy application files copy: src: ./app/ dest: /opt/myapp/ notify: restart app service - name: Ensure app service is started service: name: myapp state: started handlers: - name: restart app service service: name: myapp state: restarted -
Using
with_itemsto loop over a list:--- - name: Install multiple packages hosts: webservers become: yes tasks: - name: Install packages apt:
{% raw %} name: "{{ item }}" {% endraw %} state: present loop: - nginx - python3 - ufw ```
- Using
with_fileglobto loop over files:--- - name: Deploy configuration files hosts: servers become: yes tasks: - name: Copy all .conf files from local dir copy:
{% raw %} src: configs/{{ item }} {% endraw %} dest: /etc/myapp/conf.d/ {% raw %} loop: "{{ query('fileglob', 'configs/*.conf') }}" {% endraw %} notify: restart myapp ```
-
Gathering facts and using them in tasks:
--- - name: Configure firewall based on OS hosts: all become: yes tasks: - name: Install firewalld on RedHat family yum: name: firewalld state: present when: ansible_os_family == "RedHat" - name: Install ufw on Debian family apt: name: ufw state: present when: ansible_os_family == "Debian" -
Using
blockandrescue/alwaysfor error handling:--- - name: Deploy with error handling hosts: webservers become: yes tasks: - block: - name: Attempt to deploy command: /opt/deploy_script.sh - name: Ensure service is running service: name: webapp state: started rescue: - name: Send notification on failure mail: to: admin@example.com
{% raw %} subject: "Deployment failed on {{ inventory_hostname }}" {% endraw %} body: "Deployment failed. Please investigate." always: - name: Clean up temporary files file: path: /tmp/deploy_artifact state: absent ```
- Templating configuration files with Jinja2:
templates/nginx.conf.j2:server {
{% raw %} listen {{ nginx_port }}; {% endraw %} {% raw %} server_name {{ server_name }}; {% endraw %} {% raw %} root {{ web_root }}; {% endraw %}
location / {
try_files $uri $uri/ =404;
}
}
Playbook task: yaml
—
- name: Configure Nginx
hosts: webservers
vars:
nginx_port: 80
server_name: example.com
web_root: /var/www/html
become: yes
tasks:
- name: Create Nginx config file
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/sites-available/default
notify: reload nginx
```
Gotchas
- Idempotency: While Ansible modules are designed to be idempotent (running them multiple times has the same effect as running them once), custom scripts or modules might not be. Always strive for idempotent tasks.
commandvs.shellmodule: Thecommandmodule does not process shell features like pipes (|), redirection (>), or variable expansion ($VAR). Use theshellmodule for these.- Privilege Escalation (
--become): Tasks often require root privileges. Remember to use--become(orbecome: yesin playbooks) and potentially--ask-become-pass(orbecome_ask_pass: yes) if the privilege escalation password is not configured or differs from the login password. - SSH Key Permissions: Ensure your SSH private key has strict permissions (e.g.,
chmod 600 ~/.ssh/id_rsa). Incorrect permissions can prevent Ansible from connecting. - Inventory Hostname Resolution: Ansible relies on the hostnames in your inventory to resolve and connect to hosts. Ensure these hostnames are resolvable via DNS or
/etc/hostson the control machine, or use IP addresses. registerandchanged_when: When a task usesregisterto save its output, subsequent tasks might be skipped if the registered task is determined to have "not changed" the system state. Usechanged_when: trueto force a "changed" status if needed, orignore_errors: yesto allow playbook execution to continue even if a task fails.- Module Defaults: Be aware of the default behavior of modules. For example, the
copymodule by default preserves file permissions and ownership. gather_facts: By default, Ansible gathers facts about managed hosts at the beginning of a playbook run. This can be time-consuming. If your playbook doesn’t need facts, disable it (gather_facts: no) for faster execution.- Order of Operations: Playbooks execute tasks sequentially within a play. Handlers are executed only once at the end of the play, after all tasks have run, and only if notified.
- Windows Remote Management (WinRM): For managing Windows hosts, ensure WinRM is correctly configured on the target machines and that Ansible is set up to use the
winrmconnection plugin. This often involves specific Python libraries (pywinrm) and certificate configurations.