Jinja2 if/else on user defined variable - jinja2

Attempting to make a decision in a template based on the last character of a variable (third level domain hostname) , but the epiphany alludes me. Make a config stanza if value else, do the other.
I set a fact in play:
- name: Set third level domain name to a variable
set_fact:
my_3rd_levelname: "{{ ansible_nodename.split('.')[0] }}"
- name: Ascertain if which server we're on
set_fact:
my_one_or_two: "{{ my_3rd_levelname[-1]|int }}"
...which appears to echo out with debug, save the casting as an int...see below.
TASK [role-test : Echo out my_one_or_two] *******************************************************************************************************************
ok: [w.x.y.42] => {
"my_one_or_two": "2"
}
Then in the template.j2...
{# If my_one_or_two is even list server1 first. If not, second. #}
{% if lookup('vars,',my_one_or_two) + my_one_or_two|int is 1 %}
[some config file stanza here]
{% else %}
[some other config file stanza instead]
I've poked and hoped until I can stand it no longer and am reaching out. I've tried just using the raw variable, e.g., {% if my_one_or_two|int == 1 %} along with many other attempts, but I'm stuck. I can't seem to overcome this error:
AnsibleError: template error while templating string: expected token 'name', got 'integer'. String: [the contents of my template]
Any input would be greatly appreciated at this juncture.
Thanks

Okay...leaving this here in case someone else doesn't realize you can use any Python method that the object supports. Here's what I did. Remember the server names end in 1 or 2 and its a String.
Created a varible in /roles/[rolename]/vars...
my_simple_hostname: "{{ ansible_nodename.split('.')[0] }}"
Then used the 'endswith' method to evaluate it....
% if my_simple_hostname.endswith('1') == true %}
[content if true]
{% else %}
[content when false]
{% endif %}

Related

Using Saltstack cmd.script with args to Insert Jinja Variable

I am running a powershell script to obtain a particular user's credentials, and then use those credentials in a salt state. This works fine when the username is hardcoded directly in the powershell script. But, I am unable to pass the username as an argument. Here is my salt state:
{% set creds = salt['cmd.powershell']('C:\test2.ps1' 'username') %}
test_output:
cmd.run:
- name: echo {{ creds }}
I have also tried this too...but it doesn't work.
{% set creds = salt['cmd.script'](shell='powershell' source='C:\test2.ps1' args='username')
%}
How do i correctly pass an argument to my powershell script to set my variable?
The rules for strings and function calls are the same in Jinja as for Python. Either " or ' are the string delimiters, and \ is the escape character. Function arguments are separated by commas.
{% set creds = salt['cmd.powershell']('C:\\test2.ps1 username') %}
For cmd.script, the source should be a remote URL, not a local file.
{% set creds = salt['cmd.script'](shell='powershell', source='salt://path/test2.ps1', args='username') %}

Saltstack - Unable to print output in Jinja templating

I am trying to reuse the output of a command in Saltstack, but when I try to print the output using "cmd.run", it is failing with the below error. Not sure in which format the data is getting returned from "cmd.run".
{% set output = salt['cmd.shell']('ifconfig') %}
display:
cmd.run:
- name: echo '{{ output }}'
Error:
Data failed to compile:
----------
Rendering SLS 'base:patching.install_patches' failed: mapping values are not allowed in this context
The issue seems to be due to the placement of quotes. The single quotes are required around the entire command to run.
Below should work:
{% set output = salt['cmd.shell']('ifconfig') %}
display:
cmd.run:
- name: 'echo "{{ output }}"'

jinja to json in Salt

I have a jinja template in salt that every time apply.state is ran, salt thinks it has changed when it hasn't.
The issue is that a reconfigure command executes when the file "changes", and this reconfigure would change the file from a single lined JSON to a lined JSON. For example:
salt creates the file like:
{ "key1": { "sub-key1": "sub-value1", "sub-key2": "sub-value2"}, "key2": "value2" }
but when the reconfigure command executes, it changes the file to:
{
"key1": {
"sub-key1": "sub-value1",
"sub-key2": "sub-value2"
},
"key2": "value2"
}
Is there a way to have salt create the file as a formatted JSON to begin with?
This is what I have:
{{ gb_server.secrets_file }}:
file.managed:
- source: {{ gb_server.secrets_tmpl }}
- user: {{ gb_server.secure_user }}
- group: {{ gb_server.secure_user }}
- mode: 600
- template: jinja
the template is as follow:
{%- import_yaml 'gb-server/defaults.yaml' as defaultmap -%}
{%- set secrets = gb_server_config['secrets'] -%}
{{ secrets|tojson|replace('\\\\n','\\n') }}
The values are in pillar in yaml format.
Ideally, the resulted JSON would have its key sorted alphabetically, that way the content of the resulted file wouldn't "change" and the reconfigure command wouldn't run every single time.
Is there a way to make this happen with salt?
Thank you.
It looks like file.serialize will do what you want here
{{ gb_server.secrets_file }}:
file.serialize:
- user: {{ gb_server.secure_user }}
- group: {{ gb_server.secure_user }}
- mode: 600
- data: {{ gb_server.secrets }}
- serializer: json
- serializer_opts:
- indent: 2
- sort_keys: true

