I am using Ansible 2.3.0.0 and I have tested in Ansible 2.4.0.0, obtaining the same result. My problem is very simple, but I cannot see the problem.
I have defined a list of objects in Ansible as follows:
vars:
password_text_to_encrypt:
- { line: "{{truststore_pass }}" , regexp: '\${TRUSTSTORE_PASS}*'}
- { line: "{{ keystore_pass }}" , regexp: '\${KEYSTORE_PASS}*'}
- { line: "{{ gp_pass }}" , regexp: '\${GP_PASS}*'}
- { line: "{{ datasource_password }}" , regexp: '\${DATASOURCE_PASS}*'}
- { line: "{{ server_password }}" , regexp: '\${SERVER_PASSWORD}*'}
- { line: "{{ sftp_password }}" , regexp: '\${SFTP_PASSWORD}*'}
- { line: "{{ db_userpassword }}" , regexp: '\${DB_PASSWORD}*'}
roles:
- basic_role
My Basic_role just prints the items, and I would like to obtain the content of each line:
---
- name: "print password"
debug:
msg: "The content of the line is: {{ item.line}}"
with_nested:
- "{{password_text_to_encrypt}}"
But the result that I obtain is:
FAILED! => {"failed": true, "msg": "the field 'args' has an invalid value, which appears to include a variable that is undefined. The error was: 'list object' has no attribute 'line'\n\nThe error appears to have been in.....
If I change item.line to just item, it works but it prints:
ok: [localhost] => (item=[u'regexp', u'line']) => {
"item": [
"regexp",
"line"
],
"msg": "The content of the line is: [u'regexp', u'line']"
}
.
.
.
To summarize, Ansible does not consider the content of line o regexp. I have been doing tests and the variable which are used to init line and regexp are not empty.
Use with_items instead of with_nested.
I think you want loops and includes because you're getting a flattened list which is expected (as per the documentation).
Related
Let me introduce my problem. I have some list of dictionary in my Ansible code:
my_example_list = [
{
"key1" : "value_of_first_key"
},
{
"key2": "value_of_second_key"
},
{
"key3": "value_of_third_key"
}
]
I need execute command which will iterate over this list and it should look something like:
- name: 'Example'
shell: 'Here is my {{ item.key }} and here is {{ item.value }}'
What I've do or try to do:
I was trying to do that with with_items but i'm not able to point into value of particular key.
I've also try to filter values using | first and | last but it's not worked in my case.
What I want to achieve:
Creating loop which will iterate via that list and inject separated key and value into command.
I was asked to show how I was trying to resolve my issue:
Here is some code:
# Showing last component failing
- name: "Try to show last component of my list"
debug:
msg: "{{ my_example_list[1] | last }}"
# When i'm trying to show first component of my list i get "key1"
- name: "Try to show first component of my list"
debug:
msg: "{{ my_example_list[1] | first }}"
# This shows me my list of dict
- name: "Trying use with_items"
debug:
msg: "{{ item }}"
with_items: "{{ my_example_list }}"
# But when i'm trying point to key and value for example
- name: "Trying use with_items point to key and value"
debug:
msg: "Here is my {{ item.key }} which keep {{ item.value }}"
with_items: "{{ my_example_list }}"
# It's failing.
Sorry it's not maybe solution with using loop. I'm just stack with that issue over few days... And as first step I want to know how correctly point to pair keys and values.
It also works well:
- name: Correct solution
debug:
msg: "This is my {{ item.key }} and my value {{ item.value }}"
with_dict: "{{ my_example_list }}"
Thanks #U880D for help! I'm not able to add some plus for your solution because I'm new joiner. Appreciate your answer! :)
Your data structure and naming seems to be unfavorable. There is no need to number the key name and therefore it should be avoided. Furthermore counting list elements in Python starts at 0 not 1.
The following minimal example playbook
---
- hosts: localhost
become: false
gather_facts: false
vars:
example_list: |
[
{
"key1" : "value_of_first_key"
},
{
"key2": "value_of_second_key"
},
{
"key3": "value_of_third_key"
}
]
tasks:
- name: Example loop
debug:
msg: "{{ item }} is of type {{ item | type_debug }}"
loop: "{{ example_list }}"
- name: Example loop
debug:
msg: "{{ item.values() }}"
loop: "{{ example_list }}"
will result into an output of
TASK [Example loop] ******************************************
ok: [localhost] => (item={u'key1': u'value_of_first_key'}) =>
msg: '{u''key1'': u''value_of_first_key''} is of type dict'
ok: [localhost] => (item={u'key2': u'value_of_second_key'}) =>
msg: '{u''key2'': u''value_of_second_key''} is of type dict'
ok: [localhost] => (item={u'key3': u'value_of_third_key'}) =>
msg: '{u''key3'': u''value_of_third_key''} is of type dict'
TASK [Example loop] ******************************************
ok: [localhost] => (item={u'key1': u'value_of_first_key'}) =>
msg:
- value_of_first_key
ok: [localhost] => (item={u'key2': u'value_of_second_key'}) =>
msg:
- value_of_second_key
ok: [localhost] => (item={u'key3': u'value_of_third_key'}) =>
msg:
- value_of_third_key
Further Readings
How to work with lists and dictionaries in Ansible
Extended loop variables
The goal is to read the input file for a string that matches the regex expression then to match the expression to my config file.. when there is a match ansible is to replace the existing line in the config file with the matching line in the delta file.
I was able to perform this task but noticed that ansible would read one line and essentially be done. I added the .splitlines() option to my code so that it would read line by line and perform the same action but i received the following error:
- name: Search for multiple reg expressions and replace in config file
vars:
# pull data from config file
input: "{{ lookup('file', '{{ record }}').splitlines() }}"
delta: "{{ input | regex_search('^.*[a-zA-Z]+.*_.*[a-zA-Z]+.*?', multiline=True )}}"
delta1: "{{ input | regex_search('^.*[a-zA-Z]+.*_.*[a-zA-Z]+.*', multiline=True)}}"
record: "/etc/ansible/roles/file_config/files/records/records.config.{{ inventory_hostname }}"
lineinfile:
path: /dir/dir/WCRUcachedir/records.config
# Line to search/Match against
regexp: "{{item.From}}"
# Line to replace with
line: "{{item.To}}"
state: present
backup: yes
with_items:
- { From: '{{delta}}', To: '{{delta1}}' }
This happened to be my end result
"msg": "An unhandled exception occurred while templating '{{ input | regex_search('^.*[a-zA-Z]+.*_.*[a-zA-Z]+.*', multiline=True )}}'. Error was a <class 'ansible.errors.AnsibleError'>, original message: Unexpected templating type error occurred on ({{ input | regex_search('^.*[a-zA-Z]+.*_.*[a-zA-Z]+.*', multiline=True )}}): expected string or buffer"
}
these are what i believe my conflicting lines are
input: "{{ lookup('file', '{{ record }}').splitlines() }}"
AND
delta1: "{{ input | regex_search('^.[a-zA-Z]+._.[a-zA-Z]+.', multiline=True)}}"
OK, you do have another problem.
In the vars section, when you're setting delta and delta1, regex_search is expecting a string, but your passing a list (which splitlines() created). But you need it to work on one line at a time.
So, rather that input, use item, which will be set in the loop:
vars:
input: "{{ lookup('file', '{{ record }}').splitlines() }}"
delta: "{{ item | regex_search('^.*[a-zA-Z]+.*_.*[a-zA-Z]+.*?')}}"
delta1: "{{ item | regex_search('^.*[a-zA-Z]+.*_.*[a-zA-Z]+.*')}}"
Obviously, you don't need the multiline=True any more.
Now, the loop will look like this:
lineinfile:
path: /etc/opt/CCURcache/records.config
regexp: "{{ delta }}"
line: "{{ delta1 }}"
state: present
backup: yes
loop: "{{ input }}"
when: delta != ""
Yes, you only have one item to loop over. That item has two elements, From and To.
From is {{ input | regex_search('^.*[a-zA-Z]+.*_.*[a-zA-Z]+.*?', multiline=True )}}
To is {{ input | regex_search('^.*[a-zA-Z]+.*_.*[a-zA-Z]+.*', multiline=True)}}
All you did in the vars section was define strings, which will get executed later, when the regex and line are used in the module.
Now, assuming you need to put these together, you need to zip them:
lineinfile:
path: /etc/opt/CCURcache/records.config
regexp: "{{item.0}}"
line: "{{item.1}}"
state: present
backup: yes
loop: "{{ delta|zip(delta1)|list }}"
Here's a simple example:
---
- hosts: localhost
connection: local
gather_facts: no
vars:
list1:
- key_1
- key_2
- key_n
list2:
- value_1
- value_2
- value_n
tasks:
- name: Loop over lists
debug:
msg: "key is {{ item.0 }}; value is {{ item.1 }}"
loop: "{{ list1|zip(list2)|list }}"
And the results:
PLAY [localhost] *******************************************************************************
TASK [Loop over lists] *************************************************************************
ok: [localhost] => (item=['key_1', 'value_1']) => {
"msg": "key is key_1; value is value_1"
}
ok: [localhost] => (item=['key_2', 'value_2']) => {
"msg": "key is key_2; value is value_2"
}
ok: [localhost] => (item=['key_n', 'value_n']) => {
"msg": "key is key_n; value is value_n"
}
PLAY RECAP *************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Good luck!
I do have a simple json file, where i need to pull a set of value from EACH array item, but during iteration it fails.
My playbook looks like:
code:
---
- name: direct - this works like charm
set_fact:
bb: "{{ pr_json.json.issues[0].fields.customfield_11756.value }}"
- debug:
var: bb
- name: via array - this is not working since iteration is not happening
set_fact:
dd_branch: "{{ pr_json.json.issues[{{ item }}].fields.customfield_11756.value }}"
register: mass
- debug:
var: mass
Getting output as:
TASK [jira_update : direct - this works like charm] ********************************************************************************************************************
task path: /home/test/ansible_jira/roles/jira_update/tasks/call.yml:3
ok: [localhost] => {
"ansible_facts": {
"bb": "R4.19"
},
"changed": false
}
TASK [jira_update : debug] *********************************************************************************************************************************************
task path: /home/test/ansible_jira/roles/jira_update/tasks/call.yml:7
ok: [localhost] => {
"bb": "R4.19"
}
TASK [jira_update : via array - this is not working since iteratoin is not happening] **********************************************************************************
task path: /home/test/ansible_jira/roles/jira_update/tasks/call.yml:10
fatal: [localhost]: FAILED! => {
"msg": "template error while templating string: expected token ':', got '}'. String: {{ pr_json.json.issues[{{ item }}].fields.customfield_11756.value }}"
}
Please do let us know how can I iterate through an array variable value on every sequence.
tried this too, but can somebody help to iterate the array values, please.
- name: Create PR request in TEMS JIRA
jira:
uri: "{{ tems_jira }}"
username: "{{ user }}"
password: "{{ pass }}"
operation: create
project: PR
issuetype: 'PR-Form'
summary: "{{ pr_json.json| json_query('issues[].fields.summary') }}"
description: "{{ pr_json.json | json_query('issues[].fields.description') }}"
args:
fields:
customfield_10303:
value: "{{ pr_json.json | json_query('issues[].fields.customfield_11756.value') }}"
Youy need to feed your list into a with_items iterator. Thats what sets the item variable for looping purposes.
- name: via array - this is not working since iteration is not happening
set_fact:
dd_branch: "{{ pr_json.json.issues[ item ].fields.customfield_11756.value }}"
register: mass
with_items:
- 0
- 1
That will iterate through all of the list items of pr_json.json.issues which will let you dive deeper into the variable structure like you are looking for. There are a lot of other factors that you can feed into the loop that might interest you that you can find detailed here.
https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html
I'm trying to write a playbook that will load vars from a group vars file then check if a variable exists
my playbook is like this:
---
- hosts: "{{ target }}"
roles:
- app
tasks:
- name: alert if variable does not exist
fail:
msg: "{{ item }} is not defined"
when: "{{ item }}" is not defined
with_items:
- country
- city
- street
...
My inventory file contains
[app]
ansible-slave1
ansible-slave2
[db]
ansible-db
[multi:children]
app
db
and I have the roles/app/vars/main.yml containing
country: "France"
city: "Paris"
What I was expecting is the playbook to output "street is not defined" but I have a syntax issue I can't resolve
[vagrant#ansible-master vagrant]$ ansible-playbook --inventory-file=ansible_master_hosts test_variables.yml --extra-vars "target=ansible-slave1" --syntax-check
ERROR! Syntax Error while loading YAML.
The error appears to have been in '/vagrant/test_variables.yml': line 10, column 24, but may be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
msg: "{{ item }} is not defined"
when: "{{ item }}" is not defined
^ here
We could be wrong, but this one looks like it might be an issue with
missing quotes. Always quote template expression brackets when they
start a value. For instance:
with_items:
- {{ foo }}
Should be written as:
with_items:
- "{{ foo }}"
I'd be happy with any hints.
Thanks
You have "" in invalid place of "when" statement. This should be like this:
msg: "{{ item }} is not defined"
when: "{{ item }} is not defined"
So the output will be:
failed: [hostname] (item=street) => {"changed": false, "item": "street", "msg": "street is not defined"}
there is on open issue conditional is defined fails to capture undefined var .
as a workaround I'd suggest to change the where condition to the following:
when: "{{ item }}" == ""
I'm getting this error from Ansible 1.9.4:
TASK: [rabbitmq | add rabbitmq vhost] *****************************************
failed: [prod-sensu01] => (item={'name': u'/sensu'})
=> {"failed": true, "item": {"name": "/sensu"}}
msg: this module requires key=value arguments
(['name:', '/sensu', 'node:', 'rabbit',
'tracing:', 'no', 'state:', 'present'])
But as you can see from the error message, the item is actually a key=value argument (hash).
Here are the variables:
# playbooks/roles/rabbitmq/vars/main.yml
# Set the rabbitmq vhost
rabbitmq_vhost_definitions:
- name: "{{ sensu_server_rabbitmq_vhost }}"
And the role's task:
# playbooks/roles/rabbitmq/tasks/vhost.yml
- name: add rabbitmq vhost
rabbitmq_vhost: >
name: "{{ item.name }}"
node: "{{ item.node | default('rabbit') }}"
tracing: "{{ item.tracing | default('no') }}"
state: present
with_items: rabbitmq_vhost_definitions
What is going on here?
Key/value pairs of arguments for Ansible module passed as string should be separated by =, not by :. Correct task should look like this:
# playbooks/roles/rabbitmq/tasks/vhost.yml
- name: add rabbitmq vhost
rabbitmq_vhost: >
name="{{ item.name }}"
node="{{ item.node | default('rabbit') }}"
tracing="{{ item.tracing | default('no') }}"
state=present
with_items: rabbitmq_vhost_definitions
Why they're passed as string? Because you're using folded block scalar > after name of the module, in this line:
rabbitmq_vhost: >
You could also try to remove > scalar (and leave colons) if this module supports both ways of passing arguments.