1
votes

I'm new to Ansible. I have the following directory structure for my Ansible playbooks:

$HOME/playbooks
├── project1
├── project2
└── roles

I collect my commonly used roles such as my "nginx" role in the role directory and then I'll reference these roles in my project directories by adding the following line to each project's ansible.cfg file:

# $HOME/playbooks/project1/ansible.cfg
[defaults]
roles_path = $HOME/playbooks/roles

How do you configure your individual Ansible playbook tasks and and common role tasks when a common role task depends on which server the task is being run on? In my case, I have a "webservers.yml" playbook for configuring my web server and a "fileservers.yml" playbook for configuring my file server. My website will run on my web server but my static and media files will be served from a separate file server. I currently include the following task in my nginx role's main/task.yml file:

# $HOME/playbooks/roles/nginx/tasks/main.yml
- name: create nginx config file
  template: src=nginx.conf.j2
            dest=/etc/nginx/sites-available/{{ domain }}.conf
  notify: restart nginx
  become: True
  tags: nginx

The problem is that the nginx config files on each server will be a little different since (I think) the nginx process running on my web server will need to redirect static file requests to the nginx process running on my file server. Should I leave the "create nginx config file" task in my common nginx role and have it copy a minimal template onto each server but then call that task again in my webserver and fileserver playbooks and have each one deploy a server-specific version of the config file (e.g. nginx.webserver.conf.j2 versus nginx.fileserver.conf.j2)? Should I just leave the create config file out of my main role task and just execute it in each playbook? (This approach seems wrong to me) Or is there a more robust way to do this?

Thanks!

2

2 Answers

0
votes

Inventory or group variables as already mentioned by @Petro026 are one option. In your nginx role or in the configuration template you then simply check those variables.

But I think there are better options available in this case, since the roles are explicitly called in the context of the webservers or fileservers playbook. You can simply introduce a role parameter which affects how the role works.

- role: nginx
  mode: direct

Or for the webservers mode: redirect.

Then you can either filter on task level

- some: task
  when: mode == "redirect"

Or handle everything in the template per conditions.

{% if mode == "redirect" %}
# I am a webserver
{% else %}
# I am a fileserver
{% endif %}

Now, the outcome of this is exactly the same thing as if you had used group or host vars, but I think it is much more visible what is happening to someone looking at the playbook, that the nginx role supports different modes and where that info is defined (as a role parameter).

Should I just leave the create config file out of my main role task and just execute it in each playbook? (This approach seems wrong to me) Or is there a more robust way to do this?

This is another option. Indeed, putting the task right in the playbook seems wrong. But what about nested roles? That again gives you a clean solution.

Keep your nginx role as it is and just remove the part that differs between the fileservers and webservers.

Then you create two new roles. One named nginx_web and one nginx_file. Now in both roles you set a dependency to the common nginx role in <role>/meta/main.yml:

dependencies:
  - role: nginx
    tags: nginx

Now you only need to add the differing pieces into the tasks/main.yml of nginx_web and nginx_file, which looks like it only is that single template task. You even can keep the handler for restarting nginx in your common nginx role and call it from the other roles.

All you have to reference in your playbooks now is either nginx_web or nginx_file. The nginx role automatically is executed as a dependency before the task in nginx_web/file.


Update: Describing role parameters

Roles can be applied in different ways. As a string or as an dictionary. Example as a string

roles:
  - roleName

If you wish to add additional information, like tags, conditions or other parameters, you can provide a dictionary. In that case the role name goes into the dict key role:

roles:
  - role: roleName
    tags: someTag
    when: someVar is defined
    become: True
    foo: bar

All additional data is passed on to every task inside the role. Ansible will act as if tags, become and when were applied to every single task inside the role. Everything else, that is not known to Ansible will be treat as a variable.

Alternatively you can write the dict as JSON, because YAML supports limited JSON format.

roles:
  - {role: "roleName", tags: "someTag", when: "someVar is defined", become: True, foo: "bar"}

But I find that butt ugly and prefer the pure YAML definition

The latter is the format that is shown in the documentation:

Also, should you wish to parameterize roles, by adding variables, you can do so, like this:

---

- hosts: webservers
  roles:
    - common
    - { role: foo_app_instance, dir: '/opt/a',  app_port: 5000 }
    - { role: foo_app_instance, dir: '/opt/b',  app_port: 5001 }
0
votes

If the nginx configuration is different on each machine, then you should have inventory variables to drive the generation of the configuration file on each server. Template out your nginx.conf file appropriately to handle your webserver and fileserver configurations. If the configuration is completely difference you should either consider having separate roles (nginx_web and nginx_file) or at least separate jinja templates for each config.