Undefined variable when passing dictionary to a submodule-role - ansible

I'm having a really weird problem on my ansible role. I send two dictionaries to a submodule like that:
import_role:
name: .submodules/monitoring-plugins
vars:
monitoring_plugins:
check_content:
command: "files/icinga/commands/check_content"
dest: "{{ icinga_server_plugin_directory }}"
group: "root"
owner: "root"
mode: "0755"
package: "curl"
src: "files/plugins/server/check_content"
check_http_response_time:
command: "files/icinga/commands/check_http_response_time"
dest: "{{ icinga_server_plugin_directory }}"
group: "root"
owner: "root"
mode: "0775"
src: "files/plugins/server/check_http_response_time"
check_https_response_time:
command: "files/icinga/commands/check_https_response_time"
dest: "{{ icinga_server_plugin_directory }}"
group: "root"
owner: "root"
mode: "0775"
src: "files/plugins/server/check_https_response_time"
check_port:
command: "files/icinga/commands/check_port"
dest: "{{ icinga_server_plugin_directory }}"
group: "root"
owner: "root"
mode: "0775"
src: "files/plugins/server/check_port"
check_ssl_cert:
command: "files/icinga/commands/check_ssl_cert"
dest: "{{ icinga_server_plugin_directory }}"
group: "root"
owner: "root"
mode: "0775"
src: "files/plugins/server/check_ssl_cert"
custom_services:
content:
service-preamble: 'apply Service "content"'
configuration: |
{{ common_service_header }}
assign where host.vars.ansible.system == "Linux"
definition:
"{{ lookup('template', 'config/services/content.conf') }}"
http_response_time:
service-preamble: 'apply Service "http_response_time"'
configuration: |
{{ common_service_header }}
assign where host.vars.ansible.system == "Linux"
definition:
"{{ lookup('template', 'config/services/http_response_time.conf') }}"
https_response_time:
service-preamble: 'apply Service "https-response-time"'
configuration: |
{{ common_service_header }}
assign where host.vars.ansible.system == "Linux"
definition:
"{{ lookup('template', 'config/services/https_response_time.conf') }}"
http_port:
service-preamble: 'apply Service "http-port"'
configuration: |
{{ common_service_header }}
assign where host.vars.ansible.system == "Linux"
definition:
"{{ lookup('template', 'config/services/http_port.conf') }}"
https_port:
service-preamble: 'apply Service "https-port"'
configuration: |
{{ common_service_header }}
assign where host.vars.ansible.system == "Linux"
definition:
"{{ lookup('template', 'config/services/https_port.conf') }}"
ssl_cert:
service-preamble: 'apply Service "ssl-cert"'
configuration: |
{{ common_service_header }}
assign where host.vars.ansible.system == "Linux"
definition:
"{{ lookup('template', 'config/services/ssl_cert.conf') }}"
on the submodule I created two debug tasks:
- debug:
var: monitoring_plugins
- debug:
var: custom_services
output:
ok: [server.test] => {
"monitoring_plugins": {
"check_content": {
"command": "files/icinga/commands/check_content",
"dest": "/usr/lib/nagios/plugins",
"group": "root",
"mode": "0755",
"owner": "root",
"package": "curl",
"src": "files/plugins/server/check_content"
},
"check_http_response_time": {
"command": "files/icinga/commands/check_http_response_time",
"dest": "/usr/lib/nagios/plugins",
"group": "root",
"mode": "0775",
"owner": "root",
"src": "files/plugins/server/check_http_response_time"
},
"check_https_response_time": {
"command": "files/icinga/commands/check_https_response_time",
"dest": "/usr/lib/nagios/plugins",
"group": "root",
"mode": "0775",
"owner": "root",
"src": "files/plugins/server/check_https_response_time"
},
"check_port": {
"command": "files/icinga/commands/check_port",
"dest": "/usr/lib/nagios/plugins",
"group": "root",
"mode": "0775",
"owner": "root",
"src": "files/plugins/server/check_port"
},
"check_ssl_cert": {
"command": "files/icinga/commands/check_ssl_cert",
"dest": "/usr/lib/nagios/plugins",
"group": "root",
"mode": "0775",
"owner": "root",
"src": "files/plugins/server/check_ssl_cert"
}
}
}
TASK [.submodules/monitoring-plugins : debug] ************************************
ok: [server.test] => {
"custom_services": "VARIABLE IS NOT DEFINED!"
}
Does anybody have any idea what could possibly go wrong? I sent both of them from the very same task but yet one of them looks just fine and the other don't. I even tried to remove the submodule and add it again but it still doesn't work. Any other suggestions would be very appreciated!

