Execute curl command in ansible - shell

I am trying to pass this curl command in ansible playbook :
shell: 'curl -k -u {{ AMBARI_USER }}:{{ AMBARI_PASSWORD }} -H 'X-Requested-By: ambari' -X POST -d '[{"Event": {"specs": [{"principal_type": "users", "sync_type": "all"}, {"principal_type": "groups", "sync_type": "all"}]}}]' https://{{AMBARI_SERVER_HOST}}:8083/api/v1/ldap_sync_events
here's my ansible playbook :
- hosts: adm
tasks:
- name: sync ldap
shell: "curl -k -u {{ AMBARI_USER }}:{{ AMBARI_PASSWORD }} -H 'X-Requested-By: ambari' -X POST -d '[{"Event": {"specs": [{"principal_type": "users", "sync_type": "all"}, {"principal_type": "groups", "sync_type": "all"}]}}]' https://{{AMBARI_SERVER_HOST}}:8083/api/v1/ldap_sync_events"
The thing is that this command has multiple double and simple cotes, so it's not wrking, is there anyway to pass it here or i should create a shell script for it ?
Thanks

Is there a reason you're using plain shell commands instead of the uri module? A general best practice is to prefer Ansible modules. They already fit to Ansible (e.g. change detection, error handling), save you work and may care about security if needed.
For example, in your case it avoids struggeling with multiple nested quotes, since it abstracts the parameters instead of having a large call. Raw shell commands should only be used in special cases, when the Ansible module doesn't work for the use case.
Something like this should fit for the base request using the uri module:
- name: Make an API call
uri:
method: POST
url: "https://{{ AMBARI_SERVER_HOST }}:8083/api/v1/ldap_sync_events"
url_username: "{{ AMBARI_USER }}"
url_password: "{{ AMBARI_PASSWORD }}"
body_format: json
headers:
'X-Requested-By': 'ambari'
Several ways exists to pass the JSON body:
As string, which would be possible here since you don't need additional quotes as you needed for the curl call.
Something like this could be used:
body: '[{"Event": ...'
A Ansible data structure like an array. Ansible would automatically convert it to JSON, as long as body_format is set to the corresponding type as the case in my example above
From a file. I'd prefer this if the JSON is larger, because you can directly paste the object there and have proper formatting, without messing the Ansible part. Just use lookup like this:
body: "{{ lookup('file','api-body.json') }}"
Just add the desired body property to uri. For example, If you want to have a json file, like this:
- name: Make an API call
uri:
method: POST
url: "https://{{ AMBARI_SERVER_HOST }}:8083/api/v1/ldap_sync_events"
url_username: "{{ AMBARI_USER }}"
url_password: "{{ AMBARI_PASSWORD }}"
body: "{{ lookup('file','api-body.json') }}"
body_format: json
headers:
'X-Requested-By': 'ambari'
# If you're interested in the response
return_content: yes
register: api_result

Related

Accessing ENV vars in facts.d scripts

I have a need for dynamic facts, and as a solution, have written a script that lives in /etc/ansible/facts.d/my_facts.fact
It has to pull some of its information from a cloud provider, and therefore, needs an API token, which, for obvious reasons, one does not want to store in plain text on the server.
Ansible is always triggered by a process that includes MY_SECRET_API_TOKEN exported in it's environment.
The ansible code looks like such with MY_SECRET_API_TOKEN :
---
- name: do the things
import_tasks: my_tasks.yml
environment:
MY_SECRET_API_TOKEN: "{{ lookup('env', 'MY_SECRET_TOKEN') }}"
when: ansible_facts['os_family'] == "Debian"
And the my_facts.fact bash file has a line like this that'll trip fact gathering with a rc: 99 when the TOKEN is not present in the environment.
[[ ${MY_SECRET_TOKEN} == '' ]] && { echo Secret not found && exit 99 ; }
curl --silent -XGET -H "Authorization: Bearer ${MY_SECRET_TOKEN}" https://web.site.com/api/lookup
I've tried putting the environment: block at the role level to no avail as well.
How can I get Gather Facts on the remote server to pick up that ENV variable?
You have to set environment at the play level if you want it to affect the play-level fact gathering:
- hosts: all
gather_facts: true
environment:
MY_SECRET_API_TOKEN: "{{ lookup('env', 'MY_SECRET_TOKEN') }}"
tasks:
# etc.
If you do not want to (or cannot) modify the play, you can instead add an explicit setup task with the correct environment:
- name: Re-gather local facts
setup:
gather_subset: local
environment:
MY_SECRET_API_TOKEN: "{{ lookup('env', 'MY_SECRET_TOKEN') }}"

Using a job identifier to group ansible tasks into logical task group

