Ansible not escaping windows path first argument - ansible

I have playbook with windows path name in the extra arguments. first argument not escaping the drive letter and slash.
ansible-playbook d.yaml --extra-vars "ainstalldir=c:\\test stagedir=D:\packages outdir=d:\output\log"
TASK [print inpurt arguments] ********************************************************************************************************
ok: [127.0.0.1] => {
"msg": "installdir=c:\test, stragedir=D:\\packages, outdir=d:\\output\\log"
}
installdir prints as c:\test, I expect it should print as c:\\test
Here is my playbook.
---
- name: test command line arguments
connection: local
hosts: 127.0.0.1
gather_facts: false
vars:
installdir: "{{ ainstalldir }}"
stagedir: "{{ stagedir }}"
outdir: "{{ outdir }}"
tasks:
- name: print inpurt arguments
debug:
msg="installdir={{ installdir }}, stragedir={{ stagedir }}, outdir={{ outdir }}"
Any idea how to resolve this issue?

installdir prints as c:\test, I expect it should print as c:\\test
installdir contains: c : tab e s t.
tab is replaced with \t in the debug module output and in effect you see c:\test on the screen.
Other characters starting with backslash in your example (\p, \o, \l) do not have special meaning, so they are treated as two character strings; but you'd observe the same phenomenon with \n (and other escape sequences).
Don't use debug module to debug things concerned with data, it processes strings to make them printable.
Instead, use copy with content parameter and check the output in a file:
- copy:
content: |-
installdir={{ installdir }}
stragedir={{ stagedir }}
outdir={{ outdir }}
dest: ./result.txt
(remember you could/should use hexdump to verify what's really inside).
Use:
ansible-playbook d.yaml --extra-vars "ainstalldir=c:\\\test stagedir=D:\\\packages outdir=d:\\\output\\\log"
or
ansible-playbook d.yaml --extra-vars 'ainstalldir=c:\\test stagedir=D:\\packages outdir=d:\\output\\log'
Backslashes in double- and single quotes are interpreted differently by shell (see for example this question).

Related

Newline('\n') is replaced by space(' ') in ansible shell module

I have an ansible task where I make use of shell module to run a custom bash script. This bash script expects an optional parameter which could be a multiline string. Example
~/myScript.sh -e "Each \nword \nin \nnew \nline"
This works perfectly fine when I run it in the bash shell. However, I have tried several ways but haven't made it run via the ansible task. The value being a variable itself. The task below -
- name: Sample Task
shell:
cmd: "{{ home_dir }}/myScript.sh -e '{{ string_with_escaped_newlines }}'"
Here, the value of variable string_with_escaped_newlines is set to Each \nword \nin \nnew \nline programmatically by other tasks. The output of the task confirms this.
I had put an echo in my shell script to debug & I observe that the echo would print 2 different sequences in the 2 cases(running directly via shell & running via ansible).
Debug output via shell-
Each
word
in
new
line
Debug output via ansible -
Each word in new line
Notice that there is an extra space introduced in the value in place on \n. I do not understand why this happens and how do I stop this. I have checked/tried shell as well as command modules of ansible.
cmd: "{{ home_dir }}/myScript.sh -e '{{ string_with_escaped_newlines }}'"
is first converted (by Jinja2) to
cmd: "<THE HOME>/myScript.sh -e 'Each \nword \nin \nnew \nline'"
Here \n is within double quotes and in YAML double quoted \ns are NEWLINEs (think "\n" in C or JSON).
For your case you can just write:
cmd: {{ home_dir }}/myScript.sh -e '{{ string_with_escaped_newlines }}'
Or to be more safe:
cmd: |
{{ home_dir }}/myScript.sh -e '{{ string_with_escaped_newlines }}'
YAML syntax is quite "confusing". You can take a look at Ansible's own YAML intro.
Given the script
shell> cat ~/myScript.sh
#!/usr/bin/sh
echo ${1}
shell> ~/myScript.sh "Each \nword \nin \nnew \nline"
Each
word
in
new
line
Q: "Newline '\n' is replaced by space ' ' in Ansible shell module."
A: It depends on how you quote and escape the string. The simplest option is single-quoted style because only ' needs to be escaped
string_with_escaped_newlines1: 'Each \nword \nin \nnew \nline'
You have to escape \n in double-quoted style
string_with_escaped_newlines2: "Each \\nword \\nin \\nnew \\nline"
Both variables expand to the same string
- debug:
var: string_with_escaped_newlines1
- debug:
var: string_with_escaped_newlines2
gives
string_with_escaped_newlines1: Each \nword \nin \nnew \nline
string_with_escaped_newlines2: Each \nword \nin \nnew \nline
Then, the quotation in the command is not significant. All options below give the same result
- command: ~/myScript.sh "{{ string_with_escaped_newlines1 }}"
- command: ~/myScript.sh '{{ string_with_escaped_newlines1 }}'
- command: ~/myScript.sh "{{ string_with_escaped_newlines2 }}"
- command: ~/myScript.sh '{{ string_with_escaped_newlines2 }}'
Example of a complete playbook for testing
- hosts: localhost
vars:
string_with_escaped_newlines1: 'Each \nword \nin \nnew \nline'
string_with_escaped_newlines2: "Each \\nword \\nin \\nnew \\nline"
tasks:
- command: ~/myScript.sh "{{ string_with_escaped_newlines1 }}"
register: out
- debug:
var: out.stdout_lines
- command: ~/myScript.sh '{{ string_with_escaped_newlines1 }}'
register: out
- debug:
var: out.stdout_lines
- command: ~/myScript.sh "{{ string_with_escaped_newlines2 }}"
register: out
- debug:
var: out.stdout_lines
- command: ~/myScript.sh '{{ string_with_escaped_newlines2 }}'
register: out
- debug:
var: out.stdout_lines
gives (abridged) four times the same result
TASK [debug] *********************************************************
ok: [localhost] =>
out.stdout_lines:
- 'Each '
- 'word '
- 'in '
- 'new '
- line
Notes:
Using shell instead of command gives the same results.
The best practice is using the module command unless the ansible.builtin.shell module is explicitly required.
Have you tried \r\n instead of \n
It helped me elsewhere in ansible

Ansible - convert a comma delimited string into a space delimited string with double quotes

Following the great help I got for my last question I have another :).
I have a requirement to convert the contents of the string variable: Targets
debug:
msg:
- "Targets: {{ targets }}"
Returns the following:
ok: [host] => {
"msg": [
"Targets: 10.0.1.1,10.0.1.2"
I need to be able to split the targets string and change it to a space delimited string, with each element encapsulated with a double quote. This will then be passed to another command as a parameter.
Updated for context.
I am passing the targets variable to a role which in turn launches an executable:
- import_role:
name: run_exectable_file
vars:
# passing all the params here.
- params: -a -b -c {{ targets }}
- runtime: 3600
The executable command line needs to be:
executable.exe -a -b -c "10.0.1.1" "10.0.1.2"
Desired output:
The targets variable need to produce the target IP addresses in a space delimited string, with each element encapsulated with a double quote.
For example, the following debug output:
ok: [host] => {
"msg": [
"Targets: "10.0.1.1" "10.0.1.2""
I look forward to your help as always.
You can use Jinja to get what you want. For example
- debug:
msg: "Targets: {{ _targets }}"
vars:
_targets: |
{% for i in targets.split(',') %}"{{ i }}" {% endfor %}
gives
msg: |-
Targets: "10.0.1.1" "10.0.1.2"

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"
]

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

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"
}

Ansible environment variable or default

How do I get a value from an environment variable, but use a default if the environment variable is unset?
This is an example that does not work
---
- name: a playbook
hosts: all
vars:
build_dir: "{{ lookup('env','BUILD_DIR') | default('builds/1.0.0/LATEST') }}"
tasks:
- debug: msg="{{ build_dir }}"
Running this playbook returns an empty string instead of the default.
$ ansible-playbook build.yml
TASK [debug] ********************
ok: [amber] => {
"msg": ""
}
However, it works as expected to obtain the environment variable.
$ BUILD_DIR=LOL ansible-playbook build.yml
TASK [debug] ****************
ok: [amber] => {
"msg": "LOL"
}
Discovered this that is more concise and easier to read than some other options I have seen
"{{ lookup('env','BUILD_DIR') or 'builds/1.0.0/LATEST' }}"
The last parameter to Jinja's default template built-in function should be true, like this:
vars:
build_dir: "{{ lookup('env','BUILD_DIR')|d('builds/1.0.0/LATEST', true) }}"
Better not to have too many sources of truth, but I always try to set intelligent defaults in defaults/main.yml. I also make frequent use of the default() filter, like this:
db_url : "{{ DB_HOST }}:{{ db_port | default(1521) }}:{{ DB_SVC | default(SID|default('')) }}"
Then a playbook can always overwrite a role's variable with a lookup that defaults to a literal -
vars:
db_port: "{{ lookup('env','db_port')|default('9999') }}"
or with a value dynamically written into a vars_file before the play begins, or into the hosts file or groups file, or on the ansible command-line with --extra-vars, etc.
Look at the variable precedence rules, but be careful not to get too complex if it can be avoided. Flexibility is good, but KISS, else "that way lies madness..."

Resources