First, some basic definitions and rules: In Ansible, there are a set of reserved variables called magic variables. Because they are reserved, they cannot be set by users and, if tried, Ansible will override them.
This article focuses one magic variable in particular: hostvars
, which can access variables defined for any host in a play at any point in a playbook run.
Most Ansible users know the inventory may contain additional variables that are assigned to a specific host. Sometimes, these variables associated with a host may be needed by other hosts during a playbook run. In this article, I'll expand your understanding of using hostvars
outside of the linear host variable-to-host relationship.
[ Compare Ansible vs. Red Hat Ansible Automation Platform ]
Create a simple inventory
First, I'll create a simple ini
type inventory based on some generic role-playing character types. For simplicity, I will avoid using groups
or group_vars
. I'll also avoid spinning up any additional hosts for this exercise by using the loopback for the ansible_host
variable.
inventory.ini
servera ansible_host=127.0.0.1 character_type=Bard spell1=Blindness spell2=Confusion
serverb ansible_host=127.0.0.1 character_type=Wizard spell1=Lightning spell2=Fireball
serverc ansible_host=127.0.0.1 character_type=Druid spell1=Poison spell2=Plague
serverd ansible_host=127.0.0.1 character_type=Paladin spell1="Magic Missile" spell2=Fear
Each host is assigned the following variables: ansible_host
, character_type
, spell1
, and spell2
. Each value, with the exception of ansible_host
, is unique to the host in the inventory.
Display hostvars
I'll create a playbook to showcase the hostvars
available for use. I'll also skip gathering facts to streamline the playbook.
example1.yml
---
- name: Example playbook to showcase hostvars
hosts: all
connection: local
gather_facts: false
tasks:
- name: Display the specific hostvars that are set in the inventory for each host
ansible.builtin.debug:
var: hostvars[inventory_hostname]|json_query('[character_type,spell1,spell2]')
NOTE: You must install the community.general collection to use the json_query filter.
Now, I'll run the first playbook:
$ ansible-playbook -i inventory.ini example1.yml
example1.yml results
PLAY [Example playbook to showcase hostvars] ***********************************
TASK [Display all of the hostvars for each host] *******************************
Sunday 09 April 2023 13:09:46 -0400 (0:00:00.018) 0:00:00.018 **********
ok: [servera] => {
"hostvars[inventory_hostname]|json_query('[character_type,spell1,spell2]')": [
"Bard",
"Blindness",
"Confusion"
]
}
ok: [serverb] => {
"hostvars[inventory_hostname]|json_query('[character_type,spell1,spell2]')": [
"Wizard",
"Lightning",
"Fireball"
]
}
ok: [serverc] => {
"hostvars[inventory_hostname]|json_query('[character_type,spell1,spell2]')": [
"Druid",
"Poison",
"Plague"
]
}
ok: [serverd] => {
"hostvars[inventory_hostname]|json_query('[character_type,spell1,spell2]')": [
"Paladin",
"Magic Missile",
"Fear"
]
}
PLAY RECAP *********************************************************************
servera : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
serverb : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
serverc : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
serverd : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
As you can see above, the playbook extracted the unique hostvars
as it ran against each host. This is a typical use of hostvars
.
[ Write your first Ansible playbook in this hands-on interactive lab. ]
What if I need to use hostvars from a different host?
Sometimes, a single host may need a variable from another host. I'll tackle that in playbook number 2.
example2.yml
---
- name: Example playbook to showcase hostvars
hosts: all
gather_facts: false
connection: local
tasks:
- name: Set a fact for servera using serverd host variable
ansible.builtin.set_fact:
opponent: "{{ hostvars['serverd']['character_type'] }}"
when: inventory_hostname == "servera"
- name: Print servera hostvars setting derived from a variable from serverd
ansible.builtin.debug:
var: hostvars[inventory_hostname]['opponent']
when:
- opponent is defined
- name: Print PvP message
ansible.builtin.debug:
msg:
- "Round1: {{ character_type }} vs {{ opponent }}"
when: opponent is defined
$ ansible-playbook -i inventory.ini example2.yml
example2.yml results
PLAY [Example playbook to showcase hostvars] ***********************************
TASK [Set a fact from serverd to use for servera] ******************************
Sunday 09 April 2023 14:34:19 -0400 (0:00:00.029) 0:00:00.030 **********
skipping: [serverb]
ok: [servera]
skipping: [serverc]
skipping: [serverd]
TASK [Print servera hostvars setting derived from a variable from serverd] ***************
Sunday 09 April 2023 14:34:20 -0400 (0:00:00.061) 0:00:00.091 **********
skipping: [serverb]
ok: [servera] => {
"hostvars[inventory_hostname]['opponent']": "Paladin"
}
skipping: [serverc]
skipping: [serverd]
TASK [Print PvP message] *******************************************************
Sunday 09 April 2023 14:34:20 -0400 (0:00:00.079) 0:00:00.170 **********
ok: [servera] => {
"msg": [
"Round1: Bard vs Paladin"
]
}
skipping: [serverb]
skipping: [serverc]
skipping: [serverd]
PLAY RECAP *********************************************************************
servera : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
serverb : ok=0 changed=0 unreachable=0 failed=0 skipped=3 rescued=0 ignored=0
serverc : ok=0 changed=0 unreachable=0 failed=0 skipped=3 rescued=0 ignored=0
serverd : ok=0 changed=0 unreachable=0 failed=0 skipped=3 rescued=0 ignored=0
Well, that's cool! Using the character_type
variable from serverd
, the playbook could assign it as a variable for servera
to use.
What if I need to use all specific hostvars from each host on all the hosts?
Sometimes you need to dynamically get the desired host variables from all hosts and use them on all servers. I'll create playbook 3 to show this.
example3.yml
---
- name: Example playbook to showcase hostvars
hosts: all
gather_facts: false
connection: local
tasks:
- name: Set a fact called all_character_types
ansible.builtin.set_fact:
all_character_types: "{{ all_character_types|default([]) + [ hostvars[item]['character_type'] ] }}"
loop: "{{ groups['all'] }}"
run_once: true
- name: Print hostvars that shows servera has all character_types
ansible.builtin.debug:
var: all_character_types
when: inventory_hostname == "servera"
- name: Print hostvars that shows serverb has all character_types
ansible.builtin.debug:
var: all_character_types
when: inventory_hostname == "serverb"
- name: Print hostvars that shows serverc has all character_types
ansible.builtin.debug:
var: all_character_types
when: inventory_hostname == "serverc"
- name: Print hostvars that shows serverd has all character_types
ansible.builtin.debug:
var: all_character_types
when: inventory_hostname == "serverd"
Now I'll run playbook 3:
$ ansible-playbook -i inventory.ini example3.yml
example3.yml results
PLAY [Example playbook to showcase hostvars] ***********************************
TASK [Set a fact called all_character_types] ******************************************
Sunday 09 April 2023 14:59:33 -0400 (0:00:00.030) 0:00:00.030 **********
ok: [servera] => (item=servera)
ok: [servera] => (item=serverb)
ok: [servera] => (item=serverc)
ok: [servera] => (item=serverd)
TASK [Print hostvars that shows servera has all character_types] *********************
Sunday 09 April 2023 14:59:33 -0400 (0:00:00.080) 0:00:00.111 **********
skipping: [serverb]
ok: [servera] => {
"all_character_types": [
"Bard",
"Wizard",
"Druid",
"Paladin"
]
}
skipping: [serverc]
skipping: [serverd]
TASK [Print hostvars that shows serverb has all character_types] *********************
Sunday 09 April 2023 14:59:33 -0400 (0:00:00.056) 0:00:00.167 **********
skipping: [servera]
skipping: [serverc]
ok: [serverb] => {
"all_character_types": [
"Bard",
"Wizard",
"Druid",
"Paladin"
]
}
skipping: [serverd]
TASK [Print hostvars that shows serverc has all character_types] *********************
Sunday 09 April 2023 14:59:33 -0400 (0:00:00.054) 0:00:00.221 **********
skipping: [servera]
skipping: [serverb]
ok: [serverc] => {
"all_character_types": [
"Bard",
"Wizard",
"Druid",
"Paladin"
]
}
skipping: [serverd]
TASK [Print hostvars that shows serverd has all character_types] *********************
Sunday 09 April 2023 14:59:33 -0400 (0:00:00.053) 0:00:00.275 **********
skipping: [servera]
skipping: [serverb]
skipping: [serverc]
ok: [serverd] => {
"all_character_types": [
"Bard",
"Wizard",
"Druid",
"Paladin"
]
}
PLAY RECAP *********************************************************************
servera : ok=2 changed=0 unreachable=0 failed=0 skipped=3 rescued=0 ignored=0
serverb : ok=1 changed=0 unreachable=0 failed=0 skipped=3 rescued=0 ignored=0
serverc : ok=1 changed=0 unreachable=0 failed=0 skipped=3 rescued=0 ignored=0
serverd : ok=1 changed=0 unreachable=0 failed=0 skipped=3 rescued=0 ignored=0
In the above output, you can see that the playbook sets a fact called all_character_types
. You can also see that each host now has that fact available to it. By creating a fact based on the hostvars
of each host, you now have a list of all the character_types
. You can use this fact later.
Spells battle
I'll create a playbook matching character_types
and a random spell for each.
I'll loop through all of the hosts in the inventory and leverage the hostvars
of each during the loop. I'll also supplement the playbook to use the following Jinja filters:
example4.yml
---
- name: Example playbook to showcase hostvars
hosts: all
gather_facts: false
connection: local
tasks:
- name: Select a spell to be used against another opponent
ansible.builtin.debug:
msg:
- "{{ character_type }} uses his {{ [spell1,spell2]|random }} spell against {{ hostvars[item]['character_type'] }}"
- "{{ hostvars[item]['character_type'] }} uses {{ hostvars[item][('spell1','spell2')|random] }} spell against {{ character_type }}"
loop: "{{ groups['all']|reject('search',inventory_hostname)|sort|list }}"
Now I'll run playbook 4:
$ ansible-playbook -i inventory.ini example4.yml
example4.yml results
PLAY [Example playbook to showcase hostvars] ***********************************
TASK [Select a spell to be used against another opponent] *******************
Sunday 09 April 2023 17:48:38 -0400 (0:00:00.030) 0:00:00.030 **********
ok: [servera] => (item=serverb) => {
"msg": [
"Bard uses his Blindness spell against Wizard",
"Wizard uses Fireball spell against Bard"
]
}
ok: [serverb] => (item=servera) => {
"msg": [
"Wizard uses his Fireball spell against Bard",
"Bard uses Blindness spell against Wizard"
]
}
ok: [serverc] => (item=servera) => {
"msg": [
"Druid uses his Plague spell against Bard",
"Bard uses Confusion spell against Druid"
]
}
ok: [servera] => (item=serverc) => {
"msg": [
"Bard uses his Confusion spell against Druid",
"Druid uses Poison spell against Bard"
]
}
ok: [serverd] => (item=servera) => {
"msg": [
"Paladin uses his Fear spell against Bard",
"Bard uses Confusion spell against Paladin"
]
}
ok: [serverb] => (item=serverc) => {
"msg": [
"Wizard uses his Lightning spell against Druid",
"Druid uses Poison spell against Wizard"
]
}
ok: [serverc] => (item=serverb) => {
"msg": [
"Druid uses his Poison spell against Wizard",
"Wizard uses Lightning spell against Druid"
]
}
ok: [servera] => (item=serverd) => {
"msg": [
"Bard uses his Confusion spell against Paladin",
"Paladin uses Fear spell against Bard"
]
}
ok: [serverb] => (item=serverd) => {
"msg": [
"Wizard uses his Fireball spell against Paladin",
"Paladin uses Fear spell against Wizard"
]
}
ok: [serverd] => (item=serverb) => {
"msg": [
"Paladin uses his Fear spell against Wizard",
"Wizard uses Lightning spell against Paladin"
]
}
ok: [serverc] => (item=serverd) => {
"msg": [
"Druid uses his Poison spell against Paladin",
"Paladin uses Magic Missile spell against Druid"
]
}
ok: [serverd] => (item=serverc) => {
"msg": [
"Paladin uses his Magic Missile spell against Druid",
"Druid uses Plague against spell Paladin"
]
}
PLAY RECAP *********************************************************************
servera : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
serverb : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
serverc : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
serverd : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
In the final example, I'll do a simple extraction of variables from a group using map
and json_query
against the hostvars
. While this is very similar to the first example, I'm actually extracting the hostvars
using the Jinja map filter. I then pass that information to json_query
and grab 2 specific bits of information
NOTE: The json_query
filter requires that you install the community.general
collection.
example5.yml
---
- name: Example playbook to showcase hostvars
hosts: all
gather_facts: false
connection: local
tasks:
- name: Print specific hostvars from all groups
ansible.builtin.debug:
var: groups['all'] | map('extract',hostvars)|json_query('[].[character_type,spell2]')
delegate_to: localhost
become: false
run_once: true
Now I'll run playbook 5:
$ ansible-playbook -i inventory.ini example5.yml
example5.yml results
PLAY [Example playbook to showcase hostvars] ***********************************
TASK [Print specific hostvars from all groups ] ******************
ok: [servera -> localhost] => {
"groups['all'] | map('extract',hostvars)|json_query('[].[character_type,spell2]')": [
[
"Bard",
"Confusion"
],
[
"Wizard",
"Fireball"
],
[
"Druid",
"Plague"
],
[
"Paladin",
"Fear"
]
]
}
PLAY RECAP *********************************************************************
servera : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Wrap up
Personally, it took me a while before I began using the hostvars
magic variables in the manner I have shown in this article. However, you don't have to be a magician to use hostvars
because the magic is in the examples above. Get past the illusion of hostvars
being difficult and begin using them in your playbooks, tasks, and roles. You'll be amazed at how easy it is and how much your code will improve.
[ Need more on Ansible? Take a no-cost technical overview course from Red Hat. Ansible Essentials: Simplicity in Automation Technical Overview. ]
저자 소개
Randy Romero is a Senior Ansible Consultant and Red Hat Certified Architect at Red Hat where he specializes in IT automation with Ansible and Ansible Automation Platform. He has experience in the cable, insurance, and loyalty-marketing industries, having performed multiple roles starting with junior Systems Administrator all the way to Cloud Architect with over 27 years of experience in the IT industry.
Randy fervently advocates for automation with Ansible to increase an individual's ability to grow by removing mundane repeatable tasks that tend to stand in the way of a person's own professional development and growth. He is also motivated in his mission to evangelize automation through Ansible by changing the minds of people who say "This is how we've always done it'. In his spare time when he is not studying or working, he enjoys mentoring individuals (young and old) seeking to achieve their Red Hat Certified System Administrator and Red Hat Certified Engineer certifications. When he's not doing IT related things he enjoys watching/reading all things paranormal.
채널별 검색
오토메이션
기술, 팀, 인프라를 위한 IT 자동화 최신 동향
인공지능
고객이 어디서나 AI 워크로드를 실행할 수 있도록 지원하는 플랫폼 업데이트
오픈 하이브리드 클라우드
하이브리드 클라우드로 더욱 유연한 미래를 구축하는 방법을 알아보세요
보안
환경과 기술 전반에 걸쳐 리스크를 감소하는 방법에 대한 최신 정보
엣지 컴퓨팅
엣지에서의 운영을 단순화하는 플랫폼 업데이트
인프라
세계적으로 인정받은 기업용 Linux 플랫폼에 대한 최신 정보
애플리케이션
복잡한 애플리케이션에 대한 솔루션 더 보기
오리지널 쇼
엔터프라이즈 기술 분야의 제작자와 리더가 전하는 흥미로운 스토리
제품
- Red Hat Enterprise Linux
- Red Hat OpenShift Enterprise
- Red Hat Ansible Automation Platform
- 클라우드 서비스
- 모든 제품 보기
툴
체험, 구매 & 영업
커뮤니케이션
Red Hat 소개
Red Hat은 Linux, 클라우드, 컨테이너, 쿠버네티스 등을 포함한 글로벌 엔터프라이즈 오픈소스 솔루션 공급업체입니다. Red Hat은 코어 데이터센터에서 네트워크 엣지에 이르기까지 다양한 플랫폼과 환경에서 기업의 업무 편의성을 높여 주는 강화된 기능의 솔루션을 제공합니다.