How to run Ansible task to a limit number of items? - ansible

I have a dynamic inventory which returns me my hosts addresses.
But sometimes, I would like to apply some configuration to a limited number of hosts.
A sample with N hosts but only 5 are echoed:
- name: "Run silly shell script"
shell: |
echo {{ item }}
exit 0
with_items: "{{ hosts | only(5) }}"

to get the first X elements from a list, use: list_var[:X].
full example as PB below:
---
- hosts: localhost
gather_facts: false
vars:
list_var:
- 1
- 2
- 3
- 4
- 5
- 6
tasks:
- name: print full list
debug:
var: list_var
- name: print list of first 3 elements
debug:
var: list_var[:3]
hope it helps.

Related

How to loop vars in ansible playbook [duplicate]

The documentation for import_tasks mentions
Any loops, conditionals and most other keywords will be applied to the included tasks, not to this statement itself.
This is exactly what I want. Unfortunately, when I attempt to make import_tasks work with a loop
- import_tasks: msg.yml
with_items:
- 1
- 2
- 3
I get the message
ERROR! You cannot use loops on 'import_tasks' statements. You should use 'include_tasks' instead.
I don't want the include_tasks behaviour, as this applies the loop to the included file, and duplicates the tasks. I specifically want to run the first task for each loop variable (as one task with the standard with_items output), then the second, and so on. How can I get this behaviour?
Specifically, consider the following:
Suppose I have the following files:
playbook.yml
---
- hosts: 192.168.33.100
gather_facts: no
tasks:
- include_tasks: msg.yml
with_items:
- 1
- 2
msg.yml
---
- name: Message 1
debug:
msg: "Message 1: {{ item }}"
- name: Message 2
debug:
msg: "Message 2: {{ item }}"
I would like the printed messages to be
Message 1: 1
Message 1: 2
Message 2: 1
Message 2: 2
However, with import_tasks I get an error, and with include_tasks I get
Message 1: 1
Message 2: 1
Message 1: 2
Message 2: 2
You can add a with_items loop taking a list to every task in the imported file, and call import_tasks with a variable which you pass to the inner with_items loop. This moves the handling of the loops to the imported file, and requires duplication of the loop on all tasks.
Given your example, this would change the files to:
playbook.yml
---
- hosts: 192.168.33.100
gather_facts: no
tasks:
- import_tasks: msg.yml
vars:
messages:
- 1
- 2
msg.yml
---
- name: Message 1
debug:
msg: "Message 1: {{ item }}"
with_items:
- "{{ messages }}"
- name: Message 2
debug:
msg: "Message 2: {{ item }}"
with_items:
- "{{ messages }}"
It is not possible. include/import statements operate with task files as a whole.
So with loops you'll have:
Task 1 with Item 1
Task 2 with Item 1
Task 3 with Item 1
Task 1 with Item 2
Task 2 with Item 2
Task 3 with Item 2
Task 1 with Item 3
Task 2 with Item 3
Task 3 with Item 3

How to read a particular part of file in ansible

I have a file which has the following data
!
multiply 4 and 5
multiply 5 and 6
!
add 3 to 4
add 8 to 4
!
sub 3 from 6
sub 9 from 5
!
!
div 6 by 2
div 8 by 1
I want to read only add commands from the file.
Using lookup plugin I was able to read the data of the entire file.
But I don't know how to read only the specific add commands from the file
Here's the code that I've written.
---
- name: Extracting the Add commands from the File
hosts: 127.0.0.1
connection: local
vars:
contents: "{{lookup('file', 'file1.txt')}}"
tasks:
- name: Printing the Add commands of the File
debug:
var: contents
I am stuck at this point.Could anyone help me out of how to read the specific part of a file in ansible.
Use with_lines. The play below
- hosts: localhost
vars:
my_data_file: "{{ playbook_dir }}/data.txt"
my_commands: []
tasks:
- set_fact:
my_commands: "{{ my_commands + [ item ] }}"
with_lines: "cat {{ my_data_file }}"
when: item is search('^add')
- debug:
var: my_commands
gives
"my_commands": [
"add 3 to 4",
"add 8 to 4"
]

Run all tasks in playbook with_items

