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