Terminal showing AI-generated Ansible playbook for server configuration automation
← All Articles
AI + DevOps

How to Use AI to Write Ansible Playbooks (Prompts That Actually Work)

Why Ansible Is Perfect for AI-Assisted Writing

Ansible playbooks are declarative, repetitive, and follow clear patterns. This makes them ideal for AI generation. Unlike application code where business logic varies wildly, Ansible tasks follow predictable structures — install packages, configure services, manage files, set permissions, restart handlers.

The challenge has always been that writing Ansible is tedious, not difficult. You know what the playbook should do. Writing out every task, handler, template, and variable definition just takes time.

AI collapses that time. Describe the desired state, get a working playbook back. After using this approach across dozens of client infrastructure projects, it has cut Ansible development time by roughly 70%.

Terminal showing Ansible playbook execution AI-generated Ansible playbooks follow the same patterns experienced engineers write — faster

Setting Up AI for Ansible Work

Two approaches work well:

Claude Code (recommended for complex roles): Reads your entire project structure — inventory, group_vars, existing roles — and generates playbooks that fit your conventions.

Ollama with a custom Modelfile (for quick tasks): Run locally with zero cost. Create an Ansible-specific model:

cat << 'EOF' > Modelfile-ansible
FROM llama3.1:8b

SYSTEM """You are an Ansible automation expert. When writing playbooks:
- Always use FQCN (fully qualified collection names)
- Use handlers for service restarts
- Make tasks idempotent
- Include become: true when needed
- Use variables for anything configurable
- Follow the role directory structure convention
- Add tags to every task block"""

PARAMETER temperature 0.2
EOF

ollama create ansible-ai -f Modelfile-ansible

Low temperature (0.2) is critical for Ansible — you want deterministic, predictable output, not creative variations.

Prompt Pattern 1: Server Hardening Playbook

The prompt:

Write an Ansible playbook that hardens Ubuntu 22.04:
- Disable root SSH login
- Change SSH port to 2222
- Enable key-only authentication
- Install and configure fail2ban (3 attempts, 1 hour ban)
- Configure UFW: allow 2222, 80, 443. Deny everything else
- Set up automatic security updates
- Disable unused services
- Configure sysctl for network hardening
Use handlers for service restarts. Tag everything.

What the AI generates:

---
- name: Harden Ubuntu 22.04 server
  hosts: all
  become: true

  vars:
    ssh_port: 2222
    fail2ban_max_retry: 3
    fail2ban_ban_time: 3600
    allowed_ports:
      - "{{ ssh_port }}"
      - "80"
      - "443"

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

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

    - name: Reload UFW
      community.general.ufw:
        state: reloaded

  tasks:
    - name: SSH hardening
      tags: [ssh]
      block:
        - name: Set SSH port
          ansible.builtin.lineinfile:
            path: /etc/ssh/sshd_config
            regexp: '^#?Port'
            line: "Port {{ ssh_port }}"
          notify: Restart SSH

        - name: Disable root login
          ansible.builtin.lineinfile:
            path: /etc/ssh/sshd_config
            regexp: '^#?PermitRootLogin'
            line: 'PermitRootLogin no'
          notify: Restart SSH

        - name: Disable password authentication
          ansible.builtin.lineinfile:
            path: /etc/ssh/sshd_config
            regexp: '^#?PasswordAuthentication'
            line: 'PasswordAuthentication no'
          notify: Restart SSH

    - name: Install and configure fail2ban
      tags: [fail2ban]
      block:
        - name: Install fail2ban
          ansible.builtin.apt:
            name: fail2ban
            state: present
            update_cache: true

        - name: Configure fail2ban jail
          ansible.builtin.template:
            src: jail.local.j2
            dest: /etc/fail2ban/jail.local
            owner: root
            group: root
            mode: '0644'
          notify: Restart fail2ban

    - name: Configure UFW firewall
      tags: [firewall]
      block:
        - name: Set UFW default deny incoming
          community.general.ufw:
            default: deny
            direction: incoming

        - name: Allow required ports
          community.general.ufw:
            rule: allow
            port: "{{ item }}"
            proto: tcp
          loop: "{{ allowed_ports }}"

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

