I created a playbook which clears cache, I'm trying to pass a url to a variable, and when I execute the playbook I get an empty array for that parameter.
In my playbook I have a vars module with this variable (environment), it gets defined when you pass in a variable to the ansible-playbook command
vars:
environment: "{{testenv}}"
-e testenv=https://www.test1.com
When I execute the playbook I get this error.
Do I need to format the url in someway?
fatal: [localhost]: FAILED! => {"changed": false, "msg": "unknown url type: '[]/content/clear_cache?
Your issue is coming from the fact that environment is a reserved variable, as pointed in the second row of this table in the documentation:
Valid variable names
Not valid
foo
*foo, Python keywords such as async and lambda
foo_env
playbook keywords such as environment
foo_port
foo-port, foo port, foo.port
foo5, _foo
5foo, 12
Source: https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#creating-valid-variable-names
So, you just need to change your variable name to something else and it will work.
Given the playbook:
- hosts: all
gather_facts: no
tasks:
- debug:
msg: "{{ _environment }}"
vars:
_environment: "{{ testenv }}"
When run:
$ ansible-playbook play.yml -e testenv=https://www.test1.com
PLAY [all] **********************************************************************************************************
TASK [debug] ********************************************************************************************************
ok: [localhost] => {
"msg": "https://www.test1.com"
}
PLAY RECAP **********************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Related
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.
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
I have a play like this:
- name: Perform an action on a Runtime
hosts: all
roles:
- role: mule_action_on_Runtime
A variable at invocation (--extra-vars 'mule_runtime=MuleS01-3.7.3-Testing') has a prefix of the host needed (MuleS01). I want to set hosts: MuleS01. How do I do this?
Given that your pattern is always PartIWant-PartIDonCareAbout-AnotherPartAfterOtherDash you could use the split method of Python, then get the first item of the list via the Jinja filter first.
Here is full working playbook as example:
- hosts: local
gather_facts: no
tasks:
- debug:
msg: "{{ mule_runtime.split('-') | first }}"
This yield the recap:
play.yml --extra-vars 'mule_runtime=MuleS01-3.7.3-Testing'
PLAY [local] *******************************************************************
TASK [debug] *******************************************************************
ok: [local] => {
"msg": "MuleS01"
}
PLAY RECAP *********************************************************************
local : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
With the inventory
shell> cat hosts
MuleS01
MuleS02
MuleS03
this playbook
shell> cat pb.yml
- hosts: all
tasks:
- debug:
msg: Set {{ mule_runtime }}
when: mule_runtime.split('-').0 == inventory_hostname
gives
skipping: [MuleS02]
ok: [MuleS01] => {
"msg": "Set MuleS01-3.7.3-Testing"
}
skipping: [MuleS03]
I am trying to conditionally import a playbook by dynamically generating the name of the playbook to import.
I have the following 2 files:
root#ubuntu:~/test# ls -ltr
total 8
-rw-r--r-- 1 root root 117 sij 2 12:07 child_playbook_1.yaml
-rw-r--r-- 1 root root 210 sij 2 12:11 start.yaml
root#ubuntu:~/test#
start.yaml:
---
- name: main playbook
hosts: localhost
tasks:
- set_fact:
var: "child_playbook"
- debug:
msg: "{{ var }}"
- import_playbook: "{{ var + '_1.yaml' }}"
when: var is defined
child_playbook_1.yaml:
---
- name: child_playbook
hosts: localhost
tasks:
- debug:
msg: "Message from the child playbook"
When I execute start.yaml, I get this as output:
root#ubuntu:~/test# ansible-playbook start.yaml
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
ERROR! 'var' is undefined
root#ubuntu:~/test#
Why is var not seen and why do I get this message?
How to overcome this?
To put things into a bit more perspective, the 'var' variable in start.yaml is being dynamically read which means that the name of the playbook to be imported will also be dynamic.
var is a fact defined for localhost (i.e. the only host in the play) in the first play. So it does not exist "globally", only for the given host.
Moreover, imports are made statically at time of playbook parsing (by opposition to includes which are dynamic but to not exist for playbooks).
One could then try to use the var defined for localhost by referencing hostvars['localhost'].var but:
set_fact has not yet run since it is an import.
Moreover hostvars is not yet defined at time of import.
Lastly, you are miss-interpreting how a when clause is actually working. Basically, the condition is passed to all the tasks contained in your included/imported object. So the imported object must exist. If you use an undefined var to get its name, it will always fire an error.
The only solution I currently see to pass a variable playbook name is to use an extra var on the command line. If you want to be able to 'skip' the import when no variable is defined, you could default to an empty playbook.
Here is a dummy empty.yml playbook
---
- name: Dummy empty playbook
hosts: localhost
gather_facts: false
For my tests, I used the same child playbook as in your question except I disabled facts gathering not needed here:
---
- name: child_playbook
hosts: localhost
gather_facts: false
tasks:
- debug: msg="Message from the child playbook"
The new main playbook start.yml looks like this:
---
- name: Play written in start.yml directly
hosts: localhost
gather_facts: false
tasks:
- debug:
msg: "Message from start playbook"
- import_playbook: "{{ var is defined | ternary( var | default('') + '_1.yml', 'empty.yml') }}"
Note than even though we use var is defined as a condition, var will still be interpreted in the ternary filter in all cases (defined or not). So we have to use a default value to make sure we don't have an error when we don't pass a value.
We can now call the playbook with or without an extra var. Here is the result:
$ ansible-playbook start.yml
PLAY [Play written in start.yml directly] *************************************************************************************************************************************************************************
TASK [debug] ******************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "Message from main playbook"
}
PLAY [Dummy empty playbook] ***************************************************************************************************************************************************************************************
PLAY RECAP ********************************************************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
$ ansible-playbook start.yml -e var=child_playbook
PLAY [Play written in start.yml directly] *************************************************************************************************************************************************************************
TASK [debug] ******************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "Message from main playbook"
}
PLAY [child_playbook] *********************************************************************************************************************************************************************************************
TASK [debug] ******************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "Message from the child playbook"
}
PLAY RECAP ********************************************************************************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
I have a path which looks like this -
base_dir/123/path/to/G\$/subdirectory/html/
When I try to set this path in Ansible playbook, it throws error. If add \$ to escape '\', it throws unexpected failure error.
Playbkook -
- hosts: localhost
vars:
account_id: 123
tasks:
- name: Add \ to path
debug:
var: "base_dir/{{ account_id }}/path/to/G\\$/subdirectory/html/"
Result -
TASK [Gathering Facts] *************************************************************************************************************************************************
task path: /playbooks/example_path.yml:2
ok: [localhost]
META: ran handlers
TASK [Add \ to path] ***************************************************************************************************************************************************
task path: /playbooks/exmaple_path.yml:6
fatal: [localhost]: FAILED! => {
"msg": "Unexpected failure during module execution."
}
PLAY RECAP *************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=1
As explained in the debug module documentation, the var option is expecting a variable name, not a scalar for output. You are getting an error because \ is not expected in a variable name. Running the playbook with -vvv will give you a little more explanations.
In this case you need to use the msg option.
- hosts: localhost
gather_facts: false
vars:
account_id: 123
tasks:
- name: Add \ to path
debug:
msg: "base_dir/{{ account_id }}/path/to/G\\$/subdirectory/html/"
Result
PLAY [localhost] ***************************************************************
TASK [Add \ to path] ***********************************************************
ok: [localhost] => {
"msg": "base_dir/123/path/to/G\\$/subdirectory/html/"
}
PLAY RECAP *********************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The next option is to use the Single-Quoted Style. See the example below
- hosts: localhost
vars:
my_dir1: "/scratch/tmp/G1\\$"
my_dir2: '/scratch/tmp/G2\$'
tasks:
- file:
state: directory
path: "{{ item }}"
loop:
- "{{ my_dir1 }}"
- "{{ my_dir2 }}"
# ls -1 /scratch/tmp/
'G1\$'
'G2\$'
I there any way to pass/update group variables from within the playbook task?
I need to define variables based on results of some commands from one host to use them for other roles and tasks. I know about set_fact but it stores variable as local variable so that I need to address specific host to get it but hostname/address of this host can vary.
Googling and reading docs.ansible.com doesn't help still.
UPD: there is 2 different roles that playing tasks one after another and I need to pass variables between plays.
An option would be to use ansible modules lineinfile, blockinfile, template, and ini_file to update group variables.
For example the play below
- hosts: test_jails
gather_facts: false
vars:
my_groupvar_file: "{{ inventory_dir }}/group_vars/test_jails.yml"
tasks:
- debug:
var: my_last_run
- block:
- command: date "+%F %T"
register: result
- lineinfile:
path: "{{ my_groupvar_file }}"
regexp: "^my_last_run: "
line: "my_last_run: {{ result.stdout }}"
backup: yes
delegate_to: localhost
run_once: true
with group variables group_vars/test_jails.yml
my_last_run: 2019-04-19 11:51:00
gives (abridged):
> ansible-playbook test1.yml
PLAY [test_jails]
TASK [debug]
ok: [test_01] => {
"my_last_run": "2019-04-19 11:51:00"
}
ok: [test_03] => {
"my_last_run": "2019-04-19 11:51:00"
}
ok: [test_02] => {
"my_last_run": "2019-04-19 11:51:00"
}
TASK [command]
changed: [test_01]
TASK [lineinfile]
changed: [test_01 -> localhost]
PLAY RECAP
test_01 : ok=3 changed=2 unreachable=0 failed=0
test_02 : ok=1 changed=0 unreachable=0 failed=0
test_03 : ok=1 changed=0 unreachable=0 failed=0
> cat group_vars/test_jails.yml
my_last_run: 2019-04-19 11:56:51