Score:5

How can I override a playbook var if it's set in host_vars in Ansible?

us flag

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
Score:6
br flag

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
Score:0
in flag

You can use delegate_to: localhost for the task.

Score:0
in flag
Max

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

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.