This is production-ready. FQCN module names, handlers for restarts, variables for configurable values, tags on every block. The pattern matches exactly what an experienced Ansible engineer writes.

Prompt Pattern 2: Complete Role Structure

The prompt:

Create an Ansible role called "nginx_proxy" that:
- Installs Nginx
- Deploys a reverse proxy config from a Jinja2 template
- Configures SSL with provided certificate paths
- Sets up log rotation (7 days, compressed)
- Includes health check task
- Has separate defaults, handlers, tasks, templates

Variables needed: backend_host, backend_port, domain_name, 
ssl_cert_path, ssl_key_path

The AI generates the complete role directory:

roles/nginx_proxy/defaults/main.yml:

---
nginx_backend_host: "127.0.0.1"
nginx_backend_port: 8080
nginx_domain_name: "example.com"
nginx_ssl_cert_path: "/etc/ssl/certs/{{ nginx_domain_name }}.pem"
nginx_ssl_key_path: "/etc/ssl/private/{{ nginx_domain_name }}.key"
nginx_log_retention_days: 7
nginx_worker_connections: 1024

roles/nginx_proxy/tasks/main.yml:

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

- name: Deploy reverse proxy configuration
  ansible.builtin.template:
    src: reverse-proxy.conf.j2
    dest: "/etc/nginx/sites-available/{{ nginx_domain_name }}.conf"
    owner: root
    group: root
    mode: '0644'
  notify: Reload Nginx
  tags: [nginx, config]

- name: Enable site
  ansible.builtin.file:
    src: "/etc/nginx/sites-available/{{ nginx_domain_name }}.conf"
    dest: "/etc/nginx/sites-enabled/{{ nginx_domain_name }}.conf"
    state: link
  notify: Reload Nginx
  tags: [nginx, config]

- name: Configure log rotation
  ansible.builtin.template:
    src: logrotate.j2
    dest: /etc/logrotate.d/nginx
    owner: root
    group: root
    mode: '0644'
  tags: [nginx, logging]

- name: Validate Nginx configuration
  ansible.builtin.command: nginx -t
  changed_when: false
  tags: [nginx, validate]

- name: Ensure Nginx is running
  ansible.builtin.service:
    name: nginx
    state: started
    enabled: true
  tags: [nginx]

roles/nginx_proxy/templates/reverse-proxy.conf.j2:

