# Strategy for repeated complex logic between hosts

**URL:** <https://forum.ansible.com/t/strategy-for-repeated-complex-logic-between-hosts/7858>\
**Category:** Get Help\
**Tags:** variables, ansible-core\
**Created:** [August 15, 2024, 1:57am UTC](https://forum.ansible.com/t/strategy-for-repeated-complex-logic-between-hosts/7858 "2024-08-15T01:57:54Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![LoftyGoals](https://avatars.discourse-cdn.com/v4/letter/l/da6949/32.png) [@LoftyGoals](https://forum.ansible.com/u/LoftyGoals)\
**Post date:** [August 15, 2024, 1:57am UTC](https://forum.ansible.com/t/strategy-for-repeated-complex-logic-between-hosts/7858/1 "2024-08-15T01:57:54Z")

</div>

I’ve found my “when” clauses are getting too complicated. They involve multiple filters and data from multiple hosts. In any other language I would declare intermediate variables but I can’t get that to work in Ansible.

I basically want to do:

```
- name: Gather information on host1
  host: host1
  tasks:
   - dowidget:
     register: widget_info_host1

- name: Gather information on host1
  host: host2
  tasks:
   - dowocket:
     register: wocket_info_host2

- name: Do something based on host1 and host2
  host: host3
  tasks:
   - dowhippet:
     when: (hostvars.host1.widget_info_host1 | COMPLICATED_FILTER)[0] >= (hostvars.host2.wocket_info_host2 | OTHER_COMPLICATED_FILTER)[1]
   - dowombat:
     when: (hostvars.host1.widget_info_host1 | COMPLICATED_FILTER)[0] >= (hostvars.host2.wocket_info_host2 | THIRD_COMPLICATED_FILTER)[1]

- name: Do more based on host1 and host2
  host: host4
  tasks:
   - dowhippet:
     when: (hostvars.host1.widget_info_host1 | COMPLICATED_FILTER)[0] >= host4_variable

```

So I want to store clauses like `(widget_info_host1 | COMPLICATED_FILTER)[0]` in a temporary variable like`host1_widget_mtime` because the template is 327 characters long and it is error-prone to edit it in multiple places. I can’t put it in host1’s variables as

```
host1_widget_mtime: (widget_info_host1 | COMPLICATED_FILTER)[0]

```

because hostvars aren’t treated as templates. I can’t put it in host3 or host4 because it can’t be shared. set\_fact isn’t designed for this. I don’t know of any other way to set a variable.

**How do I avoid having to repeat complex logic in multiple locations?**

Thank you!

---

<div class="post-metadata">

**Author:** ![bcoca](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/bcoca/32/9269_2.png) [@bcoca](https://forum.ansible.com/u/bcoca)\
**Post date:** [August 15, 2024, 3:10pm UTC](https://forum.ansible.com/t/strategy-for-repeated-complex-logic-between-hosts/7858/2 "2024-08-15T15:10:09Z")

</div>

something like this?:

```auto
- name: Gather information on host1
  host: host1
  tasks:
   - dowidget:
     register: widget_info_host1
   - set_fact:
        condition: '{{(widget_info_host1 | COMPLICATED_FILTER)[0]}}'

- name: Gather information on host1
  host: host2
  tasks:
   - dowocket: 
     register: wocket_info_host2
   - set_fact:
        condition_1: '{{(wocket_info_host2 | OTHER_COMPLICATED_FILTER)[1]}}'
        condition_2: '{{(wocket_info_host2 | THIRD_COMPLICATED_FILTER)[1]}}'

- name: Do something based on host1 and host2
  host: host3
  tasks:
   - dowhippet:
     when: hostvars['host1']['condition'] >= hostvars['host2']['condition_1']
   - dowombat:
     when: hostvars['host1']['condition'] >= hostvars['host2']['condition_2']

- name: Do more based on host1 and host2
  host: host4
  tasks:
   - dowhippet:
     when: hostvars['host1']['condition'] >= host4_variable

```

---

<div class="post-metadata">

**Author:** ![LoftyGoals](https://avatars.discourse-cdn.com/v4/letter/l/da6949/32.png) [@LoftyGoals](https://forum.ansible.com/u/LoftyGoals)\
**Post date:** [August 16, 2024, 5:07am UTC](https://forum.ansible.com/t/strategy-for-repeated-complex-logic-between-hosts/7858/3 "2024-08-16T05:07:17Z")

</div>

That’s plausible but I don’t think I understand why set\_facts is appropriate. Shouldn’t it be set\_variables? Why isn’t that a thing?

---

<div class="post-metadata">

**Author:** ![bcoca](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/bcoca/32/9269_2.png) [@bcoca](https://forum.ansible.com/u/bcoca)\
**Post date:** [August 16, 2024, 1:47pm UTC](https://forum.ansible.com/t/strategy-for-repeated-complex-logic-between-hosts/7858/4 "2024-08-16T13:47:53Z")

</div>

Naming issue aside (I brokered peace between 2 parties after weeks of discussion and we ended up with the inadequate `set_facts`), the main other way to create variables in Ansible is normally `vars:` (or derived), this creates ‘lazyily evaluated’ variables, while `set_facts` creates static variables.

Since `hostvars` cannot template (because it is in the wrong host context except for `hostvars[inventory_hostname]` but that is not special cased either) my example uses `set_fact` to ensure a value you do not need to template, then you can just use `hostvars` to access those values, you could even use `vars` to shorten it at that point, but that seemed excessive for my example.

---

<div class="post-metadata">

**Author:** ![LoftyGoals](https://avatars.discourse-cdn.com/v4/letter/l/da6949/32.png) [@LoftyGoals](https://forum.ansible.com/u/LoftyGoals)\
**Post date:** [August 16, 2024, 5:44pm UTC](https://forum.ansible.com/t/strategy-for-repeated-complex-logic-between-hosts/7858/5 "2024-08-16T17:44:29Z")

</div>

I appreciate that explanation, particularly the color commentary about the naming of set\_facts. FWIW (as a new Ansible user) I find the distinction between facts and variables to be somewhat confusing, particularly if you are trying to use them in a principled way.

In this case I’ve convinced myself that these are “facts” about the hosts. Later I will probably convince myself that I should delegate the facts and go through hostvars because set\_facts solves the templating problem.

But the truth is I’m choosing facts because they bypass lazy evaluation because hostvars blocks template evaluation. Some part of that is a code smell but I haven’t been here long enough to say what part.

Thank you for your help. I’ll update this thread when I settle on an answer (for posterity).

---

<div class="post-metadata">

**Author:** ![bcoca](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/bcoca/32/9269_2.png) [@bcoca](https://forum.ansible.com/u/bcoca)\
**Post date:** [August 16, 2024, 6:27pm UTC](https://forum.ansible.com/t/strategy-for-repeated-complex-logic-between-hosts/7858/6 "2024-08-16T18:27:21Z")

</div>

`set_facts` is really ‘set host scoped variable’, unless you use `cacheable=true` … then it creates 2 variables, one of them a fact. So yes, badly named and confusing . But that is not only problem with variables in ansible, why I proposed [redesign variable interface · Issue #127 · ansible/proposals · GitHub](https://github.com/ansible/proposals/issues/127)

---

<div class="post-metadata">

**Author:** ![LoftyGoals](https://avatars.discourse-cdn.com/v4/letter/l/da6949/32.png) [@LoftyGoals](https://forum.ansible.com/u/LoftyGoals)\
**Post date:** [August 16, 2024, 6:52pm UTC](https://forum.ansible.com/t/strategy-for-repeated-complex-logic-between-hosts/7858/7 "2024-08-16T18:52:47Z")

</div>

I know we’re off track for this thread, but aren’t all variables host-scoped? (inasmuch as you have to go through hostvars to get it)

---

<div class="post-metadata">

**Author:** ![bcoca](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/bcoca/32/9269_2.png) [@bcoca](https://forum.ansible.com/u/bcoca)\
**Post date:** [August 16, 2024, 6:54pm UTC](https://forum.ansible.com/t/strategy-for-repeated-complex-logic-between-hosts/7858/8 "2024-08-16T18:54:39Z")

</div>

No, hostvars does not contain all variables available in a scope, many are task/play variables. You can use the `vars` and `varnames` lookups to verify this.

---

<div class="post-metadata">

**Author:** ![LoftyGoals](https://avatars.discourse-cdn.com/v4/letter/l/da6949/32.png) [@LoftyGoals](https://forum.ansible.com/u/LoftyGoals)\
**Post date:** [August 16, 2024, 7:02pm UTC](https://forum.ansible.com/t/strategy-for-repeated-complex-logic-between-hosts/7858/9 "2024-08-16T19:02:13Z")

</div>

Oh, interesting, so inadvertently back to the thread then we’ve got:

- facts: not in hostvars, eager evaluation, does not delegate unless requested, changeable
- variables: in hostvars, lazy evaluation, delegates, changeable
- cached facts: actually a fact + a variable and cached between execution, changeable

So while I’ve been trying to choose between facts and variables based on whether it “changes” or not, in reality they can all change and in practice the choice might be driven by functional requirements instead.

Do I have that right?

---

<div class="post-metadata">

**Author:** ![bcoca](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/bcoca/32/9269_2.png) [@bcoca](https://forum.ansible.com/u/bcoca)\
**Post date:** [August 16, 2024, 7:14pm UTC](https://forum.ansible.com/t/strategy-for-repeated-complex-logic-between-hosts/7858/10 "2024-08-16T19:14:49Z")

</div>

no,

Variables are lazy in general, they get ‘resolved’ or made static on consumption in a task. `set_fact` happens to be a task and resolves the variables/templates and creates a static value, but the nature of the variable is still ‘lazy’ … it just does not have a template to resolve.

> - facts: not in hostvars, eager evaluation, changeable

Facts from `set_facts` are in hostvars, not to be confused with facts that go in `ansible_facts`

> - cached facts: actually a fact + a variable and cached between execution, changeable

Facts are only cached between executions if you configured a persistent cache plugin

Use `set_fact` when a)you need a host scoped variable or b) you need to set a static value to a variable (i.e` set_fact: now={{ q('pipe', 'date')}}`) , even you you dont want it host scoped (normally delegatae\_to/delegate\_facts to localhost as it is always ‘present’ host)
