Introduction

The no-log-password ansible-lint rule warns when tasks that handle passwords or secrets don't have no_log: true set. Without it, sensitive data appears in plaintext in Ansible's output, log files, and callback plugins — a security risk in CI/CD pipelines and shared terminals.

The Problem

# ⚠️ TRIGGERS no-log-password warning
- name: Create user accounts
  ansible.builtin.user:
    name: "{{ item.name }}"
    password: "{{ item.password | password_hash('sha512') }}"
  loop:
    - { name: alice, password: secret123 }
    - { name: bob, password: hunter2 }

Ansible output exposes the password in the loop item:

TASK [Create user accounts] **************************************************
changed: [web01] => (item={'name': 'alice', 'password': 'secret123'})
changed: [web01] => (item={'name': 'bob', 'password': 'hunter2'})

Lint output:

no-log-password: Tasks that set passwords should use no_log to prevent disclosure.

The Fix: Add no_log: true

- name: Create user accounts
  ansible.builtin.user:
    name: "{{ item.name }}"
    password: "{{ item.password | password_hash('sha512') }}"
  loop:
    - { name: alice, password: "{{ vault_alice_password }}" }
    - { name: bob, password: "{{ vault_bob_password }}" }
  no_log: true

Now Ansible output shows:

TASK [Create user accounts] **************************************************
changed: [web01] => (item=CENSORED)
changed: [web01] => (item=CENSORED)

Modules That Trigger This Rule

The rule checks tasks using these parameters:

ModuleParameter
ansible.builtin.userpassword
ansible.builtin.lineinfileline (when contains password)
ansible.builtin.uribody, url_password
ansible.builtin.command / shellArguments containing secrets
community.mysql.mysql_userpassword
community.postgresql.postgresql_userpassword
community.general.ldap_passwdpasswd

Best Practices

1. Use Ansible Vault for Secrets

Never hardcode passwords. Store them encrypted:

# Create encrypted vars file
ansible-vault create group_vars/all/vault.yml
# group_vars/all/vault.yml (encrypted)
vault_alice_password: "SuperSecret123!"
vault_bob_password: "AnotherSecret456!"
vault_db_password: "DbP@ssw0rd"

Reference in playbooks:

- name: Create database user
  community.mysql.mysql_user:
    name: app_user
    password: "{{ vault_db_password }}"
    priv: "app_db.*:ALL"
  no_log: true

2. Use no_log Conditionally for Debugging

- name: Create user
  ansible.builtin.user:
    name: deploy
    password: "{{ vault_deploy_password | password_hash('sha512') }}"
  no_log: "{{ not ansible_verbosity >= 3 }}"

This hides output normally but shows it with -vvv for debugging.

3. Use no_log at Block Level

- name: Secret operations
  no_log: true
  block:
    - name: Set database password
      community.mysql.mysql_user:
        name: app
        password: "{{ vault_db_password }}"

    - name: Create API key file
      ansible.builtin.copy:
        content: "{{ vault_api_key }}"
        dest: /etc/myapp/api.key
        mode: '0600'

4. Register Variables Carefully

# ⚠️ Registered result may contain secrets
- name: Get secret from API
  ansible.builtin.uri:
    url: https://vault.example.com/v1/secret/data/myapp
    headers:
      X-Vault-Token: "{{ vault_token }}"
  register: secret_response
  no_log: true

# Safe — only access specific fields
- name: Use the secret
  ansible.builtin.debug:
    msg: "Secret retrieved successfully"
  when: secret_response.status == 200

5. Environment Variables for CI/CD

- name: Deploy with credentials
  ansible.builtin.shell: |
    deploy --token "$DEPLOY_TOKEN"
  environment:
    DEPLOY_TOKEN: "{{ vault_deploy_token }}"
  no_log: true

When NOT to Use no_log

Don't use no_log: true on every task — it makes debugging very difficult:

# DON'T — this has no secrets
- name: Install packages
  ansible.builtin.yum:
    name: nginx
  no_log: true  # Unnecessary, hides useful debug info

# DO — only on tasks with actual secrets
- name: Configure nginx password
  ansible.builtin.htpasswd:
    path: /etc/nginx/.htpasswd
    name: admin
    password: "{{ vault_admin_password }}"
  no_log: true

Suppressing the Rule

If you've verified no real secret is exposed:

- name: Task with false positive
  ansible.builtin.user:
    name: testuser
    password: "!"  # Locked account, not a real password
  no_log: false  # noqa: no-log-password

Conclusion

Add no_log: true to any task that handles passwords, API keys, or tokens. Store secrets in Ansible Vault — never hardcode them. Use no_log at block level for groups of sensitive tasks, and use conditional no_log (not ansible_verbosity >= 3) when you need debugging capability. Only suppress the lint rule when you've verified no real secrets are exposed.