server {
    listen 443 ssl;
    server_name {{ nginx_domain_name }};

    ssl_certificate {{ nginx_ssl_cert_path }};
    ssl_certificate_key {{ nginx_ssl_key_path }};
    ssl_protocols TLSv1.2 TLSv1.3;

    access_log /var/log/nginx/{{ nginx_domain_name }}_access.log;
    error_log /var/log/nginx/{{ nginx_domain_name }}_error.log;

    location / {
        proxy_pass http://{{ nginx_backend_host }}:{{ nginx_backend_port }};
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /health {
        return 200 'OK';
        add_header Content-Type text/plain;
    }
}

server {
    listen 80;
    server_name {{ nginx_domain_name }};
    return 301 https://$server_name$request_uri;
}

Complete role in seconds. Every file follows the standard Ansible role convention.

Prompt Pattern 3: Docker Deployment Role

Write an Ansible role to deploy a Docker Compose application:
- Install Docker CE and docker-compose plugin
- Create application user (non-root)
- Deploy docker-compose.yml from template
- Deploy .env file from vault-encrypted variables
- Pull images and start services
- Set up container log rotation
- Add health check that verifies containers are running

The AI handles Docker installation, user management, template deployment, and health checks — all with proper idempotency and handlers.

Server infrastructure representing automated deployment AI-generated Ansible roles automate server setup in minutes instead of hours

Prompts That Produce Bad Ansible

Too vague:

# Bad: "Set up a web server with Ansible"
# No specifics = generic output with assumptions

Missing OS context:

# Bad: "Install PostgreSQL with Ansible"
# Is this Ubuntu? RHEL? Amazon Linux? Package names differ

No variable requirements:

# Bad: "Configure Nginx as a reverse proxy"
# Without specifying what should be configurable, AI hardcodes everything

What works: Specify the OS, the exact configuration you need, which values should be variables, and how services should be managed (handlers vs direct restart).

The Review Checklist

AI-generated Ansible should pass these checks before running on production:

1. Syntax Check

ansible-lint playbook.yml
ansible-playbook playbook.yml --syntax-check

2. Dry Run

ansible-playbook playbook.yml --check --diff

3. Common AI Mistakes to Watch For

  • Missing become: true — tasks that need root without it
  • Non-idempotent commands — using ansible.builtin.command where a module exists
  • Hardcoded paths/home/ubuntu instead of a variable
  • Missing handlers — config changes without service restarts
  • Wrong package names — mixing Ubuntu and RHEL package names
  • Template syntax errors — unbalanced Jinja2 braces

4. Test on Staging First

Always run AI-generated playbooks on a staging environment first. Use Molecule for automated testing:

molecule test

Claude Code vs Ollama for Ansible

Claude CodeOllama (Local)
Role generationExcellent (reads project)Good (no project context)
Jinja2 templatesVery accurateOccasional syntax errors
Multi-file rolesCreates all filesUsually one file at a time
Existing role refactoringExcellentCannot read existing files
CostUsage-basedFree
PrivacyCloud-basedFully local

Use Claude Code when generating complete roles that must integrate with existing playbooks. Use Ollama for quick standalone playbooks and when data privacy is critical.

Key Takeaways

  • Ansible’s declarative, pattern-based nature makes it ideal for AI generation
  • Specific prompts with OS, variable requirements, and service management details produce the best output
  • AI generates 70% of the final playbook — review for idempotency, FQCN, and handler correctness
  • Low temperature (0.2) in Ollama produces more reliable Ansible than creative settings
  • Claude Code’s project context awareness gives it an edge for role generation
  • Always run ansible-lint and test on staging before production
  • Complete role structures (tasks, handlers, defaults, templates) can be generated in one prompt

FAQ

Can AI write Ansible Vault encrypted files?

AI can generate the structure and variable definitions, but it cannot encrypt them. Generate the playbook with placeholder variables, then encrypt the values yourself with ansible-vault encrypt_string. Never put real secrets in AI prompts.

How accurate are AI-generated Jinja2 templates?

Claude Code handles Jinja2 templates well — proper variable syntax, conditionals, loops, and filters. Local models occasionally miss closing braces or use incorrect filter syntax. Always validate templates with ansible-playbook --syntax-check before running.

Should I use AI for complex Ansible roles with multiple dependencies?

Yes, but break it into steps. Generate each role separately, then write the top-level playbook that calls them. AI handles individual roles well but struggles to maintain consistency across 5+ interdependent roles in a single prompt.

Does this work with Ansible AWX or Tower?

Yes. AI-generated playbooks work identically in AWX/Tower. The generated code is standard Ansible — nothing AI-specific. Push the playbooks to your git repo and AWX pulls them as usual.

Which AI model is best for Ansible specifically?

Claude Code produces the most accurate Ansible output because of its project context awareness. For local models, Llama 3.1 8B with the custom Modelfile described above is the best balance. DeepSeek R1 is better for complex conditional logic but slower.

Conclusion

AI does not replace Ansible expertise — it eliminates the tedious parts. You still need to know what the playbook should do, how services interact, and what the correct configuration looks like. But translating that knowledge into YAML tasks, handlers, and templates is now a 5-minute task instead of an hour.

Start with a simple playbook — server hardening or package installation. Review the output carefully. Once you trust the patterns, move to full role generation. The time savings compound with every playbook you do not have to write from scratch.

Need help automating your infrastructure with Ansible? View our consulting services

Read next: How to Use AI Tools in Real Infrastructure Work

Written by
SysOpX
Battle-tested DevOps & AWS engineering guides
Need DevOps help? →