Unable to add curly braces within curly braces in a jinja2 syntax - ansible

I have a set of jinja2 actions within curly braces separated by pipes. Within that set of actions I need to add a variable, but I keep getting syntax errors.
debug:
msg: "{{ item.path | basename | regex_replace('{{ variable }}', '') }}"
with_items: "{{ content.files }}"
Note that, the variable will contain some regex string for example...
The problem ansible has with this, is that it contains a double quote inside a double quote. I tried escaping, inverting double quotes to single quotes...nothing worked.
When I run the above as is, it considers the variable as a literal value.

You don't need curly braces to denote variables inside of curly braces. Here's a simple playbook to demonstrate:
---
- name: test
hosts: localhost
gather_facts: false
vars:
content:
files:
- path: /path1/itemXXX.jpg
- path: /path2/itXem.pdf
regex_pattern: '[X]+' # Match one or more X's
tasks:
- debug:
msg: "{{ item.path | basename | regex_replace(regex_pattern, '') }}"
with_items: "{{ content.files }}"
Results are:
TASK [debug] ***********************************************************************************************************************************************************************
ok: [localhost] => (item={'path': '/path1/itemXXX.jpg'}) => {
"msg": "item.jpg"
}
ok: [localhost] => (item={'path': '/path2/itXem.pdf'}) => {
"msg": "item.pdf"
}

Related

Ansible json_query how to remove square bracket & double quote

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

Ansible Jinja split on backslash fails

I have a ansible play that tries to split a string in the format of domain\user into its parts:
This is the task
tasks:
- name: do something which requires domain and user
win_shell: echo "{{ lookup('aws_ssm', 'service_user-account-2921', decrypt=True, region='eu-central-1' )}}.split('\\')[0] }}"
This results in:
ERROR! failed at splitting arguments, either an unbalanced jinja2 block or quotes: {{'DOMAIN\USER'.split('\')[0]}}
if I change the task to remove the lookup it still fails as long as I use \ as delimiter
#win_shell: echo "{{ 'test,strings'.split(',')[0] }}" #WORKS
win_shell: echo "{{ 'DOMAIN\\USER'.split('\\')[0]}}" #FAILS
how to split on a backslash in ansible / jinja?
Q: "How to split on a backslash in ansible/jinja?"
A: Put the separator into a variable. For example
vars:
separator: '\'
text: 'domain\user'
tasks:
- debug:
msg: "{{ text.split(separator) }}"
gives
"msg": [
"domain",
"user"
]

Ansible - Read file and register variable

I have a file with the following content:
AWS_ACCESS_KEY_ID=xxxxxxx
AWS_SECRET_ACCESS_KEY=yyyyyy
AWS_SESSION_TOKEN=zzzzzzzz
How do I read this file, split the line based on "=" and set the values of the variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN) so that I can use these variables in the script for later use?
Q: How do I read this file, split the line based on "=" and set the values of the variables?
A: Use ini lookup plugin. For example the tasks below
- set_fact:
AWS_ACCESS_KEY_ID: "{{ lookup('ini', 'AWS_ACCESS_KEY_ID type=properties file=conf.ini') }}"
- debug:
var: AWS_ACCESS_KEY_ID
give
"AWS_ACCESS_KEY_ID": "xxxxxxx"
It is possible to use a list of variables. For example the play below
- hosts: localhost
vars:
my_vars_keys: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN]
tasks:
- set_fact:
my_vars: "{{ my_vars|default({})|
combine({item:
lookup('ini',
item ~ ' type=properties file=conf.ini')})
}}"
loop: "{{ my_vars_keys }}"
- debug:
msg: "{{ my_vars[item] }}"
loop: "{{ my_vars_keys }}"
gives
ok: [localhost] => (item=AWS_ACCESS_KEY_ID) => {
"msg": "xxxxxxx"
}
ok: [localhost] => (item=AWS_SECRET_ACCESS_KEY) => {
"msg": "yyyyyy"
}
ok: [localhost] => (item=AWS_SESSION_TOKEN) => {
"msg": "zzzzzzzz"
}
Q: How do I make sure that above set_fact runs on the hosts and not on the ansible tower?
A: The set_fact uses Lookup Plugins. Quoting
Like all templating, these plugins are evaluated on the Ansible control machine, not on the target/remote.

Replacing / by \ in a string with ansible

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.

How do I test in a var matches against a list of substrings in ansible?

I am fairly new to ansible and I am trying to determine how to test is a variable passed to my playbook matches against a list of substrings.
I have tried something like the following. Looping through my list of badcmds and then testing whether it is in the variable passed.
vars:
badcmds:
- clear
- no
tasks:
- name: validate input
debug:
msg: " {{ item }}"
when: item in my_command
with_items: "{{ badcmds }}"
I am getting the following error:
"msg": "The conditional check 'item in my_command' failed.
The error was: Unexpected templating type error occurred on
({% if item in my_command %} True {% else %} False {% endif %}):
coercing to Unicode: need string or buffer, bool found
Many thanks.
one problem with your playbook is that - no is automatically translated to boolean false. you should use "no" to make Ansible consider the variable as a string. without quotes:
---
- hosts: localhost
connection: local
gather_facts: false
vars:
badcmds:
- clear
- no
my_command: clear
tasks:
- name: print variable
debug:
msg: "{{ item }}"
with_items:
- "{{ badcmds }}"
output:
TASK [print variable] ***********************************************************************************************************************************************************************************************
ok: [localhost] => (item=None) => {
"msg": "clear"
}
ok: [localhost] => (item=None) => {
"msg": false
}
I guess you should enclose no in quotes, because this behavior was not your intention.
to make a loop and check if the variable matches any item from the badcmds list, you can use:
---
- hosts: localhost
connection: local
gather_facts: false
vars:
badcmds:
- "clear"
- "no"
tasks:
- name: validate input
debug:
msg: "{{ item }}"
when: item == my_command
with_items:
- "{{ badcmds }}"
hope it helps

Resources