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%.
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.
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.commandwhere a module exists - Hardcoded paths —
/home/ubuntuinstead 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 Code | Ollama (Local) | |
|---|---|---|
| Role generation | Excellent (reads project) | Good (no project context) |
| Jinja2 templates | Very accurate | Occasional syntax errors |
| Multi-file roles | Creates all files | Usually one file at a time |
| Existing role refactoring | Excellent | Cannot read existing files |
| Cost | Usage-based | Free |
| Privacy | Cloud-based | Fully 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