如何强制解析Ansible清单中引用host_vars的变量?
环境
Ansible v8.2.0
涉及文件内容
host_vars/example1.test/vault.yml:
vault_network: vault_ipv4_address: 192.168.1.42
host_vars/example1.test/vars.yml:
network: ipv4_address: "{{ vault_network['vault_ipv4_address'] }}"
inventory.yml(Ansible清单):
all: hosts: example1.test: ansible_connection: ssh ansible_host: "{{ hostvars['example1.test']['network']['ipv4_address'] }}"
ansible_control_node.yml(Ansible剧本):
--- - name: Playbook for the 'ansible_control_node' group hosts: ansible_control_node gather_facts: true pre_tasks: - name: Debug#1 - OK ansible.builtin.debug: var: hostvars['example1.test']['network']['ipv4_address'] - name: Debug#2 - OK ansible.builtin.debug: var: hostvars['example1.test']['ansible_connection'] - name: Debug#3 - NOK ansible.builtin.debug: var: hostvars['example1.test']['ansible_host']
问题现象
执行剧本后,Debug#1、Debug#2的变量均能正常解析,但Debug#3输出的ansible_host仅显示未解析的表达式{{ hostvars['example1.test']['network']['ipv4_address'] }}。
问题解答
1. 为什么hostvars['example1.test']['ansible_host']未被解析?
a. 与递归变量解析无关
你提到vault.yml中的类似引用正常工作,是因为那属于主机变量内部的引用,Ansible在加载host_vars时会递归解析同一作用域内的变量。但清单中的ansible_host属于清单变量,它的解析逻辑和主机变量完全不同,不存在递归解析的触发条件。
b. 确实与变量评估顺序有关
虽然Ansible文档说明host_vars会和清单一同加载,但实际执行流程是:
- 先解析清单的结构和基础变量(包括
ansible_host这类清单级变量); - 再加载对应主机的host_vars/group_vars并合并到hostvars中。
清单解析阶段,host_vars的变量还未被合并到hostvars上下文里,因此清单中引用hostvars['example1.test']['network']['ipv4_address']时,这个值还不存在,导致表达式无法被解析,最终以原始字符串形式保留在hostvars中。
2. 解决方向
以下两种方案可实现Debug#3正常输出解析后的ansible_host:
方案一:将ansible_host移到host_vars中定义
修改host_vars/example1.test/vars.yml,添加ansible_host的定义:
network: ipv4_address: "{{ vault_network['vault_ipv4_address'] }}" ansible_host: "{{ network['ipv4_address'] }}"
同时删除清单inventory.yml中example1.test下的ansible_host配置。
这样ansible_host成为主机变量的一部分,和network变量在同一加载阶段被解析,最终hostvars中的ansible_host会是解析后的IP地址。
方案二:在剧本中动态赋值ansible_host
在剧本的pre_tasks中添加动态设置变量的任务,覆盖清单中的原始表达式:
- name: Set ansible_host dynamically ansible.builtin.set_fact: ansible_host: "{{ hostvars['example1.test']['network']['ipv4_address'] }}" delegate_to: localhost delegate_facts: true when: inventory_hostname == 'example1.test'
这个任务会在剧本运行时(此时host_vars已完全加载)将解析后的IP赋值给ansible_host,后续Debug#3就能输出正确的值。
内容的提问来源于stack exchange,提问作者Florian Wolters

