BLOG POSTS
Creating and Running Your First Ansible Playbook

Creating and Running Your First Ansible Playbook

Ansible is a powerful automation tool that has revolutionized the way system administrators and DevOps engineers manage infrastructure. By leveraging YAML-based playbooks, Ansible enables you to automate complex server configurations, application deployments, and system maintenance tasks across hundreds or thousands of servers simultaneously. This guide will walk you through creating your first Ansible playbook from scratch, covering everything from basic concepts to advanced troubleshooting techniques that will help you avoid common pitfalls and implement robust automation solutions.

How Ansible Playbooks Work

Ansible operates on a push-based model where your control machine connects to managed nodes via SSH and executes tasks remotely. Unlike other configuration management tools, Ansible doesn’t require agents on target systems, making it lightweight and easy to deploy.

A playbook is essentially a YAML file containing a series of plays, where each play targets specific hosts and defines tasks to be executed. The Ansible engine reads these playbooks and translates them into Python modules that run on target systems. The idempotent nature ensures that running the same playbook multiple times produces consistent results without unwanted side effects.

Key components of an Ansible playbook include:

  • Hosts: Target systems where tasks will be executed
  • Tasks: Individual actions like installing packages or copying files
  • Modules: Reusable units that perform specific operations
  • Variables: Dynamic values that customize playbook behavior
  • Handlers: Special tasks triggered by other tasks

Setting Up Your Ansible Environment

Before creating your first playbook, you need to install Ansible and configure your environment properly. The setup process varies depending on your operating system.

For Ubuntu/Debian systems:

sudo apt update
sudo apt install ansible sshpass
ansible --version

For RHEL/CentOS/Fedora:

sudo dnf install ansible sshpass
# or for older systems
sudo yum install epel-release
sudo yum install ansible sshpass

Next, create your project directory structure:

mkdir ~/ansible-project
cd ~/ansible-project
mkdir inventories playbooks roles group_vars host_vars

Create an inventory file that defines your target hosts:

# inventories/production
[webservers]
web1.example.com ansible_host=192.168.1.10
web2.example.com ansible_host=192.168.1.11

[databases]
db1.example.com ansible_host=192.168.1.20

[all:vars]
ansible_user=ubuntu
ansible_ssh_private_key_file=~/.ssh/id_rsa

Test connectivity to your managed nodes:

ansible all -i inventories/production -m ping

Creating Your First Playbook

Let’s create a comprehensive playbook that demonstrates common automation tasks. This example will install and configure a web server with security hardening.

---
- name: Configure Web Server
  hosts: webservers
  become: yes
  gather_facts: yes
  
  vars:
    nginx_port: 80
    ssl_port: 443
    server_name: "{{ inventory_hostname }}"
    
  tasks:
    - name: Update package cache
      apt:
        update_cache: yes
        cache_valid_time: 3600
      when: ansible_os_family == "Debian"
    
    - name: Install required packages
      package:
        name:
          - nginx
          - ufw
          - fail2ban
        state: present
    
    - name: Start and enable nginx
      systemd:
        name: nginx
        state: started
        enabled: yes
    
    - name: Configure firewall
      ufw:
        rule: allow
        port: "{{ item }}"
        proto: tcp
      loop:
        - "{{ nginx_port }}"
        - "{{ ssl_port }}"
        - "22"
      notify: reload ufw
    
    - name: Enable firewall
      ufw:
        state: enabled
        policy: deny
        direction: incoming
    
    - name: Create nginx configuration
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/sites-available/default
        backup: yes
      notify: restart nginx
    
    - name: Ensure nginx is running
      service:
        name: nginx
        state: started
      
  handlers:
    - name: restart nginx
      systemd:
        name: nginx
        state: restarted
        
    - name: reload ufw
      ufw:
        state: reloaded

Create a Jinja2 template for nginx configuration:

# templates/nginx.conf.j2
server {
    listen {{ nginx_port }};
    server_name {{ server_name }};
    
    root /var/www/html;
    index index.html index.htm;
    
    location / {
        try_files $uri $uri/ =404;
    }
    
    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header X-Content-Type-Options "nosniff" always;
    
    # Logging
    access_log /var/log/nginx/{{ server_name }}_access.log;
    error_log /var/log/nginx/{{ server_name }}_error.log;
}

Running Your Playbook

Execute your playbook using the ansible-playbook command with various options for different scenarios:

# Basic execution
ansible-playbook -i inventories/production playbooks/webserver.yml

# Dry run to test without making changes
ansible-playbook -i inventories/production playbooks/webserver.yml --check

# Run with increased verbosity for debugging
ansible-playbook -i inventories/production playbooks/webserver.yml -vvv

