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

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