Ansible search string in stdout_lines and user later as variable - ansible

I Have below ansible output in stdout_lines
"test.stdout_lines": [
"Get VM Power State Script",
"Loading vmware.vimautomation.core",
"",
"Attempting to connect to server1. Please wait.",
"",
"Connected to server1",
"\tGathering VM Info...",
"",
"Attempting to connect to serevr2. Please wait.",
"",
"Connected to server2.",
"\tGathering VM Info...",
"File Exported to D:\\Scripts\\Exports\\VM_State_201907151824.csv . Please verify the content",
"",
"Complete."
I want to store the file location in last line of the output as variable,i.e., location:- D:\Scripts\Exports\VM_State_201907151824.csv. How can it be done?

Does this work for you?
{{ test.stdout_lines | join(' ') | regex_replace( '^.*File Exported to (.*) \. Please .*$', '\\1') }}

Try below
- name: Get the file_path
set_fact:
file_path: "{{ test.stdout_lines | select('match', 'File Exported to.+') | list | regex_replace( '^.*File Exported to (.*) \\. Please .*$', '\\1') }}"
- name: debug
debug:
msg: "{{ file_path }}"

Related

First playbook, keep getting an error on YAML - Name

I'm a bit out of my wheelhouse here. Normally i code only in Powershell.
I was asked to use AWX/Ansible to automate the creation of user centric AD groups.
I did a lot of reading, figured out how to set up an inventory/project/template/survey/import hosts/credentials/ ... .
But what i can't seem to figure out, is this YAML.
According to most information i've found, it seems to be best to use an indentation of 2 spaces, no tabs.
So i did. But i keep getting 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.
did not find expected key
The error appears to be in '.../CreateUserCentricGroups.yml': line 27, column 3, ...
The offending line appears to be:
- name: Setup variables
^ here
This is the YAML. It was bigger but i made it into the most basic i could think of, in order to find the error. Still, at the first action it fails.
---
- name: Create user centric AD-groups
hosts: "{{ domainName }}"
gather_facts: false
vars:
GroupNameUpper: "RCWR_UC_APP_{{ appName | upper }}"
groupDescription: "{{ appDescription }}"
domainDict:
"Labo": "DC=whatever"
domainDC: '{{domainDict[domainName | default("Labo")] | default("stop") }}' #Allow for future domains to be used.
companyDC: "whatever"
tasks:
- name: Do something with the variables, print full groupname.
debug:
msg: "{{ GroupNameUpper }}"
- name: Verify if the group already exists in AD #Error occurs here <-
register: lookupResult
win_shell: |
Try {
$user = Get-ADGroup {{ GroupNameUpper }}
echo "Group {{ GroupNameUpper }} already exists."
}
catch {
echo "Group {{ GroupNameUpper }} does not exist yet."
}
Try {
$user = Get-ADGroup "{{ GroupNameUpper }}_GrpMgmt"
echo "Group {{ GroupNameUpper }}_GrpMgmt already exists."
}
catch {
echo "Group {{ GroupNameUpper }}_GrpMgmt does not exist yet."
}
debug:
msg: "{{ lookupResult }}"
#gather_facts: false
- name: Inform that group already exists
debug:
msg: Group {{ GroupNameUpper }} already exists in {{ domainName }}.
meta: end_play #https://medium.com/opsops/finishing-playbook-on-condition-in-ansible-958b81d3476d THIS SHOULD STOP IF 1 OF THE 2 GROUPS EXIST.
when: "'already exists' in lookupResult.stdout"
- name: Create groups in AD
register: createOutput
win_shell: |
Try {
$SamAccountname = "{{ GroupNameUpper }}" -replace "_","" -replace "RCWRUC",""
New-ADUser -Name "{{ GroupNameUpper }}" -DisplayName "{{ GroupNameUpper }}" -SamAccountName $SamAccountName -Description "{{ groupDescription }}" -GroupCategory "Security" -GroupScope "DomainLocal" -Path "OU=Groups,{{ companyDC }},OU=Departments,{{ domainDC }},DC=com"
}
catch {
echo "Group {{ GroupNameUpper }} creation failed."
}
Try {
$SamAccountname = "{{ GroupNameUpper }}" -replace "_","" -replace "RCWRUC",""
New-ADUser -Name "{{ GroupNameUpper }}_GrpMgmt" -DisplayName "{{ GroupNameUpper }}_GrpMgmt" -SamAccountName $SamAccountName -Description "{{ groupDescription }}" -GroupCategory "Security" -GroupScope "DomainLocal" -Path "OU=Groups,{{ companyDC }},OU=Departments,{{ domainDC }},DC=com"
}
catch {
echo "Group {{ GroupNameUpper }}_GrpMgmt creation failed."
}
when: "'does not exist yet' in lookupResult.stdout"
failed_when: "('creation failed' in createOutput.stdout) or (createOutput.rc != 0)"
no_log: true
- name: Determine email address (Jenkins)
set_fact:
sendToEmailAddress: "{{ requestorMail }}"
when: requestorMail is defined
- name: Determine email address (non-Jenkins)
set_fact:
sendToEmailAddress: "{{ awx_user_email }}"
when: requestorMail is not defined
- name: Communicate result
delegate_to: localhost
mail:
subject: "Create AD-group {{ GroupNameUpper }}"
body: "Groups {{ GroupNameUpper }} have been created in {{ domainName }}"
host: "exchangeserver"
port: 25
to: "{{sendToEmailAddress}}"
from: "noreply#us.com"
secure: starttls
subtype: html
when: "'does not exist yet' in lookupResult.stdout"
no_log: true
...
What am i forgetting or doing wrong?
Thank you in advance for your time/help.
I don't get the exact error you listed, but your play is kind of play-shaped but not valid.
Your task is not a valid task, since it contains a play-level directive (gather_facts) and does not contain a valid action name.
Assuming you want these variables to apply to the entire play, this is a more valid (and more consistently formatted) reworking of the limited example you provided:
- name: Create user centric AD-groups
hosts: "{{ domainName }}"
gather_facts: false
vars:
GroupNameUpper: Prefix1_APP_{{ appName | upper }}
groupDescription: "{{ appDescription }}"
domainDict:
Labo: DC=whatever
domainDC: "{{ domainDict[domainName | default('Labo')] | default('stop') }}"
companyDC: Whatever
tasks:
- name: Do something with the variables
debug:
msg: "{{ domainDC }}"

How to compare the contain of my file with ansible? [duplicate]

In ansible, I need to check whether a particular line present in a file or not. Basically, I need to convert the following command to an ansible task. My goal is to only check.
grep -Fxq "127.0.0.1" /tmp/my.conf
Use check_mode, register and failed_when in concert. This fails the task if the lineinfile module would make any changes to the file being checked. Check_mode ensures nothing will change even if it otherwise would.
- name: "Ensure /tmp/my.conf contains '127.0.0.1'"
lineinfile:
name: /tmp/my.conf
line: "127.0.0.1"
state: present
check_mode: yes
register: conf
failed_when: (conf is changed) or (conf is failed)
- name: Check whether /tmp/my.conf contains "127.0.0.1"
command: grep -Fxq "127.0.0.1" /tmp/my.conf
register: checkmyconf
check_mode: no
ignore_errors: yes
changed_when: no
- name: Greet the world if /tmp/my.conf contains "127.0.0.1"
debug: msg="Hello, world!"
when: checkmyconf.rc == 0
Update 2017-08-28: Older Ansible versions need to use always_run: yes instead of check_mode: no.
User robo's regexp & absent method is quite clean, so I've fleshed it out here for easy use and added improvements from comments by #assylias and #Olivier:
- name: Ensure /tmp/my.conf contains 127.0.0.1
lineinfile:
path: /tmp/my.conf
regexp: '^127\.0\.0\.1.*whatever'
state: absent
check_mode: yes
changed_when: false
register: out
- debug:
msg: "Yes, line exists."
when: out.found
- debug:
msg: "Line does NOT exist."
when: not out.found
With the accepted solution, even though you ignore errors, you will still get ugly red error output on the first task if there is no match:
TASK: [Check whether /tmp/my.conf contains "127.0.0.1"] ***********************
failed: [localhost] => {"changed": false, "cmd": "grep -Fxq "127.0.0.1" /tmp/my.conf", "delta": "0:00:00.018709", "end": "2015-09-27 17:46:18.252024", "rc": 1, "start": "2015-09-27 17:46:18.233315", "stdout_lines": [], "warnings": []}
...ignoring
If you want less verbose output, you can use awk instead of grep. awk won't return an error on a non-match, which means the first check task below won't error regardless of a match or non-match:
- name: Check whether /tmp/my.conf contains "127.0.0.1"
command: awk /^127.0.0.1$/ /tmp/my.conf
register: checkmyconf
changed_when: False
- name: Greet the world if /tmp/my.conf contains "127.0.0.1"
debug: msg="Hello, world!"
when: checkmyconf.stdout | match("127.0.0.1")
Notice that my second task uses the match filter as awk returns the matched string if it finds a match.
The alternative above will produce the following output regardless of whether the check task has a match or not:
TASK: [Check whether /tmp/my.conf contains "127.0.0.1"] ***********************
ok: [localhost]
IMHO this is a better approach as you won't ignore other errors in your first task (e.g. if the specified file did not exist).
Use ansible lineinfile command, but this command will update the file with the line if it does not exists.
- lineinfile: dest=/tmp/my.conf line='127.0.0.1' state=present
Another way is to use the "replace module" then "lineinfile module".
The algo is closed to the one used when you want to change the values of two variables.
First, use "replace module" to detect if the line you are looking for is here and change it with the something else. (Like same line + something at the end).
Then if "replace" is true, It means your line is here then replace the new line with a particularity, with the new line looking.
Else the line you are looking for is not here.
Example:
# Vars
- name: Set parameters
set_fact:
newline : "hello, i love ansible"
lineSearched : "hello"
lineModified : "hello you"
# Tasks
- name: Try to replace the line
replace:
dest : /dir/file
replace : '{{ lineModified }} '
regexp : '{{ lineSearched }}$'
backup : yes
register : checkIfLineIsHere
- name: Line is here, change it
lineinfile:
state : present
dest : /dir/file
line : '{{ newline }}'
regexp : '{{ lineModified }}$'
when: checkIfLineIsHere.changed
If the file contains "hello", it will become "hello you" then "hello, i love ansible" at the end.
If the file content doesn't contain "hello", the file is not modified.
With the same idea, you can do something if the lineSearched is here:
# Vars
- name: Set parameters
set_fact:
newline : "hello, i love ansible"
lineSearched : "hello"
lineModified : "hello you"
# Tasks
- name: Try to replace the line
replace:
dest : /dir/file
replace : '{{ lineModified }} '
regexp : '{{ lineSearched }}$'
backup : yes
register : checkIfLineIsHere
# If the line is here, I want to add something.
- name: If line is here, do something
lineinfile:
state : present
dest : /dir/file
line : '{{ newline }}'
regexp : ''
insertafter: EOF
when: checkIfLineIsHere.changed
# But I still want this line in the file, Then restore it
- name: Restore the searched line.
lineinfile:
state : present
dest : /dir/file
line : '{{ lineSearched }}'
regexp : '{{ lineModified }}$'
when: checkIfLineIsHere.changed
If the file contains "hello", the line will still contain "hello" and "hello, i love ansible" at the end.
If the file content doesn't contain "hello", the file is not modified.
You can use the file plugin for this scenario.
To set a fact you can use in other tasks ... this works.
- name: Check whether /tmp/my.conf contains "127.0.0.1"
set_fact:
myconf: "{{ lookup('file', '/tmp/my.conf') }}"
ignore_errors: yes
- name: Greet the world if /tmp/my.conf contains "127.0.0.1"
debug: msg="Hello, world!"
when: "'127.0.0.1' in myconf"
To check the file content as a condition of a task ... this should work.
- name: Greet the world if /tmp/my.conf contains "127.0.0.1"
debug: msg="Hello, world!"
when: "'127.0.0.1' in lookup('file', '/tmp/my.conf')"
Another solution, also useful for other purposes is to loop over the contents of the file, line by line
- name: get the file
slurp:
src: /etc/locale.gen
register: slurped_file
- name: initialize the matches list
set_fact:
MATCHES: []
- name: collect matches in a list
set_fact:
MATCHES: "{{ MATCHES + [line2match] }}"
loop: "{{ file_lines }}"
loop_control:
loop_var: line2match
vars:
- decode_content: "{{ slurped_file.content | b64decode }}"
- file_lines: "{{ decode_content.split('\n') }}"
when: '"BE" in line2match'
- name: report matches if any
debug:
msg: "Found {{ MATCHES | length }} matches\n{{ MATCHES }}"
when: 'listlen | int > 0'
vars:
listlen: "{{ MATCHES | length }}"
This mechanism can be use to get a specific value from a matched line if you use it for example with the jinja regex_replace filter

Ansible vmware_vm_shell SED command adding extra characters

I am having trouble getting this correct.
I gather the vm facts and have a test debug that gives me the correct mac address for the vm. However when I add the query to a sed command I get extra characters added.
this works
- name: Get MAC address of VMs to add to eth0 configuration
debug:
msg: "{{ vm_guest_facts.results | json_query(s_query) }}"
vars:
s_query: "[?instance.hw_name == '{{ item }}'].instance.hw_eth0.macaddress"
with_items: "{{ inventory_hostname }}"
output
ok: [server1] => (item=server1) => {
"msg": [
"00:50:56:80:e0:a1"
]
this fails
- name: fix network phase 2 - replace template MAC
vars:
s_query: "[?instance.hw_name == '{{ item }}'].instance.hw_eth0.macaddress"
vmware_vm_shell:
hostname: '{{ deploy_vsphere_host }}'
username: '{{ deploy_vsphere_user }}'
password: '{{ deploy_vsphere_password }}'
datacenter: "{{ vsphere_datacenter }}"
validate_certs: no
vm_id: "{{ item }}"
vm_username: xxx
vm_password: xxx
vm_shell: '/bin/sed'
vm_shell_args: " -i.bak 's/^HWADDR.*/HWADDR={{ vm_guest_facts.results | json_query(s_query) }}/' /etc/sysconfig/network-scripts/ifcfg-eth0"
with_items:
- "{{ inventory_hostname }}"
delegate_to: localhost
output (snipped)
"vm_id": "server1",
"vm_id_type": "vm_name",
"vm_password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"vm_shell": "/bin/sed",
"vm_shell_args": " -i.bak 's/^HWADDR.*/HWADDR=[u'00:50:56:80:e0:a1']/' /etc/sysconfig/network-scripts/ifcfg-eth0",
"vm_shell_cwd": null,
"vm_shell_env": null,
"vm_username": "xxx",
"wait_for_process": false
The mac address in the last line is getting the extra [u at the beginning and ] at the end.
Is there a way of fixing this or could someone help me with a sed line to remove those extra characters in an extra step.
Thanks,
Kane.
Any task that is with_items always has a list of results, and you can see that in the msg: output containing the python list characters [ and ]. Thus, vm_guest_facts.results | json_query() needs either [0] or | first appended to it in order to resolve it to one thing
I gave up tried [0] in multiple places and multiple times.
Here is the cheat / dirty fix for this particular issue:
" -i.bak 's/^HWADDR.*/HWADDR={{ vm_guest_facts.results | json_query(s_query)| regex_replace(']') | regex_replace('\\[u') }}/' /etc/sysconfig/network-scripts/ifcfg-eth0"

How to extract the exact output from stdout.lines in ansible

My Ansible Playbook:
#Tag --> B.6 -->
- name: Change the Security Realm to CustomRealm from ManagementRealm
command: /jboss-as-7.1.1.Final/bin/jboss-cli.sh --connect--command="/core-service=management/management-interface=http-interface:read-attribute(name=security-realm)"
register: Realm
- debug:
msg: "{{ Realm.stdout_lines }}"
The output for the above command in the message is as follows:
ok: [342f2f7bed8e] => {
"msg": [
"{",
" \"outcome\" => \"success\","
" \"result\" => \"ManagementRealm\"",
"}"
]
}
is there a way to just exact \"result\" => \"ManagementRealm\"".
I tried using the
Realm.stdout_lines.find('result')
but that fails, AWk & grep commands doesn't seem to be working here.
Any thoughts is greatly appreciated.
Thak you
I think there are a few ways you could handle this.
1) Grep the output before it gets to Ansible:
# Note the change of 'command' to 'shell'
- name: Change the Security Realm to CustomRealm from ManagementRealm
shell: /jboss-as-7.1.1.Final/bin/jboss-cli.sh --connect--command="/core-service=management/management-interface=http-interface:read-attribute(name=security-realm)" | grep -o 'result.*'
register: Realm
2) If the output from the source script is always 4 lines long, you can just grab the 3rd line:
# List indexes start at 0
- debug:
msg: "{{ Realm.stdout_lines[2] | regex_replace('^ *(.*$)', '\\1') }}"
3) The nicest way if you have an option to modify jboss-cli.sh, would be to get the jboss-cli.sh to output valid JSON which can then be parsed by Ansible:
# Assuming jboss-cli.sh produces {"outcome": "success", "result": "ManagementRealm"}
- set_fact:
jboss_data: "{{ Realm.stdout | from_json }}"
- debug:
var: jboss_data.result