I'm new to ansible but trying to progress and learn.
I'm running a number of API calls to configure a web proxy tool.
The flow is essentially, make a GET request to see if the object exists, if does not exist, follow up with a POST request to create the object. Pretty standard.
This procedure repeats itself multiple times as the product can have a number of instances of the same type of configuration. So I would like to make the GET/POST routine as generic repeatable as possible, by associating an id.
The playbook is:
set_fact:
'vh_task_id': 01
- name: check virtual host exists
uri:
method: GET
url: "{{ admin_api_url }}/hosts?vHost={{ public_virtual_host|urlencode() }}"
return_content: yes
headers:
X-XSRF-Header: "Access"
user: "{{ admin_user }}"
password: "{{ admin_password }}"
status_code: 200
validate_certs: no
register: response
- name: set fact virtual host task id response '{{ public_virtual_host }}'
set_fact:
'response_{{ vh_task_id }}': "{{ response }}"
This all works fine, up to this point and response_{{ vh_task_id }} correctly contains the response body from the above set_fact, as I can see it, if I output it.
The issue now becomes when I want to query the JSON in response_{{ vh_task_id }} as the variable to query to get the id value.
The set_fact, is setting a literal value ofresponse_01 rather than seeing the concatenation as a registered variable.
Here is an example of how I was trying to do it:
set_fact:
'vh_id': "{{ hostvars[inventory_hostname].response_{{ vh_task_id }} | json_query('json.items[0].id') }}"
Many thanks for any help.
Try this:
set_fact:
'vh_id': "{{ hostvars[inventory_hostname].hostvar | json_query('json.items[0].id') }}"
vars:
hostvar: response_{{ vh_task_id }}
Also you don't need explicit quote ' for plain string as variable name. Like, you can change 'vh_id': to vh_id: if no other reason to keep the name with quotes.
Thanks for your time to come back, I tested this but, in the end, I have since learnt that using the with_items, gives me better results and it more repeatable

Ansible Playbook creating BitBucket Repository

I am trying to create a BitBucket Repository via Ansible Playbook.
However, running the following command using RestAPI works fine:
curl -u user:Password \
-d '#data.json' \
-X POST \
-H "Content-Type: application/json" \
http:// https://api.bitbucket.org/1.0/projects/SUN/repos
JSON includes the following:
{"name": "my-repo",
"scmId": "git",
"forkable": true}
I tried all kind of ways to convert this curl command to an ansible playbook but it`s always failing with different errors.
- hosts: localhost
tasks:
- name: Create a Bitbucket Repo
uri:
url: https://api.bitbucket.org/1.0/projects/SUN/repos
method: POST
user: user
password: password
src: data.json
force_basic_auth: yes
status_code: 201
body_format: json
When leaving "tasks" it says playbook needs to be a list of plays.
When removing tasks it says "uri" is not valid attribute.
Can anyone advise on the structure the playbook should have in this example?
Thanks a lot
The "tasks needs to be a list" message generally means you either
forgot a - (dash) before a task
- hosts: localhost
tasks
name: # no dash here.
uri:
# ...
misindented something.
I Re-formatted your code. It looks OK to me, as reformatted. Double check what you have in your playbook. But you do need the tasks: keyword or you will get the second error message you mention for sure.

Alter Ansible registered variable in place

I'm writing an Ansible role which will update the settings of an existing application configuration. The role must get the settings from the application's API. Alter a portion of the settings. Then POST the complete set back to the application.
- name: Get Current Settings
shell: curl -L -H "Accept: application/json" -u "{{auth}}" https://{{ server }}/api/settings
register: settings
- name: update settings
<Do something to modify settings.stdout as JSON>
register: newSettings # Save
when: settings.Changed = True
- name: Post Settings
shell: curl -L -u "{{auth}}" -X POST -H "Content-Type: application/json" -d {{newSettings}}
I would consider using the URI module of Ansible.
Interacts with HTTP and HTTPS web services and supports Digest, Basic and WSSE HTTP authentication mechanisms.
You can do the first part with a GET:
- uri:
url: http://www.example.com
return_content: yes
register: webpage
Do your data massaging and then do a POST:
- name: Example POST
uri:
url: https://your.example.com/rest/api/2/issue/
method: POST
user: your_username
password: your_pass
body: "{{ lookup('file','issue.json') }}"
force_basic_auth: yes
status_code: 201
body_format: json
Edit:
As was pointed out in the comments you can use set_fact to set the value you change from the information you receive from the GET call.
- set_fact: one_fact="something" other_fact="{{ local_var }}"
Inside your put you would access the new fact like you would any other variable in Ansible

Save list in key/value pair in Consul

I suppose this could be for any key/value storage type but I'm mainly using Consul for storage. I need a way to save a list in the value of a key. However, the quotes around the elements are stripped.
I've tried them all, the problem is I need them to return as a valid list so that i can run it through Ansible w/ with_flattened
curl -X PUT -d '['ui','dashboard']' http://localhost:8500/v1/kv/deploylist
curl -X PUT -d '[\'ui\',\'dashboard']' http://localhost:8500/v1/kv/deploylist
Ansible tasks:
- debug: msg="{{ deploylist_vars.value | map(attribute='Value') | first }}"
register: module_id
# Output
"msg": "[ui,dashboard]"
I need to loop through the list and perform further actions. However, I can't seem to parse the elements correctly.
- debug: msg="{{ item }}"
with_flattened:
- "{{ module_id }}"
Neither of those quotings looks quite right. I expect you want something JSON-like to be PUT, probably ["ui","dashboard"]. The following should get bash/zsh to pass that through:
curl -X PUT -d '["ui","dashboard"]' http://localhost:8500/v1/kv/deploylist
You could further do some backslash-escaping inside the single quotes, but don’t add more single quotes without some care.

Resources