Prefixing Variables and Separating Words in Ansible
__role_name_, inventory-only variables with a project prefix, and separate words with underscores in Ansible variable names.Categories:
haproxy_frontends. Prefix internal role variables with __role_name_, for
example __haproxy_frontends_rendered. Prefix inventory-only
project variables with a project prefix such as c2_. Use documented
upstream ansible_* variables only for Ansible-defined behavior such as
ansible_become_user. Write multiword variable names in lowercase snake case,
for example iis_web_app_pools, not iis_apppools.Problem
Without a clear naming rule it is not always obvious whether a variable belongs to a role’s public interface, to role-internal logic, or only to the inventory project.
That increases the chance of collisions, accidental overrides, and unclear role
contracts. Merged words such as iis_apppools also make names harder to scan
than separated forms such as iis_web_app_pools.
Context
Ansible variables are not namespaced per role or per inventory file. Variables
from roles, group_vars, host_vars, registered results, and task-level vars
all share the same runtime namespace.
The Ansible documentation allows only letters, numbers, and underscores in
variable names. In practice, ansible-lint narrows this further to lowercase
alphanumeric names with underscores and expects role variables inside roles to
use the role-name prefix.
The Red Hat CoP guidance recommends prefixing public role variables with the
role name and internal variables with a double underscore. In that guidance,
internal variables explicitly include values created with register and
set_fact, because they also remain in the runtime namespace after the role has
finished.1 The double underscore is only a naming convention,
not privacy or security.2
Inventory-only
project variables still need their own clear project
prefix, such as c2_ or gs_.
Solution
Decision rule: if a variable is not intended to be set, overridden, or consumed
by role users through inventory or role parameters, treat it as internal and
prefix it with __role_name_.
- Prefix every public
role variable with the role name, for example
haproxy_frontends. - Prefix every internal role variable with
__role_name_, for example__haproxy_frontends_rendered. - Prefix every inventory-only
project variable that exists only in
group_varsorhost_varswith a project prefix, for examplec2_cacerts2_ca_dir. - Write multiword variable names in lowercase snake case and separate words with underscores.
- Follow module or domain terminology when it helps clarify the contract, for
example prefer
iis_web_app_poolsoveriis_apppools. - Use documented upstream
ansible_*variables only for Ansible-defined settings such asansible_become_user. - If your project enforces a custom
ansible-lintnaming pattern, extend it to accept both public and internal role variable forms.
Benefits
- Makes variable ownership and scope visible.
- Distinguishes supported role inputs from internal helper variables.
- Reduces collisions across roles, inventory, and temporary variables.
- Makes names easier to scan because word boundaries stay explicit.
- Helps editor spell checking flag suspicious words more clearly.3
Examples and Implementation
Public, internal, and project variables
# defaults/main.yml
haproxy_frontends: []
# task vars or set_fact targets
__haproxy_frontends_rendered: "{{ haproxy_frontends }}"
# group_vars/all/smallca.yml
c2_cacerts2_ca_dir: /etc/pki/ca-trust/source/anchors
Internal variables from register and set_fact
- name: Read HAProxy service state
ansible.builtin.command: systemctl is-active haproxy
register: __haproxy_service_state
changed_when: false
- name: Derive desired backend list
ansible.builtin.set_fact:
__haproxy_backends_effective: >-
{{ haproxy_backends
| selectattr('enabled', 'equalto', true)
| list }}
These values are internal when they support the role implementation and are not part of the supported interface for users overriding the role.
Custom loop_var names can still be internal
For general guidance on when to keep item and when to introduce a custom
loop_var, see
Using item and loop_var in Ansible Loops
.
When a custom loop_var crosses an include_tasks boundary or would otherwise
look like a supported role input, prefer the internal form.
- name: Configure IIS web application pools
ansible.builtin.include_tasks: web_app_pool.yml
loop: "{{ iis_web_app_pools }}"
loop_control:
loop_var: __iis_web_app_pool
label: "{{ __iis_web_app_pool['name'] }}"
# web_app_pool.yml
- name: Ensure IIS web application pool exists
microsoft.iis.web_app_pool:
name: "{{ __iis_web_app_pool['name'] }}"
state: "{{ __iis_web_app_pool['state'] | default(omit) }}"
A name such as iis_web_app_pool is technically valid, but
__iis_web_app_pool more clearly signals that the value is an internal helper
passed between task files, not a public role variable.4
Separate words explicitly
iis_web_app_pools:
- name: HelloWorld
state: started
Avoid merged forms such as:
iis_apppools:
- name: HelloWorld
state: started
The separated form is clearer because it matches normal snake case, mirrors the
module term web_app_pool from microsoft.iis.web_app_pool, and makes it
obvious that the variable stores multiple resources.
ansible-lint pattern for stricter projects
# markdownlint-disable-next-line MD013
var_naming_pattern: "^(__)?(haproxy|guacamole|ansible)_[a-z0-9_]*$|^ansible_[a-z0-9_]*$"
This pattern allows public variables such as haproxy_frontends, internal
variables such as __haproxy_frontends_rendered, and documented upstream
variables such as ansible_become_user.
Additional Information
- Naming parameters — Good Practices for Ansible
- var-naming — Ansible Lint Documentation
- Using variables — Ansible Community Documentation
- Special Variables — Ansible Community Documentation
Red Hat CoP is the clearest source for this convention. It explicitly applies the internal-variable rule to values created with
registerandset_fact, because Ansible variables are not local to a role in the way many readers expect. ↩︎Ansible allows variable names that begin with an underscore, but it does not treat them as private. See
In editors with spell checking enabled, separated words are more likely to be checked individually. In practice this makes typos easier to spot visually, for example as a colored underline in VS Code depending on the user’s spell-check setup. ↩︎
Ansible documents
loop_control.loop_varand the special variableansible_loop_varfor loop handling. The opt-inansible-lintloop-var-prefixrule also supports conventions such as^(__|{role}_), which fits the idea that helper loop variables crossing task-file boundaries may use the internal__form. ↩︎
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.