Rerunning same Ansible task a few times - ansible

I am trying to implement a task that runs in a loop, for example, ten times.
Currently, the task runs only once.
I am trying something like the following:
my_role/tasks/main.yaml
---
- set_fact:
counter: 0
- name: "Iteration"
import_task: my_task.yaml
until: counter <= 10
...
my_role/tasks/my_task.yaml
---
- name: "task 1"
...
- name: "task 2"
...
- set_fact:
counter=={{ counter | int + 1 }}
...
I expected "task 1" and "task 2" to run ten times.
I would be happy to get some ideas on how to implement such loop.

You should use include_tasks module and loop over it.
my_role/tasks/main.yaml
---
- name: "Iteration"
ansible.builtin.include_tasks:
file: my_task.yaml
with_sequence: start=1 end=10
...
my_role/tasks/my_task.yaml
---
- name: "task 1"
...
- name: "task 2"
...
...

Ansible is an automation tool that is commonly used to configure and manage servers, networks, and other IT infrastructure. One of the key features of Ansible is its idempotence, which means that running the same task multiple times on the same system should produce the same end result, regardless of how many times the task is run.
Here is a plan on how to rerun the same Ansible task multiple times:
Create or open an existing Ansible playbook. A playbook is a YAML file that contains one or more plays, which are a list of tasks that are executed on a specific set of hosts.
Define the task that you want to rerun in the playbook using the Ansible modules. These modules are pre-built scripts that can perform specific actions on the remote systems, such as installing software, creating users, and configuring services.
Run the playbook using the ansible-playbook command. You can run the playbook on one or more specific hosts by specifying the -l option, or on all the hosts defined in your inventory file by using the -i option.
Ansible uses the idempotence feature to make sure that the same task is executed only once on the same host. If the task has already been executed and the state of the host hasn't changed, Ansible will not execute the task again.
If you want to force the task to rerun even if the state of the host hasn't changed, you can use the --force-handlers or --force-rerun options when running the ansible-playbook command.
To check the results of the tasks, you can check the log files generated by Ansible or the output of the ansible-playbook command.
Here is an example of how to run the same task multiple times using ansible-playbook command:
ansible-playbook playbook.yml --extra-vars "var1=value1 var2=value2"
You can run this command as many times as you want and Ansible will make sure that the task is executed only once, unless you use the --force-handlers or --force-rerun options.
Please note that if the task is modifying a state of the host, rerunning the task will have an effect and will change the state of the host.

How to implement task(s) that runs in a loop?
To do so, you may have a look into Loops and a minimal example like
---
- hosts: localhost
become: false
gather_facts: false
tasks:
- name: Example loop
debug:
msg: "{{ item }}"
loop: "{{ range(1, 11) | list }}"
register: result
resulting into an output of
TASK [Example loop] **********
ok: [localhost] => (item=1) =>
msg: 1
ok: [localhost] => (item=2) =>
msg: 2
ok: [localhost] => (item=3) =>
msg: 3
ok: [localhost] => (item=4) =>
msg: 4
ok: [localhost] => (item=5) =>
msg: 5
ok: [localhost] => (item=6) =>
msg: 6
ok: [localhost] => (item=7) =>
msg: 7
ok: [localhost] => (item=8) =>
msg: 8
ok: [localhost] => (item=9) =>
msg: 9
ok: [localhost] => (item=10) =>
msg: 10
whereby it is possible to replace the current single task debug against an import_task for a task file.
Further Documentation
Extended loop variables
What's the difference between include_tasks and import_tasks?

Related

Check if any task in block changed?

Is it possible to execute a task in Ansible only if at least one task in a specific block was changed?
So, something like this:
- name: Tasks block
block:
- name: Task 1
# ...
- name: Task 2
# ...
- name: Task 3
# ...
- name: Task 4
# ...
- name: Task 5
# ...
- name: Conditional task
when: block_result.changed
# ...
I know it is possible by registering a variable for each task in the block, then check every single one of them.
But is it possible to be done on the level of block? This is especially possible to avoid defining a lot of variables in case I have many tasks in one block.
My main goal is to evaluate the whole changed/not changed after the whole block is finished, so that the conditional task is executed only once if needed.
Your use case seems like the perfect fit for a handler, which are meant to run a task only once, even if multiple tasks are notifying them.
A typical use case, as presented in the documentation is when you are configuring a daemon and its configuration file(s), even if multiple tasks are changing some components, you want to be as less disruptive as possible and restart the daemon only once:
Notifying the same handler multiple times will result in executing the handler only once regardless of how many tasks notify it. For example, if multiple tasks update a configuration file and notify a handler to restart Apache, Ansible only bounces Apache once to avoid unnecessary restarts.
Source: https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_handlers.html#notifying-handlers
If you need to trigger it at this exact location in your playbook, you will also need a meta: flush_handlers task, in order to trigger the handlers right away, and not at the end of all the tasks.
tasks:
- block:
- name: Task 1
shell: "true"
changed_when: false
- name: Task 2
shell: "true"
changed_when: true
- name: Task 3
shell: "true"
changed_when: false
notify:
- Conditional task
- name: Flush handlers
meta: flush_handlers
- debug:
msg: I run after the handler
handlers:
- name: Conditional task
debug:
msg: Running this conditional task now
Output of a play with the above code:
TASK [Task 1] *****************************************************
ok: [localhost]
TASK [Task 2] *****************************************************
changed: [localhost]
TASK [Task 3] *****************************************************
ok: [localhost]
TASK [Flush handlers] *********************************************
RUNNING HANDLER [Conditional task] ********************************
ok: [localhost] =>
msg: Running this conditional task now
TASK [debug] ******************************************************
ok: [localhost] =>
msg: I run after the handler

