How can we execute only new task in Ansible playbook? - ansible

We have bit huge ansible tasks in main.yaml and we don't need to execute all tasks, only new task 'House Keep Task' is enough. So tried '--start-at-task' option as below, but Ansible can't find that task;
command :
ansible-playbook -u sysadmin -i ./inventories/dev/hosts --start-at-task='House Keep Task' --step batchservers.yml -K
message :
[ERROR]: No matching task "House Keep Task" found. Note: --start-at-task can only follow static includes.
batchserver.yaml
---
- hosts: batchservers
become: true
tasks:
- import_role:
name: batchservers
tags: [batch]
ansible/roles/batchservers/tasks/main.yaml
---
- name: Add SHARED_DATA_PATH env
lineinfile:
dest: ~/.bash_profile
line: export SHARED_DATA_PATH=/data
- name: Create /data if it does not exist
file:
path: /data
state: directory
mode: og+w
... other tasks include reboot task ...
- name: House Keep Task
cron:
name: "House Keep Task"
user: "{{ batch_user }}"
special_time: daily
job: "/usr/bin/find /var/log -name '*.log' -mtime +6 -type f -delete"
state: present
Is there any good way to execute particular task, House Keep Task?
Our ansible version is core 2.11.12.
Any advice would be highly apprciated.

Q: "Execute particular task House Keep Task."
A: The ansible-playbook option --start-at-task works as expected
--start-at-task 'START_AT_TASK'
start the playbook at the task matching this name
Given the tree
shell> tree .
.
├── ansible.cfg
├── hosts
├── pb.yml
└── roles
└── batchservers
└── tasks
└── main.yaml
3 directories, 4 files
The playbook
shell> cat pb.yml
- hosts: localhost
gather_facts: false
tasks:
- import_role:
name: batchservers
and the role
shell> cat roles/batchservers/tasks/main.yaml
- name: Add SHARED_DATA_PATH env
debug:
msg: Add SHARED_DATA_PATH env
- name: Create /data if it does not exist
debug:
msg: Create /data if it does not exist
- name: House Keep Task
debug:
msg: House Keep Task
give
shell> ansible-playbook pb.yml --start-at-task 'House Keep Task'
PLAY [localhost] *****************************************************************************
TASK [batchservers : House Keep Task] ********************************************************
ok: [localhost] =>
msg: House Keep Task
PLAY RECAP ***********************************************************************************
localhost: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Related

Force Ansible to reevaluate contents of an imported playbook as a task after main playbook that does the importing has started