The background
A var containing other vars expansion being undefined cannot be interpreted and is undefined.
$ ansible localhost -e undef="{{ i_dont_exist }}" -m debug -a var=undef
localhost | SUCCESS => {
"undef": "VARIABLE IS NOT DEFINED!"
}
To see what is going wrong, you need to actually expand the var at debug time
$ ansible localhost -e undef="{{ i_dont_exist }}" -m debug -a msg="{{ undef }}"
localhost | FAILED! => {
"msg": "The task includes an option with an undefined variable. The error was: 'i_dont_exist' is undefined"
}
Your specific case
The root cause is that common_service_header is undefined. Since it is used inside your variable definition for the role include, ansible tries to interpret it, fails, and the var stays undefined.
You will actually see the same result for monitoring_plugins if you undefine icinga_server_plugin_directory.
To have some more info on what actually happens, the "trick" is to force ansible to interpret the variable in your debug. There are two possible ways to do this:
- name: Use a simple jinja2 expansion
debug:
msg: "{{ custom_services }}"
- name: Use a the 'vars' lookup
debug:
msg: "{{ lookup('vars', 'custom_services') }}"
Which then gives a more meaningful info (reformated for legibility....):
TASK [.submodules/monitoring-plugins : debug] *****************************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an
undefined variable. The error was: 'common_service_header' is undefined\n\n
The error appears to be in
'/some/path/.submodules/monitoring-plugins/tasks/main.yml': line 4, column 3,
but may\nbe elsewhere in the file depending on the exact syntax problem.\n\n
The offending line appears to be:\n\n
msg: \"{{ lookup('vars', 'monitoring_plugins') }}\"\n- debug:\n ^ here\n"}

Related

Filter debug msg

I am using Ansible 2.9.13 and I have this playbook:
---
- hosts: localhost
connection: local
vars:
ansible_python_interpreter: /usr/bin/env python3
vars_files:
- vars.yml
tasks:
- name: Get Tags from given VM Name
vmware_vm_info:
validate_certs: no
hostname: '{{ vcenter_server }}'
username: '{{ vcenter_user }}'
password: '{{ vcenter_pass }}'
folder: '{{ provision_folder }}'
delegate_to: localhost
register: vm_info
- debug:
msg: "{{ vm_info.virtual_machines | json_query(query) }}"
vars:
query: "[?guest_name=='C97A1612171478']"
When I run it I am getting this output:
ok: [localhost] => {
"msg": [
{
"attributes": {},
"cluster": "xxx01",
"esxi_hostname": "xxxx",
"guest_fullname": "Microsoft Windows 10 (64-bit)",
"guest_name": "C97A1612171478",
"ip_address": "10.x.x.x",
"mac_address": [
"0x:x:x:x:xd:x"
],
"power_state": "poweredOn",
"tags": [],
"uuid": "420xxaf-xxx-xe2-9xe-a5xxxxxa3c",
"vm_network": {
"0x:x:x:xa:x:x": {
"ipv4": [
"169.x.x.x"
],
"ipv6": [
"x::x:x:x:xc"
]
},
"x:x:x:x:x0:x1": {
"ipv4": [
"169.x.x.x"
],
"ipv6": [
"x::x7:xf:x:x"
]
},
"0x:5x:x:x:ax:x": {
"ipv4": [
"10.x.x.x"
],
"ipv6": [
"x::1xx:x:8xx:x"
]
}
}
}
]
}
How can I filter the output to make it show only the "ip_address": "10.x.x.x".
In the end only the 10.x.x.x.
I have tried some ways adding the key ip_address in the message code but all of them gave me an error.
I can filter the msg using Python but if there's a way to get it using Ansible I would like to know how.
If you want to get this information without a loop:
If you need an object as a result:
- debug:
msg: "{{ vm_info.virtual_machines | json_query(query) }}"
vars:
query: "[?guest_name=='C97A1612171478'] | [0].{ip_address: ip_address}"
will yield
{
"ip_address": "10.x.x.x"
}
If you need a string as a result:
- debug:
msg: "{{ vm_info.virtual_machines | json_query(query) }}"
vars:
query: "[?guest_name=='C97A1612171478'] | [0].ip_address"
will yield
"10.x.x.x"
I can't test this properly, but try to fiddle around with the following code:
- debug:
msg: "{{ item.ip_address | json_query(query) }}"
loop: "{{ vm_info.virtual_machines }}"
vars:
query: "[?guest_name=='C97A1612171478']"

