When managing multiple servers with Ansible, you often need to distribute configuration files that share the same filename but contain different content based on the target machine's role or hostname. A classic example would be deploying /etc/nginx/nginx.conf
with role-specific configurations across web servers.
Ansible provides several elegant ways to handle this:
# Method 1: Using host_vars and group_vars
# In your inventory file:
[webservers]
web1 ansible_host=192.168.1.10 role=frontend
web2 ansible_host=192.168.1.11 role=backend
# Then in group_vars/webservers/file.yml or host_vars/web1/file.yml
file_content: |
This is specific content for {{ role }}
For more complex scenarios, Jinja2 templates work best:
- name: Deploy role-specific configuration
template:
src: "templates/file.conf.j2"
dest: "/etc/service/file.conf"
owner: root
group: root
mode: '0644'
When you need completely different source files (not just variable substitution):
- name: Copy role-specific file
copy:
src: "files/{{ inventory_hostname }}.file"
or
src: "files/{{ ansible_hostname }}.file"
or
src: "files/{{ role }}.file"
dest: "/path/to/destination/file"
For more complex decision trees:
- name: Deploy appropriate file
copy:
src: "files/{% if 'db' in group_names %}db.file{% elif 'web' in group_names %}web.file{% else %}default.file{% endif %}"
dest: "/etc/application/config.file"
- Store role-specific files in
roles/rolename/files/
directory structure - Use Ansible facts (
ansible_hostname
,inventory_hostname
) for maximum flexibility - Consider using the
first_found
lookup for fallback scenarios
Here's a full playbook example:
- hosts: all
tasks:
- name: Determine correct source file
set_fact:
config_file: "{{ 'file.' + role | default('default') }}"
- name: Deploy configuration
copy:
src: "files/{{ config_file }}"
dest: "/etc/app/config"
owner: appuser
group: appgroup
mode: '0640'
When managing infrastructure with Ansible, we often need to distribute configuration files that share the same filename but contain different content based on either:
- The target host's hostname
- The role assigned to the host
Here are three effective methods to implement this in Ansible:
1. Using Role-Based File Selection
- name: Copy role-specific file
ansible.builtin.copy:
src: "files/file.{{ ansible_role }}"
dest: "/path/to/destination/file"
2. Hostname-Based Conditional Copy
- name: Copy file based on hostname
ansible.builtin.copy:
src: "files/file.{{ inventory_hostname }}"
dest: "/path/to/destination/file"
when: inventory_hostname in ['host1', 'host2', 'host3']
3. Combined Hostname and Role Logic
- name: Copy file with complex conditions
ansible.builtin.copy:
src: "files/file.{{ ansible_role }}"
dest: "/path/to/destination/file"
when:
- ansible_role == 'webserver'
- inventory_hostname.startswith('prod-')
For more complex scenarios, consider using Ansible's vars_files
directive:
- name: Include host-specific variables
include_vars: "host_vars/{{ inventory_hostname }}.yml"
- name: Copy dynamically determined file
ansible.builtin.copy:
src: "files/file.{{ file_variant }}"
dest: "/path/to/destination/file"
- Store all variant files in a dedicated directory structure
- Use consistent naming conventions (e.g., role-based suffixes)
- Document the file distribution logic in your playbook comments
- Consider using Ansible Vault for sensitive configuration files
Here's a complete playbook example for Nginx configuration distribution:
- hosts: webservers
tasks:
- name: Determine config variant
set_fact:
config_variant: "{% if 'production' in inventory_hostname %}prod{% else %}dev{% endif %}"
- name: Copy Nginx config
ansible.builtin.copy:
src: "templates/nginx.conf.{{ config_variant }}"
dest: "/etc/nginx/nginx.conf"
owner: root
group: root
mode: '0644'