AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

Ansible Secrets Management: Best Practices for Enterprise Credential Security

By Luca Berton · Published 2024-01-01 · Category: troubleshooting

Master Ansible secrets management for enterprise environments. Ansible Vault, external secret managers, no_log, credential rotation, and security best.

Introduction

Every Ansible playbook eventually needs secrets — database passwords, API tokens, SSH keys, certificates. How you manage these secrets determines whether your automation is a security asset or liability. This guide covers every approach to secrets management in Ansible, from built-in Vault encryption to enterprise integrations.

See also: Ansible Container Security: Image Scanning, Runtime Protection, and Supply Chain Security

Ansible Vault

Encrypt a Variable File

# Create encrypted file
ansible-vault create group_vars/production/vault.yml

# Encrypt existing file ansible-vault encrypt group_vars/production/secrets.yml

# Edit encrypted file ansible-vault edit group_vars/production/vault.yml

# View without editing ansible-vault view group_vars/production/vault.yml

# Decrypt (use sparingly) ansible-vault decrypt group_vars/production/vault.yml

Encrypt Individual Variables

# Encrypt a single string
ansible-vault encrypt_string 'SuperSecretP@ss!' --name 'db_password'

# Output: # db_password: !vault | # $ANSIBLE_VAULT;1.1;AES256 # 62616463...

Use in variables:

# group_vars/production/vault.yml
vault_db_password: !vault |
  $ANSIBLE_VAULT;1.1;AES256
  62616463653266383137353...

vault_api_token: !vault | $ANSIBLE_VAULT;1.1;AES256 39363865316332613562...

Reference Pattern

# group_vars/production/vars.yml (not encrypted)
db_password: "{{ vault_db_password }}"
api_token: "{{ vault_api_token }}"
smtp_password: "{{ vault_smtp_password }}"

# group_vars/production/vault.yml (encrypted) vault_db_password: "ActualPassword123!" vault_api_token: "ghp_xxxxxxxxxxxx" vault_smtp_password: "SmtpPass456!"

This pattern lets you grep for variable usage without decrypting.

Multiple Vault IDs

# Different passwords for different environments
ansible-playbook site.yml \
  --vault-id dev@prompt \
  --vault-id prod@/path/to/prod-vault-pass

# In vault files, specify which ID: ansible-vault encrypt --vault-id prod@prompt secrets.yml

Vault Password Sources

# Interactive prompt
ansible-playbook site.yml --ask-vault-pass

# Password file ansible-playbook site.yml --vault-password-file ~/.vault_pass

# Script (dynamic password from secret manager) ansible-playbook site.yml --vault-password-file vault-pass.sh

# Environment variable (CI/CD) ANSIBLE_VAULT_PASSWORD_FILE=~/.vault_pass ansible-playbook site.yml

#!/bin/bash
# vault-pass.sh — fetch vault password from external source
aws secretsmanager get-secret-value \
  --secret-id ansible-vault-password \
  --query SecretString --output text

no_log: Protecting Output

- name: Create database user
  community.postgresql.postgresql_user:
    name: app_user
    password: "{{ db_password }}"
  no_log: true  # Prevents password from appearing in logs

- name: Deploy API configuration ansible.builtin.template: src: api-config.j2 dest: /etc/myapp/config.yml mode: '0600' no_log: true # Template contains secrets

- name: Call external API with token ansible.builtin.uri: url: "{{ api_endpoint }}" headers: Authorization: "Bearer {{ api_token }}" no_log: true register: api_result

# Show non-sensitive parts of result - name: Display API response status ansible.builtin.debug: msg: "API returned status {{ api_result.status }}"

Global no_log

# For entire play
- name: Secret-heavy deployment
  hosts: all
  no_log: true  # All tasks hidden
  tasks:
    - name: This output is hidden
      ansible.builtin.debug:
        msg: "You won't see this"

See also: Ansible Zero Trust Security: Implement Zero Trust Architecture for Enterprise Infrastructure

External Secret Managers

HashiCorp Vault

- name: Fetch secrets from HashiCorp Vault
  vars:
    db_creds: "{{ lookup('community.hashi_vault.hashi_vault',
      'secret/data/myapp/database',
      url='https://vault.example.com',
      auth_method='approle',
      role_id=lookup('env', 'VAULT_ROLE_ID'),
      secret_id=lookup('env', 'VAULT_SECRET_ID')
    ) }}"
  tasks:
    - name: Use dynamic credentials
      ansible.builtin.template:
        src: db-config.j2
        dest: /etc/myapp/db.conf
      vars:
        db_user: "{{ db_creds.data.username }}"
        db_pass: "{{ db_creds.data.password }}"
      no_log: true

AWS Secrets Manager

    - name: Fetch from AWS Secrets Manager
      ansible.builtin.set_fact:
        aws_secret: "{{ lookup('amazon.aws.aws_secret',
          'myapp/production/database',
          region='us-east-1') | from_json }}"
      no_log: true

- name: Use AWS secret ansible.builtin.template: src: config.j2 dest: /etc/myapp/config.yml vars: db_password: "{{ aws_secret.password }}" no_log: true

Azure Key Vault

    - name: Fetch from Azure Key Vault
      ansible.builtin.set_fact:
        azure_secret: "{{ lookup('azure.azcollection.azure_keyvault_secret',
          'db-password',
          vault_url='https://mykeyvault.vault.azure.net') }}"
      no_log: true

