Is it possible to conditionally set ansible host from within the play? - if-statement

If the user passes perform_action parameter as any of these telnetcurlnslookuptracerouteget_ip_address then i want the play to run on localhost else it should run on remotehosts
ansible-playbook test.yml -e perform_action='nslookup'
- name: "Play 1"
hosts: localhost
tasks:
- set_fact:
final_delegate: "{{ 'localhost' if perform_action in 'telnetcurlnslookuptracerouteget_ip_address' else 'remotehosts' }} "
- debug:
msg: "Play needs to run on {{ final_delegate }}"
- name: "Play 2"
hosts: "{{ final_delegate }}"
tasks:
- debug:
msg: "Im running on {{ inventory_hostname }}"
Output:
The Play needs to run on localhost
However, Play 2 fails with the below error:
ERROR! The field 'hosts' has an invalid value, which includes an undefined variable. The error was: 'final_delegate' is undefined
Can this condition be set with the play as i m doing or is it possible only by putting a condition on -e parameter?

Your problem is that at the moment of evaluating which hosts to run play 2 on, you are polling a variable which is only defined for localhost. It's the chicken or the egg thing. It's technically not running on localhost, so it doesn't know which host to check the variable on, to find that it should run on localhost.
I would try...
hosts: "{{ hostvars['localhost']['final_delegate'] }}"
That might work for you. The templating for host patterns can be a little picky about things.

Related

Looking to see if a key from one set of imported variables matches another so it's value can be procured

I have roughly formatted yml files with key/value pairs in them. I then imported the values of both of these files successfully into a running playbook using the include_vars module.
Now, I want to be able to compare the value of the key/value pair from file/list 1, to all of the keys of file/list 2. Then finally when there is a match, print and preferably save/register the value of the matching key from file/list 2.
Essentially I am comparing a machine name to an IP list to try to grab the IP the machine needs out of that list. The name is "dynamic" and is different each time the playbook is run, as file/list 1 is always dynamically populated on each run.
Examples:
file/list 1 contents
machine_serial: m60
s_iteration: a
site_name: dud
t_number: '001'
file/list 2 contents
m51: 10.2.5.201
m52: 10.2.5.202
m53: 10.2.5.203
m54: 10.2.5.204
m55: 10.2.5.205
m56: 10.2.5.206
m57: 10.2.5.207
m58: 10.2.5.208
m59: 10.2.5.209
m60: 10.2.5.210
m61: 10.2.5.211
In a nutshell, I want to be able to get the file/list 1 ct_machine_serial key who's value is currently: m60 to be able to find it's key match in file/list 2, and then print and/or preferably register it's value of 10.2.5.210.
What I've tried so far:
Playbook:
- name: IP gleaning comparison.
hosts: localhost
remote_user: ansible
become: yes
become_method: sudo
vars:
ansible_ssh_pipelining: yes
tasks:
- name: Try to do a variable import of the file1 file.
include_vars:
file: ~/active_ct-scanner-vars.yml
name: ctfile1_vars
become: no
- name: Try to do an import of file2 file for lookup comparison to get an IP match.
include_vars:
file: ~/machine-ip-conversion.yml
name: ip_vars
become: no
- name: Best, but failing attempt to get the value of the match-up IP.
debug:
msg: "{{ item }}"
when: ctfile1_vars.machine_serial == ip_vars
with_items:
- "{{ ip_vars }}"
Every task except the final one works perfectly.
My failed output final task:
TASK [Best, but failing attempt to get the value of the match-up IP.] ***********************************************************************************
skipping: [localhost] => (item={'m51': '10.200.5.201', 'm52': '10.200.5.202', 'm53': '10.200.5.203', 'm54': '10.200.5.204', 'm55': '10.200.5.205', 'm56': '10.200.5.206', 'm57': '10.200.5.207', 'm58': '10.200.5.208', 'm59': '10.200.5.209', 'm60': '10.200.5.210', 'm61': '10.200.5.211'})
skipping: [localhost]
What I hoped for hasn't happened, it simply skips the task, and doesn't iterate over the list like I was hoping, so there must be a problem somewhere. Hopefully there is an easy solution to this I just missed. What could be the correct answer?
Given the files
shell> cat active_ct-scanner-vars.yml
machine_serial: m60
s_iteration: a
site_name: dud
t_number: '001'
shell> cat machine-ip-conversion.yml
m58: 10.2.5.208
m59: 10.2.5.209
m60: 10.2.5.210
m61: 10.2.5.211
Read the files
- include_vars:
file: active_ct-scanner-vars.yml
name: ctfile1_vars
- include_vars:
file: machine-ip-conversion.yml
name: ip_vars
Q: "Compare the machine name to an IP list and grab the IP."
A: Both variables ip_vars and ctfile1_vars are dictionaries. Use ctfile1_vars.machine_serial as index in ip_vars
match_up_IP: "{{ ip_vars[ctfile1_vars.machine_serial] }}"
gives
match_up_IP: 10.2.5.210
Example of a complete playbook for testing
- hosts: localhost
gather_facts: false
vars:
match_up_IP: "{{ ip_vars[ctfile1_vars.machine_serial] }}"
tasks:
- include_vars:
file: active_ct-scanner-vars.yml
name: ctfile1_vars
- include_vars:
file: machine-ip-conversion.yml
name: ip_vars
- debug:
var: match_up_IP

