Global default variables for multiple Ansible roles

I am trying to determine the best approach to define global default variables for my Ansible project. I have a project that consists of multiple playbooks, multiple roles. I also have some custom modules, etc…

Sample structure:

.
├── ansible.cfg
├── group_vars
│   └── all.yaml
├── inventory
├── playbooks
└── roles
    ├── role1
    └── role2

Let’s say both my roles use git and require the git repository name to perform a job. So, my question is, where should I define the git repository name?

Based on Ansible best practices, role variables should use role name as a prefix (role1_git_repositoy_name). If I do this, I have to have the git repository defined in two places (defaults in each role).

On the other hand, I can define the Git repository name in the group_vars/all.yaml like this:

# group_vars/all.yaml
git_repo_name: "my-github-org/my-repo"'

If I use this approach, I am not sure what would be the best way to consume this variable in my role.

  1. Should I simply use git_repo_name variable in my roles?
  2. Should I re-define my variable when calling a role:
- ansible.builtin.include_role:
    name: role1
  vars:
    role1_git_repo_name: "{{ git_repo_name }}"
  1. Should I re-define my variable in defaults for a role:
# roles/role1/defaults/main.yaml
role1_git_repo_name: "{{ git_repo_name }}"

Any suggestions on how to proceed or what the best practices are?

Hi @bajo and welcome.

There is no right answer here. It’s a personal preference. If you had some additional criteria, maybe you could lean toward some definitive answer.

Looking at the provided examples, the first one is the most clean. Just a single var defined in group_vars or host_vars. You can also put a single assert task at the beginning of the role to test if git_repo_name is defined and has proper value.

The second one is just plain ugly.

The third one, my favorite, is a nice alternative but it unnecessarily grows the number of variables (more resources consumed), with essentially the same value, in case you want to globally force the value. On the other hand, this is a must have if you want to have the ability to override variable per role i.e. you don’t want to have it forced globally. Fallback to default value is also possible.

Ideally, the variable files in your inventory should be named after the role they are steering:

$ tree inventory/
inventory/
└── group_vars
    └── example
        ├── role1.yml
        └── role2.yml

$ cat inventory/group_vars/example/role1.yml
---
role1_git_repo_name: "my-github-org/my-repo"

$ cat inventory/group_vars/example/role2.yml
---
role2_git_repo_name: "my-github-org/my-repo"

If you have a truly global variable used by all your roles that you only want to define once in your inventory, then this is the way to go:

1 Like

We have a “defaults” git project where we keep a few (very few) things global to all our projects. This includes an inventory, the ansible-lint configuration our ci/cd jobs use, and a group_vars/all/_ansible-defaults.yml file. That file contains variables that projects may need but are beyond the scope of any single project: load balancer SNAT pools, Jenkins user id/group names, proxy strings, etc.

Whenever any of those files changes, the “defaults” project’s Jenkins pipeline pushes changes to the appropriate places. In the case of group_vars/all/_ansible-defaults.yml, it goes through all our git projects looking for an existing group_vars/all/_ansible-defaults.yml file and updates/commits/pushes the new version of that file in the main branch.

By making group_vars/all a directory rather than a file, projects can still have their own group_vars/all/this.yml, group_vars/all/that.yml, group_vars/all/the_other_thing.yml, etc. files without fighting with the defaults project for the space in the all group_vars namespace.

1 Like