CyberArk

    - name: Fetch from CyberArk
      ansible.builtin.set_fact:
        cyberark_creds: "{{ lookup('cyberark.conjur.conjur_variable',
          'production/database/password') }}"
      no_log: true

AAP Credential Management

# AAP Credential Types:
# - Machine (SSH)
# - Source Control (Git)
# - Vault (Ansible Vault password)
# - Network (Router/Switch)
# - Cloud (AWS, Azure, GCP)
# - Custom (any external system)

# Credentials in AAP are: # ✅ Encrypted at rest (AES-256) # ✅ Never exposed in job output # ✅ RBAC-controlled (Use vs Admin) # ✅ Injected at runtime (env vars or extra vars) # ✅ Rotatable without changing playbooks

Custom Credential Type for External Secrets

# Input Configuration
fields:
  - id: secret_server_url
    type: string
    label: Secret Server URL
  - id: secret_server_token
    type: string
    label: API Token
    secret: true
  - id: secret_path
    type: string
    label: Secret Path

# Injector Configuration env: SECRET_SERVER_URL: "{{ secret_server_url }}" SECRET_SERVER_TOKEN: "{{ secret_server_token }}" extra_vars: secret_lookup_path: "{{ secret_path }}"

See also: HashiCorp Vault Integration with Ansible Automation Platform: Credential Management at Scale

Credential Rotation

- name: Rotate database credentials
  hosts: localhost
  tasks:
    - name: Generate new password
      ansible.builtin.set_fact:
        new_password: "{{ lookup('password', '/dev/null length=32 chars=ascii_letters,digits,punctuation') }}"
      no_log: true

- name: Update password in database community.postgresql.postgresql_user: login_host: "{{ db_host }}" login_user: admin login_password: "{{ vault_db_admin_pass }}" name: app_user password: "{{ new_password }}" no_log: true

- name: Store new password in Vault community.hashi_vault.vault_kv2_write: url: "{{ vault_url }}" path: "secret/myapp/database" data: username: app_user password: "{{ new_password }}" no_log: true

- name: Update application config ansible.builtin.template: src: db-config.j2 dest: /etc/myapp/db.conf delegate_to: "{{ item }}" loop: "{{ groups['app_servers'] }}" notify: restart application no_log: true

- name: Verify application connectivity ansible.builtin.uri: url: "http://{{ item }}:8080/health" status_code: 200 loop: "{{ groups['app_servers'] }}"

Security Anti-Patterns

# ❌ NEVER: Hardcode secrets
- name: Bad practice
  ansible.builtin.command: mysql -u root -pMyPassword123

# ❌ NEVER: Secrets in task names - name: "Connect to DB with password {{ db_pass }}" ansible.builtin.debug:

# ❌ NEVER: Log secrets via debug - name: Show credentials ansible.builtin.debug: var: db_password

# ❌ NEVER: Secrets in command line # ansible-playbook site.yml -e "password=Secret123" # Shows in ps output!

# ✅ CORRECT: Use variable files + vault + no_log - name: Deploy securely ansible.builtin.template: src: config.j2 dest: /etc/myapp/config.yml no_log: true

Git Security

# .gitignore — never commit these
*.vault_pass
vault-password*
*.pem
*.key
id_rsa*
.env
secrets.yml.dec
# Pre-commit hook to prevent unencrypted secrets
#!/bin/bash
# .git/hooks/pre-commit
for file in $(git diff --cached --name-only | grep vault); do
  if ! head -1 "$file" | grep -q '$ANSIBLE_VAULT'; then
    echo "ERROR: $file is not encrypted!"
    exit 1
  fi
done

Best Practices

Encrypt at rest — All secrets in Vault-encrypted files or external secret managers no_log: true everywhere — Every task that touches secrets Vault prefix conventionvault_ prefix for encrypted vars, reference in plain vars External secret managers for production — Ansible Vault for dev; HashiCorp Vault/AWS SM for prod Rotate regularly — Automate credential rotation with playbooks Never commit plaintext — Pre-commit hooks to catch unencrypted vault files Separate vault passwords per environment — Different encryption keys for dev/staging/prod AAP credential isolation — Teams can Use credentials without seeing secret values Audit secret access — Log who accessed what secret and when

FAQ

Ansible Vault vs HashiCorp Vault?

Ansible Vault encrypts files at rest (symmetric AES-256). HashiCorp Vault is a full secret management platform with dynamic secrets, rotation, and audit. Use Ansible Vault for simple cases; HashiCorp Vault for enterprise.

Can secrets leak through Ansible facts?

Yes — if a task registers output containing secrets and facts are cached. Always use no_log: true and avoid caching sensitive registered variables.

How to handle secrets in Molecule tests?

Use test-specific vault files with known test passwords, or mock external secret lookups in test scenarios.

Conclusion

Secrets management is the foundation of secure automation. By combining Ansible Vault encryption, external secret managers, no_log protection, and automated credential rotation, you can build automation pipelines that handle sensitive data safely at enterprise scale.

Related Articles

Ansible Vault Complete GuideHashiCorp Vault Integration with AAPAnsible Zero Trust Security

Category: troubleshooting

Browse all Ansible tutorials · AnsiblePilot Home