issue while including another playbook in ansible? - ansible

I have written a playbook named as master.yaml as defined below
- hosts: master
remote_user: "{{ ansible_user }}"
tasks:
- name: Get env
command: id -g -n {{ lookup('env', '$USER') }}
register: group_user
vars:
is_done: "false"
- include: slave.yaml
vars:
sethostname: "{{ group_user }}"
worker: worker
when: is_done == "true"
where: inventory_hostname in groups['worker']
I am trying run another playbook named as slave.yaml as defined below, after a certain condition is met.
- hosts: worker
remote_user: "{{ ansible_user }}"
tasks:
- name: Write to a file for deamon setup
copy:
content: "{{ sethostname }}"
dest: "/home/ubuntu/test.text"
Now i have two question:
I am not able to set the value of var isDone. slave.yaml should
only work when isDone is true.
2.How salve.yaml access the value worker. I have defined group worker in inventory.yaml

I do not know if it's the right way to go to reach your objective. However I tried to make this playbook work by keeping as much as possible your logic. Hope it helps.
The point is that you cannot use import_playbook inside the play. Check the module documentation for more information.
So I propose to share code with a role instead of a playbook. You will be able to share the slave role between the master playbook and another playbooks, a slave playbook for example.
The ansible folder structure is the following.
├── hosts
├── master.yml
└── roles
└── slave
└── tasks
└── main.yml
Master.yml
---
- name: 'Master Playbook'
# Using the serial keyword to run the playbook for each host one by one
hosts: master
serial: 1
remote_user: "{{ ansible_user }}"
tasks:
- name: 'Get env'
command: id -g -n {{ lookup('env', '$USER') }}
register: group_user
- name: 'Calling the slave role'
import_role:
name: 'slave'
# The return value of the command is stored in stdout
vars:
sethostname: "{{ group_user.stdout }}"
# Only run when the task get env has been done (state changed)
when: group_user.changed
# Delegate the call to the worker host(s) -> don't know if it's the expected behavior
delegate_to: 'worker'
Slave main.yml
---
- name: 'Write to a file for deamon setup'
copy:
content: "{{ sethostname }}"
dest: "/tmp/test.text"
At the end the /tmp/test.text contains the effective user group name.

Related

Ansible run script on all hosts, gather output and send once to an API

I have the following task set:
- name: Initialise inventory_data variable
set_fact:
inventory_data: ""
- name: Get Instance Inventory
remote_user: me
ansible.builtin.script: scripts/inventory.sh
register: inventory
- name: Set inventory variable
set_fact:
inventory_data: "{{ inventory_data }} {{ inventory.stdout_lines | join('\n')}}"
- name: Send to API
remote_user: me
ansible.builtin.uri:
url: https://myapi.com/endpoint
method: POST
body: "{{ inventory_data }}"
status_code: 200
The desired result is that i need to gather the results from inventory.sh and send them only once at the end of the run.
I've tried different variations, with run_once, delegate_to etc.. but i cannot seem to get this!
Edit:
I am trying to gather some data from my script which is ran on every host, however i wish to aggregate the results from all hosts, and send it once to an API.
First, if your play looks something like this:
- hosts: all
tasks:
- name: Initialise inventory_data variable
set_fact:
inventory_data: ""
- name: Get Instance Inventory
remote_user: me
ansible.builtin.script: scripts/inventory.sh
register: inventory
- name: Set inventory variable
set_fact:
inventory_data: "{{ inventory_data }} {{ inventory.stdout_lines | join('\n')}}"
It's not going to do you any good. Your inventory.sh script will run on each host, which will set the inventory variable for that host, and the subsequent task will append inventory.stdout_lines to inventory_data for that host. This won't collect the output from multiple hosts. You need to restructure your playbook. First, run the inventory script on each host:
- hosts: all
gather_facts: false
tasks:
- name: Get Instance Inventory
ansible.builtin.script: scripts/inventory.sh
register: inventory
Then in a second play targeting localhost, build your merged inventory variable and send the data to the API:
- hosts: localhost
gather_facts: false
tasks:
- name: create merged inventory
set_fact:
inventory_data: "{{ inventory_data + hostvars[item].inventory.stdout }}"
vars:
inventory_data: ""
loop: "{{ groups.all }}"
- name: Send to API
remote_user: me
ansible.builtin.uri:
url: https://myapi.com/endpoint
method: POST
body: "{{ inventory_data }}"
status_code: 200
This way, (a) you build the inventory_data variable correctly and (b) you only make a single API call.
I've made a complete, runnable example of this solution available here.

Variable is defined but still getting undefined error

