# Ansible Vault SSH Key Management Guide

## Overview
This document explains how to securely manage SSH private keys using Ansible Vault and the copy module with decryption capabilities, including proper playbook configuration to handle decryption before fact gathering.

## Important: Playbook Configuration

### Critical Setup Requirements
```yaml
---
- name: Deploy with encrypted SSH key
  hosts: all
  gather_facts: false  # ⚠️ ESSENTIAL - prevents early SSH connection attempts
  vars:
    ssh_private_key_file: key/deploy  # Path to your encrypted key file
  
  tasks:
    # Your decryption and deployment tasks here
```

### Why `gather_facts: false` is Mandatory

**Without `gather_facts: false`:**
- Ansible attempts to connect to target hosts immediately
- It tries to use SSH with the default SSH agent configuration
- Fails because the encrypted key isn't decrypted yet
- Playbook stops before reaching your decryption task

**With `gather_facts: false`:**
- Ansible skips the initial fact gathering phase
- Allows your decryption task to run first
- You control when and how the SSH key is used

## Step 1: Encrypt SSH Private Key with Ansible Vault

### Encrypt an existing SSH key:
```bash
# Method 1: Interactive password prompt
ansible-vault encrypt key/deploy --ask-vault-pass

# Method 2: Using a password file
ansible-vault encrypt key/deploy --vault-password-file vault_pass.txt

# Method 3: Using vault ID
ansible-vault encrypt key/deploy --vault-id deploy@vault_pass.txt
```

### Create a new encrypted SSH key:
```bash
ansible-vault create key/deploy --vault-password-file vault_pass.txt
```

### Verify encrypted content:
```bash
ansible-vault view key/deploy --vault-password-file vault_pass.txt
```

## Step 2: Complete Ansible Playbook Example

### Full playbook structure:
```yaml
---
- name: Deploy using encrypted SSH key
  hosts: all
  gather_facts: false  # CRITICAL: Must be disabled
  vars:
    ssh_private_key_file: key/deploy
    decrypted_key_path: /tmp/ansible_deploy_key
  
  tasks:
    - name: Decrypt SSH private key
      copy:
        src: "{{ ssh_private_key_file }}"
        dest: "{{ decrypted_key_path }}"
        decrypt: yes
        mode: '0600'
      delegate_to: localhost
      become: false
      run_once: true

    - name: Enable fact gathering with decrypted key
      setup:
      delegate_to: localhost
      become: false

    - name: Use the decrypted key for deployment
      ansible.builtin.shell: |
        ssh -i "{{ decrypted_key_path }}" \
            -o StrictHostKeyChecking=no \
            -o UserKnownHostsFile=/dev/null \
            deploy@{{ inventory_hostname }} 'deployment_command'
      args:
        executable: /bin/bash

    - name: Remove decrypted SSH key (cleanup)
      file:
        path: "{{ decrypted_key_path }}"
        state: absent
      delegate_to: localhost
      become: false
      always: yes
```

## Step 3: Running the Playbook

### Execution methods:
```bash
# With password file
ansible-playbook playbook.yml --vault-password-file vault_pass.txt

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

# With vault ID
ansible-playbook playbook.yml --vault-id deploy@vault_pass.txt

# With inventory file
ansible-playbook -i hosts.ini playbook.yml --vault-password-file vault_pass.txt
```

## Step 4: Advanced Error Handling & Security

### Robust playbook with proper error handling:
```yaml
---
- name: Secure deployment with encrypted SSH key
  hosts: all
  gather_facts: false
  vars:
    ssh_private_key_file: key/deploy
    decrypted_key_path: "/tmp/ansible_deploy_key_{{ ansible_date_time.epoch }}"
  
  tasks:
    - name: Ensure encrypted key file exists
      stat:
        path: "{{ ssh_private_key_file }}"
      register: key_file
      delegate_to: localhost
      become: false
    
    - name: Fail if encrypted key is missing
      fail:
        msg: "Encrypted SSH key file {{ ssh_private_key_file }} not found"
      when: not key_file.stat.exists
      delegate_to: localhost
      become: false

    - name: Decrypt SSH private key
      copy:
        src: "{{ ssh_private_key_file }}"
        dest: "{{ decrypted_key_path }}"
        decrypt: yes
        mode: '0600'
      delegate_to: localhost
      become: false
      run_once: true

    - name: Verify decrypted key permissions
      file:
        path: "{{ decrypted_key_path }}"
        mode: '0600'
      delegate_to: localhost
      become: false

    - name: Gather facts using decrypted key (if needed)
      setup:
      delegate_to: localhost
      become: false

    - name: Perform deployment tasks
      block:
        - name: Execute remote deployment
          ansible.builtin.shell: |
            ssh -i "{{ decrypted_key_path }}" \
                -o ConnectTimeout=30 \
                -o StrictHostKeyChecking=no \
                deploy@{{ inventory_hostname }} 'your_deployment_script'
          args:
            executable: /bin/bash
          register: deployment_result
        
        - name: Display deployment output
          debug:
            var: deployment_result.stdout

      rescue:
        - name: Handle deployment failure
          debug:
            msg: "Deployment failed - check SSH connectivity and permissions"
      
      always:
        - name: Always remove decrypted key
          file:
            path: "{{ decrypted_key_path }}"
            state: absent
          delegate_to: localhost
          become: false
```

## Security Best Practices

### 1. File Security:
```bash
# Secure permissions for password files
chmod 600 vault_pass.txt

# Secure permissions for encrypted key
chmod 600 key/deploy
```

### 2. Temporary File Safety:
- Use unique temporary filenames with timestamps
- Set strict permissions (0600)
- Always clean up, even on failure

### 3. Key Management:
- Never store unencrypted keys in version control
- Rotate deployment keys regularly
- Use different keys for different environments

## Troubleshooting

### Common Issues & Solutions:

1. **"Permission denied" errors:**
   - Verify vault password is correct
   - Check encrypted file permissions
   - Ensure cleanup tasks run successfully

2. **SSH connection failures:**
   - Verify the decrypted key is authorized on target hosts
   - Check network connectivity
   - Validate target host accessibility

3. **Fact gathering issues:**
   - Use `gather_facts: false` in main playbook
   - Manually call `setup` module after decryption if needed

### Debug Commands:
```bash
# Test vault decryption
ansible-vault view key/deploy --vault-password-file vault_pass.txt

# Verify playbook syntax
ansible-playbook playbook.yml --syntax-check

# Dry run to see what would happen
ansible-playbook playbook.yml --vault-password-file vault_pass.txt --check
```

## Summary
The key points for successful encrypted SSH key management:

1. **Always use `gather_facts: false`** in the main playbook
2. **Decrypt the key early** in your tasks
3. **Use unique temporary paths** for decrypted keys
4. **Always clean up** decrypted keys, even on failures
5. **Secure your vault passwords** with proper file permissions

This approach ensures your SSH keys remain encrypted at rest and are only temporarily decrypted during execution, maintaining security throughout your deployment process.
