Home > Software engineering >  How to Loop right between Playbook and Role in Ansible
How to Loop right between Playbook and Role in Ansible

Time:09-01

i try to loop over the geerlingguy.nginx Role to create nginx vhosts. But i dont 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. Thanks!

CodePudding user response:

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:

Option 1

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;

Option 2

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: define nginx_vhosts variable
  set_fact:
    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;

- name: include vhosts.yml from geerlingguy
  include_tasks: vhosts.yml

Here you set the variable nginx_vhosts with new values, a list with a single dict. Then you perform the import 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' }
  • Related