ansible execute task when variable contain specific string - elasticsearch

i have the following playbook which check all the roles in elasticsearch, and if the specific role doesn't exist, it creates it
- name: Get all security roles
uri:
url: 'http://192.168.2.14:9200/_security/role'
method: GET
url_username: elastic
url_password: strong
register: security_roles
- debug:
msg: {{ security_roles }}
- name: make cURL call if anthill_role exists
shell: curl -u elastic:strong 192.168.2.14:9200
when: '"sobaka" not in security_roles'
and let's see the output from playbook execution
ansible-playbook elasticsearch-3dc.yml -i hosts.yml
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details
[WARNING]: Found variable using reserved name: remote_user
PLAY [Deploy & Configure Elasticsearch on 3DC] *************************************************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************************************************************************ok: [elasticsearch-db-02]
TASK [elasticsearch-3dc : Get all security roles] **********************************************************************************************************************************ok: [elasticsearch-db-02]
TASK [elasticsearch-3dc : debug] ***************************************************************************************************************************************************ok: [elasticsearch-db-02] => {
"msg": {
"changed": false,
"content_length": "8422",
"content_type": "application/json; charset=UTF-8",
"cookies": {},
"cookies_string": "",
"elapsed": 0,
"failed": false,
"json": {
"sobaka": {
"applications": [],
"cluster": [],
"indices": [
{
"allow_restricted_indices": false,
"names": [
"*"
],
"privileges": [
"create",
"index",
"read",
"read_cross_cluster",
"view_index_metadata",
"write",
"create_index"
]
}
],
"metadata": {},
"run_as": [],
"transient_metadata": {
"enabled": true
}
},
},
"msg": "OK (8422 bytes)",
"redirected": false,
"status": 200,
"url": "http://192.168.2.14:9200/_security/role"
}
}
TASK [elasticsearch-3dc : make cURL call if anthill_role exists] *******************************************************************************************************************[WARNING]: Consider using the get_url or uri module rather than running 'curl'. If you need to use command because get_url or uri is insufficient you can add 'warn: false' to
this command task or set 'command_warnings=False' in ansible.cfg to get rid of this message.
changed: [elasticsearch-db-02]
but as you can see from the output, the role sobaka is existed, why the task make cURL car if sobaka role exists is runned?

Can you try like when: '"sobaka" not in security_roles.json'. Seems like key sobaka is under value dict of key json

The return of you debug show a dict.
You have to check if that dict dont have the specific key
- name: make cURL call if anthill_role exists
shell: curl -u elastic:strong 192.168.2.14:9200
when: "'sobaka' not in {{ security_roles.json.keys()|list }}"

Related

Ansible include task and loop register different result