# Limit execution to specific hosts
ansible-playbook -i inventories/production playbooks/webserver.yml --limit web1.example.com

# Run specific tags only
ansible-playbook -i inventories/production playbooks/webserver.yml --tags "configuration"

For better organization, create an ansible.cfg file in your project root:

[defaults]
inventory = inventories/production
remote_user = ubuntu
private_key_file = ~/.ssh/id_rsa
host_key_checking = False
retry_files_enabled = False
stdout_callback = yaml

[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o UserKnownHostsFile=/dev/null
pipelining = True

Real-World Use Cases and Examples

Ansible playbooks shine in numerous practical scenarios. Here are some common use cases with implementation examples:

Database Deployment and Configuration:

---
- name: Deploy PostgreSQL Database
  hosts: databases
  become: yes
  
  vars:
    postgres_version: "13"
    db_name: "production_app"
    db_user: "app_user"
    
  tasks:
    - name: Install PostgreSQL
      package:
        name:
          - "postgresql-{{ postgres_version }}"
          - "postgresql-contrib-{{ postgres_version }}"
          - python3-psycopg2
        state: present
    
    - name: Configure PostgreSQL
      postgresql_db:
        name: "{{ db_name }}"
        state: present
      become_user: postgres
    
    - name: Create database user
      postgresql_user:
        name: "{{ db_user }}"
        password: "{{ vault_db_password }}"
        db: "{{ db_name }}"
        priv: ALL
      become_user: postgres

Application Deployment Pipeline:

---
- name: Deploy Node.js Application
  hosts: webservers
  become: yes
  
  vars:
    app_name: "my-nodejs-app"
    app_port: 3000
    node_version: "16.x"
    
  tasks:
    - name: Install Node.js repository
      shell: curl -fsSL https://deb.nodesource.com/setup_{{ node_version }} | sudo -E bash -
      
    - name: Install Node.js and PM2
      package:
        name:
          - nodejs
          - npm
        state: present
    
    - name: Install PM2 globally
      npm:
        name: pm2
        global: yes
    
    - name: Clone application repository
      git:
        repo: "https://github.com/company/{{ app_name }}.git"
        dest: "/opt/{{ app_name }}"
        version: master
      notify: restart application
    
    - name: Install dependencies
      npm:
        path: "/opt/{{ app_name }}"
        state: present
    
    - name: Start application with PM2
      shell: pm2 start /opt/{{ app_name }}/app.js --name {{ app_name }}
      ignore_errors: yes

Comparison with Alternative Tools

Understanding how Ansible compares to other configuration management tools helps you make informed decisions:

Feature Ansible Puppet Chef SaltStack
Architecture Agentless (SSH) Agent-based Agent-based Agent-based
Configuration Language YAML Puppet DSL Ruby DSL YAML/Jinja2
Learning Curve Low Medium High Medium
Execution Model Push Pull Pull Push/Pull
Performance (1000+ nodes) Good Excellent Excellent Excellent
Community Support Large Large Medium Medium

Ansible’s agentless architecture provides significant advantages for getting started quickly, but may face performance challenges at massive scale. For organizations managing fewer than 500 nodes, Ansible typically offers the best balance of simplicity and functionality.

Advanced Features and Best Practices

As you become more comfortable with basic playbooks, implementing advanced features will make your automation more robust and maintainable.

Using Ansible Vault for Sensitive Data:

# Create encrypted variable file
ansible-vault create group_vars/production/vault.yml

# Edit encrypted file
ansible-vault edit group_vars/production/vault.yml

# Run playbook with vault password
ansible-playbook playbook.yml --ask-vault-pass

Implementing Conditional Logic:

tasks:
  - name: Install Apache on RedHat systems
    yum:
      name: httpd
      state: present
    when: ansible_os_family == "RedHat"
  
  - name: Install Apache on Debian systems
    apt:
      name: apache2
      state: present
    when: ansible_os_family == "Debian"
  
  - name: Configure service based on environment
    template:
      src: "{{ item }}"
      dest: /etc/app/config.yml
    with_first_found:
      - "config_{{ environment }}.yml.j2"
      - "config_default.yml.j2"

Error Handling and Recovery:

tasks:
  - name: Attempt database connection
    postgresql_ping:
      db: "{{ database_name }}"
    register: db_connection
    ignore_errors: yes
    
  - name: Fallback to backup database
    set_fact:
      database_host: "{{ backup_db_host }}"
    when: db_connection.failed
    
  - name: Ensure critical service is running
    systemd:
      name: "{{ service_name }}"
      state: started
    retries: 3
    delay: 10
    until: service_result.state == "started"
    register: service_result

Common Issues and Troubleshooting

Even experienced administrators encounter challenges when working with Ansible. Here are the most frequent issues and their solutions:

SSH Connection Problems:

  • Host key verification failures – disable with host_key_checking = False in ansible.cfg
  • Permission denied errors – verify SSH key permissions are 600
  • Connection timeouts – increase timeout values in ansible.cfg
# Debug SSH connectivity
ansible all -m setup -vvv
ssh -vvv user@target-host

# Test with different connection methods
ansible all -m ping -c ssh
ansible all -m ping -c paramiko

Privilege Escalation Issues:

# Common sudo configurations
- name: Task requiring root privileges
  package:
    name: nginx
    state: present
  become: yes
  become_method: sudo
  become_user: root

# For systems with custom sudo configs
ansible_become_password: "{{ vault_sudo_password }}"
ansible_become_method: su

Performance Optimization:

Large-scale deployments often suffer from performance issues. These configurations significantly improve execution speed:

# ansible.cfg optimizations
[defaults]
forks = 20
host_key_checking = False
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts_cache
fact_caching_timeout = 86400

[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
pipelining = True
control_path = /tmp/ansible-ssh-%%h-%%p-%%r

Benchmark results show these optimizations can reduce playbook execution time by 40-60% on typical infrastructure deployments.

Module-Specific Troubleshooting:

# Debug failed tasks
- name: Install package with error handling
  package:
    name: "{{ package_name }}"
    state: present
  register: install_result
  failed_when: false
  
- name: Display installation details
  debug:
    var: install_result
  when: install_result.failed

# Validate file operations
- name: Copy configuration file
  copy:
    src: config.conf
    dest: /etc/app/config.conf
    validate: '/usr/bin/app --test-config %s'

Integration with CI/CD Pipelines

Modern development workflows benefit greatly from Ansible integration. Here’s how to incorporate playbooks into popular CI/CD platforms:

GitLab CI Integration:

# .gitlab-ci.yml
stages:
  - test
  - deploy

ansible-lint:
  stage: test
  script:
    - ansible-lint playbooks/

deploy-staging:
  stage: deploy
  script:
    - ansible-playbook -i inventories/staging playbooks/deploy.yml
  only:
    - develop

deploy-production:
  stage: deploy
  script:
    - ansible-playbook -i inventories/production playbooks/deploy.yml
  only:
    - master
  when: manual

Jenkins Pipeline Example:

pipeline {
    agent any
    
    stages {
        stage('Syntax Check') {
            steps {
                sh 'ansible-playbook --syntax-check playbooks/deploy.yml'
            }
        }
        
        stage('Deploy to Staging') {
            steps {
                ansiblePlaybook(
                    playbook: 'playbooks/deploy.yml',
                    inventory: 'inventories/staging',
                    extras: '--check'
                )
            }
        }
    }
}

Security Considerations and Hardening

Security should be a primary concern when implementing Ansible automation. Follow these practices to maintain a secure infrastructure:

  • Use Ansible Vault for all sensitive data including passwords, API keys, and certificates
  • Implement least-privilege access with dedicated service accounts
  • Regularly rotate SSH keys and vault passwords
  • Enable logging and monitoring for all playbook executions
  • Use dynamic inventories to avoid storing sensitive host information in version control
# Secure playbook structure
group_vars/
  all/
    vars.yml          # Non-sensitive variables
    vault.yml         # Encrypted sensitive data
  production/
    vars.yml
    vault.yml

# Example vault content
vault_database_password: !vault |
  $ANSIBLE_VAULT;1.1;AES256
  66633262313764623439643565363836616463643263306464326234653433626436363534366663
  ...

For additional security resources and best practices, consult the official Ansible Vault documentation and the Ansible best practices guide.

This comprehensive introduction to Ansible playbooks provides the foundation for automating your infrastructure management tasks. Start with simple playbooks and gradually incorporate advanced features as your requirements grow. The combination of Ansible’s simplicity and power makes it an invaluable tool for modern system administration and DevOps practices.



This article incorporates information and material from various online sources. We acknowledge and appreciate the work of all original authors, publishers, and websites. While every effort has been made to appropriately credit the source material, any unintentional oversight or omission does not constitute a copyright infringement. All trademarks, logos, and images mentioned are the property of their respective owners. If you believe that any content used in this article infringes upon your copyright, please contact us immediately for review and prompt action.

This article is intended for informational and educational purposes only and does not infringe on the rights of the copyright owners. If any copyrighted material has been used without proper credit or in violation of copyright laws, it is unintentional and we will rectify it promptly upon notification. Please note that the republishing, redistribution, or reproduction of part or all of the contents in any form is prohibited without express written permission from the author and website owner. For permissions or further inquiries, please contact us.

Leave a reply

Your email address will not be published. Required fields are marked