Convert Ansible variable to integer for arithmetic

I would like to execute a command to obtain the current AWS EC2 launch template version in an integer format so I can do basic arithmetic on it to use in subsequent queries / deletes.
For example:
tasks:
- name: Lookup current default version of EC2 launch template
command: aws ec2 describe-launch-template-versions --launch-template-id lt-xxx --filters Name=is-default-version,Value=true --query 'LaunchTemplateVersions[*].[VersionNumber]'
delegate_to: localhost
register: result
- name: Show results
debug:
msg: '{{ result.stdout }}'
delegate_to: localhost
If this output is '5' I would like to subtract 1 from it so I can execute an additional command to do the following:
aws ec2 delete-launch-template-versions --launch-template-id lt-xxx --versions {{result - 1}}
I realize this will not work as written, but this is the premise I'm going for.
Convert the string to an integer. For example
- command: echo 5
register: result
- command: "echo {{ result.stdout|int - 1 }}"
register: result
- debug:
var: result.stdout
gives
result.stdout: '4'
The type of the command return values' attribute stdout is string. See the results below
- command: echo 5
register: result
- debug:
msg: |-
result.stdout: {{ result.stdout }}
result.stdout|type_debug: {{ result.stdout|type_debug }}
result.stdout|int|type_debug: {{ result.stdout|int|type_debug }}
msg: |-
result.stdout: 5
result.stdout|type_debug: AnsibleUnsafeText
result.stdout|int|type_debug: int

How can I use regex in when condition

On an Ansible playbook, I'm trying to execute a shell command only if a service exist on the remote server.
I have 3 tasks :
service_facts
execution of shell command if tomcat is installed
display the output of the shell command if tomcat is installed
Here is my code :
- name: Get Infos
hosts: all
gather_facts: yes
become: false
remote_user: [MY_USER]
tasks:
- name: Get the list of services
service_facts:
- name: Get version of Tomcat if installed
become: true
shell: 'java -cp /opt/tomcat/lib/catalina.jar org.apache.catalina.util.ServerInfo | grep "Server version"'
register: tomcat_version
when: "'tomcat.service' in services"
- debug: msg="{{ tomcat_version.stdout_lines }}"
when: "'tomcat.service' in services"
The problem is on certains servers the service name is, for example, tomcat-8.1
How can i use regex in the when condition?
I tried regex(), regex_search(), either I'm doing it wrong or I don't know how to do it.
Have you any idea how to do it?
Thanks in advance!
Count matching items. For example
- service_facts:
- block:
- shell: smartctl --version | head -1
register: smart_version
- debug:
msg: "{{ smart_version.stdout_lines }}"
when: _srvcs|length > 0
vars:
_regex: '.*smart.*'
_srvcs: "{{ services|select('match', _regex) }}"
gives
msg:
- smartctl 7.1 2019-12-30 r5022 [x86_64-linux-5.4.0-73-generic] (local build)
The next option is to intersect the list of services, e.g.
when: _srvcs|length > 0
vars:
my_services:
- smartmontools.service
- smart-8.1
- smart-devel.0.0.1
_srvcs: "{{ my_services|intersect(services) }}"
Debug
Q: "It gives me a failure on the server where my service doesn't exist, cause the playbook still tries to execute the shell. Is it normal?"
A: No. It is not normal. Print debug and find out why the condition evaluates to true, i.e. what service(s) match either the regex or the list. For example
- debug:
msg: |
_srvcs:
{{ _srvcs|to_nice_yaml|indent(2) }}
when: debug|d(false)|bool
vars:
my_services:
- smartmontools.service
- smart-8.1
- smart-devel.0.0.1
_srvcs: "{{ my_services|intersect(services) }}"
gives
msg: |-
_srvcs:
- smartmontools.service
To enable the task run the playbook with the option -e debug=true.