Please consider the following scenario. An Ansible playbook, that uses a serries of ansible.builtin.import_playbook module calls, to chain other ansible playbooks together. We'll call this playbook main.yaml
...
- name: Import Ansible Playbook A
ansible.builtin.import_playbook: playbook_a.yaml
- name: Import Ansible Playbook B
ansible.builtin.import_playbook: playbook_b.yaml
...
My playbook_a.yaml calls a bash script, that eventually makes some changes in playbook_b.yaml using sed. In this current iteration, once main.yaml is started, my understanding is it reads the contents of all the files, including those imported via the ansible.builtin.import_playbook module, at the start of the playbook run, so any changes introduced into of those files after the start are not considered and are ignored. Essentially it plays them as they were, and not as they have become during the play.
My question is, is there a way for me to force main.yaml to reevaluate the conets of playbook_b.yaml, perhaps by adding another task between the import of playbook_a.yaml and playbook_b.yaml that would accomplish this?
Obviously I can run playbook_a.yaml seperatly, before starting main.yaml to avoid this issue, but my hope is to contain it all in one play.
TIA
It's not possible to reread imported playbooks. Instead, you can modify included tasks in the imported playbooks. For example, given the tree
shell:> tree .
.
├── ansible.cfg
├── hosts
├── pb_a.yml
├── pb_b.yml
├── pb.yml
├── tasks
│   ├── a_tasks.yml
│   └── b_tasks.yml
└── templates
├── tasks_1.yml.j2
└── tasks_2.yml.j2
Create the playbooks
shell> cat pb.yml
- hosts: localhost
vars:
playbook_updates:
- {dest: tasks/a_tasks.yml, template: tasks_1.yml.j2}
- {dest: tasks/b_tasks.yml, template: tasks_2.yml.j2}
tasks:
- name: Update playbooks
template:
src: "{{ item.template }}"
dest: "{{ playbook_dir }}/{{ item.dest }}"
loop: "{{ playbook_updates }}"
- import_playbook: pb_a.yml
- import_playbook: pb_b.yml
shell> cat pb_a.yml
- hosts: localhost
tasks:
- include_tasks: tasks/a_tasks.yml
shell> cat pb_b.yml
- hosts: localhost
tasks:
- include_tasks: tasks/b_tasks.yml
The first play updates the tasks in the imported playbooks according to the list playbook_updates. Create the templates, e.g.
shell> cat templates/tasks_1.yml.j2
- debug:
msg: tasks 1
shell> cat templates/tasks_2.yml.j2
- debug:
msg: tasks 2
Then, the playbook gives
shell> ansible-playbook pb.yml
PLAY [localhost] ****************************************************************************
TASK [Update playbooks] *********************************************************************
changed: [localhost] => (item={'dest': 'tasks/a_tasks.yml', 'template': 'tasks_1.yml.j2'})
changed: [localhost] => (item={'dest': 'tasks/b_tasks.yml', 'template': 'tasks_2.yml.j2'})
PLAY [localhost] ****************************************************************************
TASK [include_tasks] ************************************************************************
included: /export/scratch/tmp7/test-100/tasks/a_tasks.yml for localhost
TASK [debug] ********************************************************************************
ok: [localhost] =>
msg: tasks 1
PLAY [localhost] ****************************************************************************
TASK [include_tasks] ************************************************************************
included: /export/scratch/tmp7/test-100/tasks/b_tasks.yml for localhost
TASK [debug] ********************************************************************************
ok: [localhost] =>
msg: tasks 2
PLAY RECAP **********************************************************************************
localhost: ok=5 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
You can modify the included tasks by modifying the templates and variables.
After some more testing I have come up with two solutions that accomplish what I was hoping to accomplish. One is more of a a hacky solution and the other more of a proper Ansible solution.
Hacky Solution
Back in the main.yaml, instead of importing playbook_b.yaml directly, we import a new playbook, let's call it playbook_c.yaml,
...
- name: Import Ansible Playbook A
ansible.builtin.import_playbook: playbook_a.yaml
- name: Import Ansible Playbook C
ansible.builtin.import_playbook: playbook_c.yaml
...
which in turn only contains the following task:
...
tasks:
1. name: Call Playbook B
ansible.builtin.shell: ansible-playbook playbook_b.yaml
args:
executable: /bin/bash
register: call_playbook_b
changed_when: call_playbook_b != 0
Since ansible will not know any information regading playbook_b.yaml until playbook_c.yaml calls it using a bash command of ansible-playbook playbook_b.yaml, any changes introduced into playbook_b.yaml by playbook_a.yaml will be respected at the time the playbook is called. This feels cheeky but it works, with one major drawback, we don't get to see the output of playbook_b.yaml. We only see the output of playbook_a.yaml and playbook_c.yaml when we run main.yaml
Proper Solution
We leave our main.yaml as is
...
- name: Import Ansible Playbook A
ansible.builtin.import_playbook: playbook_a.yaml
- name: Import Ansible Playbook B
ansible.builtin.import_playbook: playbook_B.yaml
...
but modify the bash script called by playbook_a.yaml to make the sed changes not into playbook_b.yaml directly, but into the inventory file described in ansible.cfg, essentially adding a variable to the host(s) that playbook_b.yaml is playing against, like a dynamic inventory file. Then we add a pre_tasks to playbook_b.yaml before the tasks section, like so
...
pre_tasks:
- name: Refresh Inventory File
ansible.builtin.meta: refresh_inventory
tasks:
...
forcing ansible to reread the inventory file taking into the account the new values of the variables that were seded by the bash script called by playbook_a.yaml. Afterwards the variables can easily be referenced in playbook_b.yaml and they will take the latest value introduced after the start of main.yaml and playbook_a.yaml.

Ansible pattern matching doesn't work as expected

