docs: def _varFind(basedir, text, vars, lookup_fatal, depth=0):
      ''' Searches for a variable in text and finds its replacement in vars
  
      The variables can have two formats;
      - simple, $ followed by alphanumerics and/or underscores
      - complex, ${ followed by alphanumerics, underscores, periods, braces and brackets, ended by a }
  
      Examples:
      - $variable: simple variable that will have vars['variable'] as its replacement
      - ${variable.complex}: complex variable that will have vars['variable']['complex'] as its replacement
      - $variable.complex: simple variable, identical to the first, .complex ignored
  
      Complex variables are broken into parts by separating on periods, except if enclosed in {}.
      ${variable.{fully.qualified.domain}} would be parsed as two parts, variable and fully.qualified.domain,
      whereas ${variable.fully.qualified.domain} would be parsed as four parts.
  
      Returns a dict(replacement=<value in vars>, start=<index into text where the variable stated>,
          end=<index into text where the variable ends>)
      or None if no variable could be found in text. If replacement is None, it should be replaced with the
      original data in the caller.
      '''
  
      start = text.find("$")
      if start == -1:
          return None
      # $ as last character
      if start + 1 == len(text):
          return None
      # Escaped var
      if start > 0 and text[start - 1] == '\\':
          return {'replacement': '$', 'start': start - 1, 'end': start + 1}
  
      var_start = start + 1
      if text[var_start] == '{':
          is_complex = True
          brace_level = 1
          var_start += 1
      else:
          is_complex = False
          brace_level = 1
      # is_lookup is true for $FILE(...) and friends
      is_lookup = False
      lookup_plugin_name = None
      end = var_start
      # part_start is an index of where the current part started
      part_start = var_start
      space = vars
      while end < len(text) and (((is_lookup or is_complex) and brace_level > 0) or (not is_complex and not is_lookup)):
          if text[end].isalnum() or text[end] == '_':
              pass
          elif not is_complex and not is_lookup and text[end] == '(' and text[part_start:end].isupper():
              is_lookup = True
              lookup_plugin_name = text[part_start:end]
              part_start = end + 1
          elif is_lookup and text[end] == '(':
              brace_level += 1
          elif is_lookup and text[end] == ')':
              brace_level -= 1
          elif is_lookup:
              # lookups are allowed arbitrary contents
              pass
          elif is_complex and text[end] == '{':
              brace_level += 1
          elif is_complex and text[end] == '}':
              brace_level -= 1
          elif is_complex and text[end] in ('$', '[', ']'):
              pass
          elif is_complex and text[end] == '.':
              if brace_level == 1:
                  space = _varFindLimitSpace(basedir, vars, space, text[part_start:end], lookup_fatal, depth)
                  part_start = end + 1
          else:
              # This breaks out of the loop on non-variable name characters
              break
          end += 1
      var_end = end
      # Handle "This has $ in it"
      if var_end == part_start:
          return {'replacement': None, 'start': start, 'end': end}
  
      # Handle lookup plugins
      if is_lookup:
          # When basedir is None, handle lookup plugins later
          if basedir is None:
              return {'replacement': None, 'start': start, 'end': end}
          var_end -= 1
      	from cirruscluster.ext.ansible import utils
          args = text[part_start:var_end]
          if lookup_plugin_name == 'LOOKUP':
              lookup_plugin_name, args = args.split(",", 1)
              args = args.strip()
          # args have to be templated
          args = varReplace(basedir, args, vars, depth=depth+1, expand_lists=True)
          instance = utils.plugins.lookup_loader.get(lookup_plugin_name.lower(), basedir=basedir)
          if instance is not None:
              try:
                  replacement = instance.run(args, inject=vars)
              except errors.AnsibleError:
                  if not lookup_fatal:
                      replacement = None
                  else:
                      raise
          else:
              replacement = None
          return {'replacement': replacement, 'start': start, 'end': end}
  
      if is_complex:
          var_end -= 1
          if text[var_end] != '}' or brace_level != 0:
              return None
      space = _varFindLimitSpace(basedir, vars, space, text[part_start:var_end], lookup_fatal, depth)
      return {'replacement': space, 'start': start, 'end': end}