Call variable in Ansible - ansible

Apologies for this simple question, but I tried various approach without success.
This is my vars file
---
preprod:
name: nginx
prod:
name: apache
I am trying to pass the value of name based on the environment name user provides (preprod, prod etc).
This is my template
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: {{ env.name }}
name: {{ env.name }}
namespace: default
spec:
selector:
matchLabels:
app: {{ env.name }}
template:
metadata:
labels:
app: {{ env.name }}
spec:
containers:
- image: {{ env.name }}
imagePullPolicy: Always
name: {{ env.name }}
resources: {}
However, when I try with this using following command:
ansible-playbook playbook.yaml -e env=preprod
I am getting the following error.
fatal: [localhost]: FAILED! => {"changed": false, "msg": "AnsibleUndefinedVariable: 'str object' has no attribute 'name'"}
My expectation is the {{ env.name }} should have been replaced with the value of preprod.name as in nginx in this case.
I want users to provide the value for env via -e on the command line, it seems if I do like preprod.name directly on the template, it seems to work, but I don't want that.
I hope this clarifies what I am trying to do, but it didn't work.
May I know what I am missing?

This error message indicates that the extra var passed on command line as -e is a string, and not the key (we expect) of the dict we are loading from the vars file.
I'm making up an example playbook as you have not shown how you load your vars file. I'm using include_vars as we can name the variable to load dict into.
# include vars with a name, so that we can access testvars[env]
- include_vars:
file: testvars.yml
name: testvars
- template:
src: test/deployment.yaml.j2
dest: /tmp/deployment.yaml
vars:
_name: "{{ testvars[env]['name'] }}"
With this approach, the prod and preprod keys will be available under testvars, and can be referenced with a variable such as env.
Then the template should use _name variable, like:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: {{ _name }}

Given the variables in a place where the play can find it, e.g. group_vars/all. Optionally add default_env
shell> cat group_vars/all
preprod:
name: nginx
prod:
name: apache
default_env:
name: lighttpd
Use vars lookup plugin to "Retrieve the value of an Ansible variable". See
shell> ansible-doc -t lookup vars
For example, the playbook
shell> cat playbook.yml
- hosts: test_11
vars:
env: "{{ lookup('vars', my_env|default('default_env')) }}"
app: "{{ env.name }}"
tasks:
- debug:
var: app
by default displays
shell> ansible-playbook playbook.yml
...
app: lighttpd
Now you can select the environment by declaring the variable my_env, .e.g
shell> ansible-playbook playbook.yml -e my_env=prod
...
app: apache
and
shell> ansible-playbook playbook.yml -e my_env=preprod
...
app: nginx

Related

Ansible: creating docker containers from a variable

I have a variable structure set up as follows:
container_traefik:
image: 'traefik:latest'
networks:
...
container_oauth:
image: 'thomseddon/traefik-forward-auth:latest'
...
and another variable to define the status of these containers:
enable_traefik: true
enable_oatuh: true
...
My current playbook has a task for each container like this:
docker_container: "{{ container_traefik }}"
when: "enable_traefik | default(False)"
I started looking into running this in a loop, but I have not been able to get it right.
I changed the variable structures to the following:
containers:
traefik:
image: 'traefik:latest'
...
enable:
traefik: true
And tried to create them in a playbook like this:
docker_container: "{{ item.value }}"
when: "{{ enable.{{ item.key }} }}"
loop: "{{ containers }}"
Unfortunately this doesn't work, among the many other combinations I have tried.
How can I get this to work?

Can't extract a nested variable in ansible of a dict based on another variable

I have a variables file like so
apps:
appOne:
slug: none
ports:
- "8999:8999"
kong:
slug: somerepo/ansible.kong.git
ports:
- "8000:8000"
- "8001:8001"
- "8443:8443"
- "8445:8445"
in my vars/apps_config file
I'm trying to figure out the ports based on the {{ app_name }} but can't seem to find a way to drill into the apps object.
So in my yml file I'm executing I have
vars_files:
- "vars/app_config"
vars:
app_name: kong
container_ports: "{{ apps [{{ app_name }}]['ports'] }}"
but ansible doesn't like the nested variable {{ app_name }}. I saw someone mention using vars['somestring'] before but the following doesn't seem to work
vars['apps.{{ app_name }}']
any ideas?
the right syntax is:
container_ports: "{{ apps[app_name]['ports'] }}"
*inside {{}} app_name is seen like a variable
test:
- name: test
hosts: localhost
vars_files:
- "vars/app_config"
vars:
app_name: kong
container_ports: "{{ apps[app_name]['ports'] }}"
tasks:
- debug:
var: container_ports
result:
ok: [localhost] =>
container_ports:
- 8000:8000
- 8001:8001
- 8443:8443
- 8445:8445

