Loop through Ansible variable in in .j2 file - ansible

I have one ansible var list defined in group_vars
member_list:
- a
- b
I have one proxy.j2 template
{% for var in members_list %}
server {
server_name {{ var }}-{{ server_name }};
{% endfor %}
How could I loop through that list to get the value in .j2 file?

You should be using {{ item }} instead of {{ var }}.

Related

Controlling indentation in Jinja2 template with Ansible

I have this playbook:
(venv) bash-3.2$ cat playbooks/nginx_config_generate.yml
---
- name: Generate Nginx Config
hosts: all
gather_facts: False
roles:
- ../roles/nginx_config_generate
Here is the files that make up the role:
(venv) bash-3.2$ cat roles/nginx_config_generate/tasks/main.yml
---
- name: Generate Nginx Config
import_tasks: gen_config.yml
(venv) bash-3.2$ cat roles/nginx_config_generate/tasks/gen_config.yml
- name: Generate Nginx Config File From Template
ansible.builtin.template:
src: sp.conf.j2
dest: "/tmp/nginx.cfg"
(venv) bash-3.2$ cat roles/nginx_config_generate/templates/sp.conf.j2
something here
# Upstreams
{% for ups in my_vars.upstreams %}
{{ ups.entry }}
{% endfor %}
# HTTP Insecure
server {
blha blah blah
something here = that
this = that
{% for ups in my_vars.http_locations %}
{{ ups.entry }}
{% endfor %}
}
Here are my host_vars ...
(venv) bash-3.2$ cat inventory/prod/sfo/host_vars/127.0.0.1
my_vars_str: "host,127.0.0.1,srv1,srv2"
my_vars:
upstreams:
- entry: |
upstream something {
foo: blah blah
}
- entry: |
upstream completely {
foo: blah blah
different: True
}
http_locations:
- entry: |
location / {
set something
}
- entry: |
location /foo {
set something
hover: craft
}
When I run my play it produces this file:
(venv) bash-3.2$ cat /tmp/nginx.cfg
something here
# Upstreams
upstream something {
foo: blah blah
}
upstream completely {
foo: blah blah
different: True
}
# HTTP Insecure
server {
blha blah blah
something here = that
this = that
location / {
set something
}
location /foo {
set something
hover: craft
}
}
As you can see the above is probably a valid nginx config but how can I fix up the indentation on my location lines to make it more readable?
UPDATE: I tried Carlos's suggestion so I have tried this:
...
# HTTP Insecure
server {
blha blah blah
something here = that
this = that
{% for loc in my_vars.http_locations %}
{{ loc.entry }}
{% endfor %}
}
But that yields the same output:
# HTTP Insecure
server {
blha blah blah
something here = that
this = that
location / {
set something
}
location /foo {
set something
hover: craft
}
Ok. I am started playing with the indent filter in my template. Here is the template I am using to generate the output I wanted:
something here
# Upstreams
{% for ups in my_vars.upstreams %}
{{ ups.entry }}
{% endfor %}
# HTTP Insecure
server {
blha blah blah
something here = that
this = that
{% for loc in my_vars.http_locations %}
{{ loc.entry | indent(4)}}
{% endfor %}
}
Now I get this output:
something here
# Upstreams
upstream something {
foo: blah blah
}
upstream completely {
foo: blah blah
different: True
}
# HTTP Insecure
server {
blha blah blah
something here = that
this = that
location / {
set something
}
location /foo {
set something
hover: craft
}
}
have you tried modifying the indentation in the second loop?
# HTTP Insecure
server {
blha blah blah
something here = that
this = that
{% for ups in my_vars.http_locations %}
{{ ups.entry }}
{% endfor %}
}

Stuck with a looping situation using jinja template with Ansible

I am stuck with a looping situation using jinja template with Ansible.
My jinja template:
{% for int in interfaces | difference(existing_conf) %}
interface {{ int }}
{% for ip in dhcp_servers | difference(existing_conf) %}
ip dhcp relay address {{ ip }}
{% endfor %}
{% endfor %}
TASK [view the existing config] *******************************************************************************************************************************************************************************************************************************************************************************
ok: [nxos-1] => {
"existing_conf": [
"Vlan1",
"10.1.1.2",
"10.1.1.3",
"10.1.1.4",
"Ethernet1/49",
"10.1.1.2",
"Ethernet1/50",
"10.1.1.2"
]
}
TASK [Needed config] *****************************************************************************************************************************************************************************************************************************************************************
ok: [nxos-1]
TASK [View the needed config] ************************************************************************************************************************************************************************************************************************************************************************************
ok: [nxos-1] => {
"needed_conf": [
"Vlan1",
"10.1.1.2",
"10.1.1.3",
"10.1.1.4",
"Ethernet1/49",
"10.1.1.2",
"10.1.1.3",
"10.1.1.4",
"Ethernet1/50",
"10.1.1.2",
"10.1.1.3",
"10.1.1.4"
]
}
My variables:
interfaces=["Vlan1", "Ethernet1/49", "Ethernet1/50"]
dhcp_servers=["10.1.1.2", "10.1.1.3", "10.1.1.4"]
With my template, I am getting an empty config.
Config I want:
interface Ethernet1/49
ip dhcp relay address 10.1.1.3
ip dhcp relay address 10.1.1.4
interface Ethernet1/50
ip dhcp relay address 10.1.1.3
ip dhcp relay address 10.1.1.4
Note: I only want to add the config which is not in the running-config of switch.
Q: "... I am getting an empty config."
A: There are no items to iterate over. The difference
- debug:
msg: "{{ interfaces|difference(existing_conf) }}"
gives an empty list
msg: []

Generate a file from a yaml template

Is there a possibility in Helm to generate a file from a yaml template?
I need to create a configuration that is dynamic depending on the setup.
I need to add it as a secret/configuration file to the container when starting it.
Update:
This is the file contents that I would like to parameterize:
version: 1.4.9
port: 7054
debug: {{ $debug }}
...
tls:
enabled: {{ $tls_enable }}
certfile: {{ $tls_certfile }}
keyfile: {{ $tls_keyfile }}
....
ca:
name: {{ $ca_name }}
keyfile: {{ $ca_keyfile }}
certfile: {{ $ca_certfile }}
....
affiliations:
{{- range .Values.Organiza }}: []
All these values are
I don't have a clue how to pass this file contents into ConfigMap or any other k8s object that would generate a final version of the file.

How to correctly define subelements in Ansible jinja2 template(with subelements)?

I'm writing ansible-playbook to divide nginx.conf with includes. In my opinion it would be more comfortable to use nginx.conf with such option cause I can include or exclude some config block at playbook vars.
At current time I have trouble with part: name: 2. Copy nginx.conf config.
playbook.yml:
- name: "setup_nginx"
hosts: "TEST_HOST"
gather_facts: yes
remote_user: root
vars:
nginx_worker_processes: "{{ ansible_processor_cores }}"
nginx_worker_connections: "32768"
nginx_worker_rlimit_nofile: "{{ (ansible_processor_cores*{{ nginx_worker_connections }}*2)|int|abs }}"
nginx_directories:
- directory: inc
nginx_files:
- file: "gzip.inc"
- file: "logs.inc"
- file: "mime.types"
- file: "tuning.inc"
- file: "proxy.inc"
- file: "ssl.inc"
- directory: sites
nginx_files:
- file: "mysite1"
- file: "mysite2"
- tasks:
- name: 1. Create nginx directories
file:
path: "/etc/nginx/{{ item.directory }}"
state: directory
owner: nginx
group: nginx
with_items:
- "{{ nginx_directories }}"
- name: 2. Copy nginx.conf config.
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
mode: 0640
owner: nginx
group: nginx
backup: yes
with_subelements:
- "{{ nginx_directories }}"
- nginx_files
nginx.conf.j2:
user nginx;
worker_processes {{ ansible_processor_cores }};
events {
worker_connections 32768;
use epoll;
multi_accept on;
}
worker_rlimit_nofile {{ (ansible_processor_cores*32768*2)|int|abs }};
http {
{% for val in nginx_directories %}
include /etc/nginx/{{ item.0.directory }}/{{ item.1.file }};
{% endfor %}
}
I expect result:
user nginx;
worker_processes 1;
events {
worker_connections 32768;
use epoll;
multi_accept on;
}
worker_rlimit_nofile 65536;
http {
include /etc/nginx/inc/gzip.inc;
include /etc/nginx/inc/logs.inc;
include /etc/nginx/inc/mime.types;
include /etc/nginx/inc/tuning.inc;
include /etc/nginx/inc/proxy.inc;
include /etc/nginx/inc/ssl.inc;
include /etc/nginx/sites/mysite1;
include /etc/nginx/sites/mysite2;
}
But actual result:
user nginx;
worker_processes 1;
events {
worker_connections 32768;
use epoll;
multi_accept on;
}
worker_rlimit_nofile 65536;
http {
include /etc/nginx/sites/mysite2.j2;
include /etc/nginx/sites/mysite2.j2;
}
I think the trouble is that I'm not correctly define subelements at template nginx.conf.j2.
Regards
Remove the loop in you template task and in your template use
{% for val in nginx_directories | subelements('nginx_files') %}
include /etc/nginx/{{ val.0.directory }}/{{ val.1.file }};
{% endfor %}
FWIW. There is an elegant solution with config_encoder_filters which encodes YAML data
my_nginx_vhost_config:
- server:
- listen 8080
- server_name www.example.com
- "location /":
- root /usr/local/www/nginx-dist/
- index index.html
with simple template
# {{ ansible_managed }}
{{ my_nginx_vhost_config | encode_nginx }}
to nginx configuration
# cat /usr/local/etc/nginx/conf.d/default.conf
# Ansible managed
server {
listen 8080;
server_name www.example.com;
location / {
root /usr/local/www/nginx-dist/;
index index.html;
}
}
Details are available in the nginx role.

Storing multiple network devices in YAML and referrencing it in jinja2

How do I construct a yaml file to hold multiple network devices and reference to a specific vendor/type from a jinja2 file?
For example:
—
ip: 10.10.10.10
hostname: core-fw-domain-location
username: admin
password: password
vendor: cisco
type: firewall
ip: 20.20.20.20
hostname: core-rt-domain-location
username: admin
password: password
vendor: cisco
type: router
The above obviously doesn't work - how do I structure it so it's hierarchal?
In my jinja2 file, how do I reference it?
For example:
{% if node.hostname == 'core-fw-domain-location' %}
enable secret {{ node.secret }}
username admin privilege password {{ node.secret }}
{% endif %}
Structure your YAML as a list of maps:
- ip: 10.10.10.10
hostname: core-fw-domain-location
username: admin
password: password
vendor: cisco
type: firewall
- ip: 20.20.20.20
hostname: core-rt-domain-location
username: admin
password: password
vendor: cisco
type: router
Then in your python code, change node to nodes when you render the template because there are now multiple nodes configured:
config = baseline.render(nodes = node_object)
And in your jinja2 file, loop through nodes accordingly:
{% for node in nodes -%}
{% if node.hostname == 'core-fw-domain-location' %}
enable secret {{ node.password }}
username admin privilege password {{ node.password }}
{% endif %}
{%- endfor %}

Resources