How to Configure Environment-Specific SSH Credentials in Ansible: Production vs Staging Setup


2 views

When managing infrastructure across multiple environments, hardcoding SSH credentials in ansible.cfg creates operational headaches. The limitation becomes apparent when you need:

  • Different SSH keys for staging vs production
  • Varying remote user accounts per environment
  • Environment-specific sudo configurations

The most elegant solution leverages Ansible's inventory system to define environment-specific credentials. Here's how to structure your inventory files:

# staging_inventory.ini
[staging]
staging-server-1 ansible_host=192.168.1.10
staging-server-2 ansible_host=192.168.1.11

[staging:vars]
ansible_ssh_private_key_file=~/.ssh/staging_key.pem
ansible_user=stage_admin
ansible_become_user=stage_root
# production_inventory.ini
[production]
prod-server-1 ansible_host=10.0.0.10
prod-server-2 ansible_host=10.0.0.11

[production:vars]
ansible_ssh_private_key_file=~/.ssh/production_key.pem
ansible_user=prod_admin
ansible_become_user=root

For complex environments, consider using group variable files:

# group_vars/staging.yml
---
ansible_ssh_private_key_file: "{{ lookup('env','HOME') }}/.ssh/staging_key.pem"
ansible_user: stage_admin
ansible_port: 2222
# group_vars/production.yml
---
ansible_ssh_private_key_file: "{{ lookup('env','HOME') }}/.ssh/production_key.pem"
ansible_user: prod_admin
ansible_port: 22

For enhanced security, integrate with secret management systems:

# vars/main.yml
---
ssh_credentials:
  staging:
    key: "vaulted_staging_key"
    user: "stage_admin"
  production:
    key: "vaulted_prod_key"
    user: "prod_admin"

Then reference these in your playbook:

- name: Apply environment-specific configuration
  hosts: all
  vars:
    env_creds: "{{ ssh_credentials[inventory_hostname.split('.')[0]] }}"
  tasks:
    - name: Debug SSH config
      debug:
        msg: "Using key {{ env_creds.key }} for user {{ env_creds.user }}"

Run playbooks with environment-specific inventories:

ansible-playbook -i staging_inventory.ini site.yml
ansible-playbook -i production_inventory.ini site.yml

When working with Ansible across multiple environments, a common pain point emerges: SSH credential management. While Ansible's inventory system elegantly handles server separation through -i flags, SSH configurations in ansible.cfg remain stubbornly global. This creates security and operational challenges when different environments require distinct authentication methods.

The traditional approach of hardcoding credentials in /etc/ansible/ansible.cfg presents several issues:

[defaults]
private_key_file=/home/caleb/.ssh/staging_key.pem
remote_user=ubuntu
sudo_user=root

This configuration forces all environments to use the same SSH key and user, which violates security best practices where production systems typically demand stricter access controls.

1. Environment-Specific Inventory Variables

The most elegant solution leverages Ansible's inventory variable precedence. Create separate inventory files with environment-specific SSH configurations:

# staging_inventory.ini
[all:vars]
ansible_ssh_private_key_file=/home/caleb/.ssh/staging_key.pem
ansible_user=staging_user

# production_inventory.ini  
[all:vars]
ansible_ssh_private_key_file=/home/caleb/.ssh/production_key.pem
ansible_user=prod_admin
ansible_become=true

2. Group Variables Directory Structure

For more complex setups, organize credentials using Ansible's group variables:

inventory/
├── production
│   ├── group_vars
│   │   └── all.yml
├── staging
│   ├── group_vars
│   │   └── all.yml

Example staging/group_vars/all.yml:

ansible_ssh_private_key_file: "{{ lookup('env','HOME') }}/.ssh/staging_key.pem"
ansible_user: deploy_staging

3. Dynamic SSH Config with Include Files

For maximum flexibility, generate SSH configurations at runtime:

# playbook.yml
- hosts: all
  tasks:
    - name: Set SSH config based on environment
      set_fact:
        ansible_ssh_private_key_file: "{% if inventory_file == 'staging' %}/path/to/staging_key{% else %}/path/to/production_key{% endif %}"

Always ensure:

  • Production keys have stricter file permissions (600)
  • Consider using Ansible Vault for credential management
  • Implement different SSH bastion hosts per environment

For granular control, set credentials at host level:

# inventory.ini
[production]
prod1.example.com ansible_ssh_private_key_file=/keys/prod1_key ansible_user=admin1
prod2.example.com ansible_ssh_private_key_file=/keys/prod2_key ansible_user=admin2