Lazy evaluation of “composed” default variable in dependent Ansible role

ansible

I have two Ansible roles, let's call them postgres and myservice.

In my postgres role, I define a few default variables (in postgres/defaults/main.yml):

postgres_db: pgdb
postgres_user: postgres
postgres_systemd_docker_service_name: postgres_{{postgres_db}}

In my postgres role, I create a systemd service unit file from the postgres_systemd_docker_service_name variable. The idea here being that if I want to run more than one postgres docker containers for multiple services, I can distinguish them using the postgres_db variable.

Then I have the myservice role, in which I depend on the postgres role and override some of the default variables.

In my myservice/meta/main.yml:

dependencies:
  - {role: docker}
  - role: postgres
    postgres_user: "myservice_user"
    postgres_password: "myservice_password"
    postgres_db: "myservice"

However, when I run a playbook that uses the myservice role, it will create my systemd unit files as postgres_pgdb (which is the default value) rather than as postgres_myservice.

If I use the postgres_systemd_docker_service_name variable in my myservice role, it gets evaluated as postgres_myservice.

Is there any way for me to ensure that the composite variable in my postgres role is evaluated in such a way that my myservice role can override the default values?

Best Answer

Trying a small example, I found that (using Ansible 2.4) a composed default variable (i.e on that is in the defaults/main.yml) will only be interpolated when that default value is used for the first time.

Example, using only a single role:

roles/certs/defaults/main.yml

---
certs_dir: /etc/pki/tls/certs
cert_files:
  - "{{ ssl_domain }}.crt"
  - "{{ ssl_domain }}.key"
  - "{{ ssl_domain }}_ca-bundle.crt"

roles/certs/tasks/main.yml

---
- name: Determine the subdomain for the current host
  command: hostname --domain
  register: domain_result

- name: Set domain name as fact
  set_fact:
    ssl_domain: "{{ domain_result.stdout.strip() }}"

- name: Download certificates
  get_url:
    url: "http://{{ intranet_certificate_host }}/{{ intranet_certificate_path }}/_.{{ ssl_domain }}/{{ item }}"
    dest: "{{ certs_dir }}"
  with_items: "{{ cert_files }}"

This will work. I see this is not involving a "dependent" role, but as facts work like that across roles, I am nigh certain this will work across roles just as well. So, at least in the version that I used, lazy evaluation is the default behaviour, you only need to combine it with set_fact to provide the value that goes inside the composed default.