8

server1 is in the group it_servers, but I'd like to use ansible_connection: local. How can this be done if ansible_connection is defined in the playbook and the host_vars?

--
- hosts: it_servers
  vars:
    ansible_connection: aws_ssm
  roles:
    - nginx
    - mysql

My host_vars/server1.yml file

ansible_connection: local
Peter Mortensen
  • 2,318
  • 5
  • 23
  • 24
bayman
  • 243
  • 1
  • 4
  • 10

3 Answers3

9

Q: "How to override playbook var if it's set in host_vars in Ansible?"

A: Take a look at Variable precedence. The precedence of play vars is 12. There are 10 more possibilities of how to override play vars, but none of them will let you selectively override a variable for a single host.

You'll have to delete the declaration of ansible_connection: aws_ssm from the playbook if you want to change it for a single host. The best place for the group declaration of the connection is group_vars (precedence 3-7) and the best place to override the group_vars for a single host is host_vars (precedence 8-10). For example

shell> cat hosts
[aws1]
server1 ansible_connection=local      # precedence 8.
server2
server3

[aws1:vars]
ansible_connection=aws_ssm            # precedence 3.

There are many combinations of host_vars and group_vars to achieve this scenario. But, if you set a variable at play vars (precedence 12) you can't override it for a single host anymore.


Dynamic variable

It's possible to declare the variable dynamically. For example

ansible_connection: "{{ 'local'
                        if inventory_hostname == 'server1'
                        else
                        'aws_ssm' }}"

This would work at any precedence level. But, because of the lazy evaluation, it's very inefficient. The variable will be evaluated each time referenced.


'Instantiate' the dynamic variable

If you really need to use the dynamic variable 'instantiate' it to avoid repeated evaluation. What does it mean? For example, given the inventory (in YAML for better readability)

shell> cat hosts
all:
  hosts:
    server1:
      ansible_host: localhost
      ansible_python_interpreter: /usr/bin/python3.8
    server2:
      ansible_host: 10.1.0.62
      ansible_user: admin
      ansible_python_interpreter: /usr/local/bin/python3.8
    server3:
      ansible_host: 10.1.0.63
      ansible_user: admin
      ansible_python_interpreter: /usr/local/bin/python3.8
  children:
    servers:
      hosts:
        server1:
        server2:
        server3:

The playbook

- hosts: servers
  vars:
    ansible_connection: "{{ 'local'
                            if inventory_hostname == 'server1'
                            else
                            'ssh' }}"
  tasks:
    - debug:
        msg: "{{ ansible_play_hosts|
                 map('extract', hostvars, 'ansible_connection') }}"
      run_once: true
    - debug:
        var: ansible_connection

gives

PLAY [servers] *****************************************************

TASK [Gathering Facts] *********************************************
ok: [server1]
ok: [server2]
ok: [server3]

TASK [debug] *******************************************************
ok: [server1] => 
  msg: '[AnsibleUndefined, AnsibleUndefined, AnsibleUndefined]'

TASK [debug] *******************************************************
ok: [server1] => 
  ansible_connection: local
ok: [server2] => 
  ansible_connection: ssh
ok: [server3] => 
  ansible_connection: ssh

The connections work as expected, but the variable ansible_connection is not included in the hostvars. Use the module set_fact and 'instantiate' the variable, e.g.

    - set_fact:
        ansible_connection: "{{ ansible_connection }}"
    - debug:
        msg: "{{ ansible_play_hosts|
                 map('extract', hostvars, 'ansible_connection') }}"
      run_once: true

gives

TASK [set_fact] ****************************************************
ok: [server1]
ok: [server2]
ok: [server3]

TASK [debug] *******************************************************
ok: [server1] => 
  msg:
  - local
  - ssh
  - ssh
Vladimir Botka
  • 5,138
  • 8
  • 20
0

You can use delegate_to: localhost for the task.

Gerald Schneider
  • 23,274
  • 8
  • 57
  • 89
0

Ansible does apply variable precedence, and you might have a use for it. Here is the order of precedence from least to greatest (the last listed variables override all other variables):

command line values (for example, -u my_user, these are not variables)

role defaults (defined in role/defaults/main.yml) 1

inventory file or script group vars 2

inventory group_vars/all 3

playbook group_vars/all 3

inventory group_vars/* 3

playbook group_vars/* 3

inventory file or script host vars 2

inventory host_vars/* 3

playbook host_vars/* 3

host facts / cached set_facts 4

play vars

play vars_prompt

play vars_files

role vars (defined in role/vars/main.yml)

block vars (only for tasks in block)

task vars (only for the task)

include_vars

set_facts / registered vars

role (and include_role) params

include params

extra vars (for example, -e "user=my_user")(always win precedence)

This is better explained here: https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable

Max
  • 11
  • 1