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