Search code examples
pythonansiblejinja2

How to create a dynamic selection list in Ansible


I am attempting to create an installation ansible playbook that automates the installation of many different installers. I am able to figure out the installation of these programs just fine, but I'm having a tough time creating a selector to select which installers to use.

In a previous playbook I was able to find a trick by using a vars_prompt which then creates a variable with a value that you type in which looks like this:

vars_prompt:
  - name: "san_location"
    prompt: "\nWhat SAN(s) do you want to mount?\n1- SAN1\n2- SAN2\n3- Both\n"
    private: no

tasks:
  - name: Add SAN1 MDC1 to host file
    lineinfile:
      state: present
      path: '/etc/hosts'
      line: 192.168.x.x   SAN1
    when: ( san_location == "1" ) or
          ( san_location == "3" )

This allows you to select a 1, 2, or 3 and execute tons of tasks based upon a user input/selection. Now I am trying to do this but by taking a playbook that has a "find" module in it that looks for all of the installers in a directory and lists them to be selected to install.

Here is the find task:

tasks:
  - name: Register all files
    find:
      paths: "{{ installer_path }}"
      patterns: '*.pkg,*.dmg'
      file_type: file
    register: installers

Is there any built in way in Ansible to make a list of these files selectable and then execute tasks based upon that?


Solution

  • For example, given the directory

    shell> tree installers/
    installers/
    ├── A
    ├── B
    └── C
    

    The playbook below

    shell> cat pb.yml
    - hosts: localhost
    
      tasks:
    
        - pause:
            prompt: "Select installer {{ item|basename }}? [y/n]"
          register: out
          loop: "{{ query('fileglob', 'installers/*')|sort }}"
          loop_control:
            label: "{{ item|basename }}"
    
        - debug:
            msg: "install {{ item.item|basename }}"
          loop: "{{ out.results }}"
          loop_control:
            label: "{{ item.item|basename }}"
          when: item.user_input == 'y'
    

    select the first two installers (A, B) and skip the last one (C)

    shell> ansible-playbook pb.yml 
    
    PLAY [localhost] *****************************************************************************
    
    TASK [pause] *********************************************************************************
    [pause]
    Select installer A? [y/n]:
    y^Mok: [localhost] => (item=A)
    [pause]
    Select installer B? [y/n]:
    y^Mok: [localhost] => (item=B)
    [pause]
    Select installer C? [y/n]:
    n^Mok: [localhost] => (item=C)
    
    TASK [debug] *********************************************************************************
    ok: [localhost] => (item=A) => 
      msg: install A
    ok: [localhost] => (item=B) => 
      msg: install B
    skipping: [localhost] => (item=C) 
    
    PLAY RECAP ***********************************************************************************
    localhost: ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    

    If you want to use the module find the playbook below gives the same results

    - hosts: localhost
    
      tasks:
    
        - find:
            paths: "{{ playbook_dir }}/installers"
            recurse: true
          register: installers
    
        - pause:
            prompt: "Select installer {{ item|basename }}? [y/n]"
          register: out
          loop: "{{ installers.files|map(attribute='path')|sort }}"
          loop_control:
            label: "{{ item|basename }}"
    
        - debug:
            msg: "install {{ item.item|basename }}"
          loop: "{{ out.results }}"
          loop_control:
            label: "{{ item.item|basename }}"
          when: item.user_input == 'y'
    

    The next option is to select a single installer

    - hosts: localhost
    
      vars:
    
        installers: "{{ _find.files|map(attribute='path')|sort }}"
    
      tasks:
    
        - find:
            paths: "{{ playbook_dir }}/installers"
            recurse: true
          register: _find
    
        - pause:
            prompt: |
              Select installer [{{ range(1, installers|length + 1)|join(',') }}]
              {% for i in installers %}
              {{ loop.index }} ... {{ i|basename }}
              {% endfor %}
          register: out
    
        - debug:
            msg: "install {{ installers[out.user_input|int - 1]|basename }}"