How to prevent Jinja2 substitution in Ansible playbook?

In my playbook, a JSON file is included using the include_vars module. The content of the JSON file is as given below:
{
"Component1": {
"parameter1" : "value1",
"parameter2" : "value2"
},
"Component2": {
"parameter1" : "{{ NET_SEG_VLAN }}",
"parameter2": "value2"
}
}
After the JSON file is included in the playbook, I am using uri module to sent an http request as given below:
- name: Configure Component2 variables using REST API
uri:
url: "http://0.0.0.0:5000/vse/api/v1.0/config/working/Component2/configvars/"
method: POST
return_content: yes
HEADER_x-auth-token: "{{ login_resp.json.token }}"
HEADER_Content-Type: "application/json"
body: "{{ Component2 }}"
body_format: json
As it can be seen, the body of the http request is send with the JSON data Component2. However, Jinja2 tries to substitute the {{ NET_SEG_VLAN }} in the JSON file and throws and undefined error. The intention is not to substitute anything inside the JSON file using Jinja2 and send the body as it is in http request.
How to prevent the Jinja2 substitution for the variables included from the JSON file?
You should able to escape the variable even with {{'{{NET_SEG_VLAN}}'}} to tell jinja not to template anything inside that block.
You should be able to escape the variable with {% raw %} and {% endraw %} to tell Jinja not to template anything inside that block.
!unsafe
From documentation at https://docs.ansible.com/ansible/2.10/user_guide/playbooks_advanced_syntax.html#unsafe-or-raw-strings:
When handling values returned by lookup plugins, Ansible uses a data type called unsafe to block templating. Marking data as unsafe prevents malicious users from abusing Jinja2 templates to execute arbitrary code on target machines. The Ansible implementation ensures that unsafe values are never templated. It is more comprehensive than escaping Jinja2 with {% raw %} ... {% endraw %} tags.
You can use the same unsafe data type in variables you define, to prevent templating errors and information disclosure. You can mark values supplied by vars_prompts as unsafe. You can also use unsafe in playbooks. The most common use cases include passwords that allow special characters like { or %, and JSON arguments that look like templates but should not be templated.
I am using it all the time, like this:
# Load JSON content, as a raw string with !unsafe
- tags: ["always"]
set_fact:
dashboard_content: !unsafe "{{ lookup('file', './dash.json') | to_json }}"
# Build dictionnary via template
- tags: ["always"]
set_fact:
cc: "{{ lookup('template', './templates/cm_dashboard.yaml.j2') | from_yaml }}"
## cm_dashboard.yaml.j2 content:
hello: {{ cc_dashboard_content }}
# Now, "cc" is a dict variable, with "hello" field protected!

Case statement for setting var in Ansible/Jinja2

I'm using Ansible with Jinja2 templates, and this is a scenario that I can't find a solution for in Ansible's documentation or googling around for Jinja2 examples. Here's the logic that I want to achieve in Ansible:
if {{ existing_ansible_var }} == "string1"
new_ansible_var = "a"
else if {{ existing_ansible_var }} == "string2"
new_ansible_var = "b"
<...>
else
new_ansible_var = ""
I could probably do this by combining several techniques, the variable assignment from here: Set variable in jinja, the conditional comparison here: http://jinja.pocoo.org/docs/dev/templates/#if-expression, and the defaulting filter here: https://docs.ansible.com/playbooks_filters.html#defaulting-undefined-variables ,
...but I feel like that's overkill. Is there a simpler way to do this?
If you just want to output a value in your template depending on the value of existing_ansible_var you simply could use a dict and feed it with existing_ansible_var.
{{ {"string1": "a", "string2": "b"}[existing_ansible_var] | default("") }}
You can define a new variable the same way:
{% set new_ansible_var = {"string1": "a", "string2": "b"}[existing_ansible_var] | default("") -%}
In case existing_ansible_var might not necessarily be defined, you need to catch this with a default() which does not exist in your dict:
{"string1": "a", "string2": "b"}[existing_ansible_var | default("this key does not exist in the dict")] | default("")
You as well can define it in the playbook and later then use new_ansible_var in the template:
vars:
myDict:
string1: a
string2: b
new_ansible_var: '{{myDict[existing_ansible_var | default("this key does not exist in the dict")] | default("") }}'
Something like this would work, but it's ugly. And as #podarok mentioned in his answer, it's likely unnecessary depending on exactly what you're attempting to do:
- name: set default
set_fact: new_ansible_var= ""
- name: set to 'a'
set_fact: new_ansible_var= "a"
when: "{{ existing_ansible_var }} == string1"
- name: set to 'b'
set_fact: new_ansible_var= "b"
when: "{{ existing_ansible_var }} == string2"
etc.
you don't need to set var, because I'm guessing that you trying to set var for some condition later.
Just make condition there like
- name: Later task
shell: "command is here"
when: {{ existing_ansible_var }} == "string1"
and get a profit