Cant get the private ip from an EC2 result

Trying to create a playbook that will let me choose if i want spot or On-demand EC2 with passing external var, all going fine except the part of getting the private ip from the result and settings it as fact for further usage.
i've tried different paths like item.instances[0].private_ip
and just cant seems to get the right one.
---
- hosts: localhost
tasks:
- name: when true
ec2:
region: us-east-1
instance_type: t2.nano
key_name: test_key
instance_type: t2.micro
image: ami-0aec0138b2*****
wait: yes
count: 1
vpc_subnet_id: subnet-00fdda1452d****
assign_public_ip: no
spot_price: 1
register: ci_ec2_true
when: var == "true"
- name: when false
ec2:
region: us-east-1
instance_type: t2.nano
key_name: test_key
instance_type: t2.micro
image: ami-0aec0138b2****
wait: yes
count: 1
vpc_subnet_id: subnet-00fdda1452d0****
assign_public_ip: no
register: ci_ec2_false
when: var == "false"
- name: ci_ec2_true
debug:
msg: "{{ ci_ec2_true }}"
- name: Spot / On-demand var router
set_fact:
ci_ec2: "{{ ci_ec2_true if ci_ec2_false is skipped else ci_ec2_false }}"
- name: debug ci_ec2
debug:
msg: "{{ ci_ec2 }}"
- name: ec2_prov - set fact for all ci_machine_ips
set_fact: private_ips="{{ item.instances[0].private_ip }}"
with_items: "{{ ci_ec2 }}"
register: ci_ec2_ip_results
- debug:
msg: "{{ ci_ec2_ip_results }}"
The expected result should be the private ip value, instead im getting this error:
TASK [ec2_prov - set fact for all ci_machine_ips] ******************************
**********
fatal: [localhost]: FAILED! => {"failed": true, "msg": "the field 'args' has an invalid value, which appears to include a variable that is undefined. The error was: 'ansible.vars.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'instances'\n\nThe error appears to have been in '/root/test/when-create.yml': line 50, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: ec2_prov - set fact for all ci_machine_ips\n ^ here\n"}
the command used to run the playbook is:
ansible-playbook when-create.yml -e var=true
TASK [debug ci_ec2] ************************************************************
ok: [localhost] => {
"msg": {
"changed": true,
"instance_ids": [
"i-0485794cd8a299b27"
],
"instances": [
{
"ami_launch_index": "0",
"architecture": "x86_64",
"block_device_mapping": {
"/dev/xvda": {
"delete_on_termination": true,
"status": "attached",
"volume_id": "vol-0cd42f8696aef9bbd"
}
},
"dns_name": "",
"ebs_optimized": false,
"groups": {
"sg-07c7177c": "default"
},
"hypervisor": "xen",
"id": "i-0485794cd8a299b27",
"image_id": "ami-0aec0138b2a5****",
"instance_type": "t2.micro",
"kernel": null,
"key_name": "test_Key",
"launch_time": "2019-05-11T16:41:10.000Z",
"placement": "us-east-1a",
"private_dns_name": "ip-10-220-129-224.ec2.internal",
"private_ip": "10.220.129.224",
"public_dns_name": "",
"public_ip": null,
"ramdisk": null,
"region": "us-east-1",
"root_device_name": "/dev/xvda",
"root_device_type": "ebs",
"state": "running",
"state_code": 16,
"tags": {
"Created_By": "InstanceLaunch"
},
"tenancy": "default",
"virtualization_type": "hvm"
}
],
"tagged_instances": []
}
}
so eventually i found the way...
- name: ec2_prov - set fact for all ci_machine_ips
set_fact: private_ips="{{ item.private_ip }}"
with_items: "{{ ci_ec2.instances }}"
register: ci_ec2_ip_results

