Initial commit
This commit is contained in:
162
skills/ansible/SKILL.md
Normal file
162
skills/ansible/SKILL.md
Normal file
@@ -0,0 +1,162 @@
|
||||
---
|
||||
name: ansible
|
||||
description: |
|
||||
Ansible automation reference for playbooks, roles, inventory, variables, and modules.
|
||||
Includes Proxmox VE and Docker integration via community.general and community.docker collections.
|
||||
Use when writing playbooks, troubleshooting Ansible runs, or designing automation workflows.
|
||||
Triggers: ansible, playbook, inventory, role, task, handler, vars, jinja2, galaxy, proxmox_kvm, proxmox_lxc, docker_container, docker_compose.
|
||||
---
|
||||
|
||||
# Ansible Skill
|
||||
|
||||
Ansible automation reference for configuration management and application deployment.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
```bash
|
||||
# Test connectivity
|
||||
ansible all -m ping
|
||||
ansible <group> -m ping
|
||||
|
||||
# Run playbook
|
||||
ansible-playbook playbook.yml
|
||||
ansible-playbook playbook.yml -l <host> # Limit to host
|
||||
ansible-playbook playbook.yml --check # Dry-run
|
||||
ansible-playbook playbook.yml -vvv # Verbose
|
||||
|
||||
# Tags
|
||||
ansible-playbook playbook.yml --tags "deploy"
|
||||
ansible-playbook playbook.yml --skip-tags "backup"
|
||||
ansible-playbook playbook.yml --list-tags
|
||||
|
||||
# Variables
|
||||
ansible-playbook playbook.yml -e "var=value"
|
||||
ansible-playbook playbook.yml -e "@vars.yml"
|
||||
|
||||
# Ad-hoc commands
|
||||
ansible <group> -m shell -a "command"
|
||||
ansible <group> -m copy -a "src=file dest=/path"
|
||||
ansible <group> -m apt -a "name=package state=present"
|
||||
|
||||
# Galaxy
|
||||
ansible-galaxy collection install -r requirements.yml
|
||||
ansible-galaxy role install <role>
|
||||
```
|
||||
|
||||
## Reference Files
|
||||
|
||||
Load on-demand based on task:
|
||||
|
||||
| Topic | File | When to Load |
|
||||
|-------|------|--------------|
|
||||
| Playbook Structure | [playbooks.md](references/playbooks.md) | Writing playbooks |
|
||||
| Inventory | [inventory.md](references/inventory.md) | Host/group configuration |
|
||||
| Variables | [variables.md](references/variables.md) | Variable precedence, facts |
|
||||
| Modules | [modules.md](references/modules.md) | Common module reference |
|
||||
| Troubleshooting | [troubleshooting.md](references/troubleshooting.md) | Common errors, debugging |
|
||||
|
||||
### Proxmox Integration
|
||||
|
||||
| Topic | File | When to Load |
|
||||
|-------|------|--------------|
|
||||
| Proxmox Modules | [proxmox/modules.md](references/proxmox/modules.md) | VM/LXC management via API |
|
||||
| Proxmox Auth | [proxmox/authentication.md](references/proxmox/authentication.md) | API tokens, credentials |
|
||||
| Proxmox Gotchas | [proxmox/gotchas.md](references/proxmox/gotchas.md) | Common issues, workarounds |
|
||||
| Dynamic Inventory | [proxmox/dynamic-inventory.md](references/proxmox/dynamic-inventory.md) | Auto-discover VMs/containers |
|
||||
|
||||
### Docker Integration
|
||||
|
||||
| Topic | File | When to Load |
|
||||
|-------|------|--------------|
|
||||
| Docker Deployment | [docker/deployment.md](references/docker/deployment.md) | Containers, images, networks, volumes |
|
||||
| Compose Patterns | [docker/compose-patterns.md](references/docker/compose-patterns.md) | Roles, templates, multi-service stacks |
|
||||
| Docker Troubleshooting | [docker/troubleshooting.md](references/docker/troubleshooting.md) | Common errors, debugging |
|
||||
|
||||
## Playbook Quick Reference
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Deploy application
|
||||
hosts: webservers
|
||||
become: true
|
||||
vars:
|
||||
app_port: 8080
|
||||
|
||||
pre_tasks:
|
||||
- name: Validate requirements
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- app_secret is defined
|
||||
|
||||
tasks:
|
||||
- name: Install packages
|
||||
ansible.builtin.apt:
|
||||
name: "{{ item }}"
|
||||
state: present
|
||||
loop:
|
||||
- nginx
|
||||
- python3
|
||||
|
||||
- name: Deploy config
|
||||
ansible.builtin.template:
|
||||
src: app.conf.j2
|
||||
dest: /etc/app/app.conf
|
||||
notify: Restart app
|
||||
|
||||
handlers:
|
||||
- name: Restart app
|
||||
ansible.builtin.service:
|
||||
name: app
|
||||
state: restarted
|
||||
|
||||
post_tasks:
|
||||
- name: Verify deployment
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:{{ app_port }}/health"
|
||||
```
|
||||
|
||||
## Variable Precedence (High to Low)
|
||||
|
||||
1. Extra vars (`-e "var=value"`)
|
||||
2. Task vars
|
||||
3. Block vars
|
||||
4. Role/include vars
|
||||
5. Play vars
|
||||
6. Host facts
|
||||
7. host_vars/
|
||||
8. group_vars/
|
||||
9. Role defaults
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```text
|
||||
ansible/
|
||||
├── ansible.cfg # Configuration
|
||||
├── inventory/
|
||||
│ └── hosts.yml # Inventory
|
||||
├── group_vars/
|
||||
│ ├── all.yml # All hosts
|
||||
│ └── webservers.yml # Group-specific
|
||||
├── host_vars/
|
||||
│ └── server1.yml # Host-specific
|
||||
├── roles/
|
||||
│ └── app/
|
||||
│ ├── tasks/
|
||||
│ ├── handlers/
|
||||
│ ├── templates/
|
||||
│ ├── files/
|
||||
│ └── defaults/
|
||||
├── playbooks/
|
||||
│ └── deploy.yml
|
||||
├── templates/
|
||||
│ └── config.j2
|
||||
└── requirements.yml # Galaxy dependencies
|
||||
```
|
||||
|
||||
## Idempotency Checklist
|
||||
|
||||
- [ ] Tasks produce same result on repeated runs
|
||||
- [ ] No `changed_when: true` unless necessary
|
||||
- [ ] Use `state: present/absent` not `shell` commands
|
||||
- [ ] Check mode (`--check`) shows accurate changes
|
||||
- [ ] Second run shows all "ok" (no changes)
|
||||
294
skills/ansible/references/docker/compose-patterns.md
Normal file
294
skills/ansible/references/docker/compose-patterns.md
Normal file
@@ -0,0 +1,294 @@
|
||||
# Ansible Docker Compose Patterns
|
||||
|
||||
Common patterns for managing Docker Compose stacks with Ansible.
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
roles/
|
||||
└── docker_app/
|
||||
├── tasks/
|
||||
│ └── main.yml
|
||||
├── templates/
|
||||
│ ├── docker-compose.yml.j2
|
||||
│ └── .env.j2
|
||||
├── defaults/
|
||||
│ └── main.yml
|
||||
└── handlers/
|
||||
└── main.yml
|
||||
```
|
||||
|
||||
## Role Template
|
||||
|
||||
### defaults/main.yml
|
||||
|
||||
```yaml
|
||||
app_name: myapp
|
||||
app_version: latest
|
||||
app_port: 8080
|
||||
app_data_dir: "/opt/{{ app_name }}"
|
||||
|
||||
# Compose settings
|
||||
compose_pull: always
|
||||
compose_recreate: auto # auto, always, never
|
||||
|
||||
# Resource limits
|
||||
app_memory_limit: 512M
|
||||
app_cpu_limit: 1.0
|
||||
```
|
||||
|
||||
### templates/docker-compose.yml.j2
|
||||
|
||||
```yaml
|
||||
name: {{ app_name }}
|
||||
|
||||
services:
|
||||
app:
|
||||
image: {{ app_image }}:{{ app_version }}
|
||||
container_name: {{ app_name }}
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "{{ app_port }}:{{ app_internal_port | default(app_port) }}"
|
||||
volumes:
|
||||
- {{ app_data_dir }}/data:/app/data
|
||||
{% if app_config_file is defined %}
|
||||
- {{ app_data_dir }}/config:/app/config:ro
|
||||
{% endif %}
|
||||
environment:
|
||||
TZ: {{ timezone | default('UTC') }}
|
||||
{% for key, value in app_env.items() %}
|
||||
{{ key }}: "{{ value }}"
|
||||
{% endfor %}
|
||||
{% if app_memory_limit is defined or app_cpu_limit is defined %}
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
{% if app_memory_limit is defined %}
|
||||
memory: {{ app_memory_limit }}
|
||||
{% endif %}
|
||||
{% if app_cpu_limit is defined %}
|
||||
cpus: '{{ app_cpu_limit }}'
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:{{ app_internal_port | default(app_port) }}/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
networks:
|
||||
- {{ app_network | default('default') }}
|
||||
|
||||
{% if app_network is defined %}
|
||||
networks:
|
||||
{{ app_network }}:
|
||||
external: true
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
### tasks/main.yml
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Create application directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ app_data_dir }}"
|
||||
state: directory
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "{{ ansible_user }}"
|
||||
mode: '0755'
|
||||
|
||||
- name: Create data directories
|
||||
ansible.builtin.file:
|
||||
path: "{{ app_data_dir }}/{{ item }}"
|
||||
state: directory
|
||||
owner: "{{ ansible_user }}"
|
||||
mode: '0755'
|
||||
loop:
|
||||
- data
|
||||
- config
|
||||
|
||||
- name: Deploy compose file
|
||||
ansible.builtin.template:
|
||||
src: docker-compose.yml.j2
|
||||
dest: "{{ app_data_dir }}/docker-compose.yml"
|
||||
owner: "{{ ansible_user }}"
|
||||
mode: '0644'
|
||||
notify: Redeploy stack
|
||||
|
||||
- name: Deploy environment file
|
||||
ansible.builtin.template:
|
||||
src: .env.j2
|
||||
dest: "{{ app_data_dir }}/.env"
|
||||
owner: "{{ ansible_user }}"
|
||||
mode: '0600'
|
||||
notify: Redeploy stack
|
||||
when: app_secrets is defined
|
||||
|
||||
- name: Ensure stack is running
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ app_data_dir }}"
|
||||
state: present
|
||||
pull: "{{ compose_pull }}"
|
||||
recreate: "{{ compose_recreate }}"
|
||||
register: compose_result
|
||||
|
||||
- name: Show deployment result
|
||||
ansible.builtin.debug:
|
||||
msg: "Deployed {{ compose_result.containers | length }} containers"
|
||||
when: compose_result is changed
|
||||
```
|
||||
|
||||
### handlers/main.yml
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Redeploy stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ app_data_dir }}"
|
||||
state: present
|
||||
pull: always
|
||||
recreate: always
|
||||
```
|
||||
|
||||
## Multi-Service Stack
|
||||
|
||||
### templates/docker-compose.yml.j2 (full stack)
|
||||
|
||||
```yaml
|
||||
name: {{ stack_name }}
|
||||
|
||||
services:
|
||||
app:
|
||||
image: {{ app_image }}:{{ app_version }}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_started
|
||||
environment:
|
||||
DATABASE_URL: "postgres://{{ db_user }}:{{ db_password }}@db:5432/{{ db_name }}"
|
||||
REDIS_URL: "redis://redis:6379"
|
||||
networks:
|
||||
- internal
|
||||
- web
|
||||
|
||||
db:
|
||||
image: postgres:15
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- db_data:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_USER: {{ db_user }}
|
||||
POSTGRES_PASSWORD: {{ db_password }}
|
||||
POSTGRES_DB: {{ db_name }}
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U {{ db_user }}"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
networks:
|
||||
- internal
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
networks:
|
||||
- internal
|
||||
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "{{ http_port | default(80) }}:80"
|
||||
- "{{ https_port | default(443) }}:443"
|
||||
volumes:
|
||||
- {{ app_data_dir }}/nginx/conf.d:/etc/nginx/conf.d:ro
|
||||
- {{ app_data_dir }}/nginx/ssl:/etc/nginx/ssl:ro
|
||||
depends_on:
|
||||
- app
|
||||
networks:
|
||||
- web
|
||||
|
||||
networks:
|
||||
internal:
|
||||
driver: bridge
|
||||
web:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
db_data:
|
||||
redis_data:
|
||||
```
|
||||
|
||||
## Zero-Downtime Update
|
||||
|
||||
```yaml
|
||||
- name: Zero-downtime update
|
||||
hosts: docker_hosts
|
||||
serial: 1 # One host at a time
|
||||
tasks:
|
||||
- name: Pull new image
|
||||
community.docker.docker_image:
|
||||
name: "{{ app_image }}"
|
||||
tag: "{{ app_version }}"
|
||||
source: pull
|
||||
|
||||
- name: Drain connections (if load balanced)
|
||||
# ... remove from load balancer ...
|
||||
|
||||
- name: Update stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ app_data_dir }}"
|
||||
state: present
|
||||
recreate: always
|
||||
|
||||
- name: Wait for health
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:{{ app_port }}/health"
|
||||
status_code: 200
|
||||
register: health
|
||||
until: health.status == 200
|
||||
retries: 30
|
||||
delay: 2
|
||||
|
||||
- name: Restore to load balancer
|
||||
# ... add back to load balancer ...
|
||||
```
|
||||
|
||||
## Secrets Management
|
||||
|
||||
### With ansible-vault
|
||||
|
||||
```yaml
|
||||
# group_vars/secrets.yml (encrypted)
|
||||
app_secrets:
|
||||
DB_PASSWORD: supersecret
|
||||
API_KEY: abc123
|
||||
JWT_SECRET: longsecret
|
||||
```
|
||||
|
||||
```yaml
|
||||
# templates/.env.j2
|
||||
{% for key, value in app_secrets.items() %}
|
||||
{{ key }}={{ value }}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
### With external secrets
|
||||
|
||||
```yaml
|
||||
- name: Fetch secret from 1Password
|
||||
ansible.builtin.set_fact:
|
||||
db_password: "{{ lookup('community.general.onepassword', 'database', field='password') }}"
|
||||
|
||||
- name: Deploy with secret
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ app_data_dir }}"
|
||||
env_files:
|
||||
- "{{ app_data_dir }}/.env"
|
||||
state: present
|
||||
```
|
||||
307
skills/ansible/references/docker/deployment.md
Normal file
307
skills/ansible/references/docker/deployment.md
Normal file
@@ -0,0 +1,307 @@
|
||||
# Docker Deployment with Ansible
|
||||
|
||||
Managing Docker containers and compose stacks via Ansible.
|
||||
|
||||
## Collection Setup
|
||||
|
||||
```bash
|
||||
ansible-galaxy collection install community.docker
|
||||
```
|
||||
|
||||
## Compose Deployment (Recommended)
|
||||
|
||||
### Deploy from local compose file
|
||||
|
||||
```yaml
|
||||
- name: Deploy application stack
|
||||
hosts: docker_hosts
|
||||
become: true
|
||||
tasks:
|
||||
- name: Create project directory
|
||||
ansible.builtin.file:
|
||||
path: /opt/myapp
|
||||
state: directory
|
||||
owner: "{{ ansible_user }}"
|
||||
mode: '0755'
|
||||
|
||||
- name: Copy compose file
|
||||
ansible.builtin.template:
|
||||
src: docker-compose.yml.j2
|
||||
dest: /opt/myapp/docker-compose.yml
|
||||
owner: "{{ ansible_user }}"
|
||||
mode: '0644'
|
||||
|
||||
- name: Copy environment file
|
||||
ansible.builtin.template:
|
||||
src: .env.j2
|
||||
dest: /opt/myapp/.env
|
||||
owner: "{{ ansible_user }}"
|
||||
mode: '0600'
|
||||
|
||||
- name: Deploy with compose
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: /opt/myapp
|
||||
state: present
|
||||
pull: always
|
||||
register: deploy_result
|
||||
|
||||
- name: Show deployed services
|
||||
ansible.builtin.debug:
|
||||
var: deploy_result.containers
|
||||
```
|
||||
|
||||
### Compose operations
|
||||
|
||||
```yaml
|
||||
# Pull latest images and recreate
|
||||
- name: Update stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: /opt/myapp
|
||||
state: present
|
||||
pull: always
|
||||
recreate: always
|
||||
|
||||
# Stop stack (keep volumes)
|
||||
- name: Stop stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: /opt/myapp
|
||||
state: stopped
|
||||
|
||||
# Remove stack
|
||||
- name: Remove stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: /opt/myapp
|
||||
state: absent
|
||||
remove_volumes: false # Keep data volumes
|
||||
```
|
||||
|
||||
## Container Deployment (Individual)
|
||||
|
||||
### Run container
|
||||
|
||||
```yaml
|
||||
- name: Run nginx container
|
||||
community.docker.docker_container:
|
||||
name: nginx
|
||||
image: nginx:1.25
|
||||
state: started
|
||||
restart_policy: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- /opt/nginx/html:/usr/share/nginx/html:ro
|
||||
- /opt/nginx/conf.d:/etc/nginx/conf.d:ro
|
||||
env:
|
||||
TZ: "America/Los_Angeles"
|
||||
labels:
|
||||
app: web
|
||||
env: production
|
||||
|
||||
- name: Run database
|
||||
community.docker.docker_container:
|
||||
name: postgres
|
||||
image: postgres:15
|
||||
state: started
|
||||
restart_policy: unless-stopped
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
env:
|
||||
POSTGRES_USER: "{{ db_user }}"
|
||||
POSTGRES_PASSWORD: "{{ db_password }}"
|
||||
POSTGRES_DB: "{{ db_name }}"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U {{ db_user }}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
```
|
||||
|
||||
### Container lifecycle
|
||||
|
||||
```yaml
|
||||
# Stop container
|
||||
- name: Stop container
|
||||
community.docker.docker_container:
|
||||
name: myapp
|
||||
state: stopped
|
||||
|
||||
# Restart container
|
||||
- name: Restart container
|
||||
community.docker.docker_container:
|
||||
name: myapp
|
||||
state: started
|
||||
restart: true
|
||||
|
||||
# Remove container
|
||||
- name: Remove container
|
||||
community.docker.docker_container:
|
||||
name: myapp
|
||||
state: absent
|
||||
|
||||
# Force recreate
|
||||
- name: Recreate container
|
||||
community.docker.docker_container:
|
||||
name: myapp
|
||||
image: myapp:latest
|
||||
state: started
|
||||
recreate: true
|
||||
```
|
||||
|
||||
## Image Management
|
||||
|
||||
```yaml
|
||||
# Pull image
|
||||
- name: Pull latest image
|
||||
community.docker.docker_image:
|
||||
name: myapp
|
||||
tag: latest
|
||||
source: pull
|
||||
force_source: true # Always check for updates
|
||||
|
||||
# Build from Dockerfile
|
||||
- name: Build image
|
||||
community.docker.docker_image:
|
||||
name: myapp
|
||||
tag: "{{ version }}"
|
||||
source: build
|
||||
build:
|
||||
path: /opt/myapp
|
||||
dockerfile: Dockerfile
|
||||
pull: true # Pull base image updates
|
||||
|
||||
# Remove image
|
||||
- name: Remove old images
|
||||
community.docker.docker_image:
|
||||
name: myapp
|
||||
tag: old
|
||||
state: absent
|
||||
```
|
||||
|
||||
## Network Management
|
||||
|
||||
```yaml
|
||||
# Create network
|
||||
- name: Create app network
|
||||
community.docker.docker_network:
|
||||
name: app_network
|
||||
driver: bridge
|
||||
ipam_config:
|
||||
- subnet: 172.20.0.0/16
|
||||
gateway: 172.20.0.1
|
||||
|
||||
# Create macvlan network
|
||||
- name: Create macvlan network
|
||||
community.docker.docker_network:
|
||||
name: lan
|
||||
driver: macvlan
|
||||
driver_options:
|
||||
parent: eth0
|
||||
ipam_config:
|
||||
- subnet: 192.168.1.0/24
|
||||
gateway: 192.168.1.1
|
||||
|
||||
# Attach container to network
|
||||
- name: Run container on network
|
||||
community.docker.docker_container:
|
||||
name: myapp
|
||||
image: myapp:latest
|
||||
networks:
|
||||
- name: app_network
|
||||
ipv4_address: 172.20.0.10
|
||||
```
|
||||
|
||||
## Volume Management
|
||||
|
||||
```yaml
|
||||
# Create named volume
|
||||
- name: Create data volume
|
||||
community.docker.docker_volume:
|
||||
name: app_data
|
||||
driver: local
|
||||
|
||||
# Create volume with options
|
||||
- name: Create NFS volume
|
||||
community.docker.docker_volume:
|
||||
name: shared_data
|
||||
driver: local
|
||||
driver_options:
|
||||
type: nfs
|
||||
device: ":/exports/data"
|
||||
o: "addr=192.168.1.10,rw"
|
||||
|
||||
# Backup volume
|
||||
- name: Backup volume
|
||||
community.docker.docker_container:
|
||||
name: backup
|
||||
image: alpine
|
||||
command: tar czf /backup/data.tar.gz /data
|
||||
volumes:
|
||||
- app_data:/data:ro
|
||||
- /opt/backups:/backup
|
||||
auto_remove: true
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Wait for service health
|
||||
|
||||
```yaml
|
||||
- name: Deploy database
|
||||
community.docker.docker_container:
|
||||
name: postgres
|
||||
image: postgres:15
|
||||
# ... config ...
|
||||
|
||||
- name: Wait for database
|
||||
community.docker.docker_container_info:
|
||||
name: postgres
|
||||
register: db_info
|
||||
until: db_info.container.State.Health.Status == "healthy"
|
||||
retries: 30
|
||||
delay: 2
|
||||
```
|
||||
|
||||
### Rolling update
|
||||
|
||||
```yaml
|
||||
- name: Pull new image
|
||||
community.docker.docker_image:
|
||||
name: myapp
|
||||
tag: "{{ new_version }}"
|
||||
source: pull
|
||||
|
||||
- name: Update container
|
||||
community.docker.docker_container:
|
||||
name: myapp
|
||||
image: "myapp:{{ new_version }}"
|
||||
state: started
|
||||
recreate: true
|
||||
restart_policy: unless-stopped
|
||||
```
|
||||
|
||||
### Cleanup
|
||||
|
||||
```yaml
|
||||
- name: Remove stopped containers
|
||||
community.docker.docker_prune:
|
||||
containers: true
|
||||
containers_filters:
|
||||
status: exited
|
||||
|
||||
- name: Remove unused images
|
||||
community.docker.docker_prune:
|
||||
images: true
|
||||
images_filters:
|
||||
dangling: true
|
||||
|
||||
- name: Full cleanup (careful!)
|
||||
community.docker.docker_prune:
|
||||
containers: true
|
||||
images: true
|
||||
networks: true
|
||||
volumes: false # Don't remove data!
|
||||
builder_cache: true
|
||||
```
|
||||
292
skills/ansible/references/docker/troubleshooting.md
Normal file
292
skills/ansible/references/docker/troubleshooting.md
Normal file
@@ -0,0 +1,292 @@
|
||||
# Ansible Docker Troubleshooting
|
||||
|
||||
Common issues and debugging patterns.
|
||||
|
||||
## Module Issues
|
||||
|
||||
### "Could not find docker-compose"
|
||||
|
||||
```yaml
|
||||
# docker_compose_v2 requires Docker Compose V2 (plugin)
|
||||
# NOT standalone docker-compose binary
|
||||
|
||||
# Check on target host:
|
||||
# docker compose version # V2 (plugin)
|
||||
# docker-compose version # V1 (standalone) - won't work
|
||||
```
|
||||
|
||||
Fix: Install Docker Compose V2:
|
||||
```yaml
|
||||
- name: Install Docker Compose plugin
|
||||
ansible.builtin.apt:
|
||||
name: docker-compose-plugin
|
||||
state: present
|
||||
```
|
||||
|
||||
### "Permission denied"
|
||||
|
||||
```yaml
|
||||
# User not in docker group
|
||||
- name: Add user to docker group
|
||||
ansible.builtin.user:
|
||||
name: "{{ ansible_user }}"
|
||||
groups: docker
|
||||
append: true
|
||||
become: true
|
||||
|
||||
# Then reconnect or use become
|
||||
- name: Run with become
|
||||
community.docker.docker_container:
|
||||
name: myapp
|
||||
# ...
|
||||
become: true
|
||||
```
|
||||
|
||||
### "Cannot connect to Docker daemon"
|
||||
|
||||
```yaml
|
||||
# Docker not running
|
||||
- name: Ensure Docker is running
|
||||
ansible.builtin.service:
|
||||
name: docker
|
||||
state: started
|
||||
enabled: true
|
||||
become: true
|
||||
|
||||
# Socket permission issue
|
||||
# Add become: true to docker tasks
|
||||
```
|
||||
|
||||
## Container Issues
|
||||
|
||||
### Get container logs
|
||||
|
||||
```yaml
|
||||
- name: Get logs
|
||||
community.docker.docker_container_exec:
|
||||
container: myapp
|
||||
command: cat /var/log/app.log
|
||||
register: logs
|
||||
ignore_errors: true
|
||||
|
||||
- name: Alternative - docker logs
|
||||
ansible.builtin.command: docker logs --tail 100 myapp
|
||||
register: docker_logs
|
||||
changed_when: false
|
||||
|
||||
- name: Show logs
|
||||
ansible.builtin.debug:
|
||||
var: docker_logs.stdout_lines
|
||||
```
|
||||
|
||||
### Container keeps restarting
|
||||
|
||||
```yaml
|
||||
- name: Get container info
|
||||
community.docker.docker_container_info:
|
||||
name: myapp
|
||||
register: container_info
|
||||
|
||||
- name: Show restart count
|
||||
ansible.builtin.debug:
|
||||
msg: "Restart count: {{ container_info.container.RestartCount }}"
|
||||
|
||||
- name: Show last exit code
|
||||
ansible.builtin.debug:
|
||||
msg: "Exit code: {{ container_info.container.State.ExitCode }}"
|
||||
|
||||
- name: Get logs from dead container
|
||||
ansible.builtin.command: docker logs myapp
|
||||
register: crash_logs
|
||||
changed_when: false
|
||||
|
||||
- name: Show crash logs
|
||||
ansible.builtin.debug:
|
||||
var: crash_logs.stderr_lines
|
||||
```
|
||||
|
||||
### Health check failing
|
||||
|
||||
```yaml
|
||||
- name: Check health status
|
||||
community.docker.docker_container_info:
|
||||
name: myapp
|
||||
register: info
|
||||
|
||||
- name: Show health
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
Status: {{ info.container.State.Health.Status }}
|
||||
Failing: {{ info.container.State.Health.FailingStreak }}
|
||||
Log: {{ info.container.State.Health.Log | last }}
|
||||
|
||||
# Manual health check
|
||||
- name: Test health endpoint
|
||||
ansible.builtin.command: >
|
||||
docker exec myapp curl -f http://localhost:8080/health
|
||||
register: health
|
||||
ignore_errors: true
|
||||
changed_when: false
|
||||
```
|
||||
|
||||
## Network Issues
|
||||
|
||||
### Container can't reach external network
|
||||
|
||||
```yaml
|
||||
- name: Test DNS from container
|
||||
ansible.builtin.command: docker exec myapp nslookup google.com
|
||||
register: dns_test
|
||||
changed_when: false
|
||||
ignore_errors: true
|
||||
|
||||
- name: Test connectivity
|
||||
ansible.builtin.command: docker exec myapp ping -c 1 8.8.8.8
|
||||
register: ping_test
|
||||
changed_when: false
|
||||
ignore_errors: true
|
||||
|
||||
# Check iptables
|
||||
- name: Check IP forwarding
|
||||
ansible.builtin.command: sysctl net.ipv4.ip_forward
|
||||
register: ip_forward
|
||||
changed_when: false
|
||||
|
||||
- name: Enable IP forwarding
|
||||
ansible.posix.sysctl:
|
||||
name: net.ipv4.ip_forward
|
||||
value: '1'
|
||||
state: present
|
||||
become: true
|
||||
when: "'0' in ip_forward.stdout"
|
||||
```
|
||||
|
||||
### Containers can't communicate
|
||||
|
||||
```yaml
|
||||
- name: List networks
|
||||
community.docker.docker_network_info:
|
||||
name: "{{ network_name }}"
|
||||
register: network_info
|
||||
|
||||
- name: Show connected containers
|
||||
ansible.builtin.debug:
|
||||
var: network_info.network.Containers
|
||||
|
||||
# Verify both containers on same network
|
||||
- name: Test inter-container connectivity
|
||||
ansible.builtin.command: >
|
||||
docker exec app ping -c 1 db
|
||||
register: ping_result
|
||||
changed_when: false
|
||||
```
|
||||
|
||||
## Compose Issues
|
||||
|
||||
### Services not starting in order
|
||||
|
||||
```yaml
|
||||
# depends_on only waits for container start, not readiness
|
||||
# Use healthcheck + condition
|
||||
|
||||
# In compose template:
|
||||
services:
|
||||
app:
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy # Wait for health check
|
||||
|
||||
db:
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
```
|
||||
|
||||
### Orphaned containers
|
||||
|
||||
```yaml
|
||||
# Containers from old compose runs
|
||||
- name: Remove orphans
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: /opt/myapp
|
||||
state: present
|
||||
remove_orphans: true
|
||||
```
|
||||
|
||||
### Volume data not persisting
|
||||
|
||||
```yaml
|
||||
# Check volume exists
|
||||
- name: List volumes
|
||||
ansible.builtin.command: docker volume ls
|
||||
register: volumes
|
||||
changed_when: false
|
||||
|
||||
# Check volume contents
|
||||
- name: Inspect volume
|
||||
ansible.builtin.command: docker volume inspect myapp_data
|
||||
register: volume_info
|
||||
changed_when: false
|
||||
|
||||
- name: Show volume mountpoint
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ (volume_info.stdout | from_json)[0].Mountpoint }}"
|
||||
```
|
||||
|
||||
## Debug Playbook
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Docker debug
|
||||
hosts: docker_hosts
|
||||
tasks:
|
||||
- name: Docker version
|
||||
ansible.builtin.command: docker version
|
||||
register: docker_version
|
||||
changed_when: false
|
||||
|
||||
- name: Compose version
|
||||
ansible.builtin.command: docker compose version
|
||||
register: compose_version
|
||||
changed_when: false
|
||||
|
||||
- name: List containers
|
||||
ansible.builtin.command: docker ps -a
|
||||
register: containers
|
||||
changed_when: false
|
||||
|
||||
- name: List images
|
||||
ansible.builtin.command: docker images
|
||||
register: images
|
||||
changed_when: false
|
||||
|
||||
- name: Disk usage
|
||||
ansible.builtin.command: docker system df
|
||||
register: disk
|
||||
changed_when: false
|
||||
|
||||
- name: Show all
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
Docker: {{ docker_version.stdout_lines[0] }}
|
||||
Compose: {{ compose_version.stdout }}
|
||||
Containers:
|
||||
{{ containers.stdout }}
|
||||
Images:
|
||||
{{ images.stdout }}
|
||||
Disk:
|
||||
{{ disk.stdout }}
|
||||
```
|
||||
|
||||
## Common Error Reference
|
||||
|
||||
| Error | Cause | Fix |
|
||||
|-------|-------|-----|
|
||||
| `docker.errors.DockerException` | Docker not running | Start docker service |
|
||||
| `docker.errors.APIError: 404` | Container/image not found | Check name/tag |
|
||||
| `docker.errors.APIError: 409` | Container name conflict | Remove or rename |
|
||||
| `PermissionError` | Not in docker group | Add user or use become |
|
||||
| `requests.exceptions.ConnectionError` | Docker socket inaccessible | Check socket permissions |
|
||||
| `FileNotFoundError: docker-compose` | V1 compose not installed | Use docker_compose_v2 |
|
||||
181
skills/ansible/references/inventory.md
Normal file
181
skills/ansible/references/inventory.md
Normal file
@@ -0,0 +1,181 @@
|
||||
# Ansible Inventory Reference
|
||||
|
||||
## YAML Inventory Format
|
||||
|
||||
```yaml
|
||||
all:
|
||||
children:
|
||||
webservers:
|
||||
hosts:
|
||||
web1:
|
||||
ansible_host: 192.168.1.10
|
||||
web2:
|
||||
ansible_host: 192.168.1.11
|
||||
vars:
|
||||
http_port: 80
|
||||
|
||||
databases:
|
||||
hosts:
|
||||
db1:
|
||||
ansible_host: 192.168.1.20
|
||||
db_port: 5432
|
||||
db2:
|
||||
ansible_host: 192.168.1.21
|
||||
|
||||
production:
|
||||
children:
|
||||
webservers:
|
||||
databases:
|
||||
|
||||
vars:
|
||||
ansible_user: ubuntu
|
||||
ansible_ssh_private_key_file: ~/.ssh/id_rsa
|
||||
```
|
||||
|
||||
## INI Inventory Format
|
||||
|
||||
```ini
|
||||
[webservers]
|
||||
web1 ansible_host=192.168.1.10
|
||||
web2 ansible_host=192.168.1.11
|
||||
|
||||
[webservers:vars]
|
||||
http_port=80
|
||||
|
||||
[databases]
|
||||
db1 ansible_host=192.168.1.20 db_port=5432
|
||||
db2 ansible_host=192.168.1.21
|
||||
|
||||
[production:children]
|
||||
webservers
|
||||
databases
|
||||
|
||||
[all:vars]
|
||||
ansible_user=ubuntu
|
||||
```
|
||||
|
||||
## Host Variables
|
||||
|
||||
Common host variables:
|
||||
|
||||
| Variable | Purpose |
|
||||
|----------|---------|
|
||||
| `ansible_host` | IP or hostname to connect |
|
||||
| `ansible_port` | SSH port (default: 22) |
|
||||
| `ansible_user` | SSH username |
|
||||
| `ansible_ssh_private_key_file` | SSH key path |
|
||||
| `ansible_become` | Enable sudo |
|
||||
| `ansible_become_user` | Sudo target user |
|
||||
| `ansible_python_interpreter` | Python path |
|
||||
|
||||
## Group Variables
|
||||
|
||||
```yaml
|
||||
# group_vars/webservers.yml
|
||||
http_port: 80
|
||||
document_root: /var/www/html
|
||||
|
||||
# group_vars/all.yml
|
||||
ntp_server: time.example.com
|
||||
dns_servers:
|
||||
- 8.8.8.8
|
||||
- 8.8.4.4
|
||||
```
|
||||
|
||||
## Host Variables Files
|
||||
|
||||
```yaml
|
||||
# host_vars/web1.yml
|
||||
site_name: production-web1
|
||||
ssl_cert_path: /etc/ssl/certs/web1.crt
|
||||
```
|
||||
|
||||
## Dynamic Groups
|
||||
|
||||
```yaml
|
||||
# In playbook
|
||||
- hosts: "{{ target_group | default('all') }}"
|
||||
```
|
||||
|
||||
Run with:
|
||||
```bash
|
||||
ansible-playbook playbook.yml -e "target_group=webservers"
|
||||
```
|
||||
|
||||
## Patterns
|
||||
|
||||
```bash
|
||||
# All hosts
|
||||
ansible all -m ping
|
||||
|
||||
# Single host
|
||||
ansible web1 -m ping
|
||||
|
||||
# Group
|
||||
ansible webservers -m ping
|
||||
|
||||
# Multiple groups
|
||||
ansible 'webservers:databases' -m ping
|
||||
|
||||
# Intersection (AND)
|
||||
ansible 'webservers:&production' -m ping
|
||||
|
||||
# Exclusion
|
||||
ansible 'webservers:!web1' -m ping
|
||||
|
||||
# Regex
|
||||
ansible '~web[0-9]+' -m ping
|
||||
```
|
||||
|
||||
## Limit
|
||||
|
||||
```bash
|
||||
# Limit to specific hosts
|
||||
ansible-playbook playbook.yml -l web1
|
||||
ansible-playbook playbook.yml --limit web1,web2
|
||||
ansible-playbook playbook.yml --limit 'webservers:!web3'
|
||||
```
|
||||
|
||||
## Inventory Check
|
||||
|
||||
```bash
|
||||
# List hosts
|
||||
ansible-inventory --list
|
||||
ansible-inventory --graph
|
||||
|
||||
# Host info
|
||||
ansible-inventory --host web1
|
||||
|
||||
# Validate
|
||||
ansible all --list-hosts
|
||||
```
|
||||
|
||||
## Multiple Inventories
|
||||
|
||||
```bash
|
||||
# Multiple files
|
||||
ansible-playbook -i inventory/production -i inventory/staging playbook.yml
|
||||
|
||||
# Directory of inventories
|
||||
ansible-playbook -i inventory/ playbook.yml
|
||||
```
|
||||
|
||||
## Special Groups
|
||||
|
||||
| Group | Contains |
|
||||
|-------|----------|
|
||||
| `all` | All hosts |
|
||||
| `ungrouped` | Hosts not in any group |
|
||||
|
||||
## Local Connection
|
||||
|
||||
```yaml
|
||||
localhost:
|
||||
ansible_host: 127.0.0.1
|
||||
ansible_connection: local
|
||||
```
|
||||
|
||||
Or in inventory:
|
||||
```ini
|
||||
localhost ansible_connection=local
|
||||
```
|
||||
341
skills/ansible/references/modules.md
Normal file
341
skills/ansible/references/modules.md
Normal file
@@ -0,0 +1,341 @@
|
||||
# Ansible Modules Reference
|
||||
|
||||
## File Operations
|
||||
|
||||
### copy
|
||||
|
||||
```yaml
|
||||
- name: Copy file
|
||||
ansible.builtin.copy:
|
||||
src: files/config.conf
|
||||
dest: /etc/app/config.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
backup: true
|
||||
```
|
||||
|
||||
### template
|
||||
|
||||
```yaml
|
||||
- name: Template config
|
||||
ansible.builtin.template:
|
||||
src: templates/config.j2
|
||||
dest: /etc/app/config.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
notify: Restart app
|
||||
```
|
||||
|
||||
### file
|
||||
|
||||
```yaml
|
||||
# Create directory
|
||||
- name: Create directory
|
||||
ansible.builtin.file:
|
||||
path: /opt/app
|
||||
state: directory
|
||||
owner: app
|
||||
group: app
|
||||
mode: '0755'
|
||||
|
||||
# Create symlink
|
||||
- name: Create symlink
|
||||
ansible.builtin.file:
|
||||
src: /opt/app/current
|
||||
dest: /opt/app/release
|
||||
state: link
|
||||
|
||||
# Delete file
|
||||
- name: Remove file
|
||||
ansible.builtin.file:
|
||||
path: /tmp/old-file
|
||||
state: absent
|
||||
```
|
||||
|
||||
### lineinfile
|
||||
|
||||
```yaml
|
||||
- name: Ensure line in file
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/hosts
|
||||
line: "192.168.1.10 myhost"
|
||||
state: present
|
||||
|
||||
- name: Replace line
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/config
|
||||
regexp: '^PORT='
|
||||
line: 'PORT=8080'
|
||||
```
|
||||
|
||||
## Package Management
|
||||
|
||||
### apt (Debian/Ubuntu)
|
||||
|
||||
```yaml
|
||||
- name: Install package
|
||||
ansible.builtin.apt:
|
||||
name: nginx
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Install multiple
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- nginx
|
||||
- python3
|
||||
state: present
|
||||
|
||||
- name: Remove package
|
||||
ansible.builtin.apt:
|
||||
name: nginx
|
||||
state: absent
|
||||
```
|
||||
|
||||
### package (Generic)
|
||||
|
||||
```yaml
|
||||
- name: Install package
|
||||
ansible.builtin.package:
|
||||
name: httpd
|
||||
state: present
|
||||
```
|
||||
|
||||
## Service Management
|
||||
|
||||
### service
|
||||
|
||||
```yaml
|
||||
- name: Start and enable
|
||||
ansible.builtin.service:
|
||||
name: nginx
|
||||
state: started
|
||||
enabled: true
|
||||
|
||||
- name: Restart
|
||||
ansible.builtin.service:
|
||||
name: nginx
|
||||
state: restarted
|
||||
|
||||
- name: Reload
|
||||
ansible.builtin.service:
|
||||
name: nginx
|
||||
state: reloaded
|
||||
```
|
||||
|
||||
### systemd
|
||||
|
||||
```yaml
|
||||
- name: Daemon reload
|
||||
ansible.builtin.systemd:
|
||||
daemon_reload: true
|
||||
|
||||
- name: Enable and start
|
||||
ansible.builtin.systemd:
|
||||
name: myapp
|
||||
state: started
|
||||
enabled: true
|
||||
```
|
||||
|
||||
## Command Execution
|
||||
|
||||
### command
|
||||
|
||||
```yaml
|
||||
- name: Run command
|
||||
ansible.builtin.command: /bin/mycommand arg1 arg2
|
||||
register: result
|
||||
changed_when: "'changed' in result.stdout"
|
||||
```
|
||||
|
||||
### shell
|
||||
|
||||
```yaml
|
||||
- name: Run shell command
|
||||
ansible.builtin.shell: |
|
||||
cd /opt/app
|
||||
./setup.sh && ./configure.sh
|
||||
args:
|
||||
executable: /bin/bash
|
||||
```
|
||||
|
||||
### script
|
||||
|
||||
```yaml
|
||||
- name: Run local script on remote
|
||||
ansible.builtin.script: scripts/setup.sh
|
||||
args:
|
||||
creates: /opt/app/.installed
|
||||
```
|
||||
|
||||
## User Management
|
||||
|
||||
### user
|
||||
|
||||
```yaml
|
||||
- name: Create user
|
||||
ansible.builtin.user:
|
||||
name: appuser
|
||||
groups: docker,sudo
|
||||
shell: /bin/bash
|
||||
create_home: true
|
||||
state: present
|
||||
|
||||
- name: Remove user
|
||||
ansible.builtin.user:
|
||||
name: olduser
|
||||
state: absent
|
||||
remove: true
|
||||
```
|
||||
|
||||
### group
|
||||
|
||||
```yaml
|
||||
- name: Create group
|
||||
ansible.builtin.group:
|
||||
name: appgroup
|
||||
state: present
|
||||
```
|
||||
|
||||
## Docker (community.docker)
|
||||
|
||||
### docker_container
|
||||
|
||||
```yaml
|
||||
- name: Run container
|
||||
community.docker.docker_container:
|
||||
name: myapp
|
||||
image: myapp:latest
|
||||
state: started
|
||||
restart_policy: unless-stopped
|
||||
ports:
|
||||
- "8080:80"
|
||||
volumes:
|
||||
- /data:/app/data
|
||||
env:
|
||||
DB_HOST: database
|
||||
```
|
||||
|
||||
### docker_compose_v2
|
||||
|
||||
```yaml
|
||||
- name: Deploy with compose
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: /opt/app
|
||||
project_name: myapp
|
||||
state: present
|
||||
pull: always
|
||||
env_files:
|
||||
- /opt/app/.env
|
||||
```
|
||||
|
||||
### docker_image
|
||||
|
||||
```yaml
|
||||
- name: Pull image
|
||||
community.docker.docker_image:
|
||||
name: nginx
|
||||
tag: "1.25"
|
||||
source: pull
|
||||
```
|
||||
|
||||
## Networking
|
||||
|
||||
### uri
|
||||
|
||||
```yaml
|
||||
- name: API call
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:8080/api/health"
|
||||
method: GET
|
||||
return_content: true
|
||||
register: response
|
||||
|
||||
- name: POST request
|
||||
ansible.builtin.uri:
|
||||
url: "http://api.example.com/data"
|
||||
method: POST
|
||||
body_format: json
|
||||
body:
|
||||
key: value
|
||||
```
|
||||
|
||||
### wait_for
|
||||
|
||||
```yaml
|
||||
- name: Wait for port
|
||||
ansible.builtin.wait_for:
|
||||
host: localhost
|
||||
port: 8080
|
||||
timeout: 300
|
||||
|
||||
- name: Wait for file
|
||||
ansible.builtin.wait_for:
|
||||
path: /var/log/app.log
|
||||
search_regex: "Server started"
|
||||
```
|
||||
|
||||
## Debug/Assert
|
||||
|
||||
### debug
|
||||
|
||||
```yaml
|
||||
- name: Print variable
|
||||
ansible.builtin.debug:
|
||||
msg: "Value: {{ my_var }}"
|
||||
|
||||
- name: Print var directly
|
||||
ansible.builtin.debug:
|
||||
var: my_var
|
||||
```
|
||||
|
||||
### assert
|
||||
|
||||
```yaml
|
||||
- name: Validate conditions
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- my_var is defined
|
||||
- my_var | length > 0
|
||||
fail_msg: "my_var must be defined and non-empty"
|
||||
success_msg: "Validation passed"
|
||||
```
|
||||
|
||||
### fail
|
||||
|
||||
```yaml
|
||||
- name: Fail with message
|
||||
ansible.builtin.fail:
|
||||
msg: "Required condition not met"
|
||||
when: condition
|
||||
```
|
||||
|
||||
## Misc
|
||||
|
||||
### pause
|
||||
|
||||
```yaml
|
||||
- name: Wait 10 seconds
|
||||
ansible.builtin.pause:
|
||||
seconds: 10
|
||||
|
||||
- name: Wait for user
|
||||
ansible.builtin.pause:
|
||||
prompt: "Press enter to continue"
|
||||
```
|
||||
|
||||
### stat
|
||||
|
||||
```yaml
|
||||
- name: Check file exists
|
||||
ansible.builtin.stat:
|
||||
path: /etc/config
|
||||
register: config_file
|
||||
|
||||
- name: Use result
|
||||
ansible.builtin.debug:
|
||||
msg: "File exists"
|
||||
when: config_file.stat.exists
|
||||
```
|
||||
243
skills/ansible/references/playbooks.md
Normal file
243
skills/ansible/references/playbooks.md
Normal file
@@ -0,0 +1,243 @@
|
||||
# Ansible Playbook Reference
|
||||
|
||||
## Basic Structure
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook description
|
||||
hosts: target_group
|
||||
become: true # Run as root
|
||||
gather_facts: true # Collect system info
|
||||
|
||||
vars:
|
||||
my_var: value
|
||||
|
||||
vars_files:
|
||||
- vars/secrets.yml
|
||||
|
||||
pre_tasks:
|
||||
- name: Pre-task
|
||||
ansible.builtin.debug:
|
||||
msg: "Running before main tasks"
|
||||
|
||||
roles:
|
||||
- role_name
|
||||
|
||||
tasks:
|
||||
- name: Main task
|
||||
ansible.builtin.debug:
|
||||
msg: "Main task"
|
||||
|
||||
handlers:
|
||||
- name: Handler name
|
||||
ansible.builtin.service:
|
||||
name: service
|
||||
state: restarted
|
||||
|
||||
post_tasks:
|
||||
- name: Post-task
|
||||
ansible.builtin.debug:
|
||||
msg: "Running after main tasks"
|
||||
```
|
||||
|
||||
## Task Options
|
||||
|
||||
```yaml
|
||||
tasks:
|
||||
- name: Task with common options
|
||||
ansible.builtin.command: /bin/command
|
||||
become: true # Privilege escalation
|
||||
become_user: www-data # Run as specific user
|
||||
when: condition # Conditional execution
|
||||
register: result # Store output
|
||||
ignore_errors: true # Continue on failure
|
||||
changed_when: false # Override change detection
|
||||
failed_when: result.rc != 0 # Custom failure condition
|
||||
tags:
|
||||
- deploy
|
||||
- config
|
||||
notify: Handler name # Trigger handler
|
||||
```
|
||||
|
||||
## Loops
|
||||
|
||||
```yaml
|
||||
# Simple loop
|
||||
- name: Install packages
|
||||
ansible.builtin.apt:
|
||||
name: "{{ item }}"
|
||||
state: present
|
||||
loop:
|
||||
- nginx
|
||||
- python3
|
||||
|
||||
# Loop with dict
|
||||
- name: Create users
|
||||
ansible.builtin.user:
|
||||
name: "{{ item.name }}"
|
||||
groups: "{{ item.groups }}"
|
||||
loop:
|
||||
- { name: 'user1', groups: 'admin' }
|
||||
- { name: 'user2', groups: 'users' }
|
||||
|
||||
# Loop over dict
|
||||
- name: Process items
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ item.key }}: {{ item.value }}"
|
||||
loop: "{{ my_dict | dict2items }}"
|
||||
|
||||
# Loop with index
|
||||
- name: With index
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ index }}: {{ item }}"
|
||||
loop: "{{ my_list }}"
|
||||
loop_control:
|
||||
index_var: index
|
||||
```
|
||||
|
||||
## Conditionals
|
||||
|
||||
```yaml
|
||||
# Simple when
|
||||
- name: Only on Ubuntu
|
||||
ansible.builtin.apt:
|
||||
name: package
|
||||
when: ansible_distribution == "Ubuntu"
|
||||
|
||||
# Multiple conditions
|
||||
- name: Complex condition
|
||||
ansible.builtin.command: /bin/something
|
||||
when:
|
||||
- ansible_os_family == "Debian"
|
||||
- ansible_distribution_version is version('20.04', '>=')
|
||||
|
||||
# Or conditions
|
||||
- name: Or condition
|
||||
ansible.builtin.command: /bin/something
|
||||
when: condition1 or condition2
|
||||
|
||||
# Check variable
|
||||
- name: If defined
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ my_var }}"
|
||||
when: my_var is defined
|
||||
```
|
||||
|
||||
## Blocks
|
||||
|
||||
```yaml
|
||||
- name: Block example
|
||||
block:
|
||||
- name: Task 1
|
||||
ansible.builtin.command: /bin/task1
|
||||
|
||||
- name: Task 2
|
||||
ansible.builtin.command: /bin/task2
|
||||
|
||||
rescue:
|
||||
- name: Handle failure
|
||||
ansible.builtin.debug:
|
||||
msg: "Block failed"
|
||||
|
||||
always:
|
||||
- name: Always run
|
||||
ansible.builtin.debug:
|
||||
msg: "Cleanup"
|
||||
```
|
||||
|
||||
## Handlers
|
||||
|
||||
```yaml
|
||||
tasks:
|
||||
- name: Update config
|
||||
ansible.builtin.template:
|
||||
src: config.j2
|
||||
dest: /etc/app/config
|
||||
notify:
|
||||
- Restart service
|
||||
- Reload config
|
||||
|
||||
handlers:
|
||||
- name: Restart service
|
||||
ansible.builtin.service:
|
||||
name: app
|
||||
state: restarted
|
||||
|
||||
- name: Reload config
|
||||
ansible.builtin.service:
|
||||
name: app
|
||||
state: reloaded
|
||||
```
|
||||
|
||||
Handlers run once at end of play, even if notified multiple times.
|
||||
|
||||
## Including Tasks
|
||||
|
||||
```yaml
|
||||
# Include tasks file
|
||||
- name: Include tasks
|
||||
ansible.builtin.include_tasks: tasks/setup.yml
|
||||
|
||||
# Import tasks (static)
|
||||
- name: Import tasks
|
||||
ansible.builtin.import_tasks: tasks/setup.yml
|
||||
|
||||
# Include with variables
|
||||
- name: Include with vars
|
||||
ansible.builtin.include_tasks: tasks/deploy.yml
|
||||
vars:
|
||||
environment: production
|
||||
```
|
||||
|
||||
## Tags
|
||||
|
||||
```yaml
|
||||
tasks:
|
||||
- name: Tagged task
|
||||
ansible.builtin.command: /bin/command
|
||||
tags:
|
||||
- deploy
|
||||
- always # Always runs regardless of tag selection
|
||||
|
||||
- name: Never runs by default
|
||||
ansible.builtin.command: /bin/command
|
||||
tags: never # Only runs when explicitly tagged
|
||||
```
|
||||
|
||||
Run with tags:
|
||||
```bash
|
||||
ansible-playbook playbook.yml --tags "deploy"
|
||||
ansible-playbook playbook.yml --skip-tags "slow"
|
||||
```
|
||||
|
||||
## Check Mode
|
||||
|
||||
```yaml
|
||||
# Force check mode behavior
|
||||
- name: Always runs in check
|
||||
ansible.builtin.command: /bin/command
|
||||
check_mode: false # Runs even in check mode
|
||||
|
||||
- name: Never runs in check
|
||||
ansible.builtin.command: /bin/command
|
||||
check_mode: true # Only runs in check mode
|
||||
```
|
||||
|
||||
## Delegation
|
||||
|
||||
```yaml
|
||||
# Run on different host
|
||||
- name: Update load balancer
|
||||
ansible.builtin.command: /bin/update-lb
|
||||
delegate_to: loadbalancer
|
||||
|
||||
# Run locally
|
||||
- name: Local action
|
||||
ansible.builtin.command: /bin/local-command
|
||||
delegate_to: localhost
|
||||
|
||||
# Run once for all hosts
|
||||
- name: Single execution
|
||||
ansible.builtin.command: /bin/command
|
||||
run_once: true
|
||||
```
|
||||
155
skills/ansible/references/proxmox/authentication.md
Normal file
155
skills/ansible/references/proxmox/authentication.md
Normal file
@@ -0,0 +1,155 @@
|
||||
# Ansible Proxmox Authentication
|
||||
|
||||
## API Token Setup
|
||||
|
||||
Create a dedicated Ansible user and API token on Proxmox:
|
||||
|
||||
```bash
|
||||
# On Proxmox node
|
||||
pveum user add ansible@pve
|
||||
pveum aclmod / -user ansible@pve -role PVEAdmin
|
||||
pveum user token add ansible@pve mytoken --privsep 0
|
||||
```
|
||||
|
||||
**Note:** `--privsep 0` gives the token the same permissions as the user.
|
||||
|
||||
## Playbook Variables
|
||||
|
||||
### Direct in playbook (NOT recommended)
|
||||
|
||||
```yaml
|
||||
vars:
|
||||
proxmox_api_host: proxmox.example.com
|
||||
proxmox_api_user: ansible@pve
|
||||
proxmox_api_token_id: mytoken
|
||||
proxmox_api_token_secret: "{{ vault_proxmox_token }}"
|
||||
```
|
||||
|
||||
### Group vars with vault
|
||||
|
||||
```yaml
|
||||
# group_vars/all.yml
|
||||
proxmox_api_host: proxmox.example.com
|
||||
proxmox_api_user: ansible@pve
|
||||
proxmox_api_token_id: mytoken
|
||||
|
||||
# group_vars/secrets.yml (ansible-vault encrypted)
|
||||
proxmox_api_token_secret: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
```
|
||||
|
||||
### Environment variables
|
||||
|
||||
```bash
|
||||
export PROXMOX_HOST=proxmox.example.com
|
||||
export PROXMOX_USER=ansible@pve
|
||||
export PROXMOX_TOKEN_ID=mytoken
|
||||
export PROXMOX_TOKEN_SECRET=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
```
|
||||
|
||||
```yaml
|
||||
# In playbook
|
||||
vars:
|
||||
proxmox_api_host: "{{ lookup('env', 'PROXMOX_HOST') }}"
|
||||
proxmox_api_user: "{{ lookup('env', 'PROXMOX_USER') }}"
|
||||
proxmox_api_token_id: "{{ lookup('env', 'PROXMOX_TOKEN_ID') }}"
|
||||
proxmox_api_token_secret: "{{ lookup('env', 'PROXMOX_TOKEN_SECRET') }}"
|
||||
```
|
||||
|
||||
## Reusable Auth Block
|
||||
|
||||
Define once, reuse across tasks:
|
||||
|
||||
```yaml
|
||||
vars:
|
||||
proxmox_auth: &proxmox_auth
|
||||
api_host: "{{ proxmox_api_host }}"
|
||||
api_user: "{{ proxmox_api_user }}"
|
||||
api_token_id: "{{ proxmox_api_token_id }}"
|
||||
api_token_secret: "{{ proxmox_api_token_secret }}"
|
||||
validate_certs: false # For self-signed certs
|
||||
|
||||
tasks:
|
||||
- name: Create VM
|
||||
community.general.proxmox_kvm:
|
||||
<<: *proxmox_auth
|
||||
node: joseph
|
||||
vmid: 300
|
||||
name: myvm
|
||||
state: present
|
||||
|
||||
- name: Start VM
|
||||
community.general.proxmox_kvm:
|
||||
<<: *proxmox_auth
|
||||
vmid: 300
|
||||
state: started
|
||||
```
|
||||
|
||||
## TLS Certificate Handling
|
||||
|
||||
### Self-signed certificates
|
||||
|
||||
```yaml
|
||||
community.general.proxmox_kvm:
|
||||
# ... auth params ...
|
||||
validate_certs: false
|
||||
```
|
||||
|
||||
### Custom CA
|
||||
|
||||
```bash
|
||||
export SSL_CERT_FILE=/path/to/ca-bundle.crt
|
||||
```
|
||||
|
||||
Or in ansible.cfg:
|
||||
|
||||
```ini
|
||||
[defaults]
|
||||
# For urllib3/requests
|
||||
ca_cert = /path/to/ca-bundle.crt
|
||||
```
|
||||
|
||||
## Minimum Required Permissions
|
||||
|
||||
For full VM/container management:
|
||||
|
||||
| Permission | Path | Purpose |
|
||||
|------------|------|---------|
|
||||
| VM.Allocate | / | Create VMs |
|
||||
| VM.Clone | / | Clone templates |
|
||||
| VM.Config.* | / | Modify VM config |
|
||||
| VM.PowerMgmt | / | Start/stop VMs |
|
||||
| VM.Snapshot | / | Create snapshots |
|
||||
| Datastore.AllocateSpace | / | Allocate disk space |
|
||||
| Datastore.Audit | / | List storage |
|
||||
|
||||
Or use the built-in `PVEAdmin` role for full access.
|
||||
|
||||
## Troubleshooting Auth Issues
|
||||
|
||||
```yaml
|
||||
# Debug task to test connection
|
||||
- name: Test Proxmox API connection
|
||||
community.general.proxmox_kvm:
|
||||
api_host: "{{ proxmox_api_host }}"
|
||||
api_user: "{{ proxmox_api_user }}"
|
||||
api_token_id: "{{ proxmox_api_token_id }}"
|
||||
api_token_secret: "{{ proxmox_api_token_secret }}"
|
||||
validate_certs: false
|
||||
vmid: 100
|
||||
state: current
|
||||
register: result
|
||||
ignore_errors: true
|
||||
|
||||
- name: Show result
|
||||
ansible.builtin.debug:
|
||||
var: result
|
||||
```
|
||||
|
||||
Common errors:
|
||||
|
||||
| Error | Cause | Fix |
|
||||
|-------|-------|-----|
|
||||
| 401 Unauthorized | Bad token | Verify token ID format: `user@realm!tokenname` |
|
||||
| 403 Forbidden | Insufficient permissions | Check user ACLs with `pveum user permissions ansible@pve` |
|
||||
| SSL certificate problem | Self-signed cert | Set `validate_certs: false` |
|
||||
| Connection refused | Wrong host/port | Verify API URL (port 8006) |
|
||||
195
skills/ansible/references/proxmox/dynamic-inventory.md
Normal file
195
skills/ansible/references/proxmox/dynamic-inventory.md
Normal file
@@ -0,0 +1,195 @@
|
||||
# Ansible Proxmox Dynamic Inventory
|
||||
|
||||
Query Proxmox API for automatic inventory generation.
|
||||
|
||||
## Plugin Setup
|
||||
|
||||
### Requirements
|
||||
|
||||
```bash
|
||||
pip install proxmoxer requests
|
||||
ansible-galaxy collection install community.general
|
||||
```
|
||||
|
||||
### Inventory File
|
||||
|
||||
Create `inventory/proxmox.yml`:
|
||||
|
||||
```yaml
|
||||
plugin: community.general.proxmox
|
||||
url: https://proxmox.example.com:8006
|
||||
user: ansible@pve
|
||||
token_id: mytoken
|
||||
token_secret: "{{ lookup('env', 'PROXMOX_TOKEN_SECRET') }}"
|
||||
validate_certs: false
|
||||
|
||||
# Include VMs and containers
|
||||
want_facts: true
|
||||
want_proxmox_nodes_ansible_host: false
|
||||
|
||||
# Filter by status
|
||||
filters:
|
||||
- status == "running"
|
||||
|
||||
# Group by various attributes
|
||||
groups:
|
||||
# By Proxmox node
|
||||
node_joseph: proxmox_node == "joseph"
|
||||
node_maxwell: proxmox_node == "maxwell"
|
||||
node_everette: proxmox_node == "everette"
|
||||
|
||||
# By type
|
||||
vms: proxmox_type == "qemu"
|
||||
containers: proxmox_type == "lxc"
|
||||
|
||||
# By template naming convention
|
||||
docker_hosts: "'docker' in proxmox_name"
|
||||
pihole: "'pihole' in proxmox_name"
|
||||
|
||||
# Host variables from Proxmox
|
||||
compose:
|
||||
ansible_host: proxmox_agent_interfaces[0].ip-addresses[0].ip-address | default(proxmox_name)
|
||||
ansible_user: "'ubuntu'"
|
||||
proxmox_vmid: proxmox_vmid
|
||||
proxmox_node: proxmox_node
|
||||
```
|
||||
|
||||
### Enable in ansible.cfg
|
||||
|
||||
```ini
|
||||
[inventory]
|
||||
enable_plugins = community.general.proxmox, yaml, ini
|
||||
```
|
||||
|
||||
## Testing Inventory
|
||||
|
||||
```bash
|
||||
# List all hosts
|
||||
ansible-inventory -i inventory/proxmox.yml --list
|
||||
|
||||
# Graph view
|
||||
ansible-inventory -i inventory/proxmox.yml --graph
|
||||
|
||||
# Specific host details
|
||||
ansible-inventory -i inventory/proxmox.yml --host myvm
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Filter by Tags
|
||||
|
||||
Proxmox 7+ supports VM tags:
|
||||
|
||||
```yaml
|
||||
groups:
|
||||
webservers: "'web' in proxmox_tags"
|
||||
databases: "'db' in proxmox_tags"
|
||||
production: "'prod' in proxmox_tags"
|
||||
```
|
||||
|
||||
### Filter by VMID Range
|
||||
|
||||
```yaml
|
||||
filters:
|
||||
- vmid >= 200
|
||||
- vmid < 300
|
||||
|
||||
groups:
|
||||
dev_vms: proxmox_vmid >= 200 and proxmox_vmid < 300
|
||||
prod_vms: proxmox_vmid >= 300 and proxmox_vmid < 400
|
||||
```
|
||||
|
||||
### IP Address from QEMU Agent
|
||||
|
||||
Requires QEMU guest agent running in VM:
|
||||
|
||||
```yaml
|
||||
compose:
|
||||
# Primary IP from agent
|
||||
ansible_host: >-
|
||||
proxmox_agent_interfaces
|
||||
| selectattr('name', 'equalto', 'eth0')
|
||||
| map(attribute='ip-addresses')
|
||||
| flatten
|
||||
| selectattr('ip-address-type', 'equalto', 'ipv4')
|
||||
| map(attribute='ip-address')
|
||||
| first
|
||||
| default(proxmox_name)
|
||||
```
|
||||
|
||||
### Static + Dynamic Inventory
|
||||
|
||||
Combine with static inventory:
|
||||
|
||||
```bash
|
||||
# inventory/
|
||||
# static.yml # Static hosts
|
||||
# proxmox.yml # Dynamic from Proxmox
|
||||
|
||||
ansible-playbook -i inventory/ playbook.yml
|
||||
```
|
||||
|
||||
## Available Variables
|
||||
|
||||
Variables populated from Proxmox API:
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| proxmox_vmid | VM/container ID |
|
||||
| proxmox_name | VM/container name |
|
||||
| proxmox_type | "qemu" or "lxc" |
|
||||
| proxmox_status | running, stopped, etc. |
|
||||
| proxmox_node | Proxmox node name |
|
||||
| proxmox_pool | Resource pool (if any) |
|
||||
| proxmox_tags | Tags (Proxmox 7+) |
|
||||
| proxmox_template | Is template (bool) |
|
||||
| proxmox_agent | QEMU agent enabled (bool) |
|
||||
| proxmox_agent_interfaces | Network info from agent |
|
||||
| proxmox_cpus | CPU count |
|
||||
| proxmox_maxmem | Max memory bytes |
|
||||
| proxmox_maxdisk | Max disk bytes |
|
||||
|
||||
## Caching
|
||||
|
||||
Enable caching for faster inventory:
|
||||
|
||||
```yaml
|
||||
plugin: community.general.proxmox
|
||||
# ... auth ...
|
||||
|
||||
cache: true
|
||||
cache_plugin: jsonfile
|
||||
cache_connection: /tmp/ansible_proxmox_cache
|
||||
cache_timeout: 300 # 5 minutes
|
||||
```
|
||||
|
||||
Clear cache:
|
||||
```bash
|
||||
rm -rf /tmp/ansible_proxmox_cache
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### No hosts returned
|
||||
|
||||
1. Check API connectivity:
|
||||
```bash
|
||||
curl -k "https://proxmox:8006/api2/json/cluster/resources" \
|
||||
-H "Authorization: PVEAPIToken=ansible@pve!mytoken=secret"
|
||||
```
|
||||
|
||||
2. Check filters aren't too restrictive - try removing them
|
||||
|
||||
3. Verify token permissions include `VM.Audit`
|
||||
|
||||
### QEMU agent data missing
|
||||
|
||||
- Agent must be installed and running in guest
|
||||
- `want_facts: true` must be set
|
||||
- May take a few seconds after VM boot
|
||||
|
||||
### Slow inventory queries
|
||||
|
||||
- Enable caching (see above)
|
||||
- Use filters to reduce results
|
||||
- Avoid `want_facts: true` if not needed
|
||||
202
skills/ansible/references/proxmox/gotchas.md
Normal file
202
skills/ansible/references/proxmox/gotchas.md
Normal file
@@ -0,0 +1,202 @@
|
||||
# Ansible Proxmox Gotchas
|
||||
|
||||
Common issues when using Ansible with Proxmox VE.
|
||||
|
||||
## 1. Token ID Format
|
||||
|
||||
**Wrong:**
|
||||
```yaml
|
||||
api_token_id: mytoken
|
||||
```
|
||||
|
||||
**Correct:**
|
||||
```yaml
|
||||
api_token_id: mytoken # Just the token name, NOT user@realm!tokenname
|
||||
```
|
||||
|
||||
The module combines `api_user` and `api_token_id` internally.
|
||||
|
||||
## 2. VMID Required for Most Operations
|
||||
|
||||
Unlike Terraform, you must always specify `vmid`:
|
||||
|
||||
```yaml
|
||||
# Won't auto-generate VMID
|
||||
- name: Create VM
|
||||
community.general.proxmox_kvm:
|
||||
# ... auth ...
|
||||
vmid: 300 # REQUIRED - no auto-assignment
|
||||
name: myvm
|
||||
```
|
||||
|
||||
To find next available VMID:
|
||||
```yaml
|
||||
- name: Get cluster resources
|
||||
ansible.builtin.uri:
|
||||
url: "https://{{ proxmox_api_host }}:8006/api2/json/cluster/resources"
|
||||
headers:
|
||||
Authorization: "PVEAPIToken={{ proxmox_api_user }}!{{ proxmox_api_token_id }}={{ proxmox_api_token_secret }}"
|
||||
validate_certs: false
|
||||
register: resources
|
||||
|
||||
- name: Calculate next VMID
|
||||
ansible.builtin.set_fact:
|
||||
next_vmid: "{{ (resources.json.data | selectattr('vmid', 'defined') | map(attribute='vmid') | max) + 1 }}"
|
||||
```
|
||||
|
||||
## 3. Node Parameter Required
|
||||
|
||||
Must specify which node to operate on:
|
||||
|
||||
```yaml
|
||||
- name: Create VM
|
||||
community.general.proxmox_kvm:
|
||||
# ... auth ...
|
||||
node: joseph # REQUIRED - which Proxmox node
|
||||
vmid: 300
|
||||
```
|
||||
|
||||
## 4. Clone vs Create
|
||||
|
||||
Cloning requires different parameters than creating:
|
||||
|
||||
```yaml
|
||||
# CLONE from template
|
||||
- name: Clone VM
|
||||
community.general.proxmox_kvm:
|
||||
# ... auth ...
|
||||
node: joseph
|
||||
vmid: 300
|
||||
name: myvm
|
||||
clone: tmpl-ubuntu-2404-standard # Template name or VMID
|
||||
full: true
|
||||
|
||||
# CREATE new (less common)
|
||||
- name: Create VM
|
||||
community.general.proxmox_kvm:
|
||||
# ... auth ...
|
||||
node: joseph
|
||||
vmid: 300
|
||||
name: myvm
|
||||
ostype: l26
|
||||
scsihw: virtio-scsi-pci
|
||||
bootdisk: scsi0
|
||||
scsi:
|
||||
scsi0: 'local-lvm:32,format=raw'
|
||||
```
|
||||
|
||||
## 5. Async Operations
|
||||
|
||||
Large operations (clone, snapshot) can timeout. Use async:
|
||||
|
||||
```yaml
|
||||
- name: Clone large VM
|
||||
community.general.proxmox_kvm:
|
||||
# ... auth ...
|
||||
clone: large-template
|
||||
vmid: 300
|
||||
timeout: 600 # Module timeout
|
||||
async: 900 # Ansible async timeout
|
||||
poll: 10 # Check every 10 seconds
|
||||
```
|
||||
|
||||
## 6. State Idempotency
|
||||
|
||||
`state: present` doesn't update existing VMs:
|
||||
|
||||
```yaml
|
||||
# This WON'T change cores on existing VM
|
||||
- name: Create/update VM
|
||||
community.general.proxmox_kvm:
|
||||
# ... auth ...
|
||||
vmid: 300
|
||||
cores: 4 # Ignored if VM exists
|
||||
state: present
|
||||
```
|
||||
|
||||
To modify existing VMs, use `proxmox_kvm` with `update: true` (Ansible 2.14+) or use the API directly.
|
||||
|
||||
## 7. Network Interface Format (LXC)
|
||||
|
||||
LXC containers use a specific JSON-like string format:
|
||||
|
||||
```yaml
|
||||
# WRONG
|
||||
netif:
|
||||
net0:
|
||||
bridge: vmbr0
|
||||
ip: dhcp
|
||||
|
||||
# CORRECT
|
||||
netif: '{"net0":"name=eth0,bridge=vmbr0,ip=dhcp"}'
|
||||
|
||||
# Multiple interfaces
|
||||
netif: '{"net0":"name=eth0,bridge=vmbr0,ip=dhcp","net1":"name=eth1,bridge=vmbr12,ip=dhcp"}'
|
||||
```
|
||||
|
||||
## 8. Disk Resize Only Grows
|
||||
|
||||
`proxmox_disk` resize only increases size:
|
||||
|
||||
```yaml
|
||||
# This adds 20G to current size
|
||||
- name: Grow disk
|
||||
community.general.proxmox_disk:
|
||||
# ... auth ...
|
||||
vmid: 300
|
||||
disk: scsi0
|
||||
size: +20G # Relative increase
|
||||
state: resized
|
||||
|
||||
# NOT possible to shrink
|
||||
```
|
||||
|
||||
## 9. Template vs VM States
|
||||
|
||||
Templates don't support all states:
|
||||
|
||||
```yaml
|
||||
# Can't start a template
|
||||
- name: Start template
|
||||
community.general.proxmox_kvm:
|
||||
vmid: 100
|
||||
state: started # FAILS - templates can't run
|
||||
```
|
||||
|
||||
Convert template to VM first if needed.
|
||||
|
||||
## 10. Collection Version Matters
|
||||
|
||||
Module parameters change between versions. Check installed version:
|
||||
|
||||
```bash
|
||||
ansible-galaxy collection list | grep community.general
|
||||
```
|
||||
|
||||
Update if needed:
|
||||
```bash
|
||||
ansible-galaxy collection install community.general --upgrade
|
||||
```
|
||||
|
||||
## 11. Cloud-Init Not Supported
|
||||
|
||||
Unlike Terraform's Proxmox provider, the Ansible modules have limited cloud-init support. For cloud-init VMs:
|
||||
|
||||
1. Clone template with cloud-init already configured
|
||||
2. Use API calls to set cloud-init parameters
|
||||
3. Or configure post-boot with Ansible
|
||||
|
||||
```yaml
|
||||
# Workaround: Use URI module for cloud-init config
|
||||
- name: Set cloud-init IP
|
||||
ansible.builtin.uri:
|
||||
url: "https://{{ proxmox_api_host }}:8006/api2/json/nodes/{{ node }}/qemu/{{ vmid }}/config"
|
||||
method: PUT
|
||||
headers:
|
||||
Authorization: "PVEAPIToken={{ proxmox_api_user }}!{{ proxmox_api_token_id }}={{ proxmox_api_token_secret }}"
|
||||
body_format: form-urlencoded
|
||||
body:
|
||||
ipconfig0: "ip=192.168.1.100/24,gw=192.168.1.1"
|
||||
ciuser: ubuntu
|
||||
validate_certs: false
|
||||
```
|
||||
232
skills/ansible/references/proxmox/modules.md
Normal file
232
skills/ansible/references/proxmox/modules.md
Normal file
@@ -0,0 +1,232 @@
|
||||
# Ansible Proxmox Modules
|
||||
|
||||
Proxmox VE management via `community.general` collection.
|
||||
|
||||
## Collection Setup
|
||||
|
||||
```bash
|
||||
ansible-galaxy collection install community.general
|
||||
```
|
||||
|
||||
## Core Modules
|
||||
|
||||
### proxmox (LXC Containers)
|
||||
|
||||
```yaml
|
||||
- name: Create LXC container
|
||||
community.general.proxmox:
|
||||
api_host: proxmox.example.com
|
||||
api_user: ansible@pve
|
||||
api_token_id: mytoken
|
||||
api_token_secret: "{{ proxmox_token_secret }}"
|
||||
node: joseph
|
||||
vmid: 200
|
||||
hostname: mycontainer
|
||||
ostemplate: local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst
|
||||
storage: local-lvm
|
||||
cores: 2
|
||||
memory: 2048
|
||||
disk: 10
|
||||
netif: '{"net0":"name=eth0,bridge=vmbr0,ip=dhcp"}'
|
||||
state: present
|
||||
|
||||
- name: Start container
|
||||
community.general.proxmox:
|
||||
api_host: proxmox.example.com
|
||||
api_user: ansible@pve
|
||||
api_token_id: mytoken
|
||||
api_token_secret: "{{ proxmox_token_secret }}"
|
||||
node: joseph
|
||||
vmid: 200
|
||||
state: started
|
||||
|
||||
- name: Stop container
|
||||
community.general.proxmox:
|
||||
# ... auth params ...
|
||||
vmid: 200
|
||||
state: stopped
|
||||
force: true # Force stop if graceful fails
|
||||
|
||||
- name: Remove container
|
||||
community.general.proxmox:
|
||||
# ... auth params ...
|
||||
vmid: 200
|
||||
state: absent
|
||||
```
|
||||
|
||||
### proxmox_kvm (VMs)
|
||||
|
||||
```yaml
|
||||
- name: Create VM from template
|
||||
community.general.proxmox_kvm:
|
||||
api_host: proxmox.example.com
|
||||
api_user: ansible@pve
|
||||
api_token_id: mytoken
|
||||
api_token_secret: "{{ proxmox_token_secret }}"
|
||||
node: joseph
|
||||
vmid: 300
|
||||
name: myvm
|
||||
clone: tmpl-ubuntu-2404-standard
|
||||
full: true # Full clone (not linked)
|
||||
storage: local-lvm
|
||||
format: raw
|
||||
timeout: 500
|
||||
|
||||
- name: Start VM
|
||||
community.general.proxmox_kvm:
|
||||
# ... auth params ...
|
||||
node: joseph
|
||||
vmid: 300
|
||||
state: started
|
||||
|
||||
- name: Stop VM (ACPI shutdown)
|
||||
community.general.proxmox_kvm:
|
||||
# ... auth params ...
|
||||
vmid: 300
|
||||
state: stopped
|
||||
force: false # Graceful ACPI
|
||||
|
||||
- name: Force stop VM
|
||||
community.general.proxmox_kvm:
|
||||
# ... auth params ...
|
||||
vmid: 300
|
||||
state: stopped
|
||||
force: true
|
||||
|
||||
- name: Current state (running/stopped/present/absent)
|
||||
community.general.proxmox_kvm:
|
||||
# ... auth params ...
|
||||
vmid: 300
|
||||
state: current
|
||||
register: vm_state
|
||||
```
|
||||
|
||||
### proxmox_template
|
||||
|
||||
```yaml
|
||||
- name: Convert VM to template
|
||||
community.general.proxmox_template:
|
||||
api_host: proxmox.example.com
|
||||
api_user: ansible@pve
|
||||
api_token_id: mytoken
|
||||
api_token_secret: "{{ proxmox_token_secret }}"
|
||||
node: joseph
|
||||
vmid: 100
|
||||
state: present # Convert to template
|
||||
|
||||
- name: Delete template
|
||||
community.general.proxmox_template:
|
||||
# ... auth params ...
|
||||
vmid: 100
|
||||
state: absent
|
||||
```
|
||||
|
||||
### proxmox_snap
|
||||
|
||||
```yaml
|
||||
- name: Create snapshot
|
||||
community.general.proxmox_snap:
|
||||
api_host: proxmox.example.com
|
||||
api_user: ansible@pve
|
||||
api_token_id: mytoken
|
||||
api_token_secret: "{{ proxmox_token_secret }}"
|
||||
vmid: 300
|
||||
snapname: before-upgrade
|
||||
description: "Snapshot before major upgrade"
|
||||
vmstate: false # Don't include RAM
|
||||
state: present
|
||||
|
||||
- name: Rollback to snapshot
|
||||
community.general.proxmox_snap:
|
||||
# ... auth params ...
|
||||
vmid: 300
|
||||
snapname: before-upgrade
|
||||
state: rollback
|
||||
|
||||
- name: Remove snapshot
|
||||
community.general.proxmox_snap:
|
||||
# ... auth params ...
|
||||
vmid: 300
|
||||
snapname: before-upgrade
|
||||
state: absent
|
||||
```
|
||||
|
||||
### proxmox_nic
|
||||
|
||||
```yaml
|
||||
- name: Add NIC to VM
|
||||
community.general.proxmox_nic:
|
||||
api_host: proxmox.example.com
|
||||
api_user: ansible@pve
|
||||
api_token_id: mytoken
|
||||
api_token_secret: "{{ proxmox_token_secret }}"
|
||||
vmid: 300
|
||||
interface: net1
|
||||
bridge: vmbr12
|
||||
model: virtio
|
||||
tag: 12 # VLAN tag
|
||||
state: present
|
||||
|
||||
- name: Remove NIC
|
||||
community.general.proxmox_nic:
|
||||
# ... auth params ...
|
||||
vmid: 300
|
||||
interface: net1
|
||||
state: absent
|
||||
```
|
||||
|
||||
### proxmox_disk
|
||||
|
||||
```yaml
|
||||
- name: Add disk to VM
|
||||
community.general.proxmox_disk:
|
||||
api_host: proxmox.example.com
|
||||
api_user: ansible@pve
|
||||
api_token_id: mytoken
|
||||
api_token_secret: "{{ proxmox_token_secret }}"
|
||||
vmid: 300
|
||||
disk: scsi1
|
||||
storage: local-lvm
|
||||
size: 50G
|
||||
format: raw
|
||||
state: present
|
||||
|
||||
- name: Resize disk
|
||||
community.general.proxmox_disk:
|
||||
# ... auth params ...
|
||||
vmid: 300
|
||||
disk: scsi0
|
||||
size: +20G # Increase by 20G
|
||||
state: resized
|
||||
|
||||
- name: Detach disk
|
||||
community.general.proxmox_disk:
|
||||
# ... auth params ...
|
||||
vmid: 300
|
||||
disk: scsi1
|
||||
state: absent
|
||||
```
|
||||
|
||||
## State Reference
|
||||
|
||||
| Module | States |
|
||||
|--------|--------|
|
||||
| proxmox (LXC) | present, started, stopped, restarted, absent |
|
||||
| proxmox_kvm | present, started, stopped, restarted, absent, current |
|
||||
| proxmox_template | present, absent |
|
||||
| proxmox_snap | present, absent, rollback |
|
||||
| proxmox_nic | present, absent |
|
||||
| proxmox_disk | present, absent, resized |
|
||||
|
||||
## Common Parameters
|
||||
|
||||
All modules share these authentication parameters:
|
||||
|
||||
| Parameter | Description |
|
||||
|-----------|-------------|
|
||||
| api_host | Proxmox hostname/IP |
|
||||
| api_user | User (format: user@realm) |
|
||||
| api_token_id | API token name |
|
||||
| api_token_secret | API token value |
|
||||
| validate_certs | Verify TLS (default: true) |
|
||||
| timeout | API timeout seconds |
|
||||
295
skills/ansible/references/troubleshooting.md
Normal file
295
skills/ansible/references/troubleshooting.md
Normal file
@@ -0,0 +1,295 @@
|
||||
# Ansible Troubleshooting Reference
|
||||
|
||||
## Common Errors
|
||||
|
||||
| Error | Cause | Solution |
|
||||
|-------|-------|----------|
|
||||
| SSH connection failed | Wrong host/key/user | Check ansible_host, ansible_user, key |
|
||||
| Permission denied | Need sudo/wrong user | Add `become: true`, check sudo config |
|
||||
| Module not found | Collection not installed | `ansible-galaxy collection install` |
|
||||
| Variable undefined | Missing var/typo | Check var name, define in vars |
|
||||
| Syntax error | YAML/Jinja2 issue | Run `ansible-playbook --syntax-check` |
|
||||
| Host unreachable | Network/SSH issue | `ansible host -m ping`, check firewall |
|
||||
|
||||
## Debug Commands
|
||||
|
||||
```bash
|
||||
# Test connectivity
|
||||
ansible all -m ping
|
||||
ansible host -m ping -vvv
|
||||
|
||||
# Syntax check
|
||||
ansible-playbook playbook.yml --syntax-check
|
||||
|
||||
# Dry run (check mode)
|
||||
ansible-playbook playbook.yml --check
|
||||
|
||||
# Diff mode (show changes)
|
||||
ansible-playbook playbook.yml --diff
|
||||
|
||||
# Verbose output
|
||||
ansible-playbook playbook.yml -v # Minimal
|
||||
ansible-playbook playbook.yml -vv # More
|
||||
ansible-playbook playbook.yml -vvv # Connection debug
|
||||
ansible-playbook playbook.yml -vvvv # Full debug
|
||||
|
||||
# List tasks without running
|
||||
ansible-playbook playbook.yml --list-tasks
|
||||
|
||||
# List hosts
|
||||
ansible-playbook playbook.yml --list-hosts
|
||||
|
||||
# Start at specific task
|
||||
ansible-playbook playbook.yml --start-at-task="Task name"
|
||||
|
||||
# Step through tasks
|
||||
ansible-playbook playbook.yml --step
|
||||
```
|
||||
|
||||
## Connection Issues
|
||||
|
||||
### Test SSH
|
||||
|
||||
```bash
|
||||
# Direct SSH test
|
||||
ssh -i ~/.ssh/key user@host
|
||||
|
||||
# Ansible ping
|
||||
ansible host -m ping -vvv
|
||||
|
||||
# Check SSH config
|
||||
ansible host -m debug -a "var=ansible_ssh_private_key_file"
|
||||
```
|
||||
|
||||
### Common SSH Fixes
|
||||
|
||||
```yaml
|
||||
# In inventory or ansible.cfg
|
||||
ansible_ssh_private_key_file: ~/.ssh/mykey
|
||||
ansible_user: ubuntu
|
||||
ansible_host: 192.168.1.10
|
||||
host_key_checking: False # Only for testing
|
||||
```
|
||||
|
||||
### SSH Connection Options
|
||||
|
||||
```yaml
|
||||
# In inventory
|
||||
host1:
|
||||
ansible_host: 192.168.1.10
|
||||
ansible_ssh_common_args: '-o StrictHostKeyChecking=no'
|
||||
ansible_ssh_extra_args: '-o ConnectTimeout=10'
|
||||
```
|
||||
|
||||
## Permission Issues
|
||||
|
||||
### Sudo Not Working
|
||||
|
||||
```yaml
|
||||
# Enable become
|
||||
- hosts: all
|
||||
become: true
|
||||
become_method: sudo
|
||||
become_user: root
|
||||
```
|
||||
|
||||
```bash
|
||||
# On target host, check sudoers
|
||||
sudo visudo
|
||||
|
||||
# User should have:
|
||||
# ubuntu ALL=(ALL) NOPASSWD: ALL
|
||||
```
|
||||
|
||||
### Ask for Sudo Password
|
||||
|
||||
```bash
|
||||
ansible-playbook playbook.yml --ask-become-pass
|
||||
```
|
||||
|
||||
## Variable Issues
|
||||
|
||||
### Debug Variables
|
||||
|
||||
```yaml
|
||||
- name: Print all vars
|
||||
ansible.builtin.debug:
|
||||
var: vars
|
||||
|
||||
- name: Print specific var
|
||||
ansible.builtin.debug:
|
||||
var: my_var
|
||||
|
||||
- name: Print hostvars
|
||||
ansible.builtin.debug:
|
||||
var: hostvars[inventory_hostname]
|
||||
|
||||
- name: Print facts
|
||||
ansible.builtin.debug:
|
||||
var: ansible_facts
|
||||
```
|
||||
|
||||
### Check Variable Precedence
|
||||
|
||||
```bash
|
||||
# See where variable comes from
|
||||
ansible-inventory --host hostname --yaml
|
||||
```
|
||||
|
||||
### Undefined Variable
|
||||
|
||||
```yaml
|
||||
# Provide default
|
||||
value: "{{ my_var | default('fallback') }}"
|
||||
|
||||
# Check if defined
|
||||
- name: Task
|
||||
when: my_var is defined
|
||||
|
||||
# Fail early if required
|
||||
- name: Validate
|
||||
ansible.builtin.assert:
|
||||
that: my_var is defined
|
||||
fail_msg: "my_var must be set"
|
||||
```
|
||||
|
||||
## Module Issues
|
||||
|
||||
### Module Not Found
|
||||
|
||||
```bash
|
||||
# Install collection
|
||||
ansible-galaxy collection install community.docker
|
||||
|
||||
# Check installed
|
||||
ansible-galaxy collection list
|
||||
|
||||
# Update collections
|
||||
ansible-galaxy collection install -r requirements.yml --force
|
||||
```
|
||||
|
||||
### Module Arguments
|
||||
|
||||
```bash
|
||||
# Get module documentation
|
||||
ansible-doc ansible.builtin.copy
|
||||
ansible-doc community.docker.docker_compose_v2
|
||||
```
|
||||
|
||||
## Idempotency Issues
|
||||
|
||||
### Task Always Shows "changed"
|
||||
|
||||
```yaml
|
||||
# Bad - always changed
|
||||
- name: Run script
|
||||
ansible.builtin.command: /bin/script.sh
|
||||
|
||||
# Good - check first
|
||||
- name: Run script
|
||||
ansible.builtin.command: /bin/script.sh
|
||||
args:
|
||||
creates: /opt/app/.installed
|
||||
|
||||
# Good - explicit changed_when
|
||||
- name: Run script
|
||||
ansible.builtin.command: /bin/script.sh
|
||||
register: result
|
||||
changed_when: "'Created' in result.stdout"
|
||||
```
|
||||
|
||||
### Test Idempotency
|
||||
|
||||
```bash
|
||||
# Run twice, second should show all "ok"
|
||||
ansible-playbook playbook.yml
|
||||
ansible-playbook playbook.yml # Should show "changed=0"
|
||||
```
|
||||
|
||||
## Handler Issues
|
||||
|
||||
### Handler Not Running
|
||||
|
||||
- Handlers only run if task reports "changed"
|
||||
- Handlers run at end of play, not immediately
|
||||
- Force handler run: `ansible-playbook --force-handlers`
|
||||
|
||||
```yaml
|
||||
# Force handler to run immediately
|
||||
- name: Config change
|
||||
ansible.builtin.template:
|
||||
src: config.j2
|
||||
dest: /etc/app/config
|
||||
notify: Restart app
|
||||
|
||||
- name: Flush handlers
|
||||
ansible.builtin.meta: flush_handlers
|
||||
|
||||
- name: Continue with restarted service
|
||||
ansible.builtin.uri:
|
||||
url: http://localhost:8080/health
|
||||
```
|
||||
|
||||
## Performance Issues
|
||||
|
||||
### Slow Playbook
|
||||
|
||||
```yaml
|
||||
# Disable fact gathering if not needed
|
||||
- hosts: all
|
||||
gather_facts: false
|
||||
|
||||
# Or gather specific facts
|
||||
- hosts: all
|
||||
gather_facts: true
|
||||
gather_subset:
|
||||
- network
|
||||
```
|
||||
|
||||
```bash
|
||||
# Increase parallelism
|
||||
ansible-playbook playbook.yml -f 20 # 20 forks
|
||||
|
||||
# Use pipelining (add to ansible.cfg)
|
||||
# [ssh_connection]
|
||||
# pipelining = True
|
||||
```
|
||||
|
||||
### Callback Timer
|
||||
|
||||
```ini
|
||||
# ansible.cfg
|
||||
[defaults]
|
||||
callbacks_enabled = timer, profile_tasks
|
||||
```
|
||||
|
||||
## Recovery
|
||||
|
||||
### Failed Playbook
|
||||
|
||||
```bash
|
||||
# Retry failed hosts
|
||||
ansible-playbook playbook.yml --limit @playbook.retry
|
||||
|
||||
# Start at failed task
|
||||
ansible-playbook playbook.yml --start-at-task="Failed Task Name"
|
||||
```
|
||||
|
||||
### Cleanup After Failure
|
||||
|
||||
```yaml
|
||||
- name: Risky operation
|
||||
block:
|
||||
- name: Do something
|
||||
ansible.builtin.command: /bin/risky
|
||||
rescue:
|
||||
- name: Cleanup on failure
|
||||
ansible.builtin.file:
|
||||
path: /tmp/incomplete
|
||||
state: absent
|
||||
always:
|
||||
- name: Always cleanup
|
||||
ansible.builtin.file:
|
||||
path: /tmp/lock
|
||||
state: absent
|
||||
```
|
||||
246
skills/ansible/references/variables.md
Normal file
246
skills/ansible/references/variables.md
Normal file
@@ -0,0 +1,246 @@
|
||||
# Ansible Variables Reference
|
||||
|
||||
## Variable Precedence (High to Low)
|
||||
|
||||
1. **Extra vars** (`-e "var=value"`)
|
||||
2. **Task vars** (in task)
|
||||
3. **Block vars** (in block)
|
||||
4. **Role/include vars**
|
||||
5. **set_facts / registered vars**
|
||||
6. **Play vars_files**
|
||||
7. **Play vars_prompt**
|
||||
8. **Play vars**
|
||||
9. **Host facts**
|
||||
10. **Playbook host_vars/**
|
||||
11. **Inventory host_vars/**
|
||||
12. **Playbook group_vars/**
|
||||
13. **Inventory group_vars/**
|
||||
14. **Playbook group_vars/all**
|
||||
15. **Inventory group_vars/all**
|
||||
16. **Role defaults**
|
||||
|
||||
## Defining Variables
|
||||
|
||||
### In Playbook
|
||||
|
||||
```yaml
|
||||
- hosts: all
|
||||
vars:
|
||||
app_name: myapp
|
||||
app_port: 8080
|
||||
|
||||
vars_files:
|
||||
- vars/common.yml
|
||||
- "vars/{{ environment }}.yml"
|
||||
```
|
||||
|
||||
### In Tasks
|
||||
|
||||
```yaml
|
||||
- name: Set variable
|
||||
ansible.builtin.set_fact:
|
||||
my_var: "value"
|
||||
|
||||
- name: Register output
|
||||
ansible.builtin.command: whoami
|
||||
register: user_result
|
||||
|
||||
- name: Use registered
|
||||
ansible.builtin.debug:
|
||||
msg: "User: {{ user_result.stdout }}"
|
||||
```
|
||||
|
||||
### In Roles
|
||||
|
||||
```yaml
|
||||
# roles/app/defaults/main.yml (low priority)
|
||||
app_port: 8080
|
||||
|
||||
# roles/app/vars/main.yml (high priority)
|
||||
internal_setting: value
|
||||
```
|
||||
|
||||
## Variable Types
|
||||
|
||||
```yaml
|
||||
# String
|
||||
name: "value"
|
||||
|
||||
# Number
|
||||
port: 8080
|
||||
|
||||
# Boolean
|
||||
enabled: true
|
||||
|
||||
# List
|
||||
packages:
|
||||
- nginx
|
||||
- python3
|
||||
|
||||
# Dictionary
|
||||
user:
|
||||
name: admin
|
||||
groups:
|
||||
- wheel
|
||||
- docker
|
||||
```
|
||||
|
||||
## Accessing Variables
|
||||
|
||||
```yaml
|
||||
# Simple
|
||||
msg: "{{ my_var }}"
|
||||
|
||||
# Dictionary
|
||||
msg: "{{ user.name }}"
|
||||
msg: "{{ user['name'] }}"
|
||||
|
||||
# List
|
||||
msg: "{{ packages[0] }}"
|
||||
msg: "{{ packages | first }}"
|
||||
|
||||
# Default value
|
||||
msg: "{{ my_var | default('fallback') }}"
|
||||
|
||||
# Required (fail if undefined)
|
||||
msg: "{{ my_var }}" # Fails if undefined
|
||||
```
|
||||
|
||||
## Jinja2 Filters
|
||||
|
||||
```yaml
|
||||
# Default
|
||||
value: "{{ var | default('default') }}"
|
||||
|
||||
# Mandatory
|
||||
value: "{{ var | mandatory }}"
|
||||
|
||||
# Type conversion
|
||||
port: "{{ port_string | int }}"
|
||||
flag: "{{ flag_string | bool }}"
|
||||
|
||||
# String operations
|
||||
upper: "{{ name | upper }}"
|
||||
lower: "{{ name | lower }}"
|
||||
title: "{{ name | title }}"
|
||||
|
||||
# Lists
|
||||
first: "{{ list | first }}"
|
||||
last: "{{ list | last }}"
|
||||
length: "{{ list | length }}"
|
||||
joined: "{{ list | join(',') }}"
|
||||
|
||||
# JSON
|
||||
json_str: "{{ dict | to_json }}"
|
||||
yaml_str: "{{ dict | to_yaml }}"
|
||||
|
||||
# Path operations
|
||||
basename: "{{ path | basename }}"
|
||||
dirname: "{{ path | dirname }}"
|
||||
```
|
||||
|
||||
## Facts
|
||||
|
||||
```yaml
|
||||
# Accessing facts
|
||||
os: "{{ ansible_distribution }}"
|
||||
version: "{{ ansible_distribution_version }}"
|
||||
ip: "{{ ansible_default_ipv4.address }}"
|
||||
hostname: "{{ ansible_hostname }}"
|
||||
memory_mb: "{{ ansible_memtotal_mb }}"
|
||||
cpus: "{{ ansible_processor_vcpus }}"
|
||||
```
|
||||
|
||||
### Gathering Facts
|
||||
|
||||
```yaml
|
||||
- hosts: all
|
||||
gather_facts: true # Default
|
||||
|
||||
# Or manually
|
||||
- name: Gather facts
|
||||
ansible.builtin.setup:
|
||||
filter: ansible_*
|
||||
|
||||
# Specific facts
|
||||
- name: Get network facts
|
||||
ansible.builtin.setup:
|
||||
gather_subset:
|
||||
- network
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```yaml
|
||||
# Lookup
|
||||
value: "{{ lookup('env', 'MY_VAR') }}"
|
||||
|
||||
# Set for task
|
||||
- name: Run with env
|
||||
ansible.builtin.command: /bin/command
|
||||
environment:
|
||||
MY_VAR: "{{ my_value }}"
|
||||
```
|
||||
|
||||
## Secrets/Vault
|
||||
|
||||
```bash
|
||||
# Create encrypted file
|
||||
ansible-vault create secrets.yml
|
||||
|
||||
# Edit encrypted file
|
||||
ansible-vault edit secrets.yml
|
||||
|
||||
# Encrypt existing file
|
||||
ansible-vault encrypt vars.yml
|
||||
|
||||
# Run with vault password
|
||||
ansible-playbook playbook.yml --ask-vault-pass
|
||||
ansible-playbook playbook.yml --vault-password-file ~/.vault_pass
|
||||
```
|
||||
|
||||
## Prompt for Variables
|
||||
|
||||
```yaml
|
||||
- hosts: all
|
||||
vars_prompt:
|
||||
- name: password
|
||||
prompt: "Enter password"
|
||||
private: true
|
||||
|
||||
- name: environment
|
||||
prompt: "Which environment?"
|
||||
default: "staging"
|
||||
```
|
||||
|
||||
## Conditionals with Variables
|
||||
|
||||
```yaml
|
||||
- name: Check defined
|
||||
when: my_var is defined
|
||||
|
||||
- name: Check undefined
|
||||
when: my_var is not defined
|
||||
|
||||
- name: Check truthy
|
||||
when: my_var | bool
|
||||
|
||||
- name: Check falsy
|
||||
when: not my_var | bool
|
||||
|
||||
- name: Check in list
|
||||
when: item in my_list
|
||||
|
||||
- name: Version comparison
|
||||
when: version is version('2.0', '>=')
|
||||
```
|
||||
|
||||
## Hostvars
|
||||
|
||||
Access variables from other hosts:
|
||||
|
||||
```yaml
|
||||
- name: Get from other host
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ hostvars['web1']['ansible_host'] }}"
|
||||
```
|
||||
Reference in New Issue
Block a user