When hostvars data is populated and how it is accessible? - ansible

Here is my play:
- name: Tag ec2 instances
hosts: localhost
tasks:
- name: Print Hosts
debug: var=hostvars[item]['ec2_id']
with_inventory_hostnames: all
- name: Print Hosts 2
debug: msg={{hostvars[item]['ec2_id']}}
with_inventory_hostnames: all
- name: Tag Hosts
ec2_tag:
resource: "{{ hostvars[item]['ec2_id'] }}"
region: ap-southeast-2
args:
tags: {mytag: 'myvalue'}
with_inventory_hostnames: all
Can anyone explain why the second task fails with the following error while the first one is successful?
...
ok: [localhost] => (item=172.31.11.37) => {
"hostvars[item]['ec2_id']": "i-xxxxxx",
"item": "172.31.11.37"
}
TASK [Print Hosts 2] ***********************************************************
fatal: [localhost]: FAILED! => {"failed": true, "msg": "'dict object' has no attribute 'ec2_id'"}

debug module with var=hostvars[item]['ec2_id'] will not fail if anything to the right of equal sign is undefined.
While msg={{hostvars[item]['ec2_id']}} will fail if the part in braces can't be templated.
In your example this may fail for localhost because I'm almost sure that ec2_id is not defined for localhost.
To avoid this, you can apply when statement to your loop, as follows:
- name: Print Hosts 2
debug: msg={{hostvars[item]['ec2_id']}}
when: hostvars[item]['ec2_id'] is defined
with_inventory_hostnames: all

Related

Error 'dict object' has no attribute 'list' while executing ansible playbook

I am facing error while executing below ansible playbook. I don't know what I am missing, in with_items
Trying to retrieve disk details using gather_subset module and partition each disk that is available in the file system by looping into the var
Error Message:
fatal: [localhost]: FAILED! => {"msg": "'dict object' has no attribute 'list'"}
Playbook:
---
- hosts: localhost
become: true
gather_facts: false
tasks:
- name: Collect Disk Information
setup:
gather_subset:
- hardware
- name: Print disks Details
debug:
var: hostvars[inventory_hostname].ansible_devices.keys()| list
- name: Create Partition
parted:
device: "{{ item }}"
number: 1
part_type: 'primary'
state: present
with_items: "{{ disks.list[0] }}"
Print disk details task is printing below
TASK [Print disks Details] ***************************************************************************************************
ok: [localhost] => {
"hostvars[inventory_hostname].ansible_devices.keys()| list": [
"xvdf",
"xvdb",
"xvdc",
"xvda"
]
}
I see an issue in your example, you are refering to var disks, but I cannot see this is set anywhere in your example.
You should replace your last syntax:
- name: Create Partition
parted:
device: "{{ item }}"
number: 1
part_type: 'primary'
state: present
with_items: "{{hostvars[inventory_hostname].ansible_devices.keys()}}"

Ansible only run when subelement of variable exists

I have some variables defined like this:
x_php_versions_installed:
php70:
- php70-curl
- php70-xml
- php70-xmlrpc
- php70-zip
- pecl-memcached
php71:
- php71-curl
- php71-xml
- php71-xmlrpc
- php71-zip
php72:
- php72-curl
- php72-xml
- php72-xmlrpc
- php72-zip
- pecl-memcached
And I would like to check all of the vars (php70, php71, php72 and so on) has this variable: pecl-memcached and if one has, then run a command. My playbook looks like this:
- name: memcached pecl install
pear:
executable: '/usr/local/{{ item }}/bin/pecl'
name: 'pecl/memcached'
state: 'latest'
with_items: '{{ x_php_versions_installed | list }}'
when: 'item.pecl-memcached is defined'
this should call the /usr/local/php70/bin/pecl and /usr/local/php72/bin/pecl binary to install memcached. As soon as I remove the when condition, it works very well, but it will call every variable inside x_php_versions_installed not only where pecl-memcached is defined. So I need to fix the when condition in this case, but all of my tries are gives me an error.
If you want your when to check a list properly, you'll have to use the test operator in of Jinja:
- name: memcached pecl install
pear:
executable: '/usr/local/{{ item }}/bin/pecl'
name: 'pecl/memcached'
state: 'latest'
with_items: '{{ x_php_versions_installed | list }}'
when: "'pecl-memcached' in x_php_versions_installed[item]"
Given the playbook:
- hosts: localhost
gather_facts: no
vars:
x_php_versions_installed:
php70:
- php70-curl
- php70-xml
- php70-xmlrpc
- php70-zip
- pecl-memcached
php71:
- php71-curl
- php71-xml
- php71-xmlrpc
- php71-zip
php72:
- php72-curl
- php72-xml
- php72-xmlrpc
- php72-zip
- pecl-memcached
tasks:
- debug:
msg: "{{ item }}"
with_items: "{{ x_php_versions_installed | list }}"
when: "'pecl-memcached' in x_php_versions_installed[item]"
The recap would be:
TASK [debug] *******************************************************************
ok: [localhost] => (item=php70) => {
"msg": "php70"
}
skipping: [localhost] => (item=php71)
ok: [localhost] => (item=php72) => {
"msg": "php72"
}
Your subelement is a list not a dictionary, so accessing an element key like your are trying here, i.e.:
when: "x_php_versions_installed[item]['pecl-memcached'] is defined"
would work on a dictionary like this one:
x_php_versions_installed:
php70:
php70-curl:
php70-xml:
php70-xmlrpc:
php70-zip:
pecl-memcached:
# same goes for the other versions of PHP
Given the playbook:
- hosts: localhost
gather_facts: no
vars:
x_php_versions_installed:
php70:
php70-curl:
php70-xml:
php70-xmlrpc:
php70-zip:
pecl-memcached:
php71:
php71-curl:
php71-xml:
php71-xmlrpc:
php71-zip:
php72:
php72-curl:
php72-xml:
php72-xmlrpc:
php72-zip:
pecl-memcached:
tasks:
- debug:
msg: "{{ item }}"
with_items: "{{ x_php_versions_installed | list }}"
when: "x_php_versions_installed[item]['pecl-memcached'] is defined"
The recap would be:
TASK [debug] *******************************************************************
ok: [localhost] => (item=php70) => {
"msg": "php70"
}
skipping: [localhost] => (item=php71)
ok: [localhost] => (item=php72) => {
"msg": "php72"
}