Is there any way to validate whether a file is passed as extra vars in ansible-playbook

I have an ansible playbook which requires a JSON file to be passed as a --extra-vars (or -e ) from the command line. I would like to know if there is any way that I can add a validation task in my playbook to confirm that such a file is passed.
I have checked hostvars and did not find any parameter that stores such value or name of the file. It directly loaded the variables present inside the file.
I tried echo !! (to fetch the command history) using command and shell modules which didn't work either.
Sample command
ansible-playbook test.yml -e#input_vars.json
I want a task that will fail the execution if the JSON file is not passed in --extra-vars. Is it possible?
Even if it is not possible to find out where and when a variable was defined, it might be possible to check the content of the CLI option(s).
Whereby for the CLI options --tags, --skip-tags and --limit the Special Variables ansible_run_tags, ansible_skip_tags and ansible_limit are available, to access the command line argument --extra-vars used to invoke Ansible one would need to drop a tiny Custom Action Plugin.
After that a sample playbook
---
- hosts: test
become: false
gather_facts: true
tasks:
- name: Gather Arguments Values
get_argv:
- name: Show values
debug:
msg: "{{ ansible_facts.argv }}"
called via
ansible-playbook --extra-vars="test=test" extraVars.yml
results into an output of
TASK [Show values] *************
ok: [test.example.com] =>
msg:
- /usr/bin/ansible-playbook
- --extra-vars=test=test
- extraVars.yml
... if there is any way that I can add a validation task in my playbook to confirm that such a file is passed ... I want a task that will fail the execution if the JSON file is not passed in --extra-vars ...
You would need than just to include a check of the Ansible facts and Conditionals based on ansible_facts.
Further Documentation
Action plugins
Developing plugins - Action plugins
Action plugins included with Ansible Core
Python sys - System-specific parameters and functions
The above approach gives also the opportunity to check for multiple extra vars files like in the problem What is the variable order/precedence in case of multiple 'extra_vars' files?
ansible-playbook extraVars.yml --extra-vars="test=test" -e="test=TEST"
resulting into an output of
TASK [Show values] *************
ok: [test.example.com] =>
msg:
- /usr/bin/ansible-playbook
- extraVars.yml
- --extra-vars=test=test
- -e=test=TEST
TASK [Show content of var 'test'] *************
ok: [test.example.com] =>
msg: TEST
There is no method, function, lookup plugin, special variable, etc. to find out where a variable comes from. Without knowing the run-string you also can't tell whether extra variables are used or not.
The simplest method to determine whether extra variables are used might be based on the fact that extra vars are the highest precedence. For example, test if extra vars overrode a variable
shell> cat input_vars.yml
extra_vars: true
var1: foo
var2: bar
Declare the variable extra_vars also in the playbook
shell> cat test.yml
- hosts: localhost
vars:
extra_vars: false
tasks:
- assert:
that: extra_vars|bool
fail_msg: Extra vars missing. End of play.
- debug:
msg: |
var1: {{ var1 }}
var2: {{ var2 }}
The playbook fails if you run it without extra vars
shell> ansible-playbook test.yml
PLAY [localhost] *****************************************************************************
TASK [assert] ********************************************************************************
fatal: [localhost]: FAILED! => changed=false
assertion: extra_vars|bool
evaluated_to: false
msg: Extra vars missing. End of play.
The playbook will continue if extra vars are used
shell> ansible-playbook test.yml -e#input_vars.yml
PLAY [localhost] *****************************************************************************
TASK [assert] ********************************************************************************
ok: [localhost] => changed=false
msg: All assertions passed
TASK [debug] *********************************************************************************
ok: [localhost] =>
msg: |-
var1: foo
var2: bar