I am trying to write a playbook that completes some of its tasks on the machine that the playbook is running on. I know i can use local_action for this but I am just testing if the playbook works for now. Eventually I will need to use the delegate_to. I have defined the variable and I am using delegate_to: 'variable name' but I am getting this error. : " fatal [target node]: FAILED! => { msg": "'variablename' is undefined. Below is my playbook:
name: Create certs
gather_facts: true
vars:
master: "{{ nameofhost }}"
tasks:
- name: Run command
shell: Command to run
delegate_to: "{{ master }}"
You need to target your play to a target hosts of an inventory
name: Create certs
gather_facts: true
hosts: target_hosts
vars:
master: "{{ nameofhost }}"
tasks:
- name: Run command
shell: Command to run
delegate_to: "{{ master }}"
``` 
Your inventory files may look like that:
[target_hosts]
master ansible_host=your_master_dns_or_ip
And then ansible can target that inventory group and then reduce the task scope to master host. Or you can just use the localhost as target.

Ansible: run certain yaml tasks file for all hosts

I try to run certain yaml tasks file for all hosts, as follows (main.yml):
- name: prepare nodes
include_tasks: node.yml node="{{ item }}"
loop: "{{ groups['all'] }}"
node.yml:
- block:
- name: Task 1...
...
- name: Task 100...
delegate_to: "{{ node }}"
However I get this error: Invalid options for include_tasks: node. I think it used to work in this manner. Anyway I tried to move loop from main.yml into node.yml (right after delegate_to). I also tried to skip node="{{ item }}" part. But I always get errors.
What is the proper way to apply a task file to several hosts within a role?
It should work if you put your node variable under vars then loop.
- name: include tasks
include_tasks: node.yml
vars:
node: '{{ item }}'
loop: "{{ groups['all'] }}"
Above code is working.
A play runs on the hosts you specified. You can run certain tasks on a subset of nodes using when.
But you can have multiple plays in a playbook. So you need to specify a play with hosts: all where you run the tasks you want to run everywhere and another one which runs the rest of the tasks.
Your playbook could look like this:
---
# This is a play
- name: run on all
hosts: all
vars:
somevar: 'test'
tasks:
- name: prepare nodes
include_tasks: node.yml
# This is another play
- name: run on group
hosts: hostgroup
vars:
somevar: 'example'
tasks:
- debug:
msg: 'This runs on all hosts in hostgroup'
# Both plays are in the same playbook

How can I pass Ansible playbook variables from a master playbook to a linked playbook?

I'm trying to find out how I can pass Ansible playbook variables defined in a 'master' playbook into other playbooks that I've already referenced in my master playbook.
For example, with the below 'master' playbook, I'd like to pass the values of sethostname and setipaddress to playbook[1-3].yml referenced in my tasks section. This would be akin to calling functions in other programming languages.
---
- hosts: all
become: yes
vars_prompt:
- name: "sethostname"
prompt: "What will be the machine's hostname?"
private: no
- name: "setipaddress"
prompt: "What will be the machine's IP address?"
private: no
tasks:
- include: playbook1.yml
- include: playbook2.yml
- include: playbook3.yml
As of Ansible 2.4 you can now import playbooks, meaning they will get pre-processed at the time the playbook is parsed, and will run in the order you import them. Check out the docs for the full info on import vs. include here http://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_includes.html
You can pass vars to them just like you can to other include_* tasks.
tasks:
- import_playbook: playbook1.yml
vars:
sethostname: "{{ sethostname }}"
setipaddress "{{ setipaddress }}"
Kellie's answer is not exactly accurate , playbooks may not be imported at task level only at the topmost level of a playbook. So this works:
- import_playbook: playbook1.yml
vars:
sethostname: "{{ sethostname }}"
setipaddress "{{ setipaddress }}"

Run ansible-playbook from localhost, and using vars from hosts file

Let's say that I want to run something locally. but I want to use the vars from the hosts file, so basically - I want to do for each line something locally.
In this example, I want to use ec2_tag from ansible.
hosts file for ansible playbook run:
[any]
123.123.123.123 region=eu-region ec2_instance_id=x-xxxxxxxxxxxxxxxxx
123.123.123.124 region=eu-region ec2_instance_id=x-xxxxxxxxxxxxxxxxx
ansible-playbook:
- name: something
hosts: any
tasks:
- name: test
ec2_tag:
region: "{{ region }}"
resource: "{{ ec2_instance_id }}""
state: list
register: ec2_tags
- debug: msg={{ ec2_tags }}
How can i loop localy on [any] vars? let's say get region?
It's running now with local_action and taking the vars from the hosts file.
- name: something
hosts: any
tasks:
- name: test
local_action: ec2_tag region={{ region }} resource={{ ec2_instance_id }} state=list
register: ec2_tags
- debug: msg={{ ec2_tags }}

Resources