Ansible pattern matching doesn't work as expected - ansible

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

Related

ansible host variable not working for file module with content

My inventory file looks like below
[all]
1.1.1.1 var1=vaule1 app_var2=vaule2
2.2.2.2 var1=vaule3 app_var2=vaule4
My task playbook, to create file if not exists and add content using host variable
- name: "Creating file_with content"
ansible.builtin.file:
path: "path-tofile/.test.txt"
state: touch
content: "app_env=hostvars[inventory_hostname]['var1']"
register: app_env_status
File is created with empty content, can somebody help here, does i am access variable correctly or my task has any issues.
There is no parameter content in the module file. Having run the task below
- file:
path: /tmp/test.txt
state: touch
content: "{{ var1 }}"
you must have seen the error message:
fatal: ... Unsupported parameters for (file) module: content. ...
Instead of file, use the module copy. The task below does the job
- copy:
dest: /tmp/test.txt
content: "{{ var1 }}"
Example of a complete project for testing
shell> tree .
.
├── ansible.cfg
├── hosts
└── pb.yml
0 directories, 3 files
shell> cat ansible.cfg
[defaults]
gathering = explicit
collections_path = $HOME/.local/lib/python3.9/site-packages/
inventory = $PWD/hosts
roles_path = $PWD/roles
remote_tmp = ~/.ansible/tmp
retry_files_enabled = false
stdout_callback = yaml
shell> cat hosts
[test]
test_11 var1=v1 var2=v2
test_13 var1=v3 var2=v4
[test:vars]
ansible_connection=ssh
ansible_user=admin
ansible_become=yes
ansible_become_user=root
ansible_become_method=sudo
ansible_python_interpreter=/usr/local/bin/python3.8
ansible_perl_interpreter=/usr/local/bin/perl
shell> ansible-playbook pb.yml
PLAY [all] ***********************************************************************************
TASK [copy] **********************************************************************************
changed: [test_11]
changed: [test_13]
PLAY RECAP ***********************************************************************************
test_11: ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
test_13: ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
shell> ssh admin#test_11 cat /tmp/test.txt
v1
shell> ssh admin#test_13 cat /tmp/test.txt
v3

Ansible : Select inventory file out of multiple hosts.ini for three environments based on the version passed as a variable

