Ansible Sandbox Documentation

Explore the playbooks, understand the infrastructure, and learn best practices for using the interactive Ansible Sandbox.

Introduction

Welcome to the documentation for the ArgoBox Ansible Sandbox. This interactive environment allows you to safely experiment with Ansible playbooks designed to manage various aspects of a modern infrastructure setup, including web servers, containerized applications, and Kubernetes clusters.

This documentation provides details on the available playbooks, the underlying infrastructure design of the sandbox environment, and best practices for writing and testing your own automation scripts.

Purpose

The primary goal of the sandbox is to provide a hands-on learning experience for Ansible in a pre-configured, safe environment that mirrors real-world scenarios.

Sandbox Overview

The Ansible Sandbox provides you with temporary access to a set of virtual machines (VMs) where you can execute pre-defined Ansible playbooks or even upload and run your own. The environment is reset periodically to ensure a clean state for each session.

Key Features

  • Isolated environment with multiple target VMs.
  • Pre-loaded collection of common Ansible roles and playbooks.
  • Ability to select and run specific playbooks via a web interface.
  • Real-time output streaming of playbook execution.
  • Network access between sandbox VMs for testing multi-tier applications.
  • Environment reset functionality.
Launch Ansible Sandbox

Infrastructure Design

The sandbox environment consists of several virtual machines managed by the main ArgoBox infrastructure, typically running on Proxmox VE. These VMs are provisioned specifically for sandbox use and are isolated from the core production services.

Components

Component Description OS Purpose
Control Node The VM where Ansible commands are executed from. Debian 12 Runs Ansible engine, hosts playbooks.
Web Server Node Target VM for web server playbooks (Nginx/Apache). Ubuntu 22.04 Simulates a typical web server.
Database Node Target VM for database playbooks (PostgreSQL/MySQL). Debian 12 Simulates a database server.
Docker Node Target VM with Docker installed for container playbooks. Ubuntu 22.04 Runs Docker containers via Compose.

Networking

All sandbox VMs are placed on a dedicated, isolated VLAN with controlled access to simulate a realistic network environment.

Available Playbooks

The following playbooks are available for execution within the sandbox environment. Each playbook demonstrates different Ansible concepts and common infrastructure tasks.

Web Server Deployment (Nginx)

Installs and configures the Nginx web server on the 'Web Server Node'. Includes setting up a basic virtual host and ensuring the service is running.

---
- name: Deploy Nginx Web Server
  hosts: web_server_node # Corresponds to inventory group
  become: yes # Execute tasks with sudo
  tasks:
    - name: Update apt cache
      ansible.builtin.apt:
        update_cache: yes
      tags: [install]

    - name: Install Nginx
      ansible.builtin.apt:
        name: nginx
        state: present
      tags: [install]

    - name: Ensure Nginx service is started and enabled
      ansible.builtin.service:
        name: nginx
        state: started
        enabled: yes
      tags: [configure]

    - name: Deploy basic index page (template example)
      ansible.builtin.template:
        src: templates/index.html.j2 # Example template path
        dest: /var/www/html/index.nginx-debian.html # Default Nginx path
        owner: root
        group: root
        mode: '0644'
      tags: [configure]

Docker Compose Stack (Example: Portainer)

Deploys a simple Docker Compose application (e.g., Portainer) on the 'Docker Node'. Requires Docker and Docker Compose to be pre-installed on the target.

K3s Kubernetes Cluster (Basic Setup)

Installs a single-node K3s cluster on the 'Control Node'. Note: This is a simplified setup for demonstration.

---
- name: Install K3s Single Node Cluster
  hosts: control_node # Install on the control node itself for demo
  become: yes
  tasks:
    - name: Download K3s installation script
      ansible.builtin.get_url:
        url: https://get.k3s.io
        dest: /tmp/k3s-install.sh
        mode: '0755'

    - name: Execute K3s installation script
      ansible.builtin.command:
        cmd: /tmp/k3s-install.sh
        creates: /usr/local/bin/k3s # Avoid re-running if k3s exists
      register: k3s_install_result
      changed_when: k3s_install_result.rc == 0

    - name: Ensure K3s service is started
      ansible.builtin.service:
        name: k3s
        state: started
        enabled: yes

    - name: Wait for Kubeconfig to be available
      ansible.builtin.wait_for:
        path: /etc/rancher/k3s/k3s.yaml
        timeout: 60

    - name: Read Kubeconfig file
      ansible.builtin.slurp:
        src: /etc/rancher/k3s/k3s.yaml
      register: k3s_kubeconfig

    - name: Display Kubeconfig hint (for manual use)
      ansible.builtin.debug:
        msg: "K3s installed. Use 'sudo k3s kubectl get nodes' or copy Kubeconfig."

Simplified Setup