Ansible - using for loops inside user module

I recently discovered the use of for loops in Ansible and was very excited about it.
Tries to use it inside the debug module and it worked superfine, but when I am trying to use the same inside "user" module, the control flow is not able to identify the "name" keyword of user module. Below is my poetry,
- hosts: testservers
tasks:
- name: Setting user facts
set_fact:
username: "{{ lookup('ini', 'name section=userDetails file=details.ini') }}"
userpass: "{{ lookup('ini', 'password section=userDetails file=details.ini') }}"
- name: User creation
become: true
# debug:
# msg: |
# {% for x,y in item.1,item.2 %}
# {{ x }} is the username and its password is {{ y }}
# {% endfor %}
# with_items:
# - { 1: "{{ username.split(',') }}", 2: "{{ userpass.split(',') }}" }
user: |
{% for x,y in item.1,item.2 %}
name: "{{ x }}"
password: "{{ y }}"
{% endfor %}
with_items:
- { 1: "{{ username.split(',') }}", 2: "{{ userpass.split(',') }}" }
Details.ini file contents below
#User basic details
[userDetails]
name=vasanth,vasanthnagkv
password=vasanth12,pass2
The commented part above works fine. but the uncommented part throws the below error
failed: [10.0.0.47] (item={1: [u'vasanth', u'vasanthnagkv'], 2: [u'vasanth12', u'pass2']}) => {
"changed": false,
"invocation": {
"module_args": {
"append": false,
"create_home": true,
"force": false,
"move_home": false,
"non_unique": false,
"remove": false,
"ssh_key_bits": 0,
"ssh_key_comment": "ansible-generated on APUA-02",
"ssh_key_type": "rsa",
"state": "present",
"system": false,
"update_password": "always"
}
},
"item": {
"1": [
"vasanth",
"vasanthnagkv"
],
"2": [
"vasanth12",
"pass2"
]
},
"msg": "missing required arguments: name"
}
to retry, use: --limit #/home/admin/ansiblePlaybooks/userCreation/userCreate.retry
PLAY RECAP ************************************************************************************************************************************************************************************
10.0.0.47 : ok=2 changed=0 unreachable=0 failed=1
Appreciate any kind of help here.
This line user: | means your passing a string to user module using the block style indicator: https://yaml-multiline.info/)
Since Ansible will just treat it as a string, you are not passing the required name parameter to the user module
Try splitting the names after the lookup in the first task so you can have the names and passwords list:
- name: Setting user facts
set_fact:
username: "{{ lookup('ini', 'name section=userDetails file=details.ini').split(',') }}"
userpass: "{{ lookup('ini', 'password section=userDetails file=details.ini').split(',') }}"
Once you have both the username and password list, you can use both variables by:
- user:
name: "{{ item }}"
password: "{{ userpass[index] }}"
loop: "{{ username }}"
loop_control:
index_var: index

Variable won't be filtered correctly