Ansible Jinja2 template - Remove trailing whitespace

I am trying to load an ansible vault file into an k8 configmap YAML file using Ansible Jinja template but facing an issue with a trailing whitespace getting added at the end of the contents of the YAML file. This is causing errors as below:
Vault format unhexlify error: Odd-length string
Sample of ansible template am using is :
Playbook main.yml -
- name: display multiple files
shell: cat /tmp/test.yml
register: test
Ansible Jinja Template
apiVersion: v1
data:
test.yml: |-
{{ test.stdout.splitlines()|indent(4, false)|trim|replace(' ','') }}
kind: ConfigMap
metadata:
name: test
namespace: test-namespace
test.yml example:
$ANSIBLE_VAULT;1.1;AES256
62313365396662343061393464336163383764373764613633653634306231386433626436623361
6134333665353966363534333632666535333761666131620a663537646436643839616531643561
63396265333966386166373632626539326166353965363262633030333630313338646335303630
3438626666666137650a353638643435666633633964366338633066623234616432373231333331
6564
Output YAML created from Jinja Template is below
apiVersion: v1
data:
test.yml:
$ANSIBLE_VAULT;1.1;AES256
62313365396662343061393464336163383764373764613633653634306231386433626436623361
6134333665353966363534333632666535333761666131620a663537646436643839616531643561
63396265333966386166373632626539326166353965363262633030333630313338646335303630
3438626666666137650a353638643435666633633964366338633066623234616432373231333331
6564
kind: ConfigMap
metadata:
name: test
namespace: test-namespace
Can you please let me know what i may be missing in my ansible template file to fix the above trailing whitespace issues.
I am trying to load a Ansible Vault encrypted file into a configmap using jinja2 templating
Then you are solving the wrong problem; let the to_yaml filter do all that escaping for you, rather than trying to jinja your way through it.
- command: cat /tmp/test.yml
register: tmp_test
- set_fact:
cm_skeleton:
apiVersion: v1
data:
kind: ConfigMap
metadata:
name: test
namespace: test-namespace
- copy:
content: >-
{{ cm_skeleton | combine({"data":{"test.yml": tmp_test.stdout}}) | to_yaml }}
dest: /tmp/test.configmap.yml
If you have other things you are trying to template into that ConfigMap, fine, you can still do so, but deserialize in into a dict so you can insert the literal contents of test.yml into the dict and then re-serialize using the to_yaml filter:
- set_fact:
cm_skeleton: '{{ lookup("template", "cm.j2") | from_yaml }}'
- copy:
contents: '{{ cm_sketeton | combine({"data"...}) | to_yaml }}'

Use Dict in Vars with Templates in Ansible