Only check whether a line present in a file (ansible)

In ansible, I need to check whether a particular line present in a file or not. Basically, I need to convert the following command to an ansible task. My goal is to only check.
grep -Fxq "127.0.0.1" /tmp/my.conf
Use check_mode, register and failed_when in concert. This fails the task if the lineinfile module would make any changes to the file being checked. Check_mode ensures nothing will change even if it otherwise would.
- name: "Ensure /tmp/my.conf contains '127.0.0.1'"
lineinfile:
name: /tmp/my.conf
line: "127.0.0.1"
state: present
check_mode: yes
register: conf
failed_when: (conf is changed) or (conf is failed)
- name: Check whether /tmp/my.conf contains "127.0.0.1"
command: grep -Fxq "127.0.0.1" /tmp/my.conf
register: checkmyconf
check_mode: no
ignore_errors: yes
changed_when: no
- name: Greet the world if /tmp/my.conf contains "127.0.0.1"
debug: msg="Hello, world!"
when: checkmyconf.rc == 0
Update 2017-08-28: Older Ansible versions need to use always_run: yes instead of check_mode: no.
User robo's regexp & absent method is quite clean, so I've fleshed it out here for easy use and added improvements from comments by #assylias and #Olivier:
- name: Ensure /tmp/my.conf contains 127.0.0.1
lineinfile:
path: /tmp/my.conf
regexp: '^127\.0\.0\.1.*whatever'
state: absent
check_mode: yes
changed_when: false
register: out
- debug:
msg: "Yes, line exists."
when: out.found
- debug:
msg: "Line does NOT exist."
when: not out.found
With the accepted solution, even though you ignore errors, you will still get ugly red error output on the first task if there is no match:
TASK: [Check whether /tmp/my.conf contains "127.0.0.1"] ***********************
failed: [localhost] => {"changed": false, "cmd": "grep -Fxq "127.0.0.1" /tmp/my.conf", "delta": "0:00:00.018709", "end": "2015-09-27 17:46:18.252024", "rc": 1, "start": "2015-09-27 17:46:18.233315", "stdout_lines": [], "warnings": []}
...ignoring
If you want less verbose output, you can use awk instead of grep. awk won't return an error on a non-match, which means the first check task below won't error regardless of a match or non-match:
- name: Check whether /tmp/my.conf contains "127.0.0.1"
command: awk /^127.0.0.1$/ /tmp/my.conf
register: checkmyconf
changed_when: False
- name: Greet the world if /tmp/my.conf contains "127.0.0.1"
debug: msg="Hello, world!"
when: checkmyconf.stdout | match("127.0.0.1")
Notice that my second task uses the match filter as awk returns the matched string if it finds a match.
The alternative above will produce the following output regardless of whether the check task has a match or not:
TASK: [Check whether /tmp/my.conf contains "127.0.0.1"] ***********************
ok: [localhost]
IMHO this is a better approach as you won't ignore other errors in your first task (e.g. if the specified file did not exist).
Use ansible lineinfile command, but this command will update the file with the line if it does not exists.
- lineinfile: dest=/tmp/my.conf line='127.0.0.1' state=present
Another way is to use the "replace module" then "lineinfile module".
The algo is closed to the one used when you want to change the values of two variables.
First, use "replace module" to detect if the line you are looking for is here and change it with the something else. (Like same line + something at the end).
Then if "replace" is true, It means your line is here then replace the new line with a particularity, with the new line looking.
Else the line you are looking for is not here.
Example:
# Vars
- name: Set parameters
set_fact:
newline : "hello, i love ansible"
lineSearched : "hello"
lineModified : "hello you"
# Tasks
- name: Try to replace the line
replace:
dest : /dir/file
replace : '{{ lineModified }} '
regexp : '{{ lineSearched }}$'
backup : yes
register : checkIfLineIsHere
- name: Line is here, change it
lineinfile:
state : present
dest : /dir/file
line : '{{ newline }}'
regexp : '{{ lineModified }}$'
when: checkIfLineIsHere.changed
If the file contains "hello", it will become "hello you" then "hello, i love ansible" at the end.
If the file content doesn't contain "hello", the file is not modified.
With the same idea, you can do something if the lineSearched is here:
# Vars
- name: Set parameters
set_fact:
newline : "hello, i love ansible"
lineSearched : "hello"
lineModified : "hello you"
# Tasks
- name: Try to replace the line
replace:
dest : /dir/file
replace : '{{ lineModified }} '
regexp : '{{ lineSearched }}$'
backup : yes
register : checkIfLineIsHere
# If the line is here, I want to add something.
- name: If line is here, do something
lineinfile:
state : present
dest : /dir/file
line : '{{ newline }}'
regexp : ''
insertafter: EOF
when: checkIfLineIsHere.changed
# But I still want this line in the file, Then restore it
- name: Restore the searched line.
lineinfile:
state : present
dest : /dir/file
line : '{{ lineSearched }}'
regexp : '{{ lineModified }}$'
when: checkIfLineIsHere.changed
If the file contains "hello", the line will still contain "hello" and "hello, i love ansible" at the end.
If the file content doesn't contain "hello", the file is not modified.
You can use the file plugin for this scenario.
To set a fact you can use in other tasks ... this works.
- name: Check whether /tmp/my.conf contains "127.0.0.1"
set_fact:
myconf: "{{ lookup('file', '/tmp/my.conf') }}"
ignore_errors: yes
- name: Greet the world if /tmp/my.conf contains "127.0.0.1"
debug: msg="Hello, world!"
when: "'127.0.0.1' in myconf"
To check the file content as a condition of a task ... this should work.
- name: Greet the world if /tmp/my.conf contains "127.0.0.1"
debug: msg="Hello, world!"
when: "'127.0.0.1' in lookup('file', '/tmp/my.conf')"
Another solution, also useful for other purposes is to loop over the contents of the file, line by line
- name: get the file
slurp:
src: /etc/locale.gen
register: slurped_file
- name: initialize the matches list
set_fact:
MATCHES: []
- name: collect matches in a list
set_fact:
MATCHES: "{{ MATCHES + [line2match] }}"
loop: "{{ file_lines }}"
loop_control:
loop_var: line2match
vars:
- decode_content: "{{ slurped_file.content | b64decode }}"
- file_lines: "{{ decode_content.split('\n') }}"
when: '"BE" in line2match'
- name: report matches if any
debug:
msg: "Found {{ MATCHES | length }} matches\n{{ MATCHES }}"
when: 'listlen | int > 0'
vars:
listlen: "{{ MATCHES | length }}"
This mechanism can be use to get a specific value from a matched line if you use it for example with the jinja regex_replace filter

Resources