<blockquote>
  <p>Q: " Prevent misspelled variables passed to Ansible roles ... remove the defaults/main.yml, ... But then I can't take advantage of the benefits of having defaults. Are there other solutions?</p>
</blockquote>

<p>A: Yes. Tell <em>include_role</em> to read other file <a href="https://docs.ansible.com/ansible/latest/modules/include_role_module.html#parameters" rel="nofollow noreferrer"><em>defaults_from</em></a>. Fit the content of this file to your needs. For example</p>

<pre><code>- include_role:
    name: foo
    defaults_from: main_special.yml
  vars:
    fooo_version: "2.0"
</code></pre>

CC BY-SA 4.0
2019-07-31T01:24:13.793Z
I believe this suffers from the same problems. If I loaded defaults from `main_special.yml` using `defaults_from`, and then specified `fooo_version: "2.0"` expecting it to override the default software version, I'm going to silently have problems.
Ryan O.
CC BY-SA 4.0
2019-07-30T21:10:55.160Z
2019-07-30T21:10:55.160Z
Vladimir Botka
<p>I came up with what I think is a pretty good solution.</p>

<p>First, it requires the addition of this to each role:</p>

<pre><code>- assert:
    that: "{{ item.key }} is defined"
    fail_msg: "Attempted to override an unknown variable: {{ item.key }}"
    quiet: yes
  loop: "{{ q('dict', override) }}"

- set_fact: { "{{ item.key }}":"{{ item.value }}" }
  loop: "{{ q('dict', override) }}"
</code></pre>

<p>I added those two tasks to a file, <code>override.yml</code> within <code>ansible/tasks/</code>, and then used <code>include_tasks</code> to dynamically include that file only when <code>override</code> is actually defined:</p>

<pre><code>- include_tasks: "tasks/override.yml"
  when: override is defined
</code></pre>

<p>Second, it requires that when you override defaults with <code>import_role</code> that you do the following:</p>

<pre><code>- include_role:
    name: foo
  vars:
    override:
      fooo_version: "2.0"
</code></pre>

<p>The first block of code now loops through the passed <code>override</code> dictionary, and verifies that each key already is defined as a variable. So in the above example, the assert would fail because <code>fooo_version</code> is not defined. It then loops through the <code>override</code> dictionary a second time and uses <code>set_fact</code> to set the value of each key:val pair in the dictionary.</p>

CC BY-SA 4.0
2019-07-31T03:00:27.767Z
2019-07-31T04:40:20.723Z
2019-07-31T04:40:20.723Z
Ryan O.
<p>I'm looking for strategies to mitigate silent errors related to misspelled or otherwise mistyped variables being passed to Ansible roles.</p>

<p>So for example, I have a role with a defaults/main.yml:</p>

<pre><code>foo_version: "1.0"
</code></pre>

<p>Within the tasks/main.yml:</p>

<pre><code>- get_url:
    url: "https://foo.com/foo-{{ foo_version }}.tar.gz"
    dest: /tmp/
</code></pre>

<p>Now in my main playbook, if I do this, it will install the default version, 1.0:</p>

<pre><code>- include_role: name=foo
</code></pre>

<p>If I do this, it will install version 2.0:</p>

<pre><code>- include_role: name=foo
  vars:
    foo_version: "2.0"
</code></pre>

<p>So far so good. But if I do this by accident, it will install 1.0 without my realizing it:</p>

<pre><code>- include_role: name=foo
  vars:
    fooo_version: "2.0"
</code></pre>

<p>The only "solution" I've thought of is to remove the defaults/main.yml, in which case it would fail in the last case because <code>foo_version</code> would remain undefined. But then I can't take advantage of the benefits of having defaults.</p>

<p>Are there other solutions? Thanks!</p>

<ansible>
Prevent misspelled variables passed to Ansible roles
Ryan O.
# Prevent misspelled variables passed to Ansible roles

(Asked by: username_0 on 2019-07-30)

I'm looking for strategies to mitigate silent errors related to misspelled or otherwise mistyped variables being passed to Ansible roles.
So for example, I have a role with a defaults/main.yml:
foo_version: "1.0"

Within the tasks/main.yml:
- get_url:
    url: "https://foo.com/foo-{{ foo_version }}.tar.gz"
    dest: /tmp/

Now in my main playbook, if I do this, it will install the default version, 1.0:
- include_role: name=foo

If I do this, it will install version 2.0:
- include_role: name=foo
  vars:
    foo_version: "2.0"

So far so good. But if I do this by accident, it will install 1.0 without my realizing it:
- include_role: name=foo
  vars:
    fooo_version: "2.0"

The only "solution" I've thought of is to remove the defaults/main.yml, in which case it would fail in the last case because foo_version would remain undefined. But then I can't take advantage of the benefits of having defaults.
Are there other solutions? Thanks!

**Tags:** ansible


---

## 2 answers

### Answer by username_1 on 2019-07-30. Score: 1

Q: " Prevent misspelled variables passed to Ansible roles ... remove the defaults/main.yml, ... But then I can't take advantage of the benefits of having defaults. Are there other solutions?

A: Yes. Tell include_role to read other file defaults_from. Fit the content of this file to your needs. For example
- include_role:
    name: foo
    defaults_from: main_special.yml
  vars:
    fooo_version: "2.0"

> **Comments:**
> **username_0**: I believe this suffers from the same problems. If I loaded defaults from `main_special.yml` using `defaults_from`, and then specified `fooo_version: "2.0"` expecting it to override the default software version, I'm going to silently have problems.  

---

### Answer by username_0 on 2019-07-31. Score: 0

I came up with what I think is a pretty good solution.
First, it requires the addition of this to each role:
- assert:
    that: "{{ item.key }} is defined"
    fail_msg: "Attempted to override an unknown variable: {{ item.key }}"
    quiet: yes
  loop: "{{ q('dict', override) }}"

- set_fact: { "{{ item.key }}":"{{ item.value }}" }
  loop: "{{ q('dict', override) }}"

I added those two tasks to a file, override.yml within ansible/tasks/, and then used include_tasks to dynamically include that file only when override is actually defined:
- include_tasks: "tasks/override.yml"
  when: override is defined

Second, it requires that when you override defaults with import_role that you do the following:
- include_role:
    name: foo
  vars:
    override:
      fooo_version: "2.0"

The first block of code now loops through the passed override dictionary, and verifies that each key already is defined as a variable. So in the above example, the assert would fail because fooo_version is not defined. It then loops through the override dictionary a second time and uses set_fact to set the value of each key:val pair in the dictionary.


---

