3

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.

5
  • 2
    ntpServers is an array. Use ${ntpServers[@]} to expand it as a list of words. Commented Nov 18, 2021 at 15:26
  • So ansible-playbook -i localhost, test.yml --extra-vars="ntp_list = ${ntpServers[@]}"? @Renauld Pacalet Commented Nov 18, 2021 at 15:34
  • Yes, that's how arrays must be expanded in bash. Commented Nov 18, 2021 at 15:38
  • @RenaudPacalet I now have this error: ansible-playbook: error: unrecognized arguments: Test2 Commented Nov 18, 2021 at 15:40
  • If you were typing the ansible command manually without bash parameters, what would it look like (especially the --extra-vars="ntp_list = ..." part)? Please do not answer in a comment, add this to your question. And test that it works before that. Then we will be able to suggest a bash syntax that does exactly this, without knowing how ansible works. Commented Nov 18, 2021 at 15:43

1 Answer 1

1

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
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you, I will be using your direct solution as it's more clear for the user. I would have thought this would have been possible with ansible but I guess not.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.