pass a list/array of strings from my bash script, to an ansible script

Viewed 435

I am trying to pass a list/array of strings from my bash script, to my ansible script:

Excerpt from bash script:

configureChrony() {
  ntpServer="initVal"
  ntpServers=()
  while ! [[ -z "$ntpServer" ]]; do
    read -e -p "Please input the ip address or domain name of the TP server you wish to add: " ntpServer
    if ! [[ -z "$ntpServer" ]]; then
      ntpServers+=($ntpServer)
    fi
  done

  ansible-playbook -i localhost, test.yml --extra-vars="ntp_list = $ntpServers"

}

test.yml

---


- name: "This is a test"
  hosts: all
  gather_facts: no
  tasks:

    - name: print variable - with_items
      debug:
        msg: "{{ item.name }}"
      with_items:
      - "{{ ntp_list }}"

When testing the bash script, I get this error:

Which timekeeping service would you like to use [ntp/chrony]: chrony
Please input the ip address or domain name of the TP server you wish to add: Test1
Please input the ip address or domain name of the TP server you wish to add: Test2
Please input the ip address or domain name of the TP server you wish to add:

PLAY [This is a test] ****************************************************************************************************************************************

TASK [print variable - with_items] ***************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "'ntp_list' is undefined"}

PLAY RECAP ***************************************************************************************************************************************************
localhost                  : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

The issue is the way I am passing the list/array from the bash script to ansible script as they both run given the required data.

The desired output is for each element of the list to be outputted to the screen.

Test1
Test2

Any help appreciated.

1 Answers

Direct answer

You cannot really pass the bash array as a list inside an extra var to ansible. It is possible but would require to loop over the bash array and transform it into a json parsable format that you inject inside the extra var.

The easiest way IMO is to pass a concatenation of all array element inside a string that you will later split in the playbook.

Using the form ${somevar[@]} won't work here as every bash array element will end up being parsed as a new argument to your ansible command. You will have to use instead ${somevar[*]}. You also need to quote the extra var correctly so that both bash and ansible are able to successfully parse it. The correct command call in your script is then:

ansible-playbook test.yml --extra-vars "ntp_list_raw='${ntpServers[*]}'"

You now need a bit of rework on your ansible playbook to split the received value into a list:

---
- name: "This is a test"
  hosts: localhost
  gather_facts: no

  vars:
    ntp_list: "{{ ntp_list_raw.split(' ') }}"

  tasks:

    - name: Print split variable items
      debug:
        var: item
      loop: "{{ ntp_list }}"

And now entering values and calling the playbook from your script gives:

Please input the ip address or domain name of the TP server you wish to add: toto
Please input the ip address or domain name of the TP server you wish to add: pipo
Please input the ip address or domain name of the TP server you wish to add: bingo
Please input the ip address or domain name of the TP server you wish to add: 

PLAY [This is a test] *********************************************************************

TASK [Print split variable items] *********************************************************************
ok: [localhost] => (item=toto) => {
    "ansible_loop_var": "item",
    "item": "toto"
}
ok: [localhost] => (item=pipo) => {
    "ansible_loop_var": "item",
    "item": "pipo"
}
ok: [localhost] => (item=bingo) => {
    "ansible_loop_var": "item",
    "item": "bingo"
}

PLAY RECAP *********************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

Going further

If your only goal is to ask for your ntp servers interactively, you can do all of the above directly in your playbook using vars_prompt

---
- name: "This is a test"
  hosts: localhost
  gather_facts: no

  vars_prompt:
    - name: ntp_list_raw
      prompt: "Please enter ntp servers separated by a comma without spaces"
      private: no

  vars:
    ntp_list: "{{ ntp_list_raw.split(',') }}"

  tasks:

    - name: Print split variable
      debug:
        var: item
      loop: "{{ ntp_list }}"

which gives:

$ ansible-playbook test.yaml 
Please enter ntp servers separated by a comma without spaces []: toto,pipo,bingo

PLAY [This is a test] *********************************************************************

TASK [Print split variable] *********************************************************************
ok: [localhost] => (item=toto) => {
    "ansible_loop_var": "item",
    "item": "toto"
}
ok: [localhost] => (item=pipo) => {
    "ansible_loop_var": "item",
    "item": "pipo"
}
ok: [localhost] => (item=bingo) => {
    "ansible_loop_var": "item",
    "item": "bingo"
}

PLAY RECAP *********************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

You can even bypass the prompt giving the value as an extra var directly:

$ ansible-playbook test.yaml -e ntp_list_raw=toto,pipo,bingo

PLAY [This is a test] *********************************************************************

TASK [Print split variable] *********************************************************************
ok: [localhost] => (item=toto) => {
    "ansible_loop_var": "item",
    "item": "toto"
}
ok: [localhost] => (item=pipo) => {
    "ansible_loop_var": "item",
    "item": "pipo"
}
ok: [localhost] => (item=bingo) => {
    "ansible_loop_var": "item",
    "item": "bingo"
}

PLAY RECAP *********************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
Related