Introduction to Ansible prompts and runtime variables

This tutorial is part of a series we dedicated to Ansible. Previously we talked about the Ansible basics, then we focused on some Ansible modules we can use to perform some very common administration tasks, and we also talked about Ansible loops. In this article, instead, we learn how to create interactive prompts we can use to ask for user input and how to pass variables at runtime.

In this tutorial you will learn:

  • When to use interactive prompts
  • How to use the var_prompt section inside a playbook
  • How to pass variables at runtime
Introduction to Ansible prompts and runtime variables

Software requirements and conventions used

Software Requirements and Linux Command Line Conventions
Category Requirements, Conventions or Software Version Used
System Distribution-independent
Software Ansible
Other None
Conventions # – requires given linux-commands to be executed with root privileges either directly as a root user or by use of sudo command
$ – requires given linux-commands to be executed as a regular non-privileged user

Why prompting for input?

Ansible is an automation and provisioning tool we can use to obtain specific configurations on the machines that are part of our inventory. As we discussed in previous articles, we specify tasks which should be performed inside playbooks which are defined using yaml syntax. To obtain total automation, we usually don’t want our tasks to be interactive; there are some cases, however, in which we need ask a user for a certain input. Imagine, for example, we are defining a task to create a new user. Inside a playbook, our task would pretty much look this way:

- hosts: all
  become: yes
  tasks:
    - name: Make sure user exists
      ansible.builtin.user:
        name: foo
        password: 6$IRSnSBpBZ34SVP02$9zeDzbnrUW9PRluYYSnQJR5M53s7l6oVuODjxxDkd0x8GdGMfrPytBAILTqUdDpPADhCh.bmM6xP5gIIOW1ed/




In the task above we declared that the user foo should exist on the remote machines. What interest us the most, however, is the password argument. As we know, it can be used to specify the user password in hashed form. Putting a password statically in a playbook, however, is not recommended. This is a typical case in which we could take advantage of Ansible interactive prompts. What we can do is ask the password that should be used for the user interactively, hash it, and assign the resulting value to a variable which we will use in the task. Let’s see how we can do this.

The vars_prompt section

To create a prompt and interactively ask for information when a playbook is executed, all we have to do is to create a new section called vars_prompt. Let’s see a little example, and then discuss it:

- hosts: all
  vars_prompt:
    - name: username
      prompt: Please provide the username

The vars_prompt argument takes a list as a value. We can define the variables we need as elements of this list. In this case we defined only one. With the name argument, we stated its name, which in this case is “username”. With the prompt argument, instead, we defined the content of the prompt which will be created when the playbook is executed:

Please provide the username:

The value provided as answer to the prompt is assigned to the username variable, which we will be able to use in playbook tasks. If the user doesn’t provide a value, the variable will be empty. We can, however, use the default argument to provide a fallback value. In this case the default name for the user will be “foo”:

- hosts: all
  vars_prompt:
    - name: username
      prompt: Please provide username
      default: foo

By default, what is typed in the prompt is not visualized: this is a security measure, which in this case can be avoided. This behavior is controlled via the private parameter. Its value is “yes” by default; all we should do is change it to “no”:

- hosts: all
  vars_prompt:
    - name: username
      prompt: Please provide username
      default: foo
      private: no

As we already said, once the variable is defined, it can be used in the playbook tasks:

- hosts: localhost
  become: yes

  vars_prompt:
    - name: username
      prompt: Please provide the username
      default: foo
      private: no

  tasks:
    - name: Make sure user exists
      ansible.builtin.user:
        name: '{{ username }}'

We learned how to create interactive prompts and assign provided input to a variable. What we did above, however, is not enough if we are working with password, since there are somethings missing: first it would be useful to prompt for password confirmation, then we should hash the provided password, so it can be used in the user creation task, as the value of the password parameter. We will see how to do it in the next section.

Prompting the user for a password

The first thing to do when prompting for a password is to make sure that what is typed in the prompt is not visible. We already talked about this: it’s enough to assign the “yes” value to the private parameter (since it is the default, we can omit it the parameter altogether).




We also want to prompt for password confirmation and hash the provided password. Here is how we could do it:
- hosts: localhost
  become: yes

  vars_prompt:
    - name: username
      prompt: Please provide the username
      default: foo
      private: no

    - name: password
      prompt: password
      encrypt: sha512_crypt
      confirm: yes

We used two new parameters: encrypt and confirm. With the former we specify how the password should be hashed. By default, Ansible makes use of the Python “passlib” library to perform the hashing. The library supports the following algorithms:

  • des_crypt
  • bsdi_crypt
  • bigcrypt
  • crypt16
  • md5_crypt
  • bcrypt
  • sha1_crypt
  • sun_md5_crypt
  • sha256_crypt
  • sha512_crypt
  • apr_md5_crypt
  • phpass
  • pbkdf2_digest
  • cta_pbkdf2_sha1
  • dlitz_pbkdf2_sha1
  • scram
  • bsd_nthash

If the “passlib” library is not installed, the “crypt” module is used as fallback. In that case, the choice of available algorithms depends on the platform. Generally, the following hashing methods are supported:

  • bcrypt
  • md5_crypt
  • sha256_crypt
  • sha512_crypt

The password salt is generated randomly, but we can provide our own, if so we desire, via the salt parameter. Once the playbook is executed, the following prompts are generated:

password:
confirm password:


Passing variable at runtime

As an alternative to the use of interactive prompts, we can pass variables and their values at runtime using the --extra-vars option from the command line. There are two types of syntax we can use: the first one consists into providing the variables and their values as a single quoted string:

$ ansible-playbook playbook.yml --extra-vars "var1=value var2=value"

Alternatively we can use the JSON syntax:

$ ansible-playbook playbook.yml --extra-vars '{ "var1": "value", "var2": "value" }'

As a third alternative, we could just pass the path of a file containing the defined variable with the JSON syntax as argument to --extra-vars. Supposing the file is called variables.json, we would run:

$ ansible-playbook playbook.yml --extra-vars "@variables.json"

What happens if we pass a variable and its value at runtime, but we also defined it in the playbook vars_prompt section? In that case the prompt is skipped: the value passed at runtime has precedence.

Passing password at runtime is not recommended, since they would be part of the executed command which would appear in the process list generated with the ps command, but also as part of the shell history.

Conclusions

In this tutorial we learned how to define variables using interactive prompts with Ansible, or pass them at runtime using the --extra-vars command line option. We saw some typical examples and specifically how to work with passwords: how to prompt for their confirmation and how to hash them.



Comments and Discussions