Ansible, Issue while using variable content in playbook

I need to use variable defined for some hosts (in inventory), on another host.
Here i define it in my inventory
[mygroup:vars]
service_url=my_front_url
Where mygroup contain other groups, containing my hosts.
Then my playbook :
- name: Get variable
hosts: 127.0.0.1
tasks:
- debug:
var: hostvars[groups['{{ platform }}'][0]]['service_url']
- debug:
msg: "{{ hostvars[groups['\"{{ platform }}\"'][0]]['service_url'] }}"
Where platform is an extra-var (setting which "mygroup" to use)
and where 127.0.0.1 is my ansible host, distinct from my target hosts.
ex:
ansible-playbook test.yaml --extra-vars='platform=my-group'
TASK [debug] ********************************************************************************************************************************************************************
ok: [127.0.0.1] => {
"hostvars[groups['idi_se_prod'][0]]['service_url']": "my-front-url"
}
TASK [debug] ********************************************************************************************************************************************************************
fatal: [127.0.0.1]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute '\"{{ platform }}\"'\n\nThe error appears to have been in 'XXXX/ansible/test.yaml': line 6, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n var: hostvars[groups['{{ platform }}'][0]]['service_url']\n - debug:\n ^ here\n"}
If i set static group name in yaml, this work fine.
- name: Get variable
hosts: 127.0.0.1
tasks:
- debug:
var: hostvars[groups['{{ platform }}'][0]]['service_url']
- debug:
msg: "{{ hostvars[groups['mygroup'][0]]['service_url'] }}"
TASK [debug] ********************************************************************************************************************************************************************
ok: [127.0.0.1] => {
"hostvars[groups['my-group'][0]]['service_url']": "my-front-url"
}
TASK [debug] ********************************************************************************************************************************************************************
ok: [127.0.0.1] => {
"msg": "my_front_url"
}
It look like a syntax probleme but i tried so many ways that i think i could use some help.
Thank you
Nicolas
Everything inside {{ and }} is more or less just python, so don't use recursive templates like you have:
msg: "{{ hostvars[groups['\"{{ platform }}\"'][0]]['service_url'] }}"
instead just reference the variable like it is, a variable:
msg: "{{ hostvars[groups[platform][0]]['service_url'] }}"

fail custom message with item name

I have the following task which works perfectly as expected. I am wondering if the failure message can be a bit more informative since I am passing no_log: true without which I could see the entire result in the logs. Something like:
More than one access keys available for the cos account {{ item.name }}
- name: Fail if more than one key is available for any of the COS accounts
fail: msg="More than one access keys available for the cos account"
when: (item.json)|length > 1
with_items: '{{ old_existing_creds.results }}'
no_log: true
In fact I noticed I could not even see the msg. The o/p I got is:
TASK [Fail if more than one key is available for any of the COS accounts] *****************************************************************************
skipping: [localhost] => (item=None)
failed: [localhost] (item=None) => {"censored": "the output has been hidden due to the fact that 'no_log: true' was specified for this result", "changed": false}
fatal: [localhost]: FAILED! => {"censored": "the output has been hidden due to the fact that 'no_log: true' was specified for this result", "changed": false}
to retry, use: --limit #/root/deployment/generateSLKeys.retry
You've asked Ansible not to log the result of your task by setting no_log: true, so you're not going to be able to see the result of the fail task. You can hack around this by first creating a new variable that maps names from your old_existing_creds variables to the length of the json attribute, like this:
---
- hosts: localhost
gather_facts: false
vars:
old_existing_creds:
results:
- json: [secret1,secret2]
name: foo
- json: [secret1]
name: bar
tasks:
- name: check length of json array
set_fact:
key_check: "{{ key_check|default({})|combine({item.name: item.json|length}) }}"
loop: "{{ old_existing_creds.results }}"
- debug:
var: key_check
- name: Fail if more than one key is available for any of the COS accounts
fail:
msg: "More than one access keys available for the cos account {{ item.key }}"
when: (item.value > 1)
loop: "{{ key_check|dict2items }}"
This will output:
TASK [Fail if more than one key is available for any of the COS accounts] *********************
failed: [localhost] (item={'key': u'foo', 'value': 2}) => {"changed": false, "item": {"key": "foo", "value": 2}, "msg": "More than one access keys available for the cos account foo"}
skipping: [localhost] => (item={'key': u'bar', 'value': 1})
As you can see, it shows the message from the fail task, which includes the account name, but it does not expose credentials in the log.