Ansible: What is the variable order/precedence in case of multiple 'extra_vars' files?

In this older question about "Can extra_vars receive multiple files?", the original poster answered the question, saying that multiple vars files could be accomplished by just using multiple --extra-vars parameters.
The followup question that I have is that, in such a case, where the ansible-playbook command line has two --extra-vars parameters, each pointing to a different file, what is the order or precedence of those files?
Also, what happens if both files have the same var name (e.g., my_host) in them?
For example, say I have 2 files, extraVars1.yml and extraVars2.yml and in the ansible-playbook command line I have:
ansible-playbook... --extra-vars "#extraVars1.yml" --extra-vars "#extraVars2.yml"
and the extraVars1.yml file has:
my_host: 1.2.3.4
and the extraVars2.yml file has:
my_host: 5.6.7.8
What will the value of the my_host var be when the playbook is run?
Thanks!
Jim
According the Ansible documentation about Using Variables and Understanding variable precedence
extra vars (for example, -e "user=my_user") (always win precedence)
In general, Ansible gives precedence to variables that were defined more recently ...
This means the last defined wins.
Lets have a short test here with a vars.yml playbook.
---
- hosts: localhost
become: false
gather_facts: false
vars:
my_host: 9.0.0.0
tasks:
- name: Show value
debug:
msg: "{{ my_host }}"
The execution of ansible-playbook vars.yml will result into an output of
TASK [Show value] ***
ok: [localhost] =>
msg: 9.0.0.0
The execution of ansible-playbook -e "#extraVars1.yml" vars.yml will result into an output of
TASK [Show value] ***
ok: [localhost] =>
msg: 1.2.3.4
The execution of ansible-playbook -e "#extraVars1.yml" -e "#extraVars2.yml" vars.yml will result into an output of
TASK [Show value] ***
ok: [localhost] =>
msg: 5.6.7.8
The execution of ansible-playbook -e "#extraVars2.yml" -e "#extraVars1.yml" vars.yml will result into an output of
TASK [Show value] ***
ok: [localhost] =>
msg: 1.2.3.4

How to run an ansible playbook only if a group exists?

I wonder if anyone found a solution that would avoid displaying any warnings if an inventory group is undefined or empty.
I just want to make a section of the playbook run if a group exists and is not empty, skipping without warnings if not.
Please read https://github.com/ansible/ansible/issues/35255#issuecomment-388455001 and test alternatives because I spend a good amount of time trying to find a workaround for this issue.
So far I was not able to find any way to avoid the warnings when group is not defined.
I'm slightly unsure if I'm answering the right question, but here goes. I'm interpreting "if a group exists and is not empty" to mean "the currently executing host belongs to a certain group".
If you meant to ask something like "can I find out from the current host if any other hosts belong to a group that the current host does not belong to," or "can I run a playbook without errors when the hosts defined for some groups are unreachable," then I'm afraid this doesn't answer your question :)
But running a task based on whether or not the current host belongs to a group can be done with one of Ansible's default vars, groups group_names.
The following playbook contains two tasks, one to run a debug task when the current host belongs to the group existent, and one to run a debug task when the current host belongs to the group nonexistent. As the output shows, the first task runs and the second does not.
hosts.yml
[existent]
localhost ansible_connection=local
playbook.yml
- hosts: all
gather_facts: true
tasks:
- name: This command will run.
debug:
msg: "The group `existent_1` exists!"
when:
- "'existent_1' in groups"
- name: This command will not run.
debug:
msg: "The group `existent_1` exists and this host is in it!"
when:
- "'existent_1' in groups"
- "'existent_1' in group_names"
- name: This command will run.
debug:
msg: "The group `existent_2` exists and this host is in it!"
when:
- "'existent_2' in groups"
- "'existent_2' in group_names"
- name: This command will not run.
debug:
msg: "The group `nonexistent` exists!"
when:
- "'nonexistent' in groups"
- "'nonexistent' in group_names"
Output
➜ ansible-playbook -i hosts.yml playbook.yml
PLAY [all] *********************************************************************
TASK [Gathering Facts] *********************************************************
ok: [localhost]
TASK [This command will run.] **************************************************
ok: [localhost] =>
msg: The group `existent_1` exists!
TASK [This command will not run.] **********************************************
skipping: [localhost]
TASK [This command will run.] **************************************************
ok: [localhost] =>
msg: The group `existent_2` exists and this host is in it!
TASK [This command will not run.] **********************************************
skipping: [localhost]
PLAY RECAP *********************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0
Not sure if this will suppress the warnings or not, but it should accomplish the first part ("make a section of the playbook run if a group exists and is not empty"):
- hosts: all
gather_facts: true
tasks:
- name: "This command will only run if {{ group_to_test }} is a non-empty group that exists"
debug:
msg: The group 'existent' exists and contains hosts!
when: group_to_test in groups and groups[group_to_test]
Give 'er a test and let me know if she works to suppress the warnings!
Obviously group_to_test must be replaced with a constant string or set as a variable/fact/default.

