I try to loop over the geerlingguy.nginx Role to create nginx VHosts. But I don't get it done:
Playbook.yml
- hosts: some.server
become: true
roles:
- geerlingguy.nginx
tasks:
- name: looping vhosts
include_tasks: vhosts.yml
loop:
- { name: 'vhost1.bla.com', state: 'present' }
- { name: 'vhost1.bla.com', state: 'present' }
For this Server I create a Host_vars File:
host_vars.yml
nginx_worker_processes: "auto"
nginx_worker_connections: 768
nginx_extra_http_options: |
gzip on;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
ssl_prefer_server_ciphers on;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
nginx_vhosts:
- listen: "443 ssl http2"
server_name: '{{ item.name }}'
server_name_redirect: " {{ item.name }} "
root: "/var/www/{{ item.name }}"
index: "index.php index.html index.htm"
access_log: "/var/www/{{ item.name }}/logs/access_{{ item.name }}.log"
error_log: "/var/www/{{ item.name }}/logs/erro_{{ item.name }}.log"
state: "{{ item.state }}"
template: "{{ nginx_vhost_template }}"
filename: "{{ item.name }}"
extra_parameters: |
ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
This is the vhost.yml
from the geerlingguy.nginx Role:
- name: Remove default nginx vhost config file (if configured).
file:
path: "{{ nginx_default_vhost_path }}"
state: absent
when: nginx_remove_default_vhost | bool
notify: restart nginx
- name: Ensure nginx_vhost_path exists.
file:
path: "{{ nginx_vhost_path }}"
state: directory
mode: 0755
notify: reload nginx
- name: Add managed vhost config files.
template:
src: "{{ item.template|default(nginx_vhost_template) }}"
dest: "{{ nginx_vhost_path }}/{{ item.filename|default(item.server_name.split(' ')[0] ~ '.conf') }}"
force: true
owner: root
group: "{{ root_group }}"
mode: 0644
when: item.state|default('present') != 'absent'
with_items: "{{ nginx_vhosts }}"
notify: reload nginx
tags:
- skip_ansible_lint
- name: Remove managed vhost config files.
file:
path: "{{ nginx_vhost_path }}/{{ item.filename|default(item.server_name.split(' ')[0] ~ '.conf') }}"
state: absent
when: item.state|default('present') == 'absent'
with_items: "{{ nginx_vhosts }}"
notify: reload nginx
tags:
- skip_ansible_lint
- name: Remove legacy vhosts.conf file.
file:
path: "{{ nginx_vhost_path }}/vhosts.conf"
state: absent
notify: reload nginx
So, when I run the playbook I got:
fatal: [some.server]: FAILED! => {
"msg": "[{'listen': '443 ssl http2', 'server_name': '{{ item.name }}'... HIGH:!aNULL:!MD5;\\n'}]: 'item' is undefined
I try it in different ways but always get the same error, would be greate if someone could help me.
Your approach doesn't work, you won't get anything out of a loop at this point. Furthermore, it is not possible to define a variable or data structure and have the Jinja logic evaluate it later.
The implementation of geerlingguy provides that the variable nginx_vhosts
is defined. This variable must be a list of dicts, and this list is then automatically processed.
You have two main options:
You create nginx_vhosts
as a list of dicts for all your virtual hosts.
nginx_vhosts:
- listen: "443 ssl http2"
server_name: "vhost1.bla.com"
server_name_redirect: "www.vhost1.bla.com"
root: "/var/www/vhost1.bla.com"
index: "index.php index.html index.htm"
error_page: ""
access_log: "/var/www/vhost1.bla.com/logs/access_vhost1.bla.com.log"
error_log: "/var/www/vhost1.bla.com/logs/error_vhost1.bla.com.log"
state: "present"
template: "{{ nginx_vhost_template }}"
filename: "vhost1.bla.com.conf"
extra_parameters: |
ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
- listen: "443 ssl http2"
server_name: "vhost2.bla.com"
server_name_redirect: "www.vhost2.bla.com"
root: "/var/www/vhost2.bla.com"
index: "index.php index.html index.htm"
error_page: ""
access_log: "/var/www/vhost2.bla.com/logs/access_vhost2.bla.com.log"
error_log: "/var/www/vhost2.bla.com/logs/error_vhost2.bla.com.log"
state: "present"
template: "{{ nginx_vhost_template }}"
filename: "vhost2.bla.com.conf"
extra_parameters: |
ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
A bit more complicated, but I think that was your wish, with the loop.
Create a separate file for your tasks myvhost.yml
with the following content:
---
- name: create directories
file:
path: "{{ item }}"
state: directory
with_items:
- "/var/www/{{ vhost.name }}"
- "/var/www/{{ vhost.name }}/logs"
- name: define nginx_vhosts variable
set_fact:
nginx_vhosts:
- listen: "443 ssl http2"
server_name: '{{ vhost.name }}'
# server_name_redirect: " {{ vhost.name }} "
root: "/var/www/{{ vhost.name }}"
index: "index.php index.html index.htm"
access_log: "/var/www/{{ vhost.name }}/logs/access_{{ vhost.name }}.log"
error_log: "/var/www/{{ vhost.name }}/logs/erro_{{ vhost.name }}.log"
state: "{{ vhost.state }}"
# template: "{{ nginx_vhost_template }}"
filename: "{{ vhost.name }}"
extra_parameters: |
ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
- name: include vhosts.yml from geerlingguy
include_role:
name: geerlingguy.nginx
tasks_from: vhosts
Here you set the variable nginx_vhosts
with new values, a list with a single dict. Then you perform the import the tasks vhosts
of the role from geerlingguy.
In your playbook, on the other hand, you import your new myvhost.yml
with the loop.
- name: looping vhosts
include_tasks: myvhost.yml
loop:
- { name: 'vhost1.bla.com', state: 'present' }
- { name: 'vhost2.bla.com', state: 'present' }
loop_control:
loop_var: vhost
For your loop you have to rename the loop variable, otherwise there will be conflicts with loops in the vhosts.yml
of geerlingguy (I had overlooked this at the beginning), see loop_var: vhost
. After renaming the loop variable, you must of course also change the name in myvhost.yml
from item
to vhost
.
Before running your task looping vhosts, the geerlingguy.nginx
role should have been run once, e.g. if it is listed in your playbook under roles:
.
Another change I made in myvhost.yml
. Instead of include_tasks
use better include_role
with tasks_from: vhosts
.
I commented out the server_name_redirect:
setting for now, because it creates nginx config files that crash nginx. If you really need this setting you have to analyze this in more detail.
Furthermore the certificate files (ssl-cert-snakeoil) must exist before creating the VHosts.
A complete playbook might look like this:
---
- hosts: nginx
become: true
roles:
- geerlingguy.nginx
tasks:
- name: looping vhosts
include_tasks: myvhost.yml
loop:
- { name: 'vhost1.bla.com', state: 'present' }
- { name: 'vhost2.bla.com', state: 'present' }
loop_control:
loop_var: vhost