I have the following Ansible playbook: remove.yaml that have two variable and it loops through a set of items.
- name: calling helm commands tasks
include_tasks: helm_commands.yaml
vars:
name_spce: "sdn"
extra_parameter: "--no-hooks"
loop: ['dre','hju','cny','wer','guy']
The included tasks file helm_commands.yaml is:
---
- name: check if {{item}} exist
shell:
cmd: /usr/local/bin/helm3 get manifest {{item}} --namespace {{name_spce}}
register: get_release
failed_when: get_release.rc ==2
- name: helm delete {{item}}
shell:
cmd: /usr/local/bin/helm3 delete {{item}} {{extra_parameter}} --namespace {{name_spce}}
when: get_release.rc ==0
The problem is I want the variable extra_parameter take the value --no-hooks only when it loop through the first item 'dre' otherwise take empty value ''
extra_parameter: "--no-hooks" when loop with 'dre'
"" when loop with other items
You could use the extended loop functionalities for that:
- name: calling helm commands tasks
include_tasks: helm_commands.yaml
vars:
name_spce: "sdn"
extra_parameter: "{{ '--no-hooks' if ansible_loop.first }}"
loop: ['dre','hju','cny','wer','guy']
loop_control:
extended: yes
If you want to add the flag --no-hooks regardless of the position of dre in the list, then it is as simple as:
- name: calling helm commands tasks
include_tasks: helm_commands.yaml
vars:
name_spce: "sdn"
extra_parameter: "{{ '--no-hooks' if item == 'dre' }}"
loop: ['dre','hju','cny','wer','guy']
Related
I'm trying to get the output of my Ansible script using register module but the loop I'm using is probably causing some issue.
Whats a default way of using register module with loop?
Code:
---
- name:
hosts:
gather_facts:
tasks:
- name: Execute file
ansible.builtin.shell:
environment:
"setting environment"
register: output
loop:
"value"
- debug:
vars: output.std_lines
Whats a default way of using register module with loop?
It is just registering the result.
The only difference will be, that a single task will provide you just with an dictionary result (or output in your example) and registering in a loop will provide with a list result.results (or output.results in your example). To access the .stdout_lines you will need to loop over the result set .results too.
You may have a look into the following example playbook which will show some aspects of Registering variables, Loops, data structures, dicts and lists and type debugging.
---
- hosts: localhost
become: false
gather_facts: false
tasks:
- name: Create STDOUT output (single)
command: 'echo "1"'
register: result
- name: Show full result (single)
debug:
var: result
- name: Show '.stdout' (single)
debug:
msg: "The result in '.stdout': {{ result.stdout }} is of type {{ result.stdout | type_debug }}"
- name: Create STDOUT output (loop)
command: 'echo "{{ item }}"'
register: result
loop: [1, 2, 3]
loop_control:
label: "{{ item }}"
- name: Show full result (loop)
debug:
var: result
- name: Show '.stdout' (loop)
debug:
msg: "The result in '.stdout': {{ item.stdout }} is of type {{ item.stdout | type_debug }}"
loop: "{{ result.results }}"
loop_control:
label: "{{ item.item }}"
By running it and going through the output you can get familiar with the differences in your tasks.
Further Q&A
Register Variables in Loop in an Ansible Playbook
Ansible: loop, register, and stdout
Register variables in loop in Ansible playbook
... and many more here on SO.
I'm creating a requirements.yml in an Ansible project, and I want to identify all of the modules that need to be installed from ansible-galaxy that are used by project playbooks. ansible-doc --list --playbook-dir foo seems like the right tool for the job, but it lists all locally available modules, not just the ones which are actually used in the foo directory. ansible-galaxy list doesn't account for any which are needed but not installed.
Is there a way to do this where I don't end up writing a shell script to sed|awk|grep the info I want?
The best approach I've been able to come up with so far is to ansible-playbook --syntax-check each of the playbooks. This will throw errors such as
ERROR! the role 'foo' was not found ...
ERROR! couldn't resolve module/action 'bar'. This often indicates a misspelling, missing collection, or incorrect module path.
but this is not ideal because it exits as soon as any error occurs. I have to fix each one and run the syntax check again.
FWIW, as a concept, the playbook below lists the modules used in a role
- hosts: localhost
vars:
keywords:
- always
- become
- block
- loop
- loop_control
- name
- notify
- register
- tags
- vars
- when
tasks:
- name: The variable my_role_path is mandatory
assert:
that: my_role_path|d('')|length > 0
- name: Find tasks files
find:
path: "{{ my_role_path }}/tasks"
patterns: '*.yml,*.yaml'
recurse: true
register: result
- name: Create list of tasks
set_fact:
lft: "{{ lft|d([]) + lookup('file', item)|from_yaml }}"
loop: "{{ result.files|map(attribute='path')|list }}"
- name: Get list of keys
set_fact:
lfk: "{{ lfk|d([]) + item.keys()|list }}"
loop: "{{ lft }}"
- name: Get list of keys from block/rescue/always
set_fact:
lfk: "{{ lfk|d([]) + item.keys()|list }}"
loop: "{{ lft|json_query('[].[block, rescue, always]')|flatten }}"
- name: Display list of modules
debug:
var: lfk|unique|sort|difference(keywords)
For example, analyze the role systemd
shell> ansible-playbook pb.yml -e my_role_path=roles/ansible-role-systemd
...
lfk|unique|sort|difference(keywords):
- command
- file
- include_tasks
- meta
- systemd
- template
Complete the list of the keywords.
Use the tasks below to analyze a playbook
tasks:
- name: The variable my_playbook_path is mandatory
assert:
that: my_playbook_path|d('')|length > 0
- name: Create list of tasks
set_fact:
lft: "{{ _playbook|map(attribute='tasks')|flatten }}"
vars:
_playbook: "{{ lookup('file', my_playbook_path)|from_yaml }}"
- name: Get list of keys
set_fact:
lfk: "{{ lfk|d([]) + item.keys()|list }}"
loop: "{{ lft }}"
- name: Get list of keys from block/rescue/always
set_fact:
lfk: "{{ lfk|d([]) + item.keys()|list }}"
loop: "{{ lft|json_query('[].[block, rescue, always]')|flatten }}"
- name: Display list of modules
debug:
var: lfk|unique|sort|difference(keywords)
For example, analyzing the first playbook gives
lfk|unique|sort|difference(keywords):
- assert
- debug
- find
- set_fact
I have this vars in the playbook and multiple tasks, I just want to filter the items from vars list based on the prompt input. Right now I have to exclude that item using when from multiple tasks. Please see following example:
vars_prompt:
- name: rmetcd
prompt: "remove etcd: YES OR NO?"
private: no
vars:
containers:
- "etcd"
- "mysql"
- "postgres"
folders:
- "etcd"
- "mysql"
- "postgres"
tasks:
- name: remove container
shell: "docker rm -f {{ item }}"
with_items: "{{ containers }}"
when:
- '"etcd" not in item'
- name: remove folder
file:
path: "{{ item }}"
state: absent
with_items: "{{ folders }}"
when:
- '"etcd" not in item'
when: rmetcd == "NO"
I would take the problem in reverse order: rather than filtering my list for each task to potentially take out an element, I would define my list with default elements and add the additional one if user answered yes.
Note: your two lists are identical, I only kept one in the following example:
---
- hosts: localhost
gather_facts: false
vars_prompt:
- name: rmetcd
prompt: "Remove etcd [yes/no]?"
private: no
vars:
_default_services:
- mysql
- postgres
services: "{{ _default_services + (rmetcd | bool | ternary(['etcd'], [])) }}"
tasks:
- name: task1
debug:
msg: "remove container {{ item }}"
loop: "{{ services }}"
- name: taks2
debug:
msg: "remove folder {{ item }}"
loop: "{{ services }}"
The key points:
I defined a "private" variable _default_services. This is the list of services that will always be included.
I calculated the variable services which is an addition of two lists: _default_services and the additional value to add depending on user input. For this last one:
I used rmetcd containing the value (which should be "yes" or "no")
I applied the bool filter to cast the value to boolean
and I used the ternary filter to select a single element list if true (['etcd']) and an empty list if false ([]).
I'm trying to pass multiple lines as a value to my Ansible file But it's only just passing the first line.
cat slot_number.txt
slot4
slot2
slot1
slot3
My ansible file as below
update_bios.yml
tasks:
- name: Powering off slot number
command: "/usr/local/bin/power-util {{slot_number}} off"
- name: Starting to update BIOS
command: "/usr/bin/fw-util {{slot_number}} --update --bios"
ansible-playbook -l myhosts update_bios.yml -e "slot_number=$(cat slot_number.txt)"
I want to run my command like below:
/usr/local/bin/power-util slot1 off
/usr/local/bin/power-util slot2 off
/usr/local/bin/power-util slot3 off
Edit after understanding the question details:
Map the slot numbers to a host:
cat slot_numbers.yml
slot_numbers:
server1:
-slot4
-slot2
server2:
-slot1
-slot3
After that distinguish between hosts in the playbook as follows,
tasks:
- name: Powering off slot number
command: "/usr/local/bin/power-util {{ item }} off"
loop: "{{ slot_numbers[inventory_hostname] }}"
- name: Starting to update BIOS
command: "/usr/bin/fw-util {{ item }} --update --bios"
loop: "{{ slot_numbers[inventory_hostname] }}"
In this approach we are doing the mapping between hosts and slots in the slot_numbers variable file. Alternatively, you can define the variables as host variables also.
Previous answer:
Ansible understands yaml and json.
Also, playbook needs to handle what happens with the data available to it via variables. So, if you want to loop through a list of slot numbers you have to tell your playbook to do exactly that.
Define your slot number file as follows:
In this file, you are creating a list of slot numbers and assigning it to the key 'slot_numbers'
cat slot_numbers.yml
slot_numbers:
-slot4
-slot2
-slot1
-slot3
Modify your update_bios.yml to loop through the slot numbers you are passing,
tasks:
- name: Powering off slot number
command: "/usr/local/bin/power-util {{ item }} off"
loop: "{{ slot_numbers }}"
- name: Starting to update BIOS
command: "/usr/bin/fw-util {{ item }} --update --bios"
loop: "{{ slot_numbers }}"
then use:
ansible-playbook -l myhosts update_bios.yml -e "#slot_numbers.yml"
Detailed documentation of how to use variables and playbooks can be found here
Something like this should work.
vars:
slot_numbers: "{{ lookup('file', './slot_number.txt').splitlines() }}"
tasks:
- name: Powering off slot number
command: "/usr/local/bin/power-util {{ item }} off"
loop: "{{ slot_numbers }}"
- name: Starting to update BIOS
command: "/usr/bin/fw-util {{ item }} --update --bios"
loop: "{{ slot_numbers }}"
We need to go through this structure
Zone spec
https://gist.github.com/git001/9230f041aaa34d22ec82eb17d444550c
I was able to run the following snipplet but now I'm stucked at the error checking.
playbook
--
- hosts: all
gather_facts: no
vars_files:
- "../doc/application-zone-spec.yml"
roles:
- { role: ingress_add, customers: "{{ application_zone_spec }}" }
role
- name: check if router exists
shell: "oc get dc -n default {{ customers.zone_name }}-{{ item.type }}"
with_items: "{{ customers.ingress }}"
ignore_errors: True
register: check_router
- name: Print ingress hostnames
debug: var=check_router
- name: create new router
shell: "echo 'I will create a router'"
with_items: "{{ customers.ingress }}"
when: check_router.rc == 1
Output of a ansible run
https://gist.github.com/git001/dab97d7d12a53edfcf2a69647ad543b7
The problem is that I need to go through the ingress items and I need to map the error of the differnt types from the "check_router" register.
It would be nice to make something like.
Pseudo code.
Iterate through the "customers.ingress"
check in "check_router" if the rc is ! 0
execute command.
We use.
ansible-playbook --version
ansible-playbook 2.1.0.0
config file = /etc/ansible/ansible.cfg
configured module search path = Default w/o overrides
You can replace the second loop with:
- name: create new router
shell: "echo 'I will create a router with type {{ item.item }}'"
with_items: "{{ check_router.results }}"
when: item.rc == 1
This will iterate over every step of check_route loop and you can access original items via item.item.