Ansible: mixing roles and tasks prohibited? - ansible

I am using the following site.yml playbook and calling it via
ansible-playbook site.yml
- hosts: some_hosts
vars:
pip_install_packages:
- name: docker
- tasks:
- name: Conditionally include bar vars
include_vars:
file: bar_vars.yml
when: some_condition == "bar"
- name: Conditionally include foo vars
include_vars:
file: foo_vars.yml
when: some_condition == "foo"
roles:
- role1
- role2
environment:
SOME_ENV_VAR: "{{ vault_some_env_var }}"
Call is failing as follows:
ERROR! the field 'hosts' is required but was not set
But as is apparent above, the hosts field has been set!
Any suggestions?

You can mix tasks and roles in a playbook, you can also control when the tasks execute by using "pre_tasks" and "post_tasks".
It looks to me like you have a - on tasks that should not be there, probably considering it to be a new play.
- hosts: some_hosts
vars:
pip_install_packages:
- name: docker
- tasks: <-- This should not have a dash
Example using pre and post tasks to control when tasks execute in relation to a role:
---
- hosts: all
name: Roles with pre and post tasks
vars:
somevar: foobar
roles:
- { role: common, tags: ["common"] }
pre_tasks:
- debug:
msg: I execute before roles
post_tasks:
- debug:
msg: I execute after roles

Related

How can I explicitely run the setup module before calling ansible roles when gather_facts is false?

I want to set gather_facts to false but then use the setup module to gather facts and run the roles afterwards.
My code looks like:
---
- name: RDS check
hosts: "{{ run_on_node|default('cdh[0]')}}"
gather_facts: False
setup: #not sure about the indentation, but want to execute that before roles.
roles:
- { role: r1, when: "'10.200.1.197' in inventory_hostname" }
setup module has to be executed in a task section. pre_tasks are executed before roles.
Try as below:
---
- name: RDS check
hosts: "{{ run_on_node|default('cdh[0]')}}"
gather_facts: False
pre_tasks:
- name: Gather facts
setup:
roles:
- { role: r1, when: "'10.200.1.197' in inventory_hostname" }

--tags does not skip vars_prompt in ansible

When I run ansible-playbook --tags tag2, why does not it skip vars_prompt from tag1? Anyhow it skips debug msg from tag1. Please help. This is making me write 2 different playbooks.
---
- name: variable print using var
hosts: all
gather_facts: no
tags: tag1
vars_prompt:
- name: ask_user
prompt: enter your name
private: no
tasks:
- debug:
msg: "{{ ask_user}} works in ABC company"
- hosts: all
gather_facts: no
tags: tag2
tasks:
- name: normal message
debug:
msg: "This is 2nd tag"
Q: "--tags does not skip vars_prompt in Ansible"
A: vars_prompt is not a task and therefore can't be skipped. Quoting from Tags
Using tags to execute or skip selected tasks is a two-step process:
Add tags to your tasks, either individually or with tag inheritance from a block, play, role, or import.
Select or skip tags when you run your playbook.
If you want to skip the prompting for a variable use pause instead of vars_prompt. For example, the playbook below does what you want
- hosts: localhost
gather_facts: false
tags: tag1
tasks:
- pause:
prompt: Enter your name
echo: true
register: result
- set_fact:
ask_user: "{{ result.user_input }}"
- debug:
msg: "{{ ask_user }} works in ABC company"
- hosts: localhost
gather_facts: false
tags: tag2
tasks:
- debug:
msg: This is tag2

Ansible how to set_fact with condition or var base on condition

