home / skills / basher83 / lunar-claude / ansible-secrets

This skill securely retrieves Infisical secrets in Ansible, enforces no_log, and supports fallbacks for safe, reusable secret handling.

npx playbooks add skill basher83/lunar-claude --skill ansible-secrets

Review the files below or copy the command above to add this skill to your agents.

Files (2)
SKILL.md
8.6 KB
---
name: ansible-secrets
description: >
  This skill should be used when working with secrets in Ansible playbooks, integrating
  Infisical vault, using no_log directive, retrieving credentials securely, or
  implementing fallback patterns for secrets. Covers the reusable Infisical lookup task.
---

# Ansible Secrets Management

Secure secrets handling with Infisical integration and proper security practices.

## Architecture Overview

```text
┌──────────────┐
│   Ansible    │
│   Playbook   │
└──────┬───────┘
       │
       │ include_tasks: infisical-secret-lookup.yml
       │
       ▼
┌──────────────────┐
│ Infisical Lookup │
│      Task        │
└──────┬───────────┘
       │
       ├─> Try Universal Auth (preferred)
       │   - INFISICAL_UNIVERSAL_AUTH_CLIENT_ID
       │   - INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET
       │
       ├─> Fallback to Environment Variable (optional)
       │   - Uses specified fallback_env_var
       │
       ▼
┌──────────────┐
│  Infisical   │ (Vault)
│     API      │
└──────────────┘
```

## Reusable Task Pattern

The repository provides a reusable task for secret retrieval at `ansible/tasks/infisical-secret-lookup.yml`.

### Basic Usage

```yaml
- name: Retrieve Proxmox password
  ansible.builtin.include_tasks: tasks/infisical-secret-lookup.yml
  vars:
    secret_name: 'PROXMOX_PASSWORD'
    secret_var_name: 'proxmox_password'
    infisical_project_id: '7b832220-24c0-45bc-a5f1-ce9794a31259'
    infisical_env: 'prod'
    infisical_path: '/proxmox-cluster'

# Now use the secret
- name: Create Proxmox user
  community.proxmox.proxmox_user:
    api_password: "{{ proxmox_password }}"
    # ... other config ...
  no_log: true
```

### With Fallback

```yaml
- name: Retrieve database password
  ansible.builtin.include_tasks: tasks/infisical-secret-lookup.yml
  vars:
    secret_name: 'DB_PASSWORD'
    secret_var_name: 'db_password'
    fallback_env_var: 'DB_PASSWORD'  # Falls back to $DB_PASSWORD
    infisical_project_id: '7b832220-24c0-45bc-a5f1-ce9794a31259'
    infisical_env: 'prod'
    infisical_path: '/database'
```

### Task Parameters

| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `secret_name` | Yes | - | Name of secret in Infisical |
| `secret_var_name` | Yes | - | Variable name to store secret |
| `infisical_project_id` | No | (repo default) | Infisical project ID |
| `infisical_env` | No | `prod` | Environment (prod, dev, staging) |
| `infisical_path` | No | `/apollo-13/vault` | Path within project |
| `fallback_env_var` | No | - | Env var to use as fallback |
| `allow_empty` | No | `false` | Allow empty secret values |

## Authentication

### Universal Auth (Recommended)

Set environment variables before running playbooks:

```bash
export INFISICAL_UNIVERSAL_AUTH_CLIENT_ID="ua-abc123"
export INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET="secret-xyz789"

cd ansible
uv run ansible-playbook playbooks/my-playbook.yml
```

### Environment Fallback

For local development or CI without Infisical:

```bash
export PROXMOX_PASSWORD="local-dev-password"

cd ansible
uv run ansible-playbook playbooks/my-playbook.yml
```

## Security Best Practices

### 1. Use no_log

On tasks that handle secrets:

```yaml
- name: Set database password
  ansible.builtin.command: set-password {{ password }}
  no_log: true

- name: Deploy config with secrets
  ansible.builtin.template:
    src: config.j2
    dest: /etc/app/config.yml
  no_log: true
```

