I need to run task that would check and create if missing folders on Ansible Control Node (where the ansible-playbook command is run) - next tasks will copy some specified files respectively to these local sub-folders:
I have a task:
tasks:
- name: Create local directory
file:
path: "remotes/{{ inventory_hostname }}"
state: directory
recurse: yes
delegate_to: localhost
tags:
localfolders
however when I run with --check it is going to "change" (create folders) on each remote:
TASK [Create local directory] ****************************************************************************************************************
changed: [ansible -> localhost]
changed: [remote1 -> localhost]
changed: [remote2 -> localhost]
Why it not runs task on local only?
Expected result is that on ansible host (only), following folders are created:
remotes/ansible
remotes/remote1
remotes/remote2
To get a better understand of Controlling where tasks run: delegation and local actions and run_once works, I've prepared a small test with an inventory file of
[test]
remote01.example.com
remote02.example.com
and a playbook local.yml
---
- hosts: test
become: false
gather_facts: false
tasks:
- name: Check where I am running on
delegate_to: localhost
shell:
cmd: "hostname && hostname -i"
register: result
run_once: true
- name: Show result
debug:
msg: "{{ result.stdout_lines }}"
run_once: false
executed on control.example.com node via
sshpass -p ${PASSWORD} ansible-playbook --user ${ACCOUNT} --ask-pass local.yml
resulting into an output of
PLAY [test] ********************************
TASK [Check where I am running on] *********
changed: [remote01.example.com -> localhost]
TASK [Show result] *************************
ok: [remote01.example.com] =>
msg:
- control.example.com
- 192.0.2.1
ok: [remote02.example.com] =>
msg:
- control.example.com
- 192.0.2.1
PLAY RECAP *************************************************************************************************
remote01.example.com : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
remote02.example.com : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Interesting Doc
IPv4 Address Blocks Reserved for Documentation
Related
I tried lookup on machine installed on it ansible and it works, but when uploading playbook to awx it does not work.
- name: get file
set_fact:
policer: "{{ lookup('file', 'file.txt') }}"
it gives An unhandled exception occurred while running the lookup plugin 'file'. Error was a <class 'ansible.errors.AnsibleError'>, original message: could not locate file in lookup"
although the file in the same repo of playbook, and have worked on machine but not awx. And if there is something specific to remote hosts how to know the path
Ansible Lookup plugins "execute and are evaluated on the Ansible control machine."
The play works fine when you run it at the localhost (control machine) where the file is located
shell> hostname
test_11
shell> cat /tmp/file.txt
content of file /tmp/file.txt
shell> cat pb1.yml
- hosts: localhost
vars:
policer: "{{ lookup('file', '/tmp/file.txt') }}"
tasks:
- debug:
var: policer
shell> ansible-playbook pb1.yml
PLAY [localhost] *****************************************************************************
TASK [debug] *********************************************************************************
ok: [localhost] =>
policer: content of file /tmp/file.txt
PLAY RECAP ***********************************************************************************
localhost: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
When you move to another controller (might be AWX) the file remains at the remote host (test_11 in the example) and is not available at the localhost controller. You can test it. See the block below
shell> hostname
awx
shell> cat /tmp/file.txt
cat: /tmp/file.txt: No such file or directory
shell> ssh admin#test_11 cat /tmp/file.txt
content of file /tmp/file.txt
shell> cat pb2.yml
- hosts: test_11
vars:
policer: "{{ lookup('file', '/tmp/file.txt') }}"
tasks:
- block:
- stat:
path: /tmp/file.txt
register: st
- debug:
var: st.stat.exists
delegate_to: localhost
- debug:
var: policer
shell> ansible-playbook pb2.yml
PLAY [test_11] *******************************************************************************
TASK [stat] **********************************************************************************
ok: [test_11 -> localhost]
TASK [debug] *********************************************************************************
ok: [test_11 -> localhost] =>
st.stat.exists: false
TASK [debug] *********************************************************************************
[WARNING]: Unable to find '/tmp/file.txt' in expected paths (use -vvvvv to see paths)
fatal: [test_11]: FAILED! =>
msg: 'An unhandled exception occurred while templating ''{{ lookup(''file'', ''/tmp/file.txt'') }}''. Error was a <class ''ansible.errors.AnsibleError''>, original message: An unhandled exception occurred while running the lookup plugin ''file''. Error was a <class ''ansible.errors.AnsibleError''>, original message: could not locate file in lookup: /tmp/file.txt. could not locate file in lookup: /tmp/file.txt'
PLAY RECAP ***********************************************************************************
test_11: ok=2 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
There are many options for how to fix it. The trivial one is moving also the file to the localhost controller. If the file remains at the remote host you can either read it by command or slurp, or fetch it. The module command always reports changed. The modules slurp and fetch are idempotent.
Read the file by command
shell> cat pb3.yml
- hosts: test_11
vars:
policer: "{{ out.stdout }}"
tasks:
- command: cat /tmp/file.txt
register: out
- debug:
var: policer
shell> ansible-playbook pb3.yml
PLAY [test_11] *******************************************************************************
TASK [command] *******************************************************************************
changed: [test_11]
TASK [debug] *********************************************************************************
ok: [test_11] =>
policer: content of file /tmp/file.txt
PLAY RECAP ***********************************************************************************
test_11: ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Read the file by slurp. This should be used for smaller files only because (quoting from slurp): "This module returns an ‘in memory’ base64 encoded version of the file, take into account that this will require at least twice the RAM as the original file size."
shell> cat pb4.yml
- hosts: test_11
vars:
policer: "{{ out.content|b64decode }}"
tasks:
- slurp:
path: /tmp/file.txt
register: out
- debug:
var: policer
shell> ansible-playbook pb4.yml
PLAY [test_11] *******************************************************************************
TASK [slurp] *********************************************************************************
ok: [test_11]
TASK [debug] *********************************************************************************
ok: [test_11] =>
policer: |-
content of file /tmp/file.txt
PLAY RECAP ***********************************************************************************
test_11: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The next option is to fetch the file to the local directory dest (will be created). By default, the file(s) is stored in the directory named by the remote host
shell> cat pb5.yml
- hosts: test_11
vars:
file_path: "/tmp/fetched_files/{{ inventory_hostname }}/tmp/file.txt"
policer: "{{ lookup('file', file_path) }}"
tasks:
- fetch:
src: /tmp/file.txt
dest: /tmp/fetched_files
- debug:
var: policer
shell> ansible-playbook pb5.yml
PLAY [test_11] *******************************************************************************
TASK [fetch] *********************************************************************************
changed: [test_11]
TASK [debug] *********************************************************************************
ok: [test_11] =>
policer: content of file /tmp/file.txt
PLAY RECAP ***********************************************************************************
test_11: ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
shell> cat /tmp/fetched_files/test_11/tmp/file.txt
content of file /tmp/file.txt
Notes
An absolute path to the file is used in the examples. See The magic of ‘local’ paths on how to use relative paths locally.
Is it possible during Ansible execution to add another host in the play, without starting a new play?
I am aware of the add_host module, but that requires the start of a new play to add the host, which is undesired.
No. By design, it's not possible to add hosts to 'in-flight play'. Quoting from the Summary of Ansible bug #59401:
By design, the in-flight play will not start running tasks on newly-added hosts, but it will stop running tasks on hosts that have disappeared. Newly-created hosts from an inventory refresh are immediately visible in ansible_play_hosts, even though they're not executing.
Notes
The bug claims refresh_inventory and add_host should have the same effects.
One might expect that the option refresh_inventory of the module meta does the job. The scenario would be:
Start a play
Modify the source of the inventory
Run - meta: refresh_inventory
Unfortunately, the example of the INI file below shows that this doesn't work. The host host03 is added to the inventory and to the list ansible_play_hosts_all as well. But, then, the following task debug doesn't run at this host. Play recap doesn't include this host either.
shell> cat hosts
[test]
host01
host02
The playbook below
shell> cat playbook.yml
- hosts: test
gather_facts: false
tasks:
- debug:
var: ansible_play_hosts_all
run_once: true
- community.general.ini_file:
path: hosts
section: test
option: "{{ item.host }}"
state: "{{ item.state }}"
allow_no_value: true
loop:
- {host: host03, state: present}
run_once: true
delegate_to: localhost
- meta: refresh_inventory
- debug:
var: ansible_play_hosts_all
run_once: true
- debug:
var: inventory_hostname
gives
shell> ansible-playbook -i hosts playbook.yml
PLAY [test] **********************************************************************************
TASK [debug] *********************************************************************************
ok: [host01] =>
ansible_play_hosts_all:
- host01
- host02
TASK [community.general.ini_file] ************************************************************
changed: [host01 -> localhost] => (item={'host': 'host03', 'state': 'present'})
TASK [meta] **********************************************************************************
TASK [debug] *********************************************************************************
ok: [host01] =>
ansible_play_hosts_all:
- host01
- host02
- host03
TASK [debug] *********************************************************************************
ok: [host01] =>
inventory_hostname: host01
ok: [host02] =>
inventory_hostname: host02
PLAY RECAP ***********************************************************************************
host01 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
host02 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
When I run this playbook, its not finding ansible_uptime_seconds variable. But ansible hostname -m setup gives this variable. I am using ansible 2.9.23 version.
- hosts: all
become: yes
become_method: sudo
gather_facts: yes
tasks:
- name: Print all available facts
ansible.builtin.debug:
var: ansible_facts
Getting this message
'ansible_uptime_seconds' is undefined
How to get this value in the play book?
The fact name is uptime_seconds, when facts are collected without setup module. however its "ansible_uptime_seconds" when collected with setup module.
---
- name: Sample playbook
connection: local
# gather_facts: false
hosts: localhost
tasks:
- name: print uptime sec
debug:
msg: "{{ ansible_facts.uptime_seconds }}"
Output of the above playbook is:
PLAY [Sample playbook] *********************************************************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************************************************************
ok: [localhost]
TASK [print uptime sec] **********************************************************************************************************************************************************
ok: [localhost] => {
"msg": "172603"
}
PLAY RECAP *********************************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
I want to validate few things before i run my main play in Ansible. For example below command is taking 2 input arguments from the user so I want to validate them before executing the main tasks.
ansible-playbook -i my-inventory my-main.yml --tags=repodownload -e release_version=5.0.0-07 -e target_env=dev/prod/preprod
In the above case, release_version should not be empty and target_env must be these type of values -
5.0.0.34
I want to display a message to user about what is wrong. How do i achieve it?
Any help is appreciated.
If you absolutely need the user to provide the variables, I would first of all use vars_prompt so that the variable value is asked interactively if user forgot to provide them as extra vars. This also makes a good inline documentation.
Then you can use pre_tasks to validate the input that was provided, either interactively or as an extra var. For validation, I usually use the fail module. The point here is to use run_once: true to force the test to run only once even if there are several hosts in your play.
Here is an example based on your input. Adapt to your exact needs
---
- name: Prompt and validation demo
hosts: all
gather_facts: false
vars:
_allowed_envs:
- dev
- preprod
- prod
vars_prompt:
- name: release_version
prompt: "What is the release version ? [w.x.y-z]"
private: no
- name: target_env
prompt: "What is the target environment ? [{{ _allowed_envs | join(', ') }}]"
private: no
pre_tasks:
- name: Make sure version is ok
fail:
msg: >-
Release version is not formatted correctly. Please make sure
it is of the form w.x.y-zz
when: not release_version is regex('\d*(\.\d*){2}-\d\d')
run_once: true
- name: Make sure target_env is allowed
fail:
msg: >-
Environment "{{ target_env }}" is not allowed.
Please choose a target environment in {{ _allowed_envs | join(', ') }}
when: not target_env in _allowed_envs
run_once: true
tasks:
- name: "Dummy task just to have a complete playbook for the example"
debug:
msg: "Deploying version {{ release_version }} for environment {{ target_env }} on {{ inventory_hostname }}"
And here are some examples launching the playbook:
##########################
# Fully interactive runs #
##########################
$ ansible-playbook -i localhost, playbook.yml
What is the release version ? [w.x.y-z]: wrong
What is the target environment ? [dev, preprod, prod]: prod
PLAY [Prompt and validation demo] ************************************
TASK [Make sure version is ok] ***************************************
fatal: [localhost]: FAILED! => {"changed": false, "msg": "Release version is not formatted correctly. Please make sure it is of the form w.x.y-zz"}
NO MORE HOSTS LEFT ***************************************************
PLAY RECAP **********************************************************
localhost : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
$ ansible-playbook -i localhost, playbook.yml
What is the release version ? [w.x.y-z]: 1.2.3-44
What is the target environment ? [dev, preprod, prod]: dev
PLAY [Prompt and validation demo] ************************************
TASK [Make sure version is ok] ***************************************
skipping: [localhost]
TASK [Make sure target_env is allowed] *******************************
skipping: [localhost]
TASK [Dummy task just to have a complete playbook for the example] ***
ok: [localhost] => {
"msg": "Deploying version 1.2.3-44 for environment dev on localhost"
}
PLAY RECAP ***********************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
###############
# Hybrid run #
###############
$ ansible-playbook -i localhost, playbook.yml -e target_env=prod
What is the release version ? [w.x.y-z]: 1.2.3-44
PLAY [Prompt and validation demo] ************************************
TASK [Make sure version is ok] ***************************************
skipping: [localhost]
TASK [Make sure target_env is allowed] *******************************
skipping: [localhost]
TASK [Dummy task just to have a complete playbook for the example] ***
ok: [localhost] => {
"msg": "Deploying version 1.2.3-44 for environment prod on localhost"
}
PLAY RECAP ***********************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
###################
# Fully automated #
###################
$ ansible-playbook -i localhost, playbook.yml -e target_env=prod -e release_version=1.2.3-44
PLAY [Prompt and validation demo] ************************************
TASK [Make sure version is ok] ***************************************
skipping: [localhost]
TASK [Make sure target_env is allowed] *******************************
skipping: [localhost]
TASK [Dummy task just to have a complete playbook for the example] ***
ok: [localhost] => {
"msg": "Deploying version 1.2.3-44 for environment prod on localhost"
}
PLAY RECAP ***********************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
I have the following folder-structure, inspired by the best-practices section in Ansibles documentation:
my-playbook.yml
my-role
|
|── tasks
|
|── my-task.yml
I have tagged the tasks within the my-task.yml file which is part of a role. I execute the playbook using ansible-playbook.yml --tags "mytag". Unfortunately, all tasks are skipped. Can I only filter tasks directly part of the playbook?
Within my playbook, I do something like
- hosts: ansible_server
connection: local
gather_facts: no
roles:
- validate_properties
Thanks in advance!
What you should do is call the role from a task by using the include_role module. On that task you can apply tags. Take this playbook, for example:
---
- name: Tag role test
hosts: local
connection: local
gather_facts: no
tasks:
- include_role:
name: debug
tags:
- dont_run
- debug:
msg: Solo shot first
tags:
- run
Where my role/debug consists of just a task that prints Hello, world!.
If you call this playbook directly you get this output:
PLAY [Tag role test]
TASK [debug : debug]
ok: [localhost] =>
msg: Hello, world!
TASK [debug]
ok: [localhost] =>
msg: Solo shot first
PLAY RECAP
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
But, if you exclude the dont_run task like this:
ansible-playbook tag_roles.yml --skip-tags dont_run
This is the output:
PLAY [Diff test]
TASK [debug]
ok: [localhost] =>
msg: Solo shot first
PLAY RECAP
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
I hope it helps.
You have to tag the subtasks also with your tag u want to run to:
Main Task:
- name: "test tags on sub task"
include_tasks: subtask.yml
with_items: "{{ myList }}"
loop_control:
label: item
tags: test
Sub task:
debug: msg="Sub Task"
tags: test