I am unable to find why this simple pattern doesn't seem to match anything. I have two Ansible hosts as targets. This is my inventory file:
[web_Xubuntu]
192.168.160.128
[database_Fedora]
192.168.160.132
And this is what my YAML playbook looks like:
# Hosts: where our play will run and options it will run with
hosts: *Fedora
become: True
#gather_facts: False
# Vars: variables that will apply to the play, on all target systems
vars:
motd: "Welcome to Fedora Linux - Ansible Rocks\n"
# Tasks: the list of tasks that will be executed within the playbook
tasks:
- name: Configure a MOTD (message of the day)
copy:
content: "{{ motd }}"
dest: /etc/motd
notify: MOTD changed
# Handlers: the list of handlers that are executed as a notify key from a task
handlers:
- name: MOTD changed
debug:
msg: The MOTD was changed
On processing this playbook, Ansible reports the following error:
ERROR! We were unable to read either as JSON nor YAML, these are the errors we got from each:
JSON: Expecting value: line 1 column 1 (char 0)
Syntax Error while loading YAML.
found undefined alias
The offending line appears to be:
# Hosts: where our play will run and options it will run with
hosts: *Fedora
^ here
What is the right way to use a wildcard?
You can use the asterisk *( wildcard) with FQDN or IP only. For example,
192.0.*
*.example.com
*.com
See Patterns: targeting hosts and groups.
Use the inventory plugin constructed if you want to run all *Fedora groups. See
shell> ansible-doc -t inventory constructed
For example, given the tree
shell> tree .
.
├── ansible.cfg
├── inventory
│   ├── 01-hosts
│   └── 02-constructed.yml
└── pb.yml
1 directory, 4 files
the inventory
shell> cat inventory/01-hosts
[web_Xubuntu]
192.168.160.128
[database_Fedora]
192.168.160.132
[web_Fedora]
192.168.160.133
the contructed plugin
shell> cat inventory/02-constructed.yml
plugin: constructed
groups:
Fedora: group_names|select('regex', '^.*Fedora$')
Test the inventory
shell> ansible-inventory -i inventory --list --yaml
all:
children:
Fedora:
hosts:
192.168.160.132: {}
192.168.160.133: {}
database_Fedora:
hosts:
192.168.160.132: {}
ungrouped: {}
web_Fedora:
hosts:
192.168.160.133: {}
web_Xubuntu:
hosts:
192.168.160.128: {}
Then, test the playbook
shell> cat pb.yml
- hosts: Fedora
gather_facts: false
tasks:
- debug:
var: inventory_hostname
gives
shell> ansible-playbook -i inventory pb.yml
PLAY [Fedora] *********************************************************************************
TASK [debug] **********************************************************************************
ok: [192.168.160.132] =>
inventory_hostname: 192.168.160.132
ok: [192.168.160.133] =>
inventory_hostname: 192.168.160.133
PLAY RECAP ************************************************************************************
192.168.160.132: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.160.133: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

How to parses multiple variables within ansible [duplicate]

