include_vars and unknown variable - ansible

I want to use Custom variable files but I have an error. please explain me How can I have a custom variable file?
my vars/var1.yml is :
---
ip_ssh_srv1: 192.168.10.10
and vars/var2.yml is :
---
ip_ssh_srv2: 192.168.10.20
my task.yml is :
---
- include_vars:
file: var1.yml
file: var2.yml
- name: -- my task --
template: src=db_info.j2 dest=/root/db_info.cnf
and my output is :
[root#anisble ansible]# ansible-playbook playbooks/get_ip_ssh.yml
TASK [Gathering Facts]
*********************************************************************
ok: [db1]
ok: [db3]
TASK [ssh : include_vars] ***************************************************************
ok: [db1]
ok: [db2]
TASK [ssh : -- my task --] *******************************************************************
fatal: [db1]: FAILED! => {"changed": false, "msg": "AnsibleUndefinedVariable: 'ip_ssh_srv1' is undefined"}
fatal: [db2]: FAILED! => {"changed": false, "msg": "AnsibleUndefinedVariable: 'ip_ssh_srv2' is undefined"}
PLAY RECAP ****************************************************************
db1 : ok=2 changed=0 unreachable=0 failed=1
db2 : ok=2 changed=0 unreachable=0 failed=1
why unknown variable?

Mistake 1
You seem to including an unspecified vars file.
Mistake 2
You should not define the same keys multiple times.
This is a YAML dictionary:
- include_vars:
file: var1.yml
file: var2.yml
It is syntactically correct, but you defined the same key file twice.
When Ansible queries it, it can get only a single value for the file key value (var2.yml).
If you look at the include_vars module docs, you'll find a few ways to include multiple files (but none through defining the same key), for example:
- include_vars:
dir: vars
files_matching: var

Related

pass a list/array of strings from my bash script, to an ansible script

I am trying to pass a list/array of strings from my bash script, to my ansible script:
Excerpt from bash script:
configureChrony() {
ntpServer="initVal"
ntpServers=()
while ! [[ -z "$ntpServer" ]]; do
read -e -p "Please input the ip address or domain name of the TP server you wish to add: " ntpServer
if ! [[ -z "$ntpServer" ]]; then
ntpServers+=($ntpServer)
fi
done
ansible-playbook -i localhost, test.yml --extra-vars="ntp_list = $ntpServers"
}
test.yml
---
- name: "This is a test"
hosts: all
gather_facts: no
tasks:
- name: print variable - with_items
debug:
msg: "{{ item.name }}"
with_items:
- "{{ ntp_list }}"
When testing the bash script, I get this error:
Which timekeeping service would you like to use [ntp/chrony]: chrony
Please input the ip address or domain name of the TP server you wish to add: Test1
Please input the ip address or domain name of the TP server you wish to add: Test2
Please input the ip address or domain name of the TP server you wish to add:
PLAY [This is a test] ****************************************************************************************************************************************
TASK [print variable - with_items] ***************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "'ntp_list' is undefined"}
PLAY RECAP ***************************************************************************************************************************************************
localhost : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
The issue is the way I am passing the list/array from the bash script to ansible script as they both run given the required data.
The desired output is for each element of the list to be outputted to the screen.
Test1
Test2
Any help appreciated.
Direct answer
You cannot really pass the bash array as a list inside an extra var to ansible. It is possible but would require to loop over the bash array and transform it into a json parsable format that you inject inside the extra var.
The easiest way IMO is to pass a concatenation of all array element inside a string that you will later split in the playbook.
Using the form ${somevar[#]} won't work here as every bash array element will end up being parsed as a new argument to your ansible command. You will have to use instead ${somevar[*]}. You also need to quote the extra var correctly so that both bash and ansible are able to successfully parse it. The correct command call in your script is then:
ansible-playbook test.yml --extra-vars "ntp_list_raw='${ntpServers[*]}'"
You now need a bit of rework on your ansible playbook to split the received value into a list:
---
- name: "This is a test"
hosts: localhost
gather_facts: no
vars:
ntp_list: "{{ ntp_list_raw.split(' ') }}"
tasks:
- name: Print split variable items
debug:
var: item
loop: "{{ ntp_list }}"
And now entering values and calling the playbook from your script gives:
Please input the ip address or domain name of the TP server you wish to add: toto
Please input the ip address or domain name of the TP server you wish to add: pipo
Please input the ip address or domain name of the TP server you wish to add: bingo
Please input the ip address or domain name of the TP server you wish to add:
PLAY [This is a test] *********************************************************************
TASK [Print split variable items] *********************************************************************
ok: [localhost] => (item=toto) => {
"ansible_loop_var": "item",
"item": "toto"
}
ok: [localhost] => (item=pipo) => {
"ansible_loop_var": "item",
"item": "pipo"
}
ok: [localhost] => (item=bingo) => {
"ansible_loop_var": "item",
"item": "bingo"
}
PLAY RECAP *********************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Going further
If your only goal is to ask for your ntp servers interactively, you can do all of the above directly in your playbook using vars_prompt
---
- name: "This is a test"
hosts: localhost
gather_facts: no
vars_prompt:
- name: ntp_list_raw
prompt: "Please enter ntp servers separated by a comma without spaces"
private: no
vars:
ntp_list: "{{ ntp_list_raw.split(',') }}"
tasks:
- name: Print split variable
debug:
var: item
loop: "{{ ntp_list }}"
which gives:
$ ansible-playbook test.yaml
Please enter ntp servers separated by a comma without spaces []: toto,pipo,bingo
PLAY [This is a test] *********************************************************************
TASK [Print split variable] *********************************************************************
ok: [localhost] => (item=toto) => {
"ansible_loop_var": "item",
"item": "toto"
}
ok: [localhost] => (item=pipo) => {
"ansible_loop_var": "item",
"item": "pipo"
}
ok: [localhost] => (item=bingo) => {
"ansible_loop_var": "item",
"item": "bingo"
}
PLAY RECAP *********************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
You can even bypass the prompt giving the value as an extra var directly:
$ ansible-playbook test.yaml -e ntp_list_raw=toto,pipo,bingo
PLAY [This is a test] *********************************************************************
TASK [Print split variable] *********************************************************************
ok: [localhost] => (item=toto) => {
"ansible_loop_var": "item",
"item": "toto"
}
ok: [localhost] => (item=pipo) => {
"ansible_loop_var": "item",
"item": "pipo"
}
ok: [localhost] => (item=bingo) => {
"ansible_loop_var": "item",
"item": "bingo"
}
PLAY RECAP *********************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

How to pass URLs as a variable to an ansible playbook

I created a playbook which clears cache, I'm trying to pass a url to a variable, and when I execute the playbook I get an empty array for that parameter.
In my playbook I have a vars module with this variable (environment), it gets defined when you pass in a variable to the ansible-playbook command
vars:
environment: "{{testenv}}"
-e testenv=https://www.test1.com
When I execute the playbook I get this error.
Do I need to format the url in someway?
fatal: [localhost]: FAILED! => {"changed": false, "msg": "unknown url type: '[]/content/clear_cache?
Your issue is coming from the fact that environment is a reserved variable, as pointed in the second row of this table in the documentation:
Valid variable names
 Not valid
 foo
 *foo, Python keywords such as async and lambda
 foo_env
 playbook keywords such as environment
 foo_port
 foo-port, foo port, foo.port
 foo5, _foo
 5foo, 12
Source: https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#creating-valid-variable-names
So, you just need to change your variable name to something else and it will work.
Given the playbook:
- hosts: all
gather_facts: no
tasks:
- debug:
msg: "{{ _environment }}"
vars:
_environment: "{{ testenv }}"
When run:
$ ansible-playbook play.yml -e testenv=https://www.test1.com
PLAY [all] **********************************************************************************************************
TASK [debug] ********************************************************************************************************
ok: [localhost] => {
"msg": "https://www.test1.com"
}
PLAY RECAP **********************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Checking whether nested variable is defined in the when statement

Nested data structures can be accessed either by indexing using the key name or dot syntax with the key name - https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#accessing-complex-variable-data
But I'm not able to access nested data structure from variables in the when statement. Here is a minimal sample to demonstrate the issue
# cat play.yml
- hosts: localhost
vars:
parent:
child1: true tasks:
- name: "Check if variable is defined"
fail:
msg: "mandatory variable {{ item }} not passed as extra args when invoking playbook"
when: item not in vars
loop:
- parent
- parent.child1
- parent.child2
Here is the sample output
ansible-playbook play.yml (livingstone-dev/monitoring)
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
PLAY [localhost] *********************************************************************************************************************************************
TASK [Gathering Facts] ***************************************************************************************************************************************
ok: [localhost]
TASK [Check if variable is defined] **************************************************************************************************************************
skipping: [localhost] => (item=parent)
failed: [localhost] (item=parent.child1) => {"ansible_loop_var": "item", "changed": false, "item": "parent.child1", "msg": "mandatory variable parent.child1 not passed as extra args when invoking playbook"}
failed: [localhost] (item=parent.child2) => {"ansible_loop_var": "item", "changed": false, "item": "parent.child2", "msg": "mandatory variable parent.child2 not passed as extra args when invoking playbook"}
PLAY RECAP ***************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
Any idea how I can have nested variables in ansible when statements. Here are some things which I've already tried and didn't work:
Using dotted notation for accessing variable
Changing the when statement to "item is not defined" without the quotes
Below code worked for me. You have to provide the values in loop within "{{}}"
- name: "Check if variable is defined"
fail:
msg: "mandatory variable {{ item }} not passed as extra args when invoking playbook"
loop:
- "{{parent.child1}}"
- "{{parent}}"
- "{{parent.child2}}"
when: item is not defined
To get the same result without warning change
when: "{{item}} is not defined"
to
when: not vars|json_query(item)
Here is the working playbook. Thanks to #smily for pointing me in the right direction. Since the loop variable item needs to be evaluated before the when condition is evaluated, I had to expand the variable item. I've done it by encompassing the entire when condition in double quotes and just expanding the item loop variable.
$ cat play.yml (livingstone-dev/monitoring)
- hosts: localhost
vars:
parent:
child1: true
tasks:
- name: show variable
debug:
var: parent
- name: "Check if variable is defined"
fail:
msg: "mandatory variable item not passed as extra args when invoking playbook"
when: "{{item}} is not defined"
loop:
- parent
- parent.child1
- parent.child2
Here is the playbook output.
$ ansible-playbook play.yml (livingstone-dev/monitoring)
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
PLAY [localhost] *********************************************************************************************************************************************
TASK [Gathering Facts] ***************************************************************************************************************************************
ok: [localhost]
TASK [show variable] *****************************************************************************************************************************************
ok: [localhost] => {
"parent": {
"child1": true
}
}
TASK [Check if variable is defined] **************************************************************************************************************************
[WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: {{item}} is not defined
skipping: [localhost] => (item=parent)
skipping: [localhost] => (item=parent.child1)
failed: [localhost] (item=parent.child2) => {"ansible_loop_var": "item", "changed": false, "item": "parent.child2", "msg": "mandatory variable item not passed as extra args when invoking playbook"}
PLAY RECAP ***************************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0

How to initilize a dictonary to some value in ansible

I am trying to set a default value to element of the dictornary, following is my playbook showing incorrect way to set the value. Can someone tell to set dictname.key.value to default. ?
-->cat initilize_dict.yml
---
- hosts: localhost
vars:
dictname:
key: 'default'
value: 'default'
tasks:
- debug: var=dictname.key.value
- debug: var=dictname.key.['value']
Current output
-->ansible-playbook initilize_dict.yml
[WARNING]: Unable to parse /etc/ansible/hosts as an inventory source
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
PLAY [localhost] *************************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [debug] *****************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"dictname.key.value": "VARIABLE IS NOT DEFINED!"
}
TASK [debug] *****************************************************************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "template error while templating string: expected name or number. String: {{dictname.key.['value']}}"}
to retry, use: --limit #/home/monk/samples/initilize_dict.retry
PLAY RECAP *******************************************************************************************************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=1
You would have to use following syntax:
- set_fact:
dictname:
key:
{ 'value': 'default' }
To validate:
- debug: var=dictname.key.value

Ansible loses variable content

I've a role with some tasks to call docker_compose. When I use a variable twice, the second task fails with error: The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'base_dir'.
It looks to me (using copy&paste) that the second call to docker_compose can't access the variable in the same scope. With other tasks (file, command, shell) it works well.
The variable is stored in roles/collabora/default/main.yml:
collabora:
base_dir: /srv/docker/collabora
and this is the inventory:
...
collabora:
hosts:
server1:
collabora_customer:
customer1:
port: 2000
customer2:
port: 2001
server2:
collabora_customer:
customer1:
port: 2000
...
This is a part of my role roles/collabora/tasks/main.yml. It iterates over all entries in host var collabora_customer.
- name: Configure Collabora instances
include_tasks: manage_collabora.yml
loop: "{{ collabora_customer | default({}) | dict2items }}"
And the manage_collabora.yml makes the work per instance.
- debug:
msg: "path: {{collabora.base_dir}}"
- name: Define location
set_fact:
instance: "{{ item }}"
collabora_path: "{{ collabora.base_dir }}/{{ item.key }}"
- name: Create "Collabora" directory
file:
path: "{{collabora_path}}"
state: directory
- name: Copy docker-compose folder "Collabora"
template:
src: "docker-compose.yml.j2"
dest: "{{collabora_path}}/docker-compose.yml"
owner: "root"
group: "root"
mode: "0640"
- name: Run docker-compose pull for "Collabora"
docker_compose:
project_src: "{{collabora_path}}"
state: present
The loop works well for the first trip per host. But the second loop failes for customer2 with error message from above in the debug message.
The funny part is, when I remove the last docker_compose task, then everything works as expected. It looks to me, as the docker_compose removes the variable from the environments - why ever.
The current result is:
PLAY [Install Collabora]
****************************************************
TASK [Gathering Facts]
****************************************************
ok: [server1]
ok: [server2]
TASK [docker_collabora : Configure Collabora instances]
****************************************************
included: roles/docker_collabora/tasks/manage_collabora.yml for server1
included: roles/docker_collabora/tasks/manage_collabora.yml for server1
included: roles/docker_collabora/tasks/manage_collabora.yml for server2
TASK [docker_collabora : debug]
****************************************************
ok: [server2] => {
"msg": "path: /srv/docker/collabora"
}
TASK [docker_collabora : Define location]
****************************************************
ok: [server2]
TASK [docker_collabora : Create "Collabora" directory]
****************************************************
ok: [server2]
TASK [docker_collabora : Copy docker-compose folder "Collabora"]
****************************************************
ok: [server2]
TASK [docker_collabora : Run docker-compose pull for "Collabora"]
****************************************************
ok: [server2]
TASK [docker_collabora : debug]
****************************************************
ok: [server1] => {
"msg": "path: /srv/docker/collabora"
}
TASK [docker_collabora : Define location]
****************************************************
ok: [server1]
TASK [docker_collabora : Create "Collabora" directory]
****************************************************
ok: [server1]
TASK [docker_collabora : Copy docker-compose folder "Collabora"]
****************************************************
ok: [server1]
TASK [docker_collabora : Run docker-compose pull for "Collabora"]
****************************************************
ok: [server1]
TASK [docker_collabora : debug]
****************************************************
fatal: [server1]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'base_dir'\n\nThe error appears to be in 'roles/docker_collabora/tasks/manage_collabora.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- debug:\n ^ here\n"}
PLAY RECAP
****************************************************
server1 : ok=9 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
server2 : ok=7 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
I would expect, that this would work.

Resources