### 2. Avoid Hard-Coded Secrets

```yaml
# BAD - Exposes secrets
- name: Create user
  community.proxmox.proxmox_user:
    api_password: "my-password-123"  # EXPOSED!

# GOOD
- name: Retrieve password
  ansible.builtin.include_tasks: tasks/infisical-secret-lookup.yml
  vars:
    secret_name: 'PROXMOX_PASSWORD'
    secret_var_name: 'proxmox_password'

- name: Create user
  community.proxmox.proxmox_user:
    api_password: "{{ proxmox_password }}"
  no_log: true
```

### 3. Validate Secret Retrieval

Add validation for critical secrets:

```yaml
- name: Get database password
  ansible.builtin.include_tasks: tasks/infisical-secret-lookup.yml
  vars:
    secret_name: 'DB_PASSWORD'
    secret_var_name: 'db_password'

- name: Validate password complexity
  ansible.builtin.assert:
    that:
      - db_password | length >= 16
    fail_msg: "Password doesn't meet complexity requirements"
  no_log: true
```

### 4. Limit Secret Scope

Retrieve secrets only when needed:

```yaml
# GOOD - Retrieve only when needed
- name: System tasks (no secrets)
  ansible.builtin.apt:
    name: nginx
    state: present

- name: Get credentials (only when needed)
  ansible.builtin.include_tasks: tasks/infisical-secret-lookup.yml
  vars:
    secret_name: 'DB_PASSWORD'
    secret_var_name: 'db_password'

- name: Configure database connection
  ansible.builtin.template:
    src: db-config.j2
    dest: /etc/app/db.yml
  no_log: true
```

### 5. Use Environment Isolation

Separate secrets by environment:

```yaml
# Production
- name: Get prod secret
  ansible.builtin.include_tasks: tasks/infisical-secret-lookup.yml
  vars:
    secret_name: 'DB_PASSWORD'
    secret_var_name: 'db_password'
    infisical_env: 'prod'
    infisical_path: '/production/database'

# Development
- name: Get dev secret
  ansible.builtin.include_tasks: tasks/infisical-secret-lookup.yml
  vars:
    secret_name: 'DB_PASSWORD'
    secret_var_name: 'db_password'
    infisical_env: 'dev'
    infisical_path: '/development/database'
```

## Multiple Secrets Pattern

```yaml
---
- name: Deploy application with secrets
  hosts: app_servers
  become: true

  vars:
    infisical_project_id: '7b832220-24c0-45bc-a5f1-ce9794a31259'
    infisical_env: 'prod'
    infisical_path: '/app-config'

  tasks:
    - name: Retrieve database password
      ansible.builtin.include_tasks: tasks/infisical-secret-lookup.yml
      vars:
        secret_name: 'DB_PASSWORD'
        secret_var_name: 'db_password'

    - name: Retrieve API key
      ansible.builtin.include_tasks: tasks/infisical-secret-lookup.yml
      vars:
        secret_name: 'API_KEY'
        secret_var_name: 'api_key'

    - name: Retrieve Redis password
      ansible.builtin.include_tasks: tasks/infisical-secret-lookup.yml
      vars:
        secret_name: 'REDIS_PASSWORD'
        secret_var_name: 'redis_password'

    - name: Deploy application config
      ansible.builtin.template:
        src: app-config.j2
        dest: /etc/app/config.yml
        owner: app
        group: app
        mode: '0600'
      no_log: true
```

## CI/CD Integration

### GitHub Actions

```yaml
name: Deploy
on: push

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Infisical
        env:
          INFISICAL_CLIENT_ID: ${{ secrets.INFISICAL_CLIENT_ID }}
          INFISICAL_CLIENT_SECRET: ${{ secrets.INFISICAL_CLIENT_SECRET }}
        run: |
          echo "INFISICAL_UNIVERSAL_AUTH_CLIENT_ID=$INFISICAL_CLIENT_ID" >> $GITHUB_ENV
          echo "INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET=$INFISICAL_CLIENT_SECRET" >> $GITHUB_ENV

      - name: Run Ansible
        run: |
          cd ansible
          uv run ansible-playbook playbooks/deploy.yml
```

