4.4 KiB
4.4 KiB
Ansible secrets onboarding playbook
Overview
This guide onboards secret management for passwords, API keys, and tokens using Ansible Vault. It defines a repeatable workflow for creating encrypted variable files, loading them safely in playbooks, and consuming secrets with idempotent Ansible modules.
What this establishes
1. Standard secret file layout
group_vars/<group>/vault.ymlfor group-level secretshost_vars/<host>/vault.ymlfor host-level secrets- Secret variable names with
_passor_secretsuffixes
2. Encrypted-at-rest secret storage
- Secrets are created and edited with
ansible-vault - Plaintext secrets are not committed to Git
- Existing ignore rules in ansible/.gitignore protect vault files from accidental commits
3. Safe secret consumption patterns
- Use
ansible.builtin.template,ansible.builtin.copy, andansible.builtin.lineinfileinstead of ad-hoc shell commands - Mark sensitive tasks with
no_log: true - Set explicit file ownership and mode for rendered secret files
Prerequisites
- Ansible installed on the control node
- Access to ansible.cfg and your inventory
- A vault password strategy:
- Interactive prompt (
--ask-vault-pass) for manual runs - Password file (
--vault-password-file) for controlled automation
- Interactive prompt (
Important
Do not store vault passwords in repository files or plaintext notes.
Step-by-step onboarding
Step 1: Create vault files
# Group-level secrets
ansible-vault create group_vars/docker/vault.yml
# Host-level secrets
ansible-vault create host_vars/docker-01/vault.yml
Step 2: Add secrets with naming conventions
# group_vars/docker/vault.yml
grafana_admin_pass: "replace-me"
watchtower_api_key_secret: "replace-me"
Step 3: Reference secrets in playbooks or roles
# playbooks/example.yml
- name: Configure app secrets
hosts: docker_hosts
become: true
tasks:
- name: Render application environment file
ansible.builtin.template:
src: templates/app.env.j2
dest: /opt/app/.env
owner: root
group: root
mode: "0600"
no_log: true
# templates/app.env.j2
GRAFANA_ADMIN_PASSWORD={{ grafana_admin_pass }}
WATCHTOWER_API_KEY={{ watchtower_api_key_secret }}
Step 4: Run with vault decryption
# Interactive
ansible-playbook -i inventory/hosts.ini playbooks/example.yml --ask-vault-pass
# Automated (secured local file)
ansible-playbook -i inventory/hosts.ini playbooks/example.yml \
--vault-password-file ~/.ansible/.vault-pass
Step 5: Verify idempotency and secrecy
# Syntax check
ansible-playbook -i inventory/hosts.ini playbooks/example.yml --syntax-check
# Idempotency check (run twice; second run should be unchanged)
ansible-playbook -i inventory/hosts.ini playbooks/example.yml --ask-vault-pass
ansible-playbook -i inventory/hosts.ini playbooks/example.yml --ask-vault-pass
Why module-first instead of shell
ansible.builtin.templateandansible.builtin.copyare idempotent and track file diffs- Explicit
owner,group, andmodeimprove auditability shellcan leak secrets into command history and logs if not handled carefully- Module output is safer to control with
no_log: true
Security guardrails
- Keep
no_log: trueon any task that reads, writes, or debugs secret values - Never print secret variables with
ansible.builtin.debug - Scope secrets to the narrowest level possible (host before group when needed)
- Rotate credentials by updating vault values and re-running playbooks
- Prefer separate vault files per scope to limit blast radius
Troubleshooting
Decryption failed
ansible-vault view group_vars/docker/vault.yml
Use the same vault password source used during file creation.
Variable is undefined
- Confirm secret file path matches inventory group/host names
- Confirm variable names match exactly in templates and tasks
- Run with
-vvand inspect which variable files loaded
Secret file committed by mistake
- Rotate affected credentials immediately
- Remove file from tracking
- Rewrite Git history if secrets were pushed to remote
Integration notes
- Follow the quality checklist in Ansible quality gates
- Keep naming aligned with Naming conventions