Ansible regex_search stdout not working, but works in regex101.com

I've read a thousand of the Ansible regex_search questions on here and have not found a satisfying answer.
Here's the test playbook. backup_stdout is set identically to what I get from the backup utility:
---
- hosts: localhost
connection: local
gather_facts: no
vars:
backup_stdout: |-
Saving active configuration...
/var/local/ucs/f5-apm-1625-081021.ucs is saved.
tasks:
- name: Get the backup name
ansible.builtin.set_fact:
backup_name: "{{ backup_stdout | regex_search(stdout_regex, multiline=True) }}"
vars:
stdout_regex: '"\/var.*ucs\b"gm'
failed_when: backup_name == ''
- debug:
var: backup_name
I can't get the regex_search to produce a match. Here's the same code on regex101, which shows that it does match. I've tried the following:
with/without the multiline
with/without the trailing '\\1' in the expression
with/without passing the result to the | first filter
using ^\/var.*ucs instead of the word boundary (also matches on regex101)
So far, no matter what I've tried, I can't get the Ansible to match. Any help appreciated.
You've got some weird quoting in your regular expression that is causing problems. Because you've written:
stdout_regex: '"\/var.*ucs\b"gm'
You're passing the literal value "\/var.*ucs\b"gm to regex_search. There are no quotes (nor is there a gm) in the content of backup_stdout, so this is never going to match.
I think you want:
- hosts: localhost
connection: local
gather_facts: no
vars:
backup_stdout: |-
Saving active configuration...
/var/local/ucs/f5-apm-1625-081021.ucs is saved.
tasks:
- name: Get the backup name
ansible.builtin.set_fact:
backup_name: "{{ backup_stdout | regex_search(stdout_regex, multiline=True) }}"
vars:
stdout_regex: '/var.*ucs\b'
failed_when: backup_name == ''
- debug:
var: backup_name
Which produces:
TASK [debug] *******************************************************************
ok: [localhost] => {
"backup_name": "/var/local/ucs/f5-apm-1625-081021.ucs"
}

Task result to a file

I have a simple playbook that run Cisco nxos command, which the playbook ran successful.
Would like to know what is the code save all the result into a file regardless how many hosts I have and use Survey to input the filename.
Currently, here is my code:
---
- name: run multiple commands on remote nodes
nxos_command:
commands:
- show clock
- show int status
- show cdp neigh
- show int desc
- show port-channel summ
- show vpc
- show vpc role
Try with code
---
- name: run multiple commands on remote nodes
register: myshell_output
nxos_command:
commands:
- show clock
- show int status
- show cdp neigh
- show int desc
- show port-channel summ
- show vpc
- show vpc role
- name: Saving data to local file
copy:
content: "{{ myshell_output.stdout|join('\n') }}"
dest: "/tmp/hello.txt"
delegate_to: localhost
It give me an error:
FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'stdout'\n\nThe error appears to be in '/tmp/awx_1869_7__9l_9l/project/roles/bcpcommands/tasks/main.yml': line 3, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- name: run multiple commands on remote nodes\n ^ here\n"}
The host normally I limit it at Ansible-Tower LIMIT column.
The ideal output of the file possible to include the hostname and commands that I key in?
Thanks
You probably got the indenting wrong. Try;
---
- hosts: my_host
tasks:
- name: run multiple commands on remote nodes
nxos_command:
commands: "{{ item }}"
loop:
- show clock
- show int status
- show cdp neigh
- show int desc
- show port-channel summ
- show vpc
- show vpc role
register: myshell_output
- debug:
msg: "{{ myshell_output }}"
- name: Saving data to local file and include hostname
copy:
content: "{{ myshell_output.stdout|join('\n') }} hostname: {{ inventory_hostname }}"
dest: "/tmp/hello.txt"
delegate_to: localhost
Edit the hostname.
The debug task must output an 'stdout' message. If that one is not present, then your copy task will fail.