Calls to Ansible lookup plugins are lazily evaluated, meaning the resolution of the value returned from the lookup isn’t performed until the playbook is running the task in which the plugin result is referenced.

If running your lookup plugin is slow, or it returns multiple values that are only valid in that particular invocation (think of a unique username and password pair returned by calling the lookup each time), you can run into some real problems with this behavior.

For example, my lookup plugin joeslookup will be called twice in this task:

  - debug:
      msg: "{{ my_creds['username'] }} {{ my_creds['password'] }}"
    vars:
      my_creds: "{{ query('joeslookup') }}"

And if the lookup results are different each time, you’ll get the username for one credential and the password for another!

I’m not the first one to notice this behavior, and people have complained about it in issues like this. From my research it seems the community has converged around the use of this lookup caching plugin as a work-around.

However, I found it was pretty easy to implement this caching with a lighter touch and without writing anything to disk. Instead you can use the task scoped variables made available to you in the LookupBase run method:

class LookupModule(LookupBase):

  def run(self, terms, variables=None, **kwargs):
    key = "my_creds"
    try:
      result = variables[key]
    except KeyError:
      # ... do lookup heavy lifting remembering to include this line at the end: 
      variables[key] = result

The great part of this solution is this variable is scoped to the single task evaluation, so you don’t end up leaking this value to other tasks.