5

I am writing roles for an ansible playbook. In the role I have a defaults folder containing a main.yml to define default values for the variables the role uses. Can I cross-reference within the same object?

For example, I have tried the following:

scripts_config: 
  host_entry:
    dir: "/foo" 
    file: "{{dir}}/config"
    foo: "{{host_entry.dir}}/foo"
    bar: "{{scripts_config.host_entry.dir}}/bar"

None of them work. I tried each of file, foo and bar one at a time:

  • file gave The task includes an option with an undefined variable. The error was: 'dir' is undefined
  • foo gave The task includes an option with an undefined variable. The error was: 'host_entry' is undefined
  • bar gave An unhandled exception occurred while templating

Is it even possible?

My use case is that I want an object with a directory and several files which would be relative to that in the default set-up. But if you override the defaults you could specify full paths for one or more of the files, so i don't want the task to assume it's relative.

0

3 Answers 3

2

What I normally do when I want something of this sort:

/parent_dir/
├── dirA/
├── dirB/
├── dirC/
└── dirD/

Is addding the following to the role defaults

parent_dir: /parent_dir/

And then this to the vars

other_dir_a: "{{ parent_dir }}/dirA"
other_dir_b: "{{ parent_dir }}/dirB"
other_dir_c: "{{ parent_dir }}/dirC"
other_dir_d: "{{ parent_dir }}/dirD"

That way your "other" directories/files... will be relative to {{ parent_dir }}.

Is this similar to what you want to do?

Update based on a comment below

All of these variables are more "important" than role vars:

https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html

  • block vars (only for tasks in block)
  • task vars (only for the task)
  • include_vars
  • set_facts / registered vars
  • role (and include_role) params
  • include params
  • extra vars (always win precedence)

You could use any of them to replace. For example, using extra vars.

ansible-playbook -i inventory playbook.yml -e other_dir_a=/new/path/for/a -e other_dir_b=/new/path/for/b

If you want to use them all as defaults you could also just have all the variables under defaults. They are at the lowest level of precedence so they could be replaced by any of the other variables.

Sign up to request clarification or add additional context in comments.

5 Comments

Could somebody tell me what's wrong with my answer? If it's wrong I'd like to know why so I stop doing it.....
I wasn't the downvoter, so don't want to speak on their behalf, but maybe because it doesn't really answer the question. It's a fine pattern and a valid approach. But my case was I wanted default values for files where the defaults have a common parent but each/any file variable can be overridden when calling the playbook independently, with a full path. So they might not be in the same parent folder if overridden
Updated my answer. Hopefully it helps.
OK, so I can do it if i make them separate variables, just not if it's a single variable which is an object? I'll try that.
Was it helpful at all?
2

It is not necessary to self reference a variable to set a default value. Just use the default filter.

---
- hosts: localhost
  vars:
    file: "{{ dir | default ('/foo') }}/config" 
  tasks:
    - debug: var=file

You can override the default:

$ ansible-playbook default.yml -e dir=/changed
PLAY [localhost] ***************************************************************

TASK [setup] *******************************************************************
ok: [localhost]

TASK [debug] *******************************************************************
ok: [localhost] => {
    "file": "/changed/config"
}

PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0   

Or use the default value:

$ ansible-playbook default.yml 
PLAY [localhost] ***************************************************************

TASK [setup] *******************************************************************
ok: [localhost]

TASK [debug] *******************************************************************
ok: [localhost] => {
    "file": "/foo/config"
}

PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0   

You can also use set_fact. You can change either the base directory for all files or the complete path for each individual file:

---
- hosts: localhost
  tasks:
    - set_fact: base="/foo"
      when: base is undefined
    - set_fact: file1={{base}}/file1
      when: file1 is undefined
    - set_fact: file2={{base}}/file2
      when: file2 is undefined
    - debug: var=file1
    - debug: var=file2

1 Comment

Yes, i know you can use default value for the directory. Point is what I want is actually a set of variables for files, where the default value for the files are all relative to a common parent, but each file path can be overwritten in it's entirety. I want defaults for the files. Will just repeat the full path in the defaults, it's not that many
0

You can do it with YAML anchors and references:

scripts_config: 
  host_entry:
    dir: &basedir "/foo"
    bar: "{{ ref.basedir }}/bar"

ref:
  basedir: *basedir

With &basedir you create an anchor and with *basedir you reference it.


An alternative would be to define the value directly somewhere else:

scripts_config: 
  host_entry:
    dir: "{{ ref.basedir }}"
    bar: "{{ ref.basedir }}/bar"

ref:
  basedir: "/foo"

2 Comments

Yeah, but those are not within the variable itself. The ref is a separate variable from the scripts_config
Yes, my solution is a workaround. There simply is no way to use {{ scripts_config.host_entry.dir }} directly in scripts_config.host_entry.bar. By using YAML anchors and references, it is easy to understand and the value itself is still at its original position. But the downside of having something like ref is a valid point.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.