How to set default values for empty vars_prompt variables in Ansible

Written by - 0 comments

Published on - last updated on April 27th 2024 - Listed in Ansible Coding


For manually creating containers on a specific host I wrote an Ansible playbook prompting the user for some container information when the playbook is run.

Ansible

Here you can see a snippet from it, prompting for the container_name and container_distro input:

  vars_prompt:
    - name: "container_name"
      private: no
      prompt: "Enter name of container"
    - name: "container_distro"
      private: no
      prompt: "Which Linux distro to use? default: debian"

The user has the option to enter the Linux distribution to use. If the user leaves that option empty (which results in an empty string), the value for container_distro should be set to "debian". There are several ways how to achieve this.

setting "default" at the vars_prompt (works)

Note: I added this way one day after I initially wrote the article. I got that hint after sharing my blog post on Mastodon. Interestingly this is by far the easiest and fastest method. I can't believe I missed that from the documentation

The vars_prompt also allow to set a default value, in case the user does not enter a value:

  vars_prompt:
    - name: "container_name"
      private: no
      prompt: "Enter name of container"
    - name: "container_distro"
      private: no
      prompt: "Which Linux distro to use? default: debian"
      default: debian

This is obviously the most straight forward and fastest solution.

To verify this works, I added a debug output right below it:

  - name: Debug - show settings
    debug:
      msg: "Container Name: {{ container_name }} /// Distro: {{ container_distro }}"

When running the playbook this results in:

ck@ansible:~$ ansible-playbook /tmp/containersetup.yaml
Enter name of container: rafiki
Which Linux distro to use? default: debian [debian]: [Enter]

TASK [Debug - show settings] ****************************************
ok: [target] => {
    "msg": "Container Name: rafiki /// Distro: debian"
}

The disadvantage with this method is that using inventory variables as default value does not seem to work. For example if you want to set a default value retrieved from the hostvars, you will run into an error:

  vars_prompt:
    - name: "container_name"
      private: no
      prompt: "Enter name of container"
    - name: "container_distro"
      private: no
      prompt: "Which Linux distro to use? default: debian"
      default: "{{ hostvars[container_name]['os']['distro'] }}"

... the playbook will return an error:

ck@ansible:~$ ansible-playbook /tmp/containersetup.yaml
ERROR! 'hostvars' is undefined

If I understand correctly this is because the playbook runs vars_prompt before gathering facts.

set_fact using when condition (works)

A set_fact can be used in combination with a when condition:

  - name: FACT SET - Default container_distro
    set_fact:
      container_distro: "debian"
    when: container_distro is defined and container_distro == ""

The set_fact task is executed if the condition is matched. The condition in this situation checks whether the variable "container_distro" is defined (as this variable is part of the prompt, it is defined) and is an empty string. If the user does not input anything in the prompt, the variable's value will be an empty string ("").

And by running the playbook, the default can be verified:

ck@ansible:~$ ansible-playbook /tmp/containersetup.yaml
Enter name of container: simba
Which Linux distro to use? default: debian: [Enter]

TASK [Debug - show settings] ****************************************
ok: [target] => {
    "msg": "Container Name: simba /// Distro: debian"
}

set_fact using jinja2 default() (not working)

As an alternative I was also looking at using the jinja2 default() function instead of using a when condition:

  - name: FACT SET - Default container_distro
    set_fact:
      container_distro: "{{ container_distro | default('debian') }}"

However this did not work as container_distro is a defined variable and the default() function would only work if the container_distro variable would not exist.

ck@ansible:~$ ansible-playbook /tmp/containersetup.yaml
Enter name of container: nala
Which Linux distro to use? default: debian: [Enter]

TASK [Debug - show settings] ****************************************
ok: [target] => {
    "msg": "Container Name: nala /// Distro: "
}

To proof that I used a non-existant variable name "distro":

  - name: FACT SET - Default container_distro
    set_fact:
      container_distro: "{{ distro | default('debian') }}"

And in this case the default() function is used:

ck@ansible:~$ ansible-playbook /tmp/containersetup.yaml
Enter name of container: pumbaa
Which Linux distro to use? default: debian: [Enter]

TASK [Debug - show settings] ****************************************
ok: [target] => {
    "msg": "Container Name: pumbaa /// Distro: debian"
}

set_fact using jinja2 if condition (works)

As seen above, the problem is that Ansible considers the empty string "" as a valid value. However we can also use jinja2 if/else conditions inside a playbook. 

The following set_fact task uses such a condition.

  - name: FACT SET - Default container_distro
    set_fact:
      container_distro: "{% if not container_distro %}debian{%else %}{{ container_distro }}{% endif %}"

Note the difference of curly brackets:

  • {{ }} is the jinja2 syntax for print
  • {% %} is the jinja2 syntax for programming jinja2, such as if conditions or setting variables

Let's try this:

ck@ansible:~$ ansible-playbook /tmp/containersetup.yaml
Enter name of container: mufasa
Which Linux distro to use? default: debian: [Enter]

TASK [Debug - show settings] ****************************************
ok: [target] => {
    "msg": "Container Name: mufasa /// Distro: debian"
}

It worked! Interestingly the jinja2 if condition handles the empty string differently than Ansible's when condition; the variable is considered empty and therefore not-existant. Meaning:

Jinja2 if condition != Ansible when condition
{% if not container_distro %} != when: container_distro is defined

Without set_fact, use jinja2 if condition when using the variable (works)

With the knowledge from above's example (jinja2 if condition), is there even a need to use a set_fact task? Let's put it to the test and completely remove the set_fact task from the playbook and instead use the jinja2 if condition when the variable is used in the task:

  - name: Debug - show settings
    debug:
      msg: "Container Name: {{ container_name }} /// Distro: {% if not container_distro %}debian{% else %}{{ container_distro }}{% endif %}"

The debug task was adjusted accordingly and now contains the jinja2 if condition. Running the playbook reveals:

ck@ansible:~$ ansible-playbook /tmp/containersetup.yaml
Enter name of container: scar
Which Linux distro to use? default: debian: [Enter]

TASK [Debug - show settings] ****************************************
ok: [target] => {
    "msg": "Container Name: scar /// Distro: debian"
}

This works, too! Awesome!

Playing around with jinja2 conditions

While working on this playbook, I learned how to use jinja2 conditions directly in the playbook. In the past I've always dealt with set_fact and when conditions - but now I'd write a lot of playbooks differently.

The Jinja2 live parser was a great help to test conditions and I can greatly recommend it before wasting time running a playbook and potentially run into a syntax error.

Here two screenshots of the same logic I used in the playbook.

The first example shows an empty string for the container_distro variable:

And in the second example a value "ubuntu" was set:

jinja2 live parser showing value from variable


Add a comment

Show form to leave a comment

Comments (newest first)

No comments yet.

RSS feed

Blog Tags:

  AWS   Android   Ansible   Apache   Apple   Atlassian   BSD   Backup   Bash   Bluecoat   CMS   Chef   Cloud   Coding   Consul   Containers   CouchDB   DB   DNS   Database   Databases   Docker   ELK   Elasticsearch   Filebeat   FreeBSD   Galera   Git   GlusterFS   Grafana   Graphics   HAProxy   HTML   Hacks   Hardware   Icinga   Influx   Internet   Java   KVM   Kibana   Kodi   Kubernetes   LVM   LXC   Linux   Logstash   Mac   Macintosh   Mail   MariaDB   Minio   MongoDB   Monitoring   Multimedia   MySQL   NFS   Nagios   Network   Nginx   OSSEC   OTRS   Office   PGSQL   PHP   Perl   Personal   PostgreSQL   Postgres   PowerDNS   Proxmox   Proxy   Python   Rancher   Rant   Redis   Roundcube   SSL   Samba   Seafile   Security   Shell   SmartOS   Solaris   Surveillance   Systemd   TLS   Tomcat   Ubuntu   Unix   VMWare   VMware   Varnish   Virtualization   Windows   Wireless   Wordpress   Wyse   ZFS   Zoneminder