I want the playbook to run once with each item in the list and not all items in the list at once.
Ansible version: 2.6.1
Tasks.yaml:
---
- name: Task 1
debug:
msg: "Message 1: {{ item }}"
with_items: "{{ messages }}"
- name: Task 2
debug:
msg: "Message 2: {{ item }}"
with_items: "{{ messages }}"
Playbook:
- hosts: localhost
gather_facts: no
tasks:
- import_tasks: Tasks.yml
vars:
messages:
- 1
- 2
This is my expected result:
Task 1 with Item 1
Task 2 with Item 1
Task 3 with Item 1
Task 1 with Item 2
Task 2 with Item 2
Task 3 with Item 2
Task 1 with Item 3
Task 2 with Item 3
Task 3 with Item 3
But when I execute the playbook, then it is like this:
Task 1 with Item 1
Task 1 with Item 2
Task 2 with Item 1
Task 2 with Item 2
Task 3 with Item 1
Task 3 with Item 2
...
I tried both import and include - both have the same result.
Your playbook.yml should implement a loop (notice you cannot loop with import_tasks; it will raise an error):
- hosts: localhost
connection: local
gather_facts: no
vars:
messages:
- 1
- 2
- 3
tasks:
- include_tasks: Tasks.yml
loop: "{{ messages }}"
And Tasks.yml should look like this (no loops inside):
---
- name: Task 1
debug:
msg: "Message 1: {{ item }}"
- name: Task 2
debug:
msg: "Message 2: {{ item }}"

Assign item to a var with_items in ansible

I am trying to create a playbook to find out on which openstack server vm is running on. I have created a list of openstack servers in vars and used delegate_to with with_items to iterate through until find vm. I am using wc -l at the end of command and 1 will be success. The aim is, once os-server is found, store servername into a var so this can be used for rest of tasks in playbook. I am unable to get the os server name in a var from the list. I am not an ansible expert. Can anyone help to achieve this? Thanks
- hosts: localhost
vars:
openstack:
- reg1
- reg2
- reg3
- reg4
tasks:
- name: Command to find os server where vm exists
shell: somecommand-to-check-if-vm-exist | wc -l
delegate_to: "{{ item }}"
with_items: "{{ openstack }}"
register: found_server
retries: 1
delay: 1
until: found_server.stdout != "1"
- debug: var=found_server
- name: set fact
set_fact: os-server = "{{ item.item }}"
when: item.stdout == "1"
with_items: "{{ found_server.results }}"
register: var2
- name: debug var
debug: var=var2
- debug: var=os-server
There's no need to retry/until here and for the second loop as well.
Try this:
- hosts: localhost
vars:
openstack: [reg1, reg2, reg3, reg4]
tasks:
- name: Command to find os server where vm exists
shell: somecommand-to-check-if-vm-exist | wc -l
delegate_to: "{{ item }}"
with_items: "{{ openstack }}"
register: vm_check
- name: set fact
set_fact:
os_server: "{{ (vm_check.results | selectattr('stdout','equalto','1') | list | first).item }}"
- name: debug var
debug:
msg: "{{ os_server }}"
This will register results from every server into vm_check.results, and then just select elements with stdout set to 1, take first element of it it (I suppose you always have one server with VM), and get .item of this element which contains the item of original loop (in our case it is server's name).

How to assign a random number to a variable in ansible?

This is an ansible script that I was expecting to print out the same random number three times. Instead, it prints out three random numbers. How do I assign a random number to a variable in ansible so that it is fixed throughout the playbook?
---
- name: Test random filter
hosts: localhost
gather_facts: False
vars:
random_number: "{{ 100 | random }}"
tasks:
- name: Print the random number
debug: var=random_number
- name: Print the random number
debug: var=random_number
- name: Print the random number
debug: var=random_number
Just use the set_fact module as a task first:
- set_fact:
r: "{{ 100 | random }}"
run_once: yes
Subsequently, debug: msg=... has the value of r fixed.
Set facts under task:
---
- name: Test random filter
hosts: localhost
gather_facts: False
tasks:
- name: set fact here
set_fact:
randome_number: "{{ 100 | random }}"
run_once: yes
- name: Print the random number
debug: var=random_number
- name: Print the random number
debug: var=random_number
- name: Print the random number
debug: var=random_number

Resources