I want to filter this variable hp but it getting print as square bracket with "".
how do i remove square bracket with "" just to get the only value. can someone please help here ?
I was looking as regex but not able to find the exact syntax.
srv_make1: '{{ basic_params | from_json | json_query("servers.server_details[*].srv_make") }}'
Thanks
I had something similar.
Was getting
["abc"]
to overcome it, had to do 2 things:
append | [0] to the json query
use replace to get rid of "
so in your case instead of
srv_make1: '{{ basic_params | from_json | json_query("servers.server_details[*].srv_make") }}'
it will look something like
srv_make1: '{{ basic_params | from_json | json_query("servers.server_details[*].srv_make | [0]") | replace('\"','') }}'
Q: "How to remove square bracket & double quote?"
json_query always returns a list. It depends on the debug task how a list is displayed. For example
vars:
srv_make1: [a,b,c]
tasks:
- debug:
var: srv_make1
- debug:
msg: "{{ srv_make1|to_yaml }}"
give
TASK [debug] ***
ok: [localhost] => {
"srv_make1": [
"a",
"b",
"c"
]
}
TASK [debug] ***
ok: [localhost] => {
"msg": "[a, b, c]\n"
}
It's possible to use template and write the list into a file without brackets and quotes. For example the template
shell> cat srv_make1.conf.j2
{% for item in srv_make1 %}{{ item }} {% endfor %}
with the task
- template:
src: srv_make1.conf.j2
dest: srv_make1.conf
gives
shell> cat srv_make1.conf
a b c
Related
In my playbook, I am trying to get list of sub-directory names using the find module and then extracting the basename from path. I have been able to get the list but the elements are prepended with u'. How can I remove those from the output?
Ansible version 2.9
I tried to look at these SO posts here and here, but couldn't get it to work.
I may not have fully understood how they should be applied
This is part of my playbook:
- name: set item.path | basename
set_fact: dir_name_list2_basename="{{ item.path | basename}}"
with_items: "{{ zookeeper_data_dir.files}}"
register: item_path_basename_list
- debug:
msg: "{{item_path_basename_list.results}}"
- name: debug item.path | basename as list
debug:
var: item.ansible_facts.dir_name_list2_basename
with_items: "{{item_path_basename_list.results}}"
- debug: msg="item_path_basename_list.results {{ item_path_basename_list.results | map(attribute='ansible_facts.dir_name_list2_basename') | list }}"
- name: set fact to array
set_fact: basename_array="{{ item_path_basename_list.results | map(attribute='ansible_facts.dir_name_list2_basename') | list }}"
- debug:
msg: "basename_array &&&&&&&& {{basename_array}}"
And this is the output of the last debug:
ok: [zk3-dev] => {
"msg": "basename_array &&&&&&&& [u'version-2_backup', u'version-2']"
}
ok: [zk2-dev] => {
"msg": "basename_array &&&&&&&& [u'version-2_backup', u'version-2']"
}
ok: [zk1-dev] => {
"msg": "basename_array &&&&&&&& [u'version-2_backup', u'version-2']"
}
I would like the basename_array to show up as ["version-2_backup", "version-2"] without the u prefix
How should I change my set fact to array task, so I will get the desired result?
Since ["version-2_backup", "version-2"] is actually a JSON array, you could use the to_json filter.
This said, your long set of tasks looks like an overcomplicated process for a requirement that can be achieved with the right set of map filters, since map can apply the same filter to all the elements of a list, you can easily fit your basename in it.
So, given:
- debug:
msg: >-
basename_array &&&&&&&&
{{
zookeeper_data_dir.files
| map(attribute='path')
| map('basename')
| to_json
}}
This yields:
ok: [localhost] => {
"msg": "basename_array &&&&&&&& [\"version-2_backup\", \"version-2\"]"
}
Note that the double quotes are escaped because you are using the JSON stdout callback. But, if you change the callback to YAML, this would yield exactly what you expected:
ok: [localhost] =>
msg: basename_array &&&&&&&& ["version-2_backup", "version-2"]
I need to replace all the / by \ in a string stored in a variable.
I'm just trying to do it a simple as possible to test it with a debug, but no matter how I try it I dont get the expected result of just replacing character to character. I think it's probably just a single/double quote problem or maybe the \ needs to be escaped in a certain way I don't know.
vars:
- SecGroup: '/stuff/foo/thing'
tasks:
- name: Display modified var
debug:
msg: "{{ SecGroup | replace('/','\') }}"
Expected output : \stuff\foo\thing
Output with differents tries :
- name: Display modified var
debug:
msg: "{{ SecGroup | replace('/','\') }}"
TASK [Display modified var]
ok: [localhost] => {
"msg": "stufffoothing"
}
- name: Display modified var
debug:
msg: "{{ SecGroup | replace('/','\\') }}"
TASK [Display modified var]
fatal: [localhost]: FAILED! => {"msg": "Unexpected failure during module execution."}
- name: Display modified var
debug:
msg: "{{ SecGroup | replace('/','\\\') }}"
TASK [Display modified var]
fatal: [localhost]: FAILED! => {"msg": "Unexpected failure during module execution."}
- name: Display modified var
debug:
msg: "{{ SecGroup | replace('/','\\\\') }}"
TASK [Display modified var]
ok: [localhost] => {
"msg": "\\\\stuff\\\\foo\\\\thing"
}
I also tried to revert the quotes :
- name: Display modified var
debug:
msg: '{{ SecGroup | replace("/","\") }}'
TASK [Display modified var]
fatal: [localhost]: FAILED! => {"msg": "Unexpected failure during module execution."}
I can't explain the output of this one
- name: Display modified var
debug:
msg: '{{ SecGroup | replace("/","\\") }}'
TASK [Display modified var]
ok: [localhost] => {
"msg": "\\\\stuff\\\\foo\\\\thing"
}
I think you've stumbled upon an edge case that involves the interaction between YAML escaping and Python escaping. The only way I was able to get it to work was introducing a guard character -- something to ensure that the \ isn't the last character in the expression, which we then remove with a subsequent replace() filter. Here I'm using a semicolon (;), but you could use anything that you're certain won't be in your SecGroup string. Note that your choice of quotes is significant; quoting the entire string with single quotes inhibits YAML escaping:
- name: With guard character
debug:
msg: '{{ SecGroup | replace("/","\;") | replace(";", "") }}'
Outputs:
TASK [With guard character] *******************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "\\stuff\\foo\\thing"
}
Which is exactly what you want (remembering that a single \ is encoded as \\ in JSON output).
Regarding this:
- name: Display modified var
debug:
msg: '{{ SecGroup | replace("/","\\") }}'
TASK [Display modified var]
ok: [localhost] => {
"msg": "\\\\stuff\\\\foo\\\\thing"
}
You are successfully replacing / with two backslashes, \\. Since a backslash must be encoded as \\ in JSON output, a double backslash will end up represented as \\\\, so this:
"msg": "\\\\stuff\\\\foo\\\\thing"
Means you actually have the string:
\\stuff\\foo\\thing
I wanted to add an alternative solution:
If you're familiar with Python, you can just write a custom filter module and avoid multiple layers of escaping. E.g., if you were to create filter_plugins/reslash.py with the following content:
#!/usr/bin/python
def filter_reslash(val):
return val.replace('/', '\\')
class FilterModule(object):
filter_map = {
'reslash': filter_reslash
}
def filters(self):
return self.filter_map
You could then write your playbook like this:
---
- hosts: localhost
gather_facts: false
vars:
- SecGroup: '/stuff/foo/thing'
tasks:
- debug:
msg: "{{ SecGroup | reslash }}"
That's arguably a cleaner solution.
The solution by #larsks didn't entirely work for me as described. I needed to escape the backslash with double slashes \ plus the guard character in order for it to work in the Ansible Playbook.
This works: replace('/','\\;') | replace(';', '')
Another easy solution is to leave escaping backslash to ansible itself. This is how i would have done.
- set_fact:
replacer: '\'
- name: With guard character
debug:
msg: '{{ SecGroup | replace("/",replacer)}}'
Same workaround if you want replace 1 backslash with double backslash on a windows path.
- hosts: localhost
gather_facts: False
vars:
- iis_manager_logdir: 'C:\inetpub\logs\manager-logs'
tasks:
- set_fact:
iis_mng_logs: "{{ iis_manager_logdir | regex_replace('\\\\', '\\\\;') | regex_replace(';', '\\\\') }}"
- name: Original path
debug:
msg: "{{ iis_manager_logdir }}"
- name: New path
debug:
msg: "{{ iis_mng_logs }}"
Thanks to the #larsks's answer i've managed to replace backslashes in ansible string variable value without intermediate replace. It's possible by supplying into regex_replace expression a regex quantifier {1} between last backslash and closing quote.
For example, expression like {{ install_path | regex_replace('\\\\{1}', '/') }} replaces all occurences of backslash \ to forward slash /. It was used to replace Windows path delimiters with Unix-like ones:
- name: install libs
win_shell: "pip install --no-index --find-links \"file://{{ install_path | regex_replace('\\\\{1}', '/') }}/libs\" attrs requests"
become: true
For what its worth, after countless struggles, this is what has worked for me without any workarounds:
Forward to Back Slash
ForwardtoBackSlash: "{{ 'c:/test' | regex_replace('\\\/', '\\\\') }}"
output:
c:\test
Single Slash to Double Slash
SingleSlashtoDoble: "{{ 'C:\test\logs\logfile.txt'| regex_replace('\\\\', '\\\\\\\\') }}"
Output:
C:\\test\\logs\\logfile.txt
I hope it helps someone.
I have key of an object variable I can't escape in Ansible playbook, possibly because of containing dots,
Here's the data structure of variable:
"results":[
{
//snip//
"changed": false,
"hostvars[item].commandResult.stdout": "abc",
//snip//
},
{
//snip//
"changed": true,
"hostvars[item].commandResult.stdout": "xyz",
//snip//
}
]
I'm unable to extract "hostvars[item].commandResult.stdout" inside it with this playbook,
- debug:
msg: "{{variable.results | map(attribute='hostvars[item].commandResult.stdout') }}"
While I can get other value just fine,
- debug:
msg: "{{variable.results | map(attribute='changed') }}"
I tried with \ , '.', and {{...}} to escape . (dot) but still no luck.
I suspect it's . because of this error message:
msg: |-
The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'hostvars[item]'
When running ansible-playbook -vvv command
How I can map "hostvars[item].commandResult.stdout" ?
Turned out I work it around by wrapping inside Ansible set_fact first:
- set_fact:
variable: "{{hostvars[item].commandResult.stdout_lines}}"
with_items: "{{ groups['servers'] }}"
register: all_result
- debug:
msg: "{{all_result.results | map(attribute='ansible_facts') | list | to_nice_json }}
The original question to escape dots remain unanswered, though.
To address a key with dots, use array notation with single quotes instead of dot notation, i.e.:
- debug:
msg: "{{variable.results | map(attribute=['hostvars[item].commandResult.stdout']) }}"
This returns the value.
cf.: Ansible FAQ
I have the following and keep in mind I do not know how many ips will be in this incoming variable but I am starting with 2 for simplicity.
vars:
host_ips: ['10.0.0.100', '10.0.0.200']
I'm trying to format them in a file using a template with Ansible.
- targets: ['10.0.0.100:9090', '10.0.0.200:9090']
What syntax in Jinja2 do I use to make the host ips look like the above targets line? I KNOW I have to iterate for sure.
-targets: [{% for ip in host_ips %}'{{ ip }}:5051'{{ "," if not loop.last else "" }} {% endfor %}]
-targets: [{% for ip in host_ips %}'{{ ip }}:5051',{% endfor %}]
test.yml playbook:
vars:
host_ips: ['10.0.0.100', '10.0.0.200','10.0.0.300']
tasks:
- debug: msg=[{% for ip in host_ips %}'{{ ip }}:5051',{% endfor %}]
ansible-playbook -i localhost test.yml
TASK [debug] *******************************************************************************************
ok: [localhost] => {
"msg": [
"10.0.0.100:5051",
"10.0.0.200:5051",
"10.0.0.300:5051"
]
}
There is no need to struggle with Jinja2 loops here. All you need is to apply a transformation for the list elements (for example with map and regex_replace filters):
host_ips | map('regex_replace', '(.*)', '\\1:9090')
With the above construct you can:
use it to set a new variable in Ansible:
- set_fact:
targets: "{{ host_ips | map('regex_replace', '(.*)', '\\1:9090') | list }}"
or "format them in a file using a template" which per your request is a JSON representation of a list:
With double quotes in the output:
- targets: {{ host_ips | map('regex_replace', '(.*)', '\\1:9090') | list | to_json }}
procudes:
- targets: ["10.0.0.100:9090", "10.0.0.200:9090"]
If you really need single quotes in the output simply replace them:
- targets: {{ host_ips | map('regex_replace', '(.*)', '\\1:9090') | list | to_json | regex_replace('"', '\'') }}
produces:
- targets: ['10.0.0.100:9090', '10.0.0.200:9090']
When should I use the from_json filter in Ansible?
I found out that using it sometimes has and sometimes have no effect.
Please consider the following example which illustrates the inconsistency I am getting.
Included in reverse order are: the questions - expected result - actual result - the playbook - the data. The data is taken from this question and the playbook is based on this answer.
The question(s):
Why storing the left part (before json_query) of the following expression in a variable and then using json_query on the variable causes the expression to be evaluated differently?
"{{ lookup('file','test.json') | json_query(query) }}"
Why does adding from_json filter alter the results (but does not if processing a variable):
"{{ lookup('file','test.json') | from_json | json_query(query) }}"
Expected result:
Last four tasks should give the same result. Alternatively, last two tasks should give the same result as previous two tasks.
Actual result (last four tasks only):
One task result differs.
TASK [This query is run against lookup value with from_json stored in a variable] ***
ok: [localhost] => {
"msg": [
678
]
}
TASK [This query is run against lookup value without from_json stored in a variable] ***
ok: [localhost] => {
"msg": [
678
]
}
TASK [This query is run directly against lookup value with from_json] **********
ok: [localhost] => {
"msg": [
678
]
}
TASK [This query is run directly against lookup value without from_json - the result is empty - why?] ***
ok: [localhost] => {
"msg": ""
}
The playbook:
---
- hosts: localhost
gather_facts: no
connection: local
tasks:
- set_fact:
from_lookup_with_from_json: "{{ lookup('file','test.json') | from_json }}"
- set_fact:
from_lookup_without_from_json: "{{ lookup('file','test.json') }}"
- name: Save the lookup value stored in a variable in a file for comparison
copy: content="{{ from_lookup_with_from_json }}" dest=./from_lookup_with_from_json.txt
- name: Save the lookup value stored in a variable in a file for comparison (they are the same)
copy: content="{{ from_lookup_without_from_json }}" dest=./from_lookup_without_from_json.txt
- name: This query is run against lookup value with from_json stored in a variable
debug: msg="{{ from_lookup_with_from_json | json_query(query) }}"
vars:
query: "Foods[].{id: Id, for: (Tags[?Key=='For'].Value)[0]} | [?for=='Tigger'].id"
- name: This query is run against lookup value without from_json stored in a variable
debug: msg="{{ from_lookup_without_from_json | json_query(query) }}"
vars:
query: "Foods[].{id: Id, for: (Tags[?Key=='For'].Value)[0]} | [?for=='Tigger'].id"
- name: This query is run directly against lookup value with from_json
debug: msg="{{ lookup('file','test.json') | from_json | json_query(query) }}"
vars:
query: "Foods[].{id: Id, for: (Tags[?Key=='For'].Value)[0]} | [?for=='Tigger'].id"
- name: This query is run directly against lookup value without from_json - the result is empty - why?
debug: msg="{{ lookup('file','test.json') | json_query(query) }}"
vars:
query: "Foods[].{id: Id, for: (Tags[?Key=='For'].Value)[0]} | [?for=='Tigger'].id"
The data (test.json):
{ "Foods" :
[ { "Id": 456
, "Tags":
[ {"Key":"For", "Value":"Heffalump"}
, {"Key":"Purpose", "Value":"Food"}
]
}
, { "Id": 678
, "Tags":
[ {"Key":"For", "Value":"Tigger"}
, {"Key":"Purpose", "Value":"Food"}
]
}
, { "Id": 911
, "Tags":
[ {"Key":"For", "Value":"Roo"}
, {"Key":"Purpose", "Value":"Food"}
]
}
]
}
json_query requires Python object (dict) as input, if you feed it with string, it gives empty string as result.
You get different result because of Ansible templating engine tricky work.
I should definitely write a post about it on my site...
After evaluating jijna2 expression Ansible try to cast complex types to Python objects (like dict or list). See my other answer.
In your case:
1.
- set_fact:
from_lookup_with_from_json: "{{ lookup('file','test.json') | from_json }}"
from_lookup_with_from_json is a dict, because you manually convert JSON-string from file to dict with from_json filter.
2.
- set_fact:
from_lookup_without_from_json: "{{ lookup('file','test.json') }}"
from_lookup_with_from_json becomes dict, because Ansible converts it when jinja2 expression ends with }}. So from_json is actually unnecessary as the last filter in chain.
3.
debug: msg="{{ lookup('file','test.json') | from_json | json_query(query) }}"
Again, you manually convert JSON-string here. So json_query get dict as input.
4.
debug: msg="{{ lookup('file','test.json') | json_query(query) }}"
In this case you feed JSON-string (not dict) as input to json_query filter. As everything happens inside one jinja2 expression, Ansible doesn't attempt to convert anything in between.
You can also get empty string result with a variable this way:
- set_fact:
from_lookup_force_string: "{{ lookup('file','test.json') | string }}"
In this case from_lookup_force_string will not be converted by Ansible tempating engine, and json_query will give you empty response on it.