ansible playbook run a task only once

I need to run a task ,set a flag and the second time the play runs run the task only if the flag is not set
Play at a later stage
- name: Dump all databases
mysql_db:
state: dump
name: all
target: /root/mysql_all.sql
when: ansible_local.mysql.replication.setup is not defined
- name: create directory for ansible custom facts
file: state=directory recurse=yes path=/etc/ansible/facts.d
- name: install custom fact stating mysql is setup
template:
src: mysql.fact.j2
dest: /etc/ansible/facts.d/mysql.fact
The problem is that the fist time this play runs its throwing an error.
FAILED! => {"failed": true, "msg": "The conditional check 'ansible_local.mysql.replication.setup is not defined' failed. The error was: error while evaluating conditional (ansible_local.mysql.replication.setup is not defined): 'ansible_local' is undefined
What is the best way to run a task only in the first run and skip for subsequent runs.
you should make a task prior that registers if /root/mysql_all.sql exists then add it to your when clause.
Example:
- name: check if dump exists
stat:
path: /root/mysql_all.sql
register: mysqldump
- name: Dump all databases
mysql_db:
state: dump
name: all
target: /root/mysql_all.sql
when:
- ansible_local.mysql.replication.setup is not defined
- mysqldump.stat.exists == true
- name: create directory for ansible custom facts
file: state=directory recurse=yes path=/etc/ansible/facts.d
- name: install custom fact stating mysql is setup
template:
src: mysql.fact.j2
dest: /etc/ansible/facts.d/mysql.fact
One option is to utilize the fact cache. When enabled, you can set cacheable facts in your play and check for them.
Fact caching always takes place. There are various fact cache plug-ins available of which the memory plug-in is the default, and json file and redis cache are the most popular. You can only set one plug-in. Refer to
https://docs.ansible.com/ansible/latest/plugins/cache.html
When you want to explore with the json file you can set the environment variables as follows:
export ANSIBLE_CACHE_PLUGIN=jsonfile
export ANSIBLE_CACHE_PLUGIN_CONNECTION="~/ansiblefactcache"
In your play book you can check for facts and set them as cacheable, Refer to
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/set_fact_module.html
A small example playbook.yaml:
- name: Example Fact Cache Playbook
hosts: all
gather_facts: false # the default is true; it gathers various host facts
tasks:
- name: Runs when examplefact equals something
debug:
msg: "Runs when examplefact equals something"
when: ansible_facts['examplefact'] is defined and ansible_facts['examplefact'] == "something"
- name: Does not run when examplefact equals something
debug:
msg: "does not run when examplefact equals something"
when: ansible_facts['examplefact'] is not defined or ansible_facts['examplefact'] != "something"
- name: Set the examplefact to something
set_fact:
examplefact: "something"
cacheable: true
Note the usage of the cacheable instruction. When true the fact goes into the cache.
After having run this small playbook you will notice the creation of a localhost file in your home's subfolder ansiblefactcache which contains your cached facts.
Also note the usage of the gather_facts instruction. The default being true will scan your machine for various details such as environment variables, network details, etc. All are cached. You can play with it and see the localhost file being populated with it.
You can also try to edit the localhost file yourself or even delete it and run the play again.
I used the following inventory file inventory.yaml:
all:
hosts:
localhost:
ansible_connection: local
And I run ansible as follows:
ansible-playbook playbook.yaml -i inventory.yaml -vvv
First run yields the following:
PLAY [Example Fact Cache Playbook] ********************************************************************************************************************************************************************************
TASK [Runs when examplefact equals something] *********************************************************************************************************************************************************************
skipping: [localhost]
TASK [Does not run when examplefact equals something] *************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "does not run when examplefact equals something"
}
TASK [Set the examplefact to something] ***************************************************************************************************************************************************************************
ok: [localhost]
Second run yields the following:
PLAY [Example Fact Cache Playbook] ********************************************************************************************************************************************************************************
TASK [Runs when examplefact equals something] *********************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "Runs when examplefact equals something"
}
TASK [Does not run when examplefact equals something] *************************************************************************************************************************************************************
skipping: [localhost]
TASK [Set the examplefact to something] ***************************************************************************************************************************************************************************
ok: [localhost]

Resources