I'm trying to use a module as an included task so I can loop several items and register the credentials in a variable.
Files:
main.yml:
---
- hosts: localhost
gather_facts: False
tasks:
- include: tasks/myvault/get-vault.yml
with_items:
- demo
register: output
- debug:
msg: "{{output}}"
get-vault.yml:
- name: Retrieve secret from Vault
community.hashi_vault.vault_kv2_get:
url: https://myvaul
path: "{{ item }}"
auth_method: token
token: '{{ mytoken }}'
namespace: MyNamespace
validate_certs: no
output:
ok: [localhost] => {
"msg": {
"changed": false,
"msg": "All items completed",
"results": [
{
"ansible_loop_var": "item",
"include": "tasks/myvault/get-vault.yml",
"include_args": {},
"item": "demo"
}
],
"skipped": false
}
}
The output doesn't display the "real" result of the task.
If I remove:
register: output and put it in get-vault.yml, here is the result:
ok: [localhost] => {
"msg": {
"changed": false,
"data": {
"data": {
"password": "Password",
"username": "Username"
}, ...
How can I get this result returned when I register the output from my included task?

Ansible, how to query deeply nested json keys

I'm using Ansible to make an API call that returns a huge data set, and I need to be able to get a nested value to print to the screen. I tried using json_query but not sure what I'm doing wrong.
My task:
- name: Get certificate by CN name.
uri:
method: GET
url: "https://mayapi/api/1/certificates?filter=cn;{{ inventory_hostname }}"
headers:
Authorization: Bearer {{ login.json.token }}
Content-Type: application/json
validate_certs: no
register: certs
- name: Print certs for application
debug:
msg: "{{ certs.json | json_query(items) }}"
This is a small snippet of the output. I want to be able to print ID, and email.
{
"msg": {
"changed": false,
"connection": "close",
"content_length": "65833",
"content_type": "application/json",
"cookies": {},
"cookies_string": "",
"date": "Mon, 10 May 2021 21:33:29 GMT",
"elapsed": 0,
"failed": false,
"json": {
"items": [
{
"active": true,
"application": [
{
"director": {
"active": true,
"email": "user#domain.com",
"fullname": "John Doe",
"id": 1611,
"manager": "John Doe",
"managerEmail": "johndoe#email.com",
"username": "jdoe"
},
...
...
...
}
I get the following error indicating "certs.items" doesn't exist:
FAILED! => {"msg": "Error in jmespath.search in json_query filter plugin:\n'items' is undefined"}
I was expecting all of the items to get printed to the screen and then if I wanted something below items I would do items.active, items.application, etc... But this is not correct since I keep erroring out.
I also tried looping through cert.json and cert.json.items:
- name: Print certs for application
debug:
msg: "{{ item.application.name }}"
loop: "{{ certs.json}}"
But get this error message:
{"msg": "Invalid data passed to 'loop', it requires a list, got this instead: {u'items': [{u'status': u'Active-Pending Install'...shows all the data of the nested json
Then I tried this:
- name: Print certs for application
debug:
msg: "{{ item.application.name }}"
loop: "{{ certs.json.items}}"
But got this error message:
{"msg": "Invalid data passed to 'loop', it requires a list, got this instead: <built-in method items of dict object at 0x7f0c9ec43050>. Hint: If you passed a list/dict of just one element, try adding wantlist=True to your lookup invocation or use q/query instead of lookup."}
Made some progress with this:
- name: Print certs for application
debug:
msg: "KEY:::: {{ item.key }}, VALUE:::: {{ item.value.0 }}"
loop: "{{ lookup('dict', certs.json) }}"
when: "'items' in item.key"
ignore_errors: yes
But this only prints items in index 0 of the list:
"msg": "KEY:::: items, VALUE:::: {u'status': u'Active-Pending Install', u'serialHex': u'1111', u'validityStart': u'2021-05-10T21:01:36+00:00', u'cn': u'node2.test.corp.net', u'validityEnd': u'2023-05-10T21:11:36+00:00', u'application': [{u'uuid': u'2222', u'name': u'abc'}], u'certType': u'CertType.INTERNAL', u'id': 2582, u'issuer': u'server1'}"
I'm trying to print the 'cn', 'id', and 'serialHex' values from each list element for the key 'items'.
This is the data set that I'm trying to query with Ansible:
{
"total": 2,
"items": [
{
"application": [
{
"uuid": "111",
"name": "CDE"
}
],
"validityEnd": "2023-05-10T21:11:36+00:00",
"certType": "CertType.INTERNAL",
"issuer": "server1",
"id": 2582,
"validityStart": "2021-05-10T21:01:36+00:00",
"status": "Active-Pending Install",
"serialHex": "aaa",
"cn": "node2.corp.net"
},
{
"application": [
{
"uuid": "222",
"name": "CDE"
}
],
"validityEnd": "2023-05-10T21:05:26+00:00",
"certType": "CertType.INTERNAL",
"issuer": "server1",
"id": 2581,
"validityStart": "2021-05-10T20:55:26+00:00",
"status": "Active-Pending Install",
"serialHex": "bbbb",
"cn": "node1.corp.net"
}
]
}
You are regrettably stepping on a quirk of "objects in ansible are python dicts" in that .items() and .keys() and quite a few other attributes-which-are-methods cannot be referenced using the . notation since jinja2 believes you intend to call that method. Rather, one must use the __getitem__ syntax of ["items"] in order to make it abundantly clear that you mean the dict key, and not the method of the same name
tasks:
- name: use json_query as you were originally asking
debug:
msg: >-
{{ certs.json | json_query('items[*].{c: cn,i: id,s: serialHex}') }}
- name: or a jinja2 for loop as you separately attempted
debug:
msg: >-
[
{%- for i in certs.json["items"] -%}
{{ "" if loop.first else "," }}
{{ [i.cn, i.id, i.serialHex ]}}
{%- endfor -%}
]
produces the output from their respective steps:
TASK [debug] ******************************************************************************************************************************
ok: [localhost] => {
"msg": [
{
"c": "node2.corp.net",
"i": 2582,
"s": "aaa"
},
{
"c": "node1.corp.net",
"i": 2581,
"s": "bbbb"
}
]
}
TASK [debug] ******************************************************************************************************************************
ok: [localhost] => {
"msg": [
[
"node2.corp.net",
2582,
"aaa"
],
[
"node1.corp.net",
2581,
"bbbb"
]
]
}

Ansible variable in dictionary key

I am trying to execute a playbook for setting up Dell servers and I have some problems with a dictionary in module idrac_redfish_config. I need to enable SOL for a specific user, but for this I want to use a key in the dictionary with a variable because ID of user can be different from server to server.
How I try to add a variable to a dictionary key like this:
- name: Store id test-user
set_fact:
ID: "{{ result.redfish_facts.user.entries | json_query(\"[?UserName=='test-user'].Id\") }}"
- name: Enable SOL for test-user
community.general.idrac_redfish_config:
category: Manager
command: SetManagerAttributes
resource_id: iDRAC.Embedded.1
manager_attributes:
Users.{{ ID[0] }}.SolEnable: "Enabled" <---
Users.{{ ID[0] }}.IpmiLanPrivilege: "Administrator" <---
baseuri: "testhost"
username: "admin"
password: "admin"
I get this error:
TASK [Store id test-user] **************************************************************************************************************************************************************************************
ok: [localhost] => {
"ansible_facts": {
"ID": [
"8"
]
},
"changed": false
}
TASK [Enable SOL for test-user] ********************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {
"changed": false,
"invocation": {
"module_args": {
"baseuri": "testhost",
"category": "Manager",
"command": [
"SetManagerAttributes"
],
"manager_attribute_name": null,
"manager_attribute_value": null,
"manager_attributes": {
"Users.{{ ID[0] }}.IpmiLanPrivilege": "Administrator",
"Users.{{ ID[0] }}.SolEnable": "Enabled"
},
"password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"resource_id": "iDRAC.Embedded.1",
"timeout": 10,
"username": "admin"
}
},
"msg": "SetManagerAttributes: Manager attribute Users.{{ ID[0] }}.SolEnable not found"
}
If I do this:
manager_attributes: "{
'Users.{{ ID[0] }}.SolEnable': Enabled
'Users.{{ ID[0] }}.IpmiLanPrivilege': Administrator
}"
I get:
fatal: [localhost]: FAILED! => {
"changed": false,
"invocation": {
"module_args": {
"baseuri": "testhost",
"category": "Manager",
"command": [
"SetManagerAttributes"
],
"manager_attributes": "{ 'Users.8.SolEnable': Enabled 'Users.8.IpmiLanPrivilege': Administrator }",
"password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"resource_id": "iDRAC.Embedded.1",
"timeout": 10,
"username": "admin"
}
},
"msg": "argument manager_attributes is of type <class 'str'> and we were unable to convert to dict: unable to evaluate string as dictionary"
}
I didn't find in Ansible documentation how to do this correctly.
According to documentation, manager_attributes should be a dict of key/value pairs to set on your device. The keys have dots in their names and you cannot "statically" create dynamic key names as you tried above (i.e. "prefix{{ dynamic_value }}suffix": "some content" does not work as you experienced by yourself since the key name does not go through jinja2 templating).
Below is a solution. It's far from being the only one but that's the first that came to my mind and I could setup an example for you quickly. In this case, I create a list of {key: X, value: Y} dicts with your dynamic names as keys and use the items2dict filter to transform this back to a dict itself.
I don't have a network device to play this against so I could not verify that the final result is actually accepted by the module. My example simply uses a debug with your input data to illustrate and outputs a dictionary as the module expects. You will have to tune the exact key names if they are wrong but at least you should be able to move forward.
- name: Dynamic dict
hosts: localhost
gather_facts: false
vars:
ID:
- "8"
my_attributes:
- key: "Users.{{ ID[0] }}.IpmiLandPrivilege"
value: Administrator
- key: "Users.{{ ID[0] }}.SolEnable"
value: Enabled
tasks:
- name: construct a dynamic dict and debug
vars:
manager_attributes: "{{ my_attributes | items2dict }}"
debug:
var: manager_attributes
Which gives:
PLAY [Dynamic dict demo] ***************************************************************************************************************************************************************************************************************
TASK [construct a dynamic dict and debug] **********************************************************************************************************************************************************************************************
ok: [localhost] => {
"manager_attributes": {
"Users.8.IpmiLandPrivilege": "Administrator",
"Users.8.SolEnable": "Enabled"
}
}
PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Edit: An additional (out of many others) example to achieve the same goal. The output is exactly the same as above:
- name: Dynamic dict
hosts: localhost
gather_facts: false
vars:
ID:
- "8"
manager_attributes: "{{
{
'Users.' + ID[0] + '.IpmiLandPrivilege': 'Administrator',
'Users.' + ID[0] + '.SolEnable': 'Enabled'
}
}}"
tasks:
- name: construct a dynamic dict and debug
debug:
var: manager_attributes

How can loop items as input parameters to a ansible role

I am trying to convert an existing ansible playbook (for extracting the webpage content of multiple webpage URL's in parallel fashion) to re-usable roles. I need the role to accept variables in a loop and produce the output for all the items in a single task which my current playbook is able to do. But the current role is only able to produce the output of the last item in the loop
I have tried registering the webpage content inside and outside the roles but of no use. And also looping the response results with_items same as of the role is producing results for non-200 values
FYI I got the expected output by including the loop inside the role but it's defeating the purpose of maintaining a role for GET call because I will not need a loop every time for the GET call. So I am expecting to loop the role in the testplaybook.yml.
Test-Role: main.yml
uri:
url: "{{ URL_Variable }}"
method: GET
status_code: 200
return_content: yes
register: response
ignore_errors: true
testplaybook.yml:
- hosts: localhost
gather_facts: true
tasks:
- name: Include roles
include_role:
name: Test-Role
vars:
URL_Variable: "http://{{ item }}:{{ hostvars[groups['group1'][0]]['port'] }}/{{ hostvars[groups['group1'][0]]['app'] }}/"
with_items: "{{ groups['group1'] }}"
- name: "Display content"
debug:
var: response.results
Expected Output:
response.results:
ok: [127.0.0.1] => (item=[0, 'item1']) => {
"ansible_loop_var": "item",
"item": [
0,
"item1"
],
"response": {
"accept_ranges": "bytes",
"changed": false,
"connection": "close",
"content": "content1",
"content_length": "719",
"content_type": "text/html",
"cookies": {},
"failed": false,
"msg": "OK (719 bytes)",
"redirected": false,
"server": "42",
"status": 200,
"url": "http://item1:port/app/"
}
}
ok: [127.0.0.1] => (item=[1, 'item2']) => {
"ansible_loop_var": "item",
"item": [
1,
"item2"
],
"response": {
"accept_ranges": "bytes",
"changed": false,
"connection": "close",
"content": "content2",
"content_length": "719",
"content_type": "text/html",
"cookies": {},
"failed": false,
"msg": "OK (719 bytes)",
"redirected": false,
"server": "42",
"status": 200,
"url": "http://item2:port/app/"
}
}
try this Test-Role: main.yml file:
- uri:
url: "{{ URL_Variable }}"
method: GET
status_code: 200
return_content: yes
register: response
ignore_errors: true
- name: Add response to responses array
set_fact:
responses_results: "{{ responses_results | default([]) + [{'URL': URL_Variable, 'response': response.content}] }}"
this works with include_tasks, i assume it would work with include_role as well, the variable responses_results should persist across roles assuming its in the same play. if not works, try to switch your code to a single role instead, with an include_tasks.
hope it helps

Fetch doesn't transfer when i use variable

fetch module does not transfer file when i use variable and getting message as "msg": "the remote file does not exist, not transferring, ignored". But file exits in the source directory. Please suggest if anything wrong in this.
ansible 2.1.0.0
---
- hosts: host_A
become_user: yes
gather_facts: no
tasks:
- name: list files
shell: ls -1 /root/stuff/install.1_comiskey-v01
register: dumpfiles
-debug: var=dumpfiles
- name: fetch files
fetch: src={{item}} dest=/tmp/fetched/ flat=yes
with_items: ('{{dumpfiles.stdout_lines}}')
register: test
- debug: var=test
Output:
TASK [fetch files]
*************************************************************
ok: [10.1.31.82] => (item=('[u'/root/stuff/install.1_comiskey-v01',u'/root/stuff/install.1_comiskey-v02']'))
TASK [debug] *******************************************************************
ok: [10.1.31.82] => {
"test": {
"changed": false,
"msg": "All items completed",
"results": [
{
"_ansible_item_result": true,
"_ansible_no_log": false,
"changed": false,
"file": "('[u'/root/stuff/install.1_comiskey-v01', u'/root/stuff/install.1_comiskey-v02']')",
"invocation": {
"module_args": {
"dest": "/tmp/fetched/",
"flat": "yes",
"src": "('[u'/root/stuff/install.1_comiskey-v01', u'/root/stuff/install.1_comiskey-v02']')"
},
"module_name": "fetch"
},
"item": "('[u'/root/stuff/install.1_comiskey-v01', u'/root/stuff/install.1_comiskey-v02']')",
"msg": "the remote file does not exist, not transferring, ignored"
}
]
}
}
Remove parentheses from with_items. You can note that item variable is not correct on your debug output.

Resources