I have a task , in which I need to select the target environment/hosts amongst the multiple inventory files placed in different folders under inventories/ directory. The selection is based on the version passed as a variable.
inventories/
preprod/
group_vars/
hosts.ini
...
systest/
group_vars/
hosts.ini
...
uat/
group_vars/
hosts.ini
...
So if the version varaiable is 1.x.x then the hosts.ini under preprod should be selected, if its 2.x.x then the hosts.ini under systest should be selected and if its 3.x.x the hosts.ini under uat should be selected.
---
- name: Select hosts from multiple inventories based on version passed as a variable
hosts: all
vars:
version: '1.0'
tasks:
- name: Get hosts from first inventory
set_fact:
hosts_1: "{{ groups['inventory_1'] | selectattr('version', 'equalto', version) | map(attribute='hostname') | list }}"
- name: Get hosts from second inventory
set_fact:
hosts_2: "{{ groups['inventory_2'] | selectattr('version', 'equalto', version) | map(attribute='hostname') | list }}"
- name: Merge hosts from both inventories
set_fact:
hosts: "{{ hosts_1 + hosts_2 }}"
- name: Run tasks with hosts
debug:
msg: "{{ item }}"
loop: "{{ hosts }}"
Short answer: Copy the inventory file in the first play, refresh_inventory, and use it in the second play. Unfortunately, this does not refresh group_vars from the directories. As a workaround put the group_vars into the inventory files.
Details: Given the project for testing
shell> tree .
.
├── ansible.cfg
├── hosts.env
├── inventory
│   ├── preprod
│   │   └── hosts.ini
│   ├── systest
│   │   └── hosts.ini
│   └── uat
│   └── hosts.ini
└── pb.yml
4 directories, 6 files
Put the group_vars into the inventory files
shell> cat inventory/preprod/hosts.ini
[all]
preprod1
preprod2
preprod3
[all:vars]
env_gv=preprod
shell> cat inventory/systest/hosts.ini
systest1
systest2
systest3
[all:vars]
env_gv=systest
shell> cat inventory/uat/hosts.ini
[all]
uat1
uat2
uat3
[all:vars]
env_gv=uat
The playbook
shell> cat pb.yml
- hosts: localhost
vars:
envs:
1: preprod
2: systest
3: uat
env: "{{ envs[version.split('.').0|int] }}"
tasks:
- debug:
msg: "Use environment: {{ env }}"
- copy:
src: "{{ playbook_dir }}/inventory/{{ env }}/hosts.ini"
dest: "{{ playbook_dir }}/hosts.env"
- meta: refresh_inventory
- hosts: all
tasks:
- block:
- debug:
var: ansible_play_hosts_all
- debug:
var: env_gv
run_once: true
gives
version=1.0 works in the environment preprod
shell> ansible-playbook -i hosts.env -e version=1.0 pb.yml
PLAY [localhost] ****************************************************************************************
TASK [debug] ********************************************************************************************
ok: [localhost] =>
msg: 'Use environment: preprod'
TASK [copy] *********************************************************************************************
changed: [localhost]
TASK [meta] *********************************************************************************************
PLAY [all] **********************************************************************************************
TASK [debug] ********************************************************************************************
ok: [preprod1] =>
ansible_play_hosts_all:
- preprod1
- preprod2
- preprod3
TASK [debug] ********************************************************************************************
ok: [preprod1] =>
env_gv: preprod
PLAY RECAP **********************************************************************************************
localhost: ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
preprod1: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
version=2.0 works in the environment systest
shell> ansible-playbook -i hosts.env -e version=2.0 pb.yml
PLAY [localhost] ****************************************************************************************
TASK [debug] ********************************************************************************************
ok: [localhost] =>
msg: 'Use environment: systest'
TASK [copy] *********************************************************************************************
changed: [localhost]
TASK [meta] *********************************************************************************************
PLAY [all] **********************************************************************************************
TASK [debug] ********************************************************************************************
ok: [systest1] =>
ansible_play_hosts_all:
- systest1
- systest2
- systest3
TASK [debug] ********************************************************************************************
ok: [systest1] =>
env_gv: systest
PLAY RECAP **********************************************************************************************
localhost: ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
systest1: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
version=3.0 works in the environment uat
shell> ansible-playbook -i hosts.env -e version=3.0 pb.yml
PLAY [localhost] ****************************************************************************************
TASK [debug] ********************************************************************************************
ok: [localhost] =>
msg: 'Use environment: uat'
TASK [copy] *********************************************************************************************
changed: [localhost]
TASK [meta] *********************************************************************************************
PLAY [all] **********************************************************************************************
TASK [debug] ********************************************************************************************
ok: [uat1] =>
ansible_play_hosts_all:
- uat1
- uat2
- uat3
TASK [debug] ********************************************************************************************
ok: [uat1] =>
env_gv: uat
PLAY RECAP **********************************************************************************************
localhost: ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
uat1: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Example. Precedence of group_vars. YAML format.
shell> tree .
.
├── ansible.cfg
├── group_vars
│   └── all.yml
├── hosts
├── inventories
│   ├── preprod
│   │   └── hosts
│   ├── systest
│   │   └── hosts
│   └── uat
│   └── hosts
├── inventory
│   ├── group_vars
│   │   └── all.yml
│   └── hosts
└── pb.yml
7 directories, 9 files
shell> cat group_vars/all.yml
env_gv3: playbook group_vars/all
shell> cat inventory/group_vars/all.yml
env_gv2: inventory group_vars/all
shell> cat inventories/preprod/hosts
all:
hosts:
preprod1:
preprod2:
preprod3:
vars:
env_gv1: preprod1 inventory file group_vars
env_gv2: preprod2 inventory file group_vars
env_gv3: preprod3 inventory file group_vars
shell> cat inventories/systest/hosts
all:
hosts:
systest1:
systest2:
systest3:
vars:
env_gv1: systest1 inventory file group_vars
env_gv2: systest2 inventory file group_vars
env_gv3: systest3 inventory file group_vars
shell> cat inventories/uat/hosts
all:
hosts:
uat1:
uat2:
uat3:
vars:
env_gv1: uat1 inventory file group_vars
env_gv2: uat2 inventory file group_vars
env_gv3: uat3 inventory file group_vars
shell> cat pb.yml
- hosts: localhost
vars:
envs:
1: preprod
2: systest
3: uat
env: "{{ envs[version.split('.').0|int] }}"
tasks:
- debug:
msg: "Use environment: {{ env }}"
- copy:
src: "{{ playbook_dir }}/inventories/{{ env }}/hosts"
dest: "{{ playbook_dir }}/inventory/hosts"
- meta: refresh_inventory
- hosts: all
tasks:
- block:
- debug:
var: ansible_play_hosts_all
- debug:
msg: |
env_gv1: {{ env_gv1 }}
env_gv2: {{ env_gv2 }}
env_gv3: {{ env_gv3 }}
run_once: true
shell> ansible-playbook -i inventory/hosts -e version=2.0 pb.yml
shows that variable precedence:
inventory file or script group vars
inventory group_vars/all
playbook group_vars/all
works as expected
PLAY [localhost] ****************************************************************************************
TASK [debug] ********************************************************************************************
ok: [localhost] =>
msg: 'Use environment: systest'
TASK [copy] *********************************************************************************************
changed: [localhost]
TASK [meta] *********************************************************************************************
PLAY [all] **********************************************************************************************
TASK [debug] ********************************************************************************************
ok: [systest1] =>
ansible_play_hosts_all:
- systest1
- systest2
- systest3
TASK [debug] ********************************************************************************************
ok: [systest1] =>
msg: |-
env_gv1: systest1 inventory file group_vars
env_gv2: inventory group_vars/all
env_gv3: playbook group_vars/all
PLAY RECAP **********************************************************************************************
localhost: ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
systest1: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Ansible: I need to create a playbook to execute the shell script in ansible