This playbook installs a basic single-node K3s cluster. Production setups require more complex configuration, multiple nodes, and security considerations.

LAMP Stack Installation

Installs Apache, MySQL (MariaDB), and PHP on the 'Web Server Node'. This playbook sets up a complete web application environment with secure defaults.

---
- name: Deploy LAMP Stack
  hosts: web_server_node
  become: yes
  vars:
    mysql_root_password: "{{ vault_mysql_root_password }}"
    php_version: "8.2"
    document_root: "/var/www/html"

  tasks:
    - name: Update apt cache
      ansible.builtin.apt:
        update_cache: yes
        cache_valid_time: 3600

    - name: Install Apache web server
      ansible.builtin.apt:
        name: [apache2, apache2-utils]
        state: present

    - name: Install MariaDB server
      ansible.builtin.apt:
        name: [mariadb-server, mariadb-client, python3-mysqldb]
        state: present

    - name: Install PHP and modules
      ansible.builtin.apt:
        name:
          - "php{{ php_version }}"
          - "php{{ php_version }}-mysql"
          - "php{{ php_version }}-curl"
          - libapache2-mod-php
        state: present

    - name: Start and enable Apache
      ansible.builtin.service:
        name: apache2
        state: started
        enabled: yes

  handlers:
    - name: Restart Apache
      ansible.builtin.service:
        name: apache2
        state: restarted

Security Note

Store sensitive data like database passwords in Ansible Vault using ansible-vault encrypt_string.

Basic Security Hardening

Applies essential security measures including UFW firewall, fail2ban for intrusion prevention, and SSH hardening.

---
- name: Security Hardening Playbook
  hosts: all
  become: yes
  vars:
    ssh_port: 22
    allowed_ssh_users: [deploy, admin]
    fail2ban_maxretry: 5

  tasks:
    # UFW Firewall
    - name: Install UFW firewall
      ansible.builtin.apt:
        name: ufw
        state: present

    - name: Set UFW default policies
      community.general.ufw:
        direction: "{{ item.direction }}"
        policy: "{{ item.policy }}"
      loop:
        - { direction: 'incoming', policy: 'deny' }
        - { direction: 'outgoing', policy: 'allow' }

    - name: Allow SSH through UFW
      community.general.ufw:
        rule: allow
        port: "{{ ssh_port }}"
        proto: tcp

    - name: Enable UFW
      community.general.ufw:
        state: enabled

    # Fail2ban
    - name: Install fail2ban
      ansible.builtin.apt:
        name: fail2ban
        state: present

    - name: Configure fail2ban jail
      ansible.builtin.template:
        src: jail.local.j2
        dest: /etc/fail2ban/jail.local
      notify: Restart fail2ban

    # SSH Hardening
    - name: Harden SSH configuration
      ansible.builtin.lineinfile:
        path: /etc/ssh/sshd_config
        regexp: "{{ item.regexp }}"
        line: "{{ item.line }}"
      loop:
        - { regexp: '^#?PermitRootLogin', line: 'PermitRootLogin no' }
        - { regexp: '^#?PasswordAuthentication', line: 'PasswordAuthentication no' }
      notify: Restart SSH

  handlers:
    - name: Restart fail2ban
      ansible.builtin.service:
        name: fail2ban
        state: restarted

    - name: Restart SSH
      ansible.builtin.service:
        name: sshd
        state: restarted

Warning

Ensure you have SSH key access configured before disabling password authentication.

Advanced Topics

Using Custom Roles

Ansible Roles provide a way to organize playbooks into reusable components. A role groups related tasks, handlers, templates, and variables into a standard directory structure.

Role Directory Structure

roles/
└── webserver/
    ├── tasks/
    │   └── main.yml      # Main task list
    ├── handlers/
    │   └── main.yml      # Handler definitions
    ├── templates/
    │   └── vhost.conf.j2 # Jinja2 templates
    ├── files/
    │   └── ssl.crt       # Static files
    ├── vars/
    │   └── main.yml      # Role variables
    ├── defaults/
    │   └── main.yml      # Default variables
    └── meta/
        └── main.yml      # Role metadata & dependencies

Example Role Tasks

# roles/webserver/tasks/main.yml
---
- name: Install nginx
  ansible.builtin.apt:
    name: nginx
    state: present
  tags: [install]

- name: Deploy virtual host config
  ansible.builtin.template:
    src: vhost.conf.j2
    dest: "/etc/nginx/sites-available/{{ domain }}.conf"
  notify: Reload nginx
  tags: [configure]

- name: Enable virtual host
  ansible.builtin.file:
    src: "/etc/nginx/sites-available/{{ domain }}.conf"
    dest: "/etc/nginx/sites-enabled/{{ domain }}.conf"
    state: link
  notify: Reload nginx