I'm trying to use templates with different sets of variables for each itteration of a determined set of tasks. For example, in one of the tasks I'd like to set specific values for postgres:
- name: Define values for postgres-ds
template:
src: postgres-ds.xml.j2
dest: /opt/ear_{{ instance_control.value }}/postgres-ds.xml
vars: "{{ postgres_desenv }}"
notify: Restart Service
In role/vars/main.yaml, I defined:
postgres_desenv:
var1: somevalue
var2: someothervalue
...
Still, I get the following error:
fatal: [rmt]: FAILED! => {
"failed": true,
"reason": "Vars in a Task must be specified as a dictionary, or a list of dictionaries
...
When I try to use the same variable in another context, it works fine:
- debug:
msg: "{{ item.key }} - {{ item.value }}"
with_dict: "{{ postgres_desenv }}"
I tried following the answers to this question but I'm still stuck.
My next step is to use a variable to call the variable inside vars, something like:
- name: Define values for postgres-ds
template:
src: postgres-ds.xml.j2
dest: /opt/ear_{{ instance_control.value }}/postgres-ds.xml
vars: postgres_{{ another_var }}
notify: Restart Service
You can do something like this:
- name: Define values for postgres-ds
template:
src: postgres-ds.xml.j2
dest: /opt/ear_{{ instance_control.value }}/postgres-ds.xml
vars:
settings: "{{ postgres_desenv }}"
notify: Restart Service
Then within the template you could refer to, e.g.,
{{ settings.var1 }}
In my case, following the answer above, all i had to do is using {{ item.value.(mydictkey) }} and that's it
In my case i defined a global variable like so:
vars:
vhosts:
web1
port: 8080
dir: /mywebsite
web2:
...
Then in the task I used:
- name: Render template
template:
src: "../templates/httpd.vhost.conf.j2" # Local template
dest: "/etc/httpd/conf.d/{{ item.key }}.conf" # Remote destination
owner: root
group: root
mode: 644
with_dict: "{{ vhosts }}"
In the template I used:
<VirtualHost *:{{ item.value.port }}>
DocumentRoot /var/www/{{ item.value.dir }}
</VirtualHost>
If postgres_desenv is defined in vars/main.yml that will be loaded automatically and be available to the role and rest of the playbook. Why do you have to specify that again using "vars" option in the template module task?

How to use template module with different set of variables?

My use case is the following :
I have a template file, and I would like to create 2 different files from that template, with the variables being filled by a different set of variables for each file.
For example, lets say I want to template the file containing the line:
mkdir -p {{myTemplateVariable}}
I would like to find a proper way to get this variable filled by "File1" and "File2". Something like :
- name: template test 1
template:
src=myTemplateFile
dest=result1
- name: template test 2
template:
src=myTemplateFile
dest=result2
where I could specify for the first templating that the variable to use is a = "File1" and for the second, b = "File2".
With Ansible 2.x you can use vars: with tasks.
Template test.j2:
mkdir -p {{myTemplateVariable}}
Playbook:
- template: src=test.j2 dest=/tmp/File1
vars:
myTemplateVariable: myDirName
- template: src=test.j2 dest=/tmp/File2
vars:
myTemplateVariable: myOtherDir
This will pass different myTemplateVariable values into test.j2.
For Ansible 2.x:
- name: template test
template:
src: myTemplateFile
dest: result1
vars:
myTemplateVariable: File1
- name: template test
template:
src: myTemplateFile
dest: result2
vars:
myTemplateVariable: File2
For Ansible 1.x:
Unfortunately the template module does not support passing variables to it, which can be used inside the template. There was a feature request but it was rejected.
I can think of two workarounds:
1. Include
The include statement supports passing variables. So you could have your template task inside an extra file and include it twice with appropriate parameters:
my_include.yml:
- name: template test
template:
src=myTemplateFile
dest=destination
main.yml:
- include: my_include.yml destination=result1 myTemplateVariable=File1
- include: my_include.yml destination=result2 myTemplateVariable=File2
2. Re-define myTemplateVariable
Another way would be to simply re-define myTemplateVariable right before every template task.
- set_fact:
myTemplateVariable: File1
- name: template test 1
template:
src=myTemplateFile
dest=result1
- set_fact:
myTemplateVariable: File2
- name: template test 2
template:
src=myTemplateFile
dest=result2
You can do this very easy, look my Supervisor recipe:
- name: Setup Supervisor jobs files
template:
src: job.conf.j2
dest: "/etc/supervisor/conf.d/{{ item.job }}.conf"
owner: root
group: root
force: yes
mode: 0644
with_items:
- { job: bender, arguments: "-m 64", instances: 3 }
- { job: mailer, arguments: "-m 1024", instances: 2 }
notify: Ensure Supervisor is restarted
job.conf.j2:
[program:{{ item.job }}]
user=vagrant
command=/usr/share/nginx/vhosts/parclick.com/app/console rabbitmq:consumer {{ item.arguments }} {{ item.job }} -e prod
process_name=%(program_name)s_%(process_num)02d
numprocs={{ item.instances }}
autostart=true
autorestart=true
stderr_logfile=/var/log/supervisor/{{ item.job }}.stderr.log
stdout_logfile=/var/log/supervisor/{{ item.job }}.stdout.log
Output:
TASK [Supervisor : Setup Supervisor jobs files] ********************************
changed: [loc.parclick.com] => (item={u'instances': 3, u'job': u'bender', u'arguments': u'-m 64'})
changed: [loc.parclick.com] => (item={u'instances': 2, u'job': u'mailer', u'arguments': u'-m 1024'})
Enjoy!
This is a solution/hack I'm using:
tasks/main.yml:
- name: parametrized template - a
template:
src: test.j2
dest: /tmp/templateA
with_items: var_a
- name: parametrized template - b
template:
src: test.j2
dest: /tmp/templateB
with_items: var_b
vars/main.yml
var_a:
- 'this is var_a'
var_b:
- 'this is var_b'
templates/test.j2:
{{ item }}
After running this, you get this is var_a in /tmp/templateA and this is var_b in /tmp/templateB.
Basically you abuse with_items to render the template with each item in the one-item list. This works because you can control what the list is when using with_items.
The downside of this is that you have to use item as the variable name in you template.
If you want to pass more than one variable this way, you can dicts as your list items like this:
var_a:
-
var_1: 'this is var_a1'
var_2: 'this is var_a2'
var_b:
-
var_1: 'this is var_b1'
var_2: 'this is var_b2'
and then refer to them in your template like this:
{{ item.var_1 }}
{{ item.var_2 }}
I did it in this way.
In tasks/main.yml
- name: template test
template:
src=myTemplateFile.j2
dest={{item}}
with_dict: some_dict
and in vars/main.yml
some_dict:
/path/to/dest1:
var1: 1
var2: 2
/path/to/dest2:
var1: 3
var2: 4
and in templates/myTemplateFile.j2
some_var = {{ item.value.var1 }}
some_other_var = {{ item.value.var2 }}
Hope this solves your problem.
I had a similar problem to solve, here is a simple solution of how to pass variables to template files, the trick is to write the template file taking advantage of the variable. You need to create a dictionary (list is also possible), which holds the set of variables corresponding to each of the file. Then within the template file access them.
see below:
the template file: test_file.j2
# {{ ansible_managed }} created by xbalaji#gmail.com
{% set dkey = (item | splitext)[0] %}
{% set fname = test_vars[dkey].name %}
{% set fip = test_vars[dkey].ip %}
{% set fport = test_vars[dkey].port %}
filename: {{ fname }}
ip address: {{ fip }}
port: {{ fport }}
the playbook
---
#
# file: template_test.yml
# author: xbalaji#gmail.com
#
# description: playbook to demonstrate passing variables to template files
#
# this playbook will create 3 files from a single template, with different
# variables passed for each of the invocation
#
# usage:
# ansible-playbook -i "localhost," template_test.yml
- name: template variables testing
hosts: all
gather_facts: false
vars:
ansible_connection: local
dest_dir: "/tmp/ansible_template_test/"
test_files:
- file_01.txt
- file_02.txt
- file_03.txt
test_vars:
file_01:
name: file_01.txt
ip: 10.0.0.1
port: 8001
file_02:
name: file_02.txt
ip: 10.0.0.2
port: 8002
file_03:
name: file_03.txt
ip: 10.0.0.3
port: 8003
tasks:
- name: copy the files
template:
src: test_file.j2
dest: "{{ dest_dir }}/{{ item }}"
with_items:
- "{{ test_files }}"
- name: copy vhosts
template: src=site-vhost.conf dest=/etc/apache2/sites-enabled/{{ item }}.conf
with_items:
- somehost.local
- otherhost.local
notify: restart apache
IMPORTANT: Note that an item does not have to be just a string, it can be an object with as many properties as you like, so that way you can pass any number of variables.
In the template I have:
<VirtualHost *:80>
ServerAdmin me#example.org
ServerName {{ item }}
DocumentRoot /vagrant/public
ErrorLog ${APACHE_LOG_DIR}/error-{{ item }}.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
Another real world example using a list
an extract for a template for php.ini
{% if 'cli/php.ini' in item.d %}
max_execution_time = 0
memory_limit = 1024M
{% else %}
max_execution_time = 300
memory_limit = 512M
{% endif %}
This is the var
php_templates:
- { s: 'php.ini.j2', d: "/etc/php/{{php_version}}/apache2/php.ini" }
- { s: 'php.ini.j2', d: "/etc/php/{{php_version}}/cli/php.ini" }
Then i deploy with this
- name: push templated files
template:
src: "{{item.s}}"
dest: "{{item.d}}"
mode: "{{item.m | default(0644) }}"
owner: "{{item.o | default('root') }}"
group: "{{item.g | default('root') }}"
backup: yes
with_items: "{{php_templates}}"

Resources