Ansible v2.2 error when using with_subelements which worked fine in 1.7

I am getting the following error while running my ansible playbook.
TASK [base : Rsyslog yapilandiriliyor.]
**************************************** fatal: [gkts.ahtapot]: FAILED! => {"failed": true, "msg": "The conditional check
'ansible_fqdn == {{item.1}}' failed. The error was: error while
evaluating conditional (ansible_fqdn == {{item.1}}): 'item' is
undefined\n\nThe error appears to have been in
'/etc/ansible/roles/base/tasks/rsyslog.yml': line 2, column 3, but
may\nbe elsewhere in the file depending on the exact syntax
problem.\n\nThe offending line appears to be:\n\n---\n- name: Rsyslog
yapilandiriliyor.\n ^ here\n"}
This worked fine in ansible 1.7 but doesn't work in ansible 2.2.1
- name: Rsyslog yapilandiriliyor.
template:
src: "{{ rsyslog['conf']['source'] }}"
dest: "{{ rsyslog['conf']['destination'] }}"
owner: "{{ rsyslog['conf']['owner'] }}"
group: "{{ rsyslog['conf']['group'] }}"
mode: "{{ rsyslog['conf']['mode'] }}"
# when: "ansible_fqdn == item.1"
when: "ansible_fqdn == {{item.1}}"
with_subelements:
- "{{ossimciks}}"
- "{{clients}}"
notify:
- rsyslog servisini yeniden baslat
sudo: yes
tags: rsyslog
ossimciks and clients are defined in my vars file:
ossimciks:
server01:
fqdn: "OSSIMCIK_FQDN"
port: "20514"
clients:
- "LOG_KAYNAGI_FQDN"
- "LOG_KAYNAGI_FQDN"
What am I missing?
with_subelements:
- "{{ossimciks}}"
- "{{clients}}"
Syntax
I don't know how where/how/when this might have changed in Ansible, but I think that
the first element in with_subelements is a variable, and
the second element in with_subelements is a key.
This works in playbooks I've written, and matches the relevant docs (since 2.5, there are no with_* loops [1] in the docs since they were always lookups anyhow [2]):
with_subelements:
- "{{ ossimciks }}"
- clients
Data
It's not clear to me which item you're attempting to target with {{ item.1 }} in your playbook, but if making a minor change to your data is permissible, you can structure it so that any of the information shown is accessible within a with_subelements loop (I've also expanded and restructured the data a bit to clarify what the loop is doing):
---
- hosts: localhost
gather_facts: false
vars:
fqdn_var: "OSSIMCIK_FQDN_2"
ossimciks:
- server: "server01"
fqdn: "OSSIMCIK_FQDN_1"
port: "20514"
clients:
- "LOG_KAYNAGI_FQDN_1"
- "LOG_KAYNAGI_FQDN_2"
- server: "server02"
fqdn: "OSSIMCIK_FQDN_2"
port: "20514"
clients:
- "LOG_KAYNAGI_FQDN_1"
- "LOG_KAYNAGI_FQDN_2"
tasks:
- name: Output ossimciks contents.
debug:
msg: "{{ item.0.server }} client: {{ item.1 }}"
with_subelements:
- "{{ ossimciks }}"
- clients
when: item.0.fqdn == fqdn_var
This outputs:
skipping: [localhost] => (item=None) => {"skip_reason": "Conditional result was False"}
skipping: [localhost] => (item=None) => {"skip_reason": "Conditional result was False"}
ok: [localhost] => (item=None) => {
"msg": "server02 client: LOG_KAYNAGI_FQDN_1"
}
ok: [localhost] => (item=None) => {
"msg": "server02 client: LOG_KAYNAGI_FQDN_2"
}
[1] http://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html
[2] Loops are actually a combination of things with_ + lookup(), so any lookup plugin can be used as a source for a loop, ‘items’ is lookup.
Edited to add: the playbooks where I've used the syntax above were definitely in the 2.x release series--I think ~2.1.

Resources