Setting vars conditionally within a *role*

The manual has examples on setting variables conditionally within a playbook. e.g.

- hosts: all
  remote_user: root
  vars_files:
    - "vars/common.yml"
    - [ "vars/{{ ansible_os_family }}.yml", "vars/os_defaults.yml" ]
  tasks:
  - name: make sure apache is running
    service: name={{ apache }} state=running

My question is how to do this within a role. Specific case:

action: copy src={{item}} dest=/{{item}} mode=750 group={{snmp_group}}

I want to set snmp_group to “wheel” when ansible_os_family==‘RedHat’, and “snmp” when ansible_os_family==‘Debian’. This logic definitely belongs in the role, not in any playbook which wraps it.

At the moment all I can think of is some Jinja2 conditional buried directly into the assignment:

snmp_group: “{% if ansible_os_family==‘RedHat’ %}wheel{% else %}snmp{% endif %}”

This seems to work, but it’s pretty ugly. I can’t see how to do anything equivalent to vars_files in a role, or indeed how to include anything under vars/ except vars/main.yml

What am I missing?

Thanks,

Brian.

I think the way I would do this would be to create a var named
"snmp_group" in vars/{{ ansible_os_family}}.yml which would take on
different values for different OS types.
I'm not sure if there is a better way but even if there was I think
that's a better/cleaner way of specifying it rather than adding logic
in your play to set it.
-John

There’s not currently a way to set variables conditionally in a role.

What normally happens here is you would use “group_by” to determine which roles, or role variations, to apply.

I am trying to avoid duplicating complex tasks when they vary by only one parameter. The following is a real example:

  • name: add executables
    action: copy src={{item}} dest=/{{item}} mode=750 group=wheel
    with_items:
  • usr/sbin/snmpd-smartctl-connector
  • usr/sbin/snmpd-mdraid-connector
  • usr/sbin/update-mdraid-cache
  • usr/sbin/update-smartctl-cache
    notify: restart snmpd

when: ansible_os_family==‘RedHat’

  • name: add executables
    action: copy src={{item}} dest=/{{item}} mode=750 group=snmp
    with_items:
  • usr/sbin/snmpd-smartctl-connector
  • usr/sbin/snmpd-mdraid-connector
  • usr/sbin/update-mdraid-cache
  • usr/sbin/update-smartctl-cache
    notify: restart snmpd

when: ansible_os_family==‘Debian’

What I want to say is:

  • when ansible_os_family==‘Debian’, set variable snmp_group=snmp
  • when ansible_os_family==‘RedHat’, set variable snmp_group=wheel
  • write the task once, with group={{snmp_group}}

It seems there is no good way of doing this in ansible. Options are:

  1. Write the tasks multiple times with conditions, as shown above
  2. Split into two separate but almost identical roles in separate files. Use groups to select the correct role. That was your suggestion. I think it makes maintaining the logic harder because it’s duplicated in different parts of the filesystem, and it puts the onus on the role user to select the correct role.
  3. Set up vars from the playbook: this means the role is split into two parts which you must remember to invoke correctly. It’s because playbooks have a feature (vars_files) that roles don’t.
  • hosts: xxx
    vars_files:
  • roles/snmp/vars/{{ansible_os_family}}.yml
    roles:
  • snmp
  1. Use inline jinja2 conditionals in vars/main.yml. This seems like the least bad option here.

[roles/snmp/vars/main.yml]
snmp_group: “{% if ansible_os_family==‘RedHat’ %}wheel{% else %}snmp{% endif %}”

  1. Maybe write a parameterised role, and have two other roles snmp_redhat and snmp_debian which call it via ‘meta’, passing the correct value for snmp_group? Obscure, and in any case the meta roles can only be executed before the roles within the main role.

Excerpts from candlerb's message of 2013-10-22 04:43:49 -0400:

- when ansible_os_family=='Debian', set variable snmp_group=snmp
- when ansible_os_family=='RedHat', set variable snmp_group=wheel

You can do something like this:

- set_fact: snmp_group=snmp
   when: ansible_os_family == 'Debian'

- set_fact: snmp_group=wheel
   when: ansible_os_family == 'RedHat'

I guess the following would work:

- set_fact: snmp_group="wheel"
   when: ansible_os_family=='RedHat'

- set_fact: snmp_group="snmp"
   when: ansible_os_family=='Debian'

- copy: src={{item}} dest=/{{item}} mode=750 group={{snmp_group}}
   with_items:
      - usr/sbin/snmpd-smartctl-connector
      - usr/sbin/snmpd-mdraid-connector
      - usr/sbin/update-mdraid-cache
      - usr/sbin/update-smartctl-cache
    notify: restart snmpd

s
Luciano

It does work, thank you !

I didn’t come across the set_fact module before, because:

  1. it is not mentioned in http://www.ansibleworks.com/docs/playbooks_variables.html

  2. it doesn’t have “var” in its name

Regards,

Brian.

Sorry about the dup!

set_fact is mentioned in the modules documentation.

I agree it’s named a bit strangely, but we don’t want to change it.

Conditional vars from roles is something we need to think about a bit.

A question on the use case: Is that example used to add new things to existing hosts, or to initially configure recently created hosts? Also (if this is for new hosts), do you use both OS families or are you future-proofing in case you ever need to do so?

Wait are you saying if I created Debian.yml in a role’s vars folder and then call that role then all the Debian hosts would import that file? Or, just that I could create Debian.yml and do an ‘include:’ for vars/{{ ansible_os_family}}.yml manually?

I have both Ubuntu and CentOS hosts that I’m managing, and I have a fairly complicated role for configuring snmpd (which includes the mad-hacking MDRAID and SMARTCTL MIBs) that I want to share between them.

This role is generally run when the machine is first installed, but it can be re-run e.g. to push out a modified version of the mad-hacking scripts, new community string etc.

This was added already on the 1.4 branch.

Please see this thread.

https://groups.google.com/forum/#!msg/Ansible-project/7Lnf5uuCWCY/-JWECvkiyxMJ