I have an Ansible playbook like the one below, I want use nested variable like this:
msg={{{{Component}}.community_release_num}}, but when I run playbook:
ansible-playbook vartest.yml -e 'version=version_402', it not work
[es#vpn-server nested-var]$ tree
.
├── vars
│   ├── horizon.yml
│   └── version_402.yml
└── vartest.yml
1 directory, 3 files
[es#vpn-server nested-var]$ cat vartest.yml
---
- name: test
hosts: localhost
vars_files:
- vars/{{version}}.yml
tasks:
- debug: msg={{{{Component}}.community_release_num}}
- debug: msg={{{{Component}}.release_num}}
[es#vpn-server nested-var]$ cat vars/horizon.yml
Component: horizon
[es#vpn-server nested-var]$ cat vars/version_402.yml
- horizon:
community_release_num: '9.0.1'
release_num: '4.0.2'
[es#vpn-server nested-var]$
error messages
[es#vpn-server nested-var]$ ansible-playbook vartest.yml -e 'version=version_402'
/usr/lib64/python2.6/site-packages/cryptography/__init__.py:25: DeprecationWarning: Python 2.6 is no longer supported by the Python core team, please upgrade your Python.
DeprecationWarning
PLAY [test] *******************************************************************************************************
/usr/lib64/python2.6/site-packages/Crypto/Util/number.py:57: PowmInsecureWarning: Not using mpz_powm_sec. You should rebuild using libgmp >= 5 to avoid timing attack vulnerability.
_warn("Not using mpz_powm_sec. You should rebuild using libgmp >= 5 to avoid timing attack vulnerability.", PowmInsecureWarning)
TASK [debug] ******************************************************************************************************
fatal: [localhost]: FAILED! => {"failed": true, "msg": "template error while templating string: expected token 'colon', got '}'. String: {{{{Component}}.community_release_num}}"}
to retry, use: --limit #/data/wangqian/artemis-code-test/artemis/ansible/update/nested-var/vartest.retry
PLAY RECAP ********************************************************************************************************
localhost : ok=0 changed=0 unreachable=0 failed=1
Can Ansible use nested variable, if yes, how to use it?
Per Ansible FAQ:
Another rule is ‘moustaches don’t stack’. We often see this:
{{ somevar_{{other_var}} }}
The above DOES NOT WORK, if you need to use a dynamic variable use the
hostvars or vars dictionary as appropriate:
{{ hostvars[inventory_hostname]['somevar_' + other_var] }}
So in your case:
- debug: msg={{hostvars[inventory_hostname][Component].community_release_num}}
Or:
- debug: msg={{vars[Component].community_release_num}}
Or (since Ansible 2.5):
- debug: msg={{(lookup('vars', Component)).community_release_num}}
Run this command:
ansible-playbook 1_lambda-facts.yml -e "func_name=mylambdaFunctionName"
- name: get lamda Info
hosts: localhost
connection: local
become: yes
gather_facts: true
tasks:
- name: List all for a specific function
lambda_facts:
query: all
function_name: "{{func_name}}"
- name: debug info
debug:
msg: "{{lambda_facts}}"
msg: "variable name is: {{vars['func_name']}}"
msg: "{{lambda_facts['function'][vars['func_name']]['function_arn']}}"

Change where Ansible looks for files

My directory structure looks like this
playbooks/
Foo.yml
tasks/
Task1.yml
Task2.yml
AllTasks.yml
The Foo.yml playbook has a - import_tasks: tasks/AllTasks.yml task. AllTasks.yml has
- import_tasks: tasks/Task1.yml
- import_tasks: tasks/Task2.yml
This works perfectly fine when I execute playbook Foo.yml. But when I execute a playbook located elsewhere (so not directly in this playbooks directory), the imports no longer work. The reason for this is that they import relative to the location of the imported playbook.
The same happens with tasks using other modules, such as copy. They look for files relative to the playbook location.
Is there a way to make my tasks work for playbooks located in different directories?
I know there is a playbook_dir variable which sadly I cannot override. I also came across inventory_dir, but for whatever reason that one is not defined.
A way to reference files relative to the file the reference is made in would work. Example:
- import_tasks: "{{ current_dir }}/Task1.yml"
- import_tasks: "{{ current_dir }}/Task2.yml"
Something relative to the inventory file of this project would also work. Example:
- import_tasks: "{{ inventory_dir }}/playbooks/tasks/Task1.yml"
- import_tasks: "{{ inventory_dir }}/playbooks/tasks/Task2.yml"
This latter approach would force me to add these paths all over the project though.
Q: "playbook_dir variable which sadly I cannot override"
A: The variable playbook_dir works as expected (but it's not needed in this case; see the second part under the line)
shell> cd /scratch/tmp
shell> cat Foo.yml
- hosts: localhost
tasks:
- import_tasks: "{{ playbook_dir }}/tasks/AllTasks.yml"
shell> cat tasks/AllTasks.yml
- import_tasks: "{{ playbook_dir }}/tasks/Task1.yml"
- import_tasks: "{{ playbook_dir }}/tasks/Task2.yml"
shell> cat tasks/Task1.yml
- debug:
msg: Task1.yml
shell> cat tasks/Task2.yml
- debug:
msg: Task2.yml
shell> cd /tmp
shell> pwd
/tmp
shell> ansible-playbook /scratch/tmp/Foo.yml
PLAY [localhost] ****
TASK [debug] ****
ok: [localhost] => {
"msg": "Task1.yml"
}
TASK [debug] ****
ok: [localhost] => {
"msg": "Task2.yml"
}
PLAY RECAP ****
localhost: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Q: "Change where Ansible looks for files"
A: No changes are needed. The defaults work fine. See Search paths in Ansible. The values of the variable ansible_search_path below explain why the paths are working
shell> cat Foo.yml
- hosts: localhost
tasks:
- debug:
var: ansible_search_path
- import_tasks: tasks/AllTasks.yml
shell> cat tasks/AllTasks.yml
- debug:
var: ansible_search_path
- import_tasks: Task1.yml
- import_tasks: Task2.yml
shell> cat tasks/Task1.yml
- debug:
msg: Task1.yml
shell> cat tasks/Task2.yml
- debug:
msg: Task2.yml
give
"ansible_search_path": [
"/scratch/tmp"
]
"ansible_search_path": [
"/scratch/tmp/tasks",
"/scratch/tmp"
]
"msg": "Task1.yml"
"msg": "Task2.yml"

How can I use Ansible nested variable?

I have an Ansible playbook like the one below, I want use nested variable like this:
msg={{{{Component}}.community_release_num}}, but when I run playbook:
ansible-playbook vartest.yml -e 'version=version_402', it not work
[es#vpn-server nested-var]$ tree
.
├── vars
│   ├── horizon.yml
│   └── version_402.yml
└── vartest.yml
1 directory, 3 files
[es#vpn-server nested-var]$ cat vartest.yml
---
- name: test
hosts: localhost
vars_files:
- vars/{{version}}.yml
tasks:
- debug: msg={{{{Component}}.community_release_num}}
- debug: msg={{{{Component}}.release_num}}
[es#vpn-server nested-var]$ cat vars/horizon.yml
Component: horizon
[es#vpn-server nested-var]$ cat vars/version_402.yml
- horizon:
community_release_num: '9.0.1'
release_num: '4.0.2'
[es#vpn-server nested-var]$
error messages
[es#vpn-server nested-var]$ ansible-playbook vartest.yml -e 'version=version_402'
/usr/lib64/python2.6/site-packages/cryptography/__init__.py:25: DeprecationWarning: Python 2.6 is no longer supported by the Python core team, please upgrade your Python.
DeprecationWarning
PLAY [test] *******************************************************************************************************
/usr/lib64/python2.6/site-packages/Crypto/Util/number.py:57: PowmInsecureWarning: Not using mpz_powm_sec. You should rebuild using libgmp >= 5 to avoid timing attack vulnerability.
_warn("Not using mpz_powm_sec. You should rebuild using libgmp >= 5 to avoid timing attack vulnerability.", PowmInsecureWarning)
TASK [debug] ******************************************************************************************************
fatal: [localhost]: FAILED! => {"failed": true, "msg": "template error while templating string: expected token 'colon', got '}'. String: {{{{Component}}.community_release_num}}"}
to retry, use: --limit #/data/wangqian/artemis-code-test/artemis/ansible/update/nested-var/vartest.retry
PLAY RECAP ********************************************************************************************************
localhost : ok=0 changed=0 unreachable=0 failed=1
Can Ansible use nested variable, if yes, how to use it?
Per Ansible FAQ:
Another rule is ‘moustaches don’t stack’. We often see this:
{{ somevar_{{other_var}} }}
The above DOES NOT WORK, if you need to use a dynamic variable use the
hostvars or vars dictionary as appropriate:
{{ hostvars[inventory_hostname]['somevar_' + other_var] }}
So in your case:
- debug: msg={{hostvars[inventory_hostname][Component].community_release_num}}
Or:
- debug: msg={{vars[Component].community_release_num}}
Or (since Ansible 2.5):
- debug: msg={{(lookup('vars', Component)).community_release_num}}
Run this command:
ansible-playbook 1_lambda-facts.yml -e "func_name=mylambdaFunctionName"
- name: get lamda Info
hosts: localhost
connection: local
become: yes
gather_facts: true
tasks:
- name: List all for a specific function
lambda_facts:
query: all
function_name: "{{func_name}}"
- name: debug info
debug:
msg: "{{lambda_facts}}"
msg: "variable name is: {{vars['func_name']}}"
msg: "{{lambda_facts['function'][vars['func_name']]['function_arn']}}"

Resources