I have 3 types of servers: dev, qa and prod. I need to send files to server specific home directories, i.e:
Dev Home Directory: /dev/home
QA Home Directory:/qa/home
PROD Home Directory: /prod/home
I have var set as boolean to determine the server type and think of using set_fact with condition to assign home directories for the servers. My playbook looks like this:
---
- hosts: localhost
var:
dev: "{{ True if <hostname matches dev> | else False }}"
qa: "{{ True if <hostname matches qa> | else False }}"
prod: "{{ True if <hostname matches prod> | else False }}"
tasks:
- set_facts:
home_dir: "{{'/dev/home/' if dev | '/qa/home' if qa | default('prod')}}"
However, then I ran the playbook, I was getting error about 'template expected token 'name', got string> Anyone know what I did wrong? Thanks!
Use match test. For example, the playbook
shell> cat pb.yml
- hosts: localhost
vars:
dev_pattern: '^dev_.+$'
qa_pattern: '^qa_.+$'
prod_pattern: '^prod_.+$'
dev: "{{ hostname is match(dev_pattern) }}"
qa: "{{ hostname is match(qa_pattern) }}"
prod: "{{ hostname is match(prod_pattern) }}"
tasks:
- set_fact:
home_dir: /prod/home
- set_fact:
home_dir: /dev/home
when: dev|bool
- set_fact:
home_dir: /qa/home
when: qa|bool
- debug:
var: home_dir
gives (abridged)
shell> ansible-playbook pb.yml -e hostname=dev_007
home_dir: /dev/home
Notes:
The variable prod is not used because /prod/home is default.
prod/home is assigned to home_dir first because it's the default. Next tasks can conditionally overwrite home_dirs.
Without the variable hostname defined the playbook will crash.
A simpler solution that gives the same result is creating a dictionary of 'pattern: home_dir'. For example
- hosts: localhost
vars:
home_dirs:
dev_: /dev/home
qa_: /qa/home
prod_: /prod/home
tasks:
- set_fact:
home_dir: "{{ home_dirs|
dict2items|
selectattr('key', 'in' , hostname)|
map(attribute='value')|
list|
first|default('/prod/home') }}"
- debug:
var: home_dir
Adding an alternative method to achieve this. This kind of extends the group_vars suggestion given by #mdaniel in his comment.
Ansible has a ready-made mechanism to build the variables based on the inventory hosts and groups. If you organize your inventory, you can avoid a lot of complication in trying to match host patterns.
Below is a simplified example, please go through the link above for more options.
Consider an inventory file /home/user/ansible/hosts:
[dev]
srv01.dev.example
srv02.dev.example
[qa]
srv01.qa.example
srv02.qa.example
[prod]
srv01.prod.example
srv02.prod.example
Using group_vars:
Then you can have below group_var files in /home/user/ansible/group_vars/ (matching inventory group names):
dev.yml
qa.yml
prod.yml
In dev.yml:
home_dir: "/dev/home"
In qa.yml:
home_dir: "/qa/home"
In prod.yml:
home_dir: "/prod/home"
Using host_vars:
Or you can have variables specific to hosts in host_vars directory /home/user/ansible/host_vars/:
srv01.dev.example.yml
srv01.prod.example.yml
# and so on
In srv01.dev.example.yml:
home_dir: "/dev/home"
In srv01.prod.example.yml:
home_dir: "/prod/home"
These variables will be picked based on which hosts you run the playbook, for example the below playbook:
---
- hosts: dev
tasks:
- debug:
var: home_dir
# will be "/dev/home"
- hosts: prod
tasks:
- debug:
var: home_dir
# will be "/prod/home"
- hosts: srv01.dev.example
tasks:
- debug:
var: home_dir
# will be "/dev/home"
- hosts: srv01.prod.example
tasks:
- debug:
var: home_dir
# will be "/prod/home"

How to deal with errors coming from ansible roles

I have been calling a playbook having multiple roles, each roles signifies a TESTCASE. I need to run the playbook without failing it if one of the role fails during execution. For which I am using ignore_errors: yes. However, this truly ignores error, I need to print at the end the name of the ROLES which are failed ? is it possible ?
- hosts: WEB
gather_facts: no
vars:
roles:
- { role: CHECK_CONNECTION, ignore_errors: yes, tags: always }
- { role: CHECK_CPU, ignore_errors: yes, tags: always }
- { role: CHECK_MEM, ignore_errors: yes, tags: always }
Question: How to execute whole playbook and at the end print the failed roles ?
An option would be to use the rescue section of Blocks
Create include_role.yml
- name: include role
block:
- include_role:
name: "{{ item_my_role }}"
rescue:
- set_fact:
failed_roles: "{{ failed_roles|default([]) + [ item_my_role ] }}"
and loop include_tasks. It is not possible to loop blocks.
vars:
my_roles:
- CHECK_CONNECTION
- CHECK_CPU
- CHECK_MEM
tasks:
- include_tasks: include_role.yml
loop: "{{ my_roles }}"
loop_control:
loop_var: item_my_role
- debug:
var: failed_roles|default([])
Use loop_control loop_var and create unique variable, e.g. item_my_role, avoiding potential conflict inside the included role. If the variable is used inside the included role the following rescue section will add the wrong item to the list.

Ansible-Execute a last mandatory role in the playbook even though any previous roles in the execution flow fails

I have ansible playbook something like this
---
- hosts: localhost
connection: local
tasks:
- set_fact:
build_date_time: "{{ ansible_date_time }}"
- hosts: localhost
connection: local
gather_facts: false
vars:
role: Appvariables
base_image_tag: Base
roles:
- role: begin-building-ami
- hosts: just_created
remote_user: "{{ default_user }}"
vars:
role: AppName
roles:
- { role: role1, become: yes }
- role: role2
- { role: role3, become: yes }
- { role: role4, become: yes }
- { role: role5, become: yes }
- hosts: localhost
connection: local
gather_facts: false
vars:
role: Appname
ansible_date_time: "{{ build_date_time }}"
roles:
- finish-building-ami
Here in this case we have a situation to execute the finish-building-ami role where we terminate the instance after baking the ami. If any reason any of the previous role1-role5 fails in the flow fails It stops the playbook and we have the failed instance which we needed to terminate automatically.Right now we are going and terminating it manually if it fails.
So needed to run finish-building-ami(mandatory role where we stop the instance and take ami and terminate the instance at last ) if even any of the role1-role5 fails in the above mentioned playbook.
You can rewrite your existing play to use import_role or include_role tasks instead of the roles section. This allows you to use blocks:
---
- hosts: localhost
gather_facts: false
tasks:
- block:
- import_role:
name: role1
- import_role:
name: role2
become: true
- import_role:
name: role3
rescue:
- set_fact:
role_failed: true
- hosts: localhost
gather_facts: false
tasks:
- debug:
msg: This task runs after our roles.

Resources