what am I doing wrong?
I use below task to get all defined log_dirs of a host. Those information are stored in a fact, which is a dict and this works like a charm.
- name: get all_log_dirs
set_fact:
all_log_dirs="{{ (all_log_dirs|default({})) | combine( { item.key:vars[item.key] } ) }}"
with_dict: "{{ vars }}"
when: item.key is search('^((?!splunk).)*_log_dir')
Here the appropriate output:
"ansible_facts": {
"all_log_dirs": {
"springboot_server_log_dir": "{{ server_deployment_dir }}/logs"}
But the problem is, if I now want to use the new dict for e. g.:
- name: create symlink for splunk if not present
file:
src: "{{ item.value }}"
dest: "{{ splunk_log_dir }}/{{ item.key | regex_replace('_server_log_dir|_log_dir') | regex_replace('eap','jboss-eap') }}"
state: link
with_dict: "{{ all_log_dirs }}"
I only get:
failed: [...] (item={'value': u'{{ server_deployment_dir }}/logs', 'key': u'springboot_server_log_dir'}) => {
"changed": false,
"invocation": {
"module_args": {
"dest": "/somedir/springboot",
"path": "/somedir/springboot",
"src": "{{ server_deployment_dir }}/logs",
"state": "link",
}
},
"msg": "src file does not exist, use \"force=yes\" if you really want to create the link: /somedir/{{ server_deployment_dir }}/logs",
"path": "/somedir/springboot",
"src": "{{ server_deployment_dir }}/logs",
"state": "absent"
}
Why isn't {{ server_deployment_dir }} filtered correctly by Ansible?
Even I change src to dest and the way around, it won't work, because the variable isn't being filtered.
The value of {{ server_deployment_dir }} is of course host specific and is sth like /opt/applicationXY/appDeployDir
Don't use vars object. Period.
It is internal variable storage intended for under-the-hood usage.
When Ansible template engine detects vars access, it stops further templating chain!
Example:
---
- hosts: localhost
connection: local
gather_facts: no
vars:
myvar1: hello
myvar2: world
myvar3: "{{ myvar2 }}"
tasks:
- debug:
msg: "{{ myvar1 }} {{ myvar3 }}"
- debug:
msg: "{{ vars['myvar1'] }} {{ vars['myvar3'] }}"
Result:
TASK [debug] ***************************
ok: [localhost] => {
"msg": "hello world"
}
TASK [debug] ***************************
ok: [localhost] => {
"msg": "hello {{ myvar2 }}"
}
Update: if you utterly need to access variable trough vars object, there's vars lookup available in Ansible 2.5; and it templates values as usual:
E.g.
- debug:
msg: "{{ lookup('vars','myvar1') }} {{ lookup('vars','myvar3') }}"
results to hello world in the context of my previous example.

Ansible parse JSON output

I am trying to parse the Ansible output the print a value
- name: Creating a new instance
os_server:
state: present
auth:
auth_url: "{{ auth_url }}"
username: "{{ username }}"
password: "{{ password }}"
project_name: "{{ project_name }}"
name: "{{ item.hostname }}"
image: "{{ item.image }}"
nics: "{{ nics }}"
with_items: "{{ servers }}"
register: "os"
Output:
"server": {
"OS-DCF:diskConfig": "MANUAL",
"OS-EXT-AZ:availability_zone": "zoneA",
"OS-EXT-STS:power_state": 1,
"OS-EXT-STS:task_state": null,
"OS-EXT-STS:vm_state": "active",
"OS-SRV-USG:launched_at": "2018-04-01T18:53:16.000000",
"OS-SRV-USG:terminated_at": null,
"accessIPv4": "10.190.230.23",
"accessIPv6": "",
"addresses": {
"provider_corenet_bif_757": [
{
"OS-EXT-IPS-MAC:mac_addr": "fa:1:3:3:5e:6a",
"OS-EXT-IPS:type": "fixed",
"addr": "10.19.23.23",
"version": 4
}
],
"provider_nmnet_bif_912": [
{
"OS-EXT-IPS-MAC:mac_addr": "fa:1:3:39:b:57",
"OS-EXT-IPS:type": "fixed",
"addr": "10.25.13.64",
"version": 4
}
]
server.addresses.provider_nmnet_bif_912.addr
},
I want to parse addr "10.25.13.64".
I tried {{ item.server.addresses.provider_nmnet_bif_912.addr }} and {{os.server.addresses.provider_nmnet_bif_912.addr}} both didnot work.
Need Help!!!
Finally figured it out:
"{{ item.server.addresses.provider_nmnet_bif_912[0].addr }}"

Resources