I have a main playbook that use include to call other playbooks in case conditions are met. That is working fine, but what I need is to execute these playbooks for n times, where n is a user input variable. So, if the user enters "5", the main playbook will call playbooks for 5 times.
This is the example:
---
- name: main playbook
hosts: localhost
connection: local
gather_facts: False
var_files: weqwewq
tasks:
- include: 1.yml
when: x == "aaa"
- include: 2.yml
when: x == "bbb"
- include: 3.yml
when: x == "ccc"
- include: 4.yml
when: x == "ddd"
What I don't need is this:
tasks:
- include: 1.yml
when: x == "aaa"
with_sequence: count= "{{ user_input }}"
- include: 2.yml
when: x == "aaa+bbb"
with_sequence: count= "{{ user_input }}"
- include: 3.yml
when: x == "ccc"
with_sequence: count= "{{ user_input }}"
- include: 4.yml
when: x == "ccc+ddd"
with_sequence: count= "{{ user_input }}"
but instead something like this
tasks:
with_sequence: count= "{{ user_input }}"
- include: 1.yml
when: x == "aaa"
- include: 2.yml
when: x == "aaa+bbb"
- include: 3.yml
when: x == "ccc"
- include: 4.yml
when: x == "ccc+ddd"
but for this I'm getting an error:
"with_sequence is not a valid attribute for a play".
Any idea?
Thanks!
Just add another include level, and add your sequence loop to that task.
First, note that the include module has been deprecated. You should be using include_tasks.
To loop over your tasks with a single loop construct, start with a top-level playbook like this:
- hosts: localhost
gather_facts: false
tasks:
- include_tasks: tasks.yaml
with_sequence: count=5
In tasks.yaml, place your include_tasks with conditions:
- name: show loop index
debug:
var: item
- include_tasks: tasks_1.yaml
when: x = 'aaa'
- include_tasks: tasks_2.yaml
when: x = 'bbb'
- include_tasks: tasks_3.yaml
when: x = 'ccc'
And then create the individual task_N.yaml files as necessary
Welcome to Stackoverflow. You can use Interactive Input to take N as input. Also, you might consider checking this for more information on the usage of variables in ansible.
Also, it is a good practice to use tasks to organize your code.
Check the following code:
Update: I added stride parameter in with_sequence so now increment value can be set other than 1 (default).
Your main playbook file:
---
- name: main playbook
hosts: localhost
connection: local
gather_facts: False
vars_prompt:
- name: counter
prompt: Enter loop counter
private: no
tasks:
- include_tasks: tasks.yml
with_sequence: start=1 end="{{ counter }}" stride="{{ 1 }}"
The additional tasks.yml:
---
- include_tasks: 1.yml
when:
- x is defined
- x = "aaa"
- include_tasks: 2.yml
when:
- x is defined
- x = "bbb"
- include_tasks: 3.yml
when:
- x is defined
- x = "ccc"
- include_tasks: 4.yml
when:
- x is defined
- x = "ddd"
Here in tasks.yml I added conditionals to check whether x is defined.
Related
My scenario is a bit similar to the one mentioned here
but a bit difference, let me take same example mentioned in above question solution by Vladimir Botka , added name:c and name:d block
output:
- Name: A
source: [a, b, c, a, b, c]
dest: [x,y,z]
- Name: B
source: [a, b, c, a, b, c]
dest: [x.c, y, z, x, y, z]
- Name: C
source:
dest:
- Name: D
source: "1.2"
dest: "x"
I am using same answer suggested in above post but a bit modifications as :
- set_fact:
out1: "{{output |replace('None','[]')}}"
- set_fact:
out: "{{ out|d([]) + [{'Name': item.Name,
'source': _src,
'dest': _dst}] }}"
loop: "{{ out1 }}"
vars:
_src: "{{ item.source|unique}}"
_dst: "{{ item.dest|unique}}"
- debug:
var: out
I used out1: "{{output |replace('None','[]')}}" cause loop is failing for name:c as source and dest is empty and not a list i am making it list by replacing None as [], to over come loop error, i am able to fix loop error.
not sure if its right approach or not but solved loop error issue.
but second error i am getting is Object of type set is not JSON serializable for name:d cause source and destination is not a list.
how can this error be fixed or any workaround ??
Q: "Error when source and destination is not a list"
A: Filter unique items if the type of the value is a list otherwise copy the value, e.g.
- set_fact:
out: "{{ out|d([]) + [{'Name': item.Name,
'source': _src,
'dest': _dst}] }}"
loop: "{{ out1 }}"
vars:
_src: "{{ (item.source|type_debug == 'list')|
ternary(item.source|unique, item.source) }}"
_dst: "{{ (item.dest|type_debug == 'list')|
ternary(item.dest|unique, item.dest) }}"
I have got an ansible problem where I have to change the items of a list in some cases.
Imagine, if there is the string "apple0" and another digit from 0 to 9 the list element should be appended by the string T2. If there is any other element, lets say banana or peach, it should stay as it is.
---
- hosts: localhost
become: no
vars:
fruit: [banana,apple05,apple04,peach]
tasks:
- name: my task
set_fact:
"{{ item | replace('^apple0[0-9]*$','?1T2') }}"
loop: "{{fruit}}"
- debug:
msg:
- "{{fruit}}"
suggested output:
ok: [localhost] => {
"msg": [
[
"banana",
"apple05T2",
"apple04T2",
"peach"
]
]
}
For example
- set_fact:
fruit2: "{{ fruit2|default([]) + [(item is match('^apple0\\d$'))|
ternary(item ~ 'T2', item)] }}"
loop: "{{ fruit }}"
gives
fruit2:
- banana
- apple05T2
- apple04T2
- peach
The next option is to map the filter regex_replace. For example, the task below gives the same result
- set_fact:
fruit2: "{{ fruit|map('regex_replace', my_regex, my_replace)|list }}"
vars:
my_regex: '^(apple0\d)$'
my_replace: '\1T2'
for this, which filter should i use?
Project Total weight = 1000000
{{project.total_weight}} >> 1.000.000
You can take advantage of Python's string formatting syntax:
{{ '{:,}'.format(project.total_weight)
Here's how I tested that in a playbook:
---
- hosts: localhost
gather_facts: false
vars:
project:
total_weight: 1000000
tasks:
- debug:
msg: "{{ '{:,}'.format(project.total_weight) }}"
The above will output:
TASK [debug] ******************************************************************************************
ok: [localhost] => {
"msg": "1,000,000"
}
This is documented in the docs for the format method:
The ',' option signals the use of a comma for a thousands separator. For a locale aware separator, use the 'n' integer presentation type instead.
i got a problem how to consume the following command output in Ansible
basically im trying to get the list of Active Directory OUs and loop over that list to search for specific name. My script works well when multiple OUs exists but i have an issue when only single OU exists. Explained below
tasks:
- name: PS - Pull System OUs from AD
win_command: powershell -
args:
stdin: "Get-ADOrganizationalUnit -LDAPFilter '(name=*)' -SearchScope 1 -SearchBase 'OU=SYSTEMS,DC=domain,DC=int' -Server domain.int | select-object name | ConvertTo-json"
become: yes
register: ou_reg_out
- name: Select Systems OU
block:
- name: Set list of standardized OUs as facts
set_fact:
ou_reg_list: "{{ ou_reg_out.stdout }}"
- name: Set System OU for system
set_fact:
ou_name: "OU={{item.name}}"
loop: "{{ ou_reg_list }}"
when: (item.name|upper) == (srv_type|upper)
when: ou_reg_out.stdout|length != 0
basically i need to be able to loop over the ou_reg_out.stdout.
It works when command returns multiple OUs as ou_reg_out.stdout returns list:
ou_reg_out.stdout:
- { name: OU1 }
- { name: OU2 }
issue is when only single OU exists , command doesnt return the list
ou_reg_out.stdout:
{ name: OU1 }
Any idea how to workaround this problem ?
Test the type of the variable and branch the code.
json_query filter helps to select the items from the list. Then ternary helps to conditionally select the value. The value of the first item that matches the condition is used. Defaults to 'NOTFOUND'.
For example the play bellow for both versions of ou_reg_list
- hosts: localhost
vars:
ou_reg_list:
- { name: OU1 }
- { name: OU2 }
# ou_reg_list:
# { name: OU1 }
srv_type: 'ou1'
tasks:
- set_fact:
ou_name: "OU={{ (ou_reg_list.name == srv_type|upper)|
ternary( ou_reg_list.name, 'NOTFOUND') }}"
when: ou_reg_list is mapping
- block:
- set_fact:
ou_names: "{{ ou_reg_list|json_query(query) }}"
vars:
query: "[?name=='{{ srv_type|upper }}'].name"
- set_fact:
ou_name: "OU={{ (ou_names|length > 0)|
ternary( ou_names.0, 'NOTFOUND') }}"
when: ou_reg_list is not mapping
- debug:
var: ou_name
gives
"ou_name": "OU=OU1"
I am trying to concatenate a string which is referenced as variable with a nested list
I looked into the options of using the set_fact and join but to no avail.
#config.yml
- name: concatenate
module_name:Test
state: present
port: {{ env_dc }}{{item.ports}}
with_items:
- "{{ my_list }}"
#group_vars\all.yml
env_dc: uk
my_list:
- {name: switch1, ports: [p1, p2, p3, p4]}
I am expecting the following output:
ukp1
ukp2
ukp3
ukp4
But I am getting;
"item": {
"ports": [
"p1",
"p2",
"p3",
"p4"
]
Actual Playbook:
Error message:
If you write this:
port: {{ env_dc }}{{item.ports}}
You are not producing a new list formated by concatenating the value in env_dc with each item in item.ports; you are simply creating a new string that has the contents of env_dc followed by the string representation of item.ports. That is, in your example, that would evaluate to something like:
uk['p1', 'p2', 'p3', 'p4']
You can solve this using the map filter (which can apply a filter to all items in a list) and the regex_replace filter, like this:
---
- hosts: localhost
gather_facts: false
vars:
env_dc: uk
my_list:
- name: switch1
ports:
- p1
- p2
- p3
- p4
tasks:
- debug:
msg: "ports: {{ item.ports|map('regex_replace', '^', env_dc)|list }}"
with_items: "{{ my_list }}"
Which given your example data would evaluate to:
TASK [debug] **********************************************************************************
ok: [localhost] => (item={u'name': u'switch1', u'ports': [u'p1', u'p2', u'p3', u'p4']}) => {
"msg": "ports: [u'ukp1', u'ukp2', u'ukp3', u'ukp4']"
}