## Troubleshooting

### Missing Authentication

**Error**: Missing Infisical authentication credentials

**Solution**:

```bash
export INFISICAL_UNIVERSAL_AUTH_CLIENT_ID="ua-abc123"
export INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET="secret-xyz789"
```

### Secret Not Found

**Error**: Failed to retrieve secret from Infisical

**Check**:

1. Secret exists at specified path in Infisical
2. Correct project_id/env/path
3. Service account has read permission

### Empty Secret Value

**Error**: Secret validation failed (empty value)

**Solutions**:

```yaml
# Option 1: Allow empty (not recommended for required secrets)
- name: Get optional secret
  ansible.builtin.include_tasks: tasks/infisical-secret-lookup.yml
  vars:
    secret_name: 'OPTIONAL_KEY'
    secret_var_name: 'optional_key'
    allow_empty: true

# Option 2: Use fallback
- name: Get secret with fallback
  ansible.builtin.include_tasks: tasks/infisical-secret-lookup.yml
  vars:
    secret_name: 'API_KEY'
    secret_var_name: 'api_key'
    fallback_env_var: 'DEFAULT_API_KEY'
```

## Additional Resources

For detailed secrets management patterns, consult:

- **`references/secrets-management.md`** - Infisical integration patterns, no_log best practices, credential security

## Related Skills

- **ansible-fundamentals** - Core Ansible patterns
- **ansible-error-handling** - Error handling for secret retrieval failures

Overview

This skill helps Ansible users retrieve and manage secrets securely using an Infisical integration and safe playbook patterns. It provides a reusable lookup task to fetch secrets, optional environment fallbacks, and guidance for keeping sensitive data out of logs and code. Use it to centralize secret access and enforce retrieval, validation, and limited scope for credentials in playbooks.

How this skill works

The skill exposes a reusable Ansible task that queries Infisical for a named secret and registers it under a provided variable name. It attempts Universal Auth via INFISICAL_UNIVERSAL_AUTH_CLIENT_ID/SECRET and can fall back to an environment variable if configured. The task supports project, environment, and path parameters and can enforce non-empty values or allow optional secrets. Tasks that consume secrets should use no_log to avoid exposing values.

When to use it

  • Retrieving credentials for services (databases, APIs, Redis) within playbooks
  • When you want a single reusable pattern for Infisical secret lookups
  • CI/CD pipelines where secrets are provisioned via environment variables or service auth
  • Local development with an environment fallback for quick iteration
  • Enforcing validation or complexity checks before using secrets in automation

Best practices

  • Always set no_log: true on tasks that handle or pass secrets to modules or templates
  • Prefer Infisical Universal Auth in CI and production; use fallback_env_var only for local/dev scenarios
  • Retrieve secrets only when needed to limit exposure and scope
  • Validate critical secrets (length, format) immediately after retrieval and fail fast on issues
  • Avoid hard-coding secrets in playbooks, templates, or inventory files

Example use cases

  • Fetch DB_PASSWORD from Infisical and assert minimum complexity before templating DB config with no_log
  • Retrieve API_KEY and set it into an application config file, ensuring file mode 0600 and no_log on the template task
  • Use fallback_env_var for local developer overrides while CI uses Universal Auth environment variables
  • Fetch multiple secrets (DB, API, Redis) using repeated include_tasks calls and then deploy a config with limited scope

FAQ

What authentication does the task use by default?

It prefers Infisical Universal Auth via INFISICAL_UNIVERSAL_AUTH_CLIENT_ID and INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET, with optional environment variable fallback if configured.

How do I avoid exposing secrets in logs?

Mark any tasks that handle or consume secrets with no_log: true and restrict templates or commands that write secrets to files with strict permissions.

What if a secret is empty or missing?

You can set allow_empty: true for optional values, or provide fallback_env_var. For required secrets, include assertions to fail early with no_log enabled.