i need to create an ansible job to execute the shell script which is having arguments for different environments (one playbook which will be applicable to all environments test, QA and prod by providing argument in the command). for example, I need to execute script ABC.sh for which normal command is
sh ABC.sh /105t (for test execution) or sh ABC.sh /105q (for QA execution).
Can someone please help me with the playbook for this? Thanks!!
I tried the below format in YML file in gitlab.
-name: execute the script
tasks:
name: execute the ABC script
script: sh script_dir_path/ABC.sh /105t
The job ran successfully but it did not trigger the script execution.
Use the module script. It runs a local script on a remote node after transferring it. For example, given the tree
shell> tree .
.
├── ansible.cfg
├── hosts
├── pb.yml
└── script_dir_path
└── ABC.sh
Create a simple script that displays the first argument
shell> cat script_dir_path/ABC.sh
echo $1
The playbook below runs on all remote hosts. It will transfer the script to the remotes, run it with the argument arg, and display the result
shell> cat pb.yml
- hosts: all
tasks:
- script:
cmd: "script_dir_path/ABC.sh {{ arg }}"
register: out
- debug:
var: out.stdout
Given the inventory
shell> cat hosts
test_11
test_13
The playbook works as expected
shell> ansible-playbook pb.yml -e arg=/105t
PLAY [all] ***********************************************************************************
TASK [script] ********************************************************************************
changed: [test_11]
changed: [test_13]
TASK [debug] *********************************************************************************
ok: [test_11] =>
out.stdout: |-
/105t
ok: [test_13] =>
out.stdout: |-
/105t
PLAY RECAP ***********************************************************************************
test_11: ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
test_13: ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

How can we execute only new task in Ansible playbook?

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

Fail instead of Warning when no hosts are matched

when you don't have any hosts in inventory, when running playbook there is only warning:
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
Is there a way to make that Error instead of Warning?
I find out that there is this parameter in ansible.cfg:
[inventory]
unparsed_is_failed = True
but it will only return error when there is no inventory file which you are trying to use. It didn't look into content.
One simple solution is:
Create the playbook "main.yml" like:
---
# Check first if the supplied host pattern {{ RUNNER.HOSTNAME }} matches with the inventory
# or forces otherwise the playbook to fail (for Jenkins)
- hosts: localhost
vars_files:
- "{{ jsonfilename }}"
tasks:
- name: "Hostname validation | If OK, it will skip"
fail:
msg: "{{ RUNNER.HOSTNAME }} not found in the inventory group or hosts file {{ ansible_inventory_sources }}"
when: RUNNER.HOSTNAME not in hostvars
# The main playbook starts
- hosts: "{{ RUNNER.HOSTNAME }}"
vars_files:
- "{{ jsonfilename }}"
tasks:
- Your tasks
...
...
...
Put your host variables in a json file "var.json":
{
"RUNNER": {
"HOSTNAME": "hostname-to-check"
},
"VAR1":{
"CIAO": "CIAO"
}
}
Run the command:
ansible-playbook main.yml --extra-vars="jsonfilename=var.json"
You can also adapt this solution as you like and pass directly the hostname with the command
ansible-playbook -i hostname-to-check, my_playbook.yml
but in this last case remember to put in your playbook:
hosts: all
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
Q: "Is there a way to make that Error instead of Warning?"
A: Yes. It is. Test it in the playbook. For example,
- hosts: localhost
tasks:
- fail:
msg: "[ERROR] Empty inventory. No host available."
when: groups.all|length == 0
- hosts: all
tasks:
- debug:
msg: Playbook started
gives with an empty inventory
fatal: [localhost]: FAILED! => {"changed": false, "msg": "[ERROR] Empty inventory. No host available."}
Example of a project for testing
shell> tree .
.
├── ansible.cfg
├── hosts
└── pb.yml
0 directories, 3 files
shell> cat ansible.cfg
[defaults]
gathering = explicit
inventory = $PWD/hosts
shell> cat hosts
shell> cat pb.yml
- hosts: localhost
tasks:
- fail:
msg: "[ERROR] Empty inventory. No host available."
when: groups.all|length == 0
- hosts: all
tasks:
- debug:
msg: Playbook started
gives
shell> ansible-playbook pb.yml
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit
localhost does not match 'all'
PLAY [localhost] *****************************************************************************
TASK [fail] **********************************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "msg": "[ERROR] Empty inventory. No host available."}
PLAY RECAP ***********************************************************************************
localhost: ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
Q: "Still I am getting a warning: [WARNING]: provided hosts list is empty, ..."
A: Feel free to turn the warning off. See LOCALHOST_WARNING.
shell> ANSIBLE_LOCALHOST_WARNING=false ansible-playbook pb.yml
PLAY [localhost] *****************************************************************************
TASK [fail] **********************************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "msg": "[ERROR] Empty inventory. No host available."}
PLAY RECAP ***********************************************************************************
localhost: ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0

Resources