Using Roles in Playbooks

# site.yml
---
- hosts: webservers
  roles:
    - role: webserver
      vars:
        domain: example.com
    - role: ssl_certificates
    - role: monitoring

Variables and Inventory Management

Proper variable and inventory management is crucial for maintaining multiple environments. Here's how to structure your configuration for development, staging, and production.

Directory Layout

inventory/
├── production/
│   ├── hosts.yml
│   └── group_vars/
│       ├── all.yml
│       └── webservers.yml
├── staging/
│   ├── hosts.yml
│   └── group_vars/
│       └── all.yml
└── development/
    └── hosts.yml

Inventory File Example

# inventory/production/hosts.yml
---
all:
  children:
    webservers:
      hosts:
        web1.example.com:
          ansible_host: 10.0.1.10
        web2.example.com:
          ansible_host: 10.0.1.11
    databases:
      hosts:
        db1.example.com:
          ansible_host: 10.0.2.10

Group Variables

# inventory/production/group_vars/webservers.yml
---
nginx_worker_processes: 4
nginx_worker_connections: 2048
ssl_certificate_path: /etc/ssl/certs/prod.crt

# Sensitive data - encrypted with ansible-vault
db_password: !vault |
  $ANSIBLE_VAULT;1.1;AES256
  63643136356536...

Variable Precedence (lowest to highest)

  • Role defaults (defaults/main.yml)
  • Inventory group_vars
  • Inventory host_vars
  • Playbook group_vars/host_vars
  • Role vars (vars/main.yml)
  • Task vars and block vars
  • Extra vars (-e "var=value") - highest priority

Ansible Best Practices

Following these practices ensures your playbooks are maintainable, reliable, and secure.

1. Always Name Your Tasks

# Good - descriptive task names
- name: Install nginx web server
  ansible.builtin.apt:
    name: nginx
    state: present

# Bad - no task name
- apt:
    name: nginx

2. Use FQCN (Fully Qualified Collection Names)

# Good - explicit and future-proof
- ansible.builtin.apt:
    name: nginx

# Avoid - ambiguous
- apt:
    name: nginx

3. Use Handlers for Service Restarts

tasks:
  - name: Update nginx config
    ansible.builtin.template:
      src: nginx.conf.j2
      dest: /etc/nginx/nginx.conf
    notify: Reload nginx  # Only runs if changed

handlers:
  - name: Reload nginx
    ansible.builtin.service:
      name: nginx
      state: reloaded

4. Secure Secrets with Ansible Vault

# Encrypt a string
ansible-vault encrypt_string 'supersecret' --name 'db_password'

# Encrypt a file
ansible-vault encrypt secrets.yml

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

5. Use Tags for Selective Execution

- name: Install packages
  ansible.builtin.apt:
    name: "{{ item }}"
  loop: "{{ packages }}"
  tags: [install, packages]

# Run only install tasks
ansible-playbook site.yml --tags install

6. Ensure Idempotency

Tasks should be safe to run multiple times without changing the result if the desired state is already achieved.

7. Use Block for Error Handling

- name: Handle deployment with rollback
  block:
    - name: Deploy application
      ansible.builtin.copy:
        src: app/
        dest: /var/www/app/

    - name: Restart service
      ansible.builtin.service:
        name: app
        state: restarted

  rescue:
    - name: Rollback on failure
      ansible.builtin.copy:
        src: backup/
        dest: /var/www/app/

Reference

Common CLI Commands

A quick reference for frequently used Ansible CLI commands.

CommandDescription
ansible --versionCheck Ansible version.
ansible-inventory --list -i inventory.ymlList inventory hosts and groups.
ansible all -m ping -i inventory.ymlPing all hosts in inventory.
ansible-playbook playbook.yml -i inventory.ymlRun a playbook.
ansible-playbook playbook.yml --checkPerform a dry run of a playbook.
ansible-playbook playbook.yml --limit web_serversRun playbook only on specific hosts/groups.
ansible-vault create secrets.ymlCreate an encrypted vault file.
ansible-playbook playbook.yml --ask-vault-passRun playbook asking for vault password.

Troubleshooting Tips

Common issues and how to resolve them when working with the sandbox or Ansible in general.

  • Connection Errors: Ensure SSH keys are correctly configured and target VMs are reachable. Check firewall rules.
  • Permission Denied: Use `become: yes` for tasks requiring root privileges. Verify sudo configuration on target nodes.
  • Module Not Found: Ensure required Ansible collections or Python libraries are installed on the control node.
  • Idempotency Issues: Review tasks to ensure they only make changes when necessary (e.g., use `creates` argument for command/shell modules).
  • Variable Undefined: Check variable definitions, scope (host_vars, group_vars), and precedence. Use `-v` or `-vvv` for verbose output.