ebenoit.info

Ansible naming conventions

I wanted to write a little something about how I name various elements when using Ansible; however, because it is in parts very dependant on how I organize my inventories and playbooks, I ended up having to explain some of that as well.

Inventory

Host names

I tend to disregard the commonly accepted best practice of using short names for hosts, and use FQDNs instead. In my opinion, it has many advantages.

Group names

I organize my inventory into relatively shallow hierarchies which correspond to various aspects. All group names in such a hierarchy, with the exception of the top-level group, are prefixed with something indicative of which hierarchy they're a part of. For example, if I have to group hosts by location, I will use a group named by_location containing subgroups using a loc_ prefix.

All group names use snake case.

Roles and tags

I use kebab-case for role names. In addition, I usually ensure that each role can be controlled using a single tag, which is identical to the role's name.

I organize most of my roles' tasks in a set of 5 phases.

These phases are not always present in a given role. If they are, the installation, configuration and runtime phases will bear the install, config and runtime tags, respectively, while preparations and post-installation do not bear any tags. This allows phases of a role to be executed selectively in a relatively predictable manner.

I very rarely use host variables. When I do, they are exceptions to items usually carried by groups, so the variable names are determined by the latter.

Variables

Prefix

All variables should be prefixed, either by a role name or a group name. While the commonly accepted practice consists in using a single _ to separate the prefix and the variable's actual name, I prefer using two of them, for example:

my_role__variable_name: value

The reasoning is that, when using a single one, a name such as a_b_c is ambiguous: it could be variable b_c of group/role a, but it could also be variable c of group/role a_b. While I never had a case of collision when I used a single separator, I saw instances of ambiguity a few times.

When the variable is related to a role, the role name in snake case will be used as the prefix, as seen above with a role called my-role.

For variables provided by groups, I will use the group name as the variable prefix. For example, if I wanted to define a vlan_tag variable that depended on the network a host is on, I would have the following type of structure:

all:
  children:
    by_network:
      children:
        net_dmz:
          vars:
            by_network__vlan_tag: 32
        net_app1:
          vars:
            by_network__vlan_tag: 33
        net_dev:
          vars:
            by_network__vlan_tag: 34

Private variables

When a variable should be considered "private" to a role or group, I will prefix its whole name with an additional _. This will be the case for almost all variables in a role's vars/main.yml file, as most of these are not meant to be used outside of a role. I also use this convention for "registered" variables and for loop iterators if item is insufficient (e.g. loops that include other tasks or roles).

Decoupling

A role should in my opinion never use a variable that isn't explicitly meant for it, as it introduces strong coupling between that role and the current structure of the inventory.

Because of this, I will use group-level variables to carry common information and assign their contents to role variables. For example, if I had a db_name variable in both role-a and role-b that needed to be assigned the same value, I would end up with something like this in my group variables:

srv_service__db_name: thing
role_a__db_name: "{{ srv_service__db_name }}"
role_b__db_name: "{{ srv_service__db_name }}"

Handlers

I always use the listen directive for my handlers. I use a name that follows the same convention as the variable names above. For example, a handler that restart the service for a role named apache would be called apache__restart. However, I always couple that with a human-readable name which will appear in the logs.

- listen: ntp__restart
  name: Restart NTP
  ansible.builtin.service:
    name: ntp
    state: restarted