I am using Ansible to deploy an environment that may have services distributed or not. I would like to conditionally include playbooks based on arguments I pass to ansible-playbook.
create_server.yml
---
- include: launch_ec2_instance.yml
- include install_postgres.yml
when {{db}} == "Y"
- include install_redis.yml
when {{redis}} == "Y"
Here is how I am calling create_server.yml
ansible-playbook create_server.yml -i local --extra-vars "db=Y redis=N"
Is it possible to do this and if so, how?
Yes. It's possible. You are missing a colon(:) on your when statement.
---
- include: launch_ec2_instance.yml
- include install_postgres.yml
when: {{ db }} == "Y"
- include install_redis.yml
when: {{ redis }} == "Y"
You can also omit the braces ({{ }}):
---
- include: launch_ec2_instance.yml
- include install_postgres.yml
when: db == "Y"
- include install_redis.yml
when: redis == "Y"
#Rico's answer is correct except that it only applies when your include statement is part of a task.
Eg.
---
tasks:
- include install_postgres.yml
when: db == "Y"
If your playbook is just a bunch of includes as your 'create_server.yml' seems to be then 'when' wont work.
Related
So far I can see using a when in ansible to determin whether to run a task but do I have to define 2 tasks to run the alternative option..
example - if I want to run the following task then run the debug task, i need to run two tasks or existStatus will not have been defined for the debug statement. Can I not use some sort of if else statement rather than include 2 separate tasks?
- name: Print user does not exist status
shell: echo 'user does not exist'
when: kafka_configs_result.stdout_lines[1] == '1'
register: existStatus
- name: Print user does not exist status
shell: echo 'user already exists so could not be created'
when: kafka_configs_result.stdout_lines[1] == '0'
register: existStatus
- debug: msg="{{ existStatus.stdout_lines }}"
You can do this in one single task without having to go through an unneeded shell execution. A simple way is to use a test and the ternary filter
I added the var section simply for readability. You can make a long one liner if you wish.
- debug:
vars:
exists_test: "{{ kafka_configs_result.stdout_lines[1] == '1' }}"
msg_exists: "user already exists so could not be created"
msg_notexists: "user does not exist"
msg: "{{ exists_test | ternary(msg_notexists, msg_exists) }}"
You can write something like this to utilize if-loop
- set_fact: build="{% if '<something>' in <something> %}<VALUE>{% else %}<VALUE>{% endif %}"
For the given below Ansible code, how can I implement a similar functionality in SaltStack (specifically when clause)?
---
- include: install-redhat.yml
when: ansible_os_family == "RedHat"
- include: install-debian.yml
when: ansible_os_family == "Debian"
Do I have to use Jinja2 templates for this? It looks like unless and onlyif can only test return codes of shell commands.
Yes, you have to use jinja for this.
Something like
{% if grains['os'] == 'Redhat' %}
include:
- install-redhat
{% endif %}
But I would rather include the states in top file
for example, in top.sls, you can do
'os:Redhat':
- match: grain
- state1_redhat
- state2_redhat
'os:FreeBSD':
- match: grain
- freebsd1
- freebsd2
I am trying to create configuration files from a template with include variables based on the fourth character of {{ ansible_hostname }}.
What works:
playbook:
---
- hosts: spock
roles:
- templaterole
role:
---
- name: testing autofs template on spock
template:
src=autofs
dest=/tmp/autofs
with_items:
- "{{ var_a }}"
when: ('{{ ansible_hostname }}' == "spock")
vars/main.yml:
var_a:
-
var_1: 'this is var_a1'
var_2: 'this is var_a2'
var_b:
-
var_1: 'this is var_b1'
var_2: 'this is var_b2'
template:
{{ item.var_1 }}
#
{{ item.var_2 }}
#
This works as expected and the output produces a /tmp/autofs file on the spock host that looks like:
this is var_a1
#
this is var_a2
#
Now, if I try to write the file based on trying to pull out the 4th character of the {{ ansible_hostname }}, the play does not get a match on the conditional and does not write the file. I'm trying this conditional in my role:
---
- name: testing autofs template on spock
template:
src=autofs
dest=/tmp/autofs
with_items:
- "{{ var_a }}"
when: ('{{ ansible_hostname }} | cut -c4' == "c") or
('{{ ansible_hostname }} | cut -c4' == "k")
the play skips this task due to not matching on the conditional. Ultimately i want to be able to pull any 4th character of our hostnames as this will always be predictable (can only be one of 4 known characters which defines my environment and lets me define the correct template variables based on these diff production environments.)
Can anyone help me to redefine my when statement such that i can do or conditionals and pull characters out of defined ansible variables like ansible_hostname?
Don't use curly brackets inside when statement, it's already a Jinja2 statement.
And in Jinja2 statements you use | to apply filter, but there is no cut filter available.
Your statement should be as simple as:
when: ansible_hostname[3] in 'ck'
I am having issues when trying to use multiple and/or conditionals in a when statement to decide whether a task needs to be ran or not. Basically I am making a playbook to do automated system patching with options for security patches, kernel only patches and to specify packages in a var file.
I run the playbook with the following commands and define the variables through extended variables option (-e)
ansible-playbook site.yml -i inventory --ask-vault -u (username) -e "security=true restart=true" -k -K
By default the playbook will update every package on the system except kernel but I would like to skip that action if I specify any of a few variables. The code I have is the following:
- name: Update all packages
yum:
name: "*"
state: latest
exclude: "kernel*"
when: security is not defined or kernel is not defined or specified_packages
is not defined and ansible_os_family == "RedHat"
Ive tried all of the following combinations:
when: (ansible_os_family == "RedHat") and (security is defined or kernel is defined or specified_packages is defined)
when: (ansible_os_family == "RedHat") and (security == true or kernel == true or specified_packages == true ) <- this case throws a not defined error because i don't define all variables every time i run the playbook
when: ansible_os_family == "RedHat"
when: security is defined or kernel is defined or specified_packages is defined
Note: I am aware and have used an extra variable such as "skip" to skip this task and use the when clause when: ansible_os_family == "RedHat" and skip is not defined but would prefer not have my users need to use an extra variable just to skip this default action.
I also am not using tags as I am gathering a list of packages before and after the upgrade to compare and report in the end so I wont be able to run those as they are local action commands. This is why I'm using one role with multiple tasks turned on and off via extended variables. I am open to any suggestion that rewrites the playbook in a more efficient way as I am sort of a noob.
It was such a simple answer!
The following works:
when: not (security is defined or kernel is defined or specified_packages is defined) and ansible_os_family == "RedHat"
As #techraf noted in comments, defined/undefined is a nasty test...
Refactor like this:
when:
- ansible_os_family == "RedHat"
- security|d('') != '' or kernel|d('') != '' or specified_packages|d('') != ''
Update. Reproducible example:
- hosts: localhost
gather_facts: no
tasks:
- debug:
msg: hello
when:
- '"RedHat" == "RedHat"'
- security|d('') != '' or kernel|d('') != '' or specified_packages|d('') != ''
execution:
ansible-playbook -e kernel=true playbook.yml
PLAY [localhost] ***************************************************************
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": "hello"
}
PLAY RECAP *********************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0
versions:
$ pip list | grep -iP 'ansible|jinja'
ansible (2.2.1.0)
Jinja2 (2.8)
I just had a similar problem needing to test two different variables to see if they were "true", but they don't always exist in the output json. The basic logic needed is:
( a is defined ) and ( a == 'present' or a == 'reinstalled' )
in this case, a is "install_vs2022_status.invocation.module_args.state", and the following three different scenarios produced the correct result:
direct:
when: (install_vs2022_status.invocation.module_args.state is defined) and (install_vs2022_status.invocation.module_args.state == 'reinstalled' or install_vs2022_status.invocation.module_args.state == 'present')
distributed:
when: (install_vs2022_status.invocation.module_args.state is defined and install_vs2022_status.invocation.module_args.state == 'present') or (install_vs2022_status.invocation.module_args.state is defined and install_vs2022_status.invocation.module_args.state == 'reinstalled')
broken into lines that imply 'and' or 'intersection', except it stops if the first item evaluates to false.
when:
- install_vs2022_status.invocation.module_args.state is defined
- install_vs2022_status.invocation.module_args.state == 'reinstalled' or install_vs2022_status.invocation.module_args.state == 'present'
The most important factor in each case is that the test for existence happens first, in order to prevent evaluation of a variable that doesn't exist.
Given the following playbook (deployment.yml):
---
- name: Debug
hosts: applicationservers
tasks:
- debug: msg="{{add_host_entries | default('false')}}"
- debug: msg="{{add_host_entries | default('false') == 'true'}}"
- debug: msg="Add host entries = {{add_host_entries | default('false') == 'true'}}"
- include: add_host_entries.yml
when: add_host_entries | default('false') == 'true'
The condition to include add_host_entries.yml always fails, even if all of the above debug messages print some sort of true (I know that in the first debug message it's a String, whereas the other two result in Booleans).
When I omit the part with the default value, add_host_entries.yml will be executed:
when: add_host_entries
I need this default value behaviour though, because it's an optional value which is only set on certain stages.
Other Attempts (without success)
Brackets
when: (add_host_entries | default('false')) == 'true'
Casting to boolean
when: add_host_entries|default('false')|bool
Other Sources and Information
Here are all the resources needed to reproduce the problem.
add_host_entries.yml
---
- name: add_host_entries
hosts: applicationservers
gather_facts: false
tasks:
- debug: msg="Add Host Entries"
inventory
[applicationservers]
127.0.0.1
[all:vars]
add_host_entries=true
Call
markus#lubuntu:~/foobar$ ansible-playbook deployment.yml -i inventory
Versions
markus#lubuntu:~/foobar$ ansible --version
ansible 2.1.1.0
config file = /etc/ansible/ansible.cfg
configured module search path = Default w/o overrides
markus#lubuntu:~/foobar$ ansible-playbook --version
ansible-playbook 2.1.1.0
config file = /etc/ansible/ansible.cfg
configured module search path = Default w/o overrides
You try to conditionally include playbook. See my other answer about different include types.
The thing is, this only works when variable is defined before Ansible parses your playbook.
But you try to define add_host_entries as host-level fact (group variable) – these variables are not yet defined during parse time.
If you call your playbook with -e add_host_entries=true your condition will work as expected, because extra-vars are known during parse time.
Use bool to convert the string value of add_host_entries into a boolean and then the condition will work.
---
- name: Debug
hosts: applicationservers
tasks:
- debug: msg="{{add_host_entries | default('false')}}"
- debug: msg="{{add_host_entries | default('false') == 'true'}}"
- debug: msg="Add host entries = {{add_host_entries | default('false') == 'true'}}"
- include: add_host_entries.yml
when: add_host_entries | default('false') | bool