Retry Ansible URI PUT API Call with a POST API Call - api

Hello I am trying to run a PUT API call via Ansible URI module for a particular API Endpoint in my application, using a dictionary that contains the json files and that is defined as:
example: { 'example1' : 'v1', 'example2': 'v2''}
- name: Update existing
block:
- name: update existing
uri:
url: "{{url}}/api/{{item.key}}/"
method: PUT
body: "{{ lookup('file', 'example/{{item.key}}/{{item.value}}.json') }}"
status_code: 200
body_format: json
headers:
Content-Type: "application/json"
Authorization: "Token {{ token.json.token }}"
with_dict: "{{ example }}"
register: result
For the PUT api call, this api endpoint will fail is the {{item.key}} does not exist, e.g. if
"{{url}}/api/{{item.key}}/" endpoint does not exist, hence it will give a 4xx error.
Given the task fails and I get a 4xx error when the api endpoint for the item does not exist, I want to run a POST command for that same json file.
How can I do this in ansible, to retry a task that failed but only specifically for that {{item.key}} and {{item.value}} in dictionary?
or
Is there a better way to do this to retry a failed PUT with a POST command
I want to use the ansible URI module
Thanks!

You can ignore the error case and then loop through result.results with filtering to keep only errors. You can have access to the original item with item.item:
- name: update existing with PUT
uri:
url: "{{url}}/api/{{item.key}}/"
method: PUT
body: "{{ lookup('file', 'example/{{item.key}}/{{item.value}}.json') }}"
status_code:
- 200
- 404 # supposing it's the "normal" error case
body_format: json
headers:
Content-Type: "application/json"
Authorization: "Token {{ token.json.token }}"
loop: "{{ example | items2dict }}"
register: result
- name: update existing with POST
uri:
url: "{{url}}/api/{{item.item.key}}/"
method: POSTT
body: "{{ lookup('file', 'example/{{item.item.key}}/{{item.item.value}}.json') }}"
status_code: 200
body_format: json
headers:
Content-Type: "application/json"
Authorization: "Token {{ token.json.token }}"
loop: "{{ result.results | rejectattr('status', '200') }}" # filter out 200 status from previous task

Related

Ansible uri module loop on files

I'm using the Ansible uri module to make a PUT API call and using all files in a directory as parameters.
I have a list of files in a directory, and I want to use the name and the content of each file in the API call
First of all i tried to list all files.
- name: "Find pipeline files in folder"
find:
paths: "/app/pipelines"
patterns: "pipeline-*.json"
file_type: "file"
register: pipe_files
- debug:
var: pipe_files
Then I want to make a loop on each file in the directory and call the API
- name: PUT PIPE
uri:
method: PUT
headers:
Content-Type: "application/json"
url: "https://api_url/**FILE_NAME**"
user: "user"
password: "user_pass"
body_format: json
body: "{{ lookup('file','/app/pipelines/**FILE_NAME.json**') }}"
validate_certs: no
force_basic_auth: yes
validate_certs: no
return_content: yes
register: pipeline_created
until: pipeline_created.status == 200
When I deploy the content, I don't have the exact filename, how can I make the loop on each file to call the API?
Best regards,
Thanks in advance.
pipe_files is a register from a find task. You can have a look at returned values in the find module documentation. You can also examine your debug task output to better get accustomed with the content of the variable.
Anyway. The list of file objects returned will be in pipe_files.files. Each element is a dict where the information you need is in the path key.
You may test with
- name: PUT pipeline
uri:
method: PUT
headers:
Content-Type: "application/json"
url: "https://api_url/{{ item.path | basename }}" # depends on input list content
user: "user"
password: "user_pass"
body_format: json
body: "{{ lookup('file', item.path) }}" # content
validate_certs: no
force_basic_auth: yes
validate_certs: no
return_content: yes
until: pipeline_created.status == 200
loop: "{{ pipe_files.files }}"
register: pipeline_created # result will become a list

Add Single Quotes around a list in Ansible

I have a list of items services and each needs to be wrapped in single quotes to configure some parameters.
The simplest solution I saw posted was:
"{{services | match('quote') | join(' OR ')}}"
This only wrapped the first element in the list in a single quote but not the remaining.
I also tried some variations of match regex.
Finally I tried adding the single quote manually in the data source, then joining. The first element retained the single quotes but subsequent were stripped off? What is going on here?
Right now they are static from the inventory
i.e.:
---
inventory:
hosts:
host1:
procs:
- splunkd.*
services:
- 'some service name'
- 'another service name'
- 'SplunkForwarder'
I need the end result to be
"Name='some service name' OR Name='another service name'"
Currently with the services single quoted in variable the quotes are stripped or ignored.
Result
Name=some service or Name=another service
you could cut your problem by using loop:
tasks:
- name: set var
set_fact:
result: "{{ result | d('') + _i + _o }}"
loop: "{{ services }}"
loop_control:
extended: yes
vars:
_i: "Name='{{ item }}'"
_o: "{{ '' if ansible_loop.last else ' OR ' }}"
- name: display result
debug:
var: result
result:
ok: [localhost] => {
"result": "Name='some service name' OR Name='another service name' OR Name='SplunkForwarder'"
}
with your vars:
- name: Join services
set_fact:
joined_services: "{{ joined_services | d('') + service + serviceAppend }}"
loop: "{{ services }}"
loop_control:
extended: yes
vars:
service: "'{{ item }}'"
serviceAppend: "{{ '' if ansible_loop.last else ' OR ' }}"

rundeck - api - ansible - uri - Add Key passwd - Value not good

I try to add a new key password, facing the issue with the API. The key is successfully created. But, when is used is not good value.
I try to generate the key with Postman, and the key is created but the value is not correct. When I use the same value manually on the interface then it works fine.
I deleted before the key (manually)
postman:
curl --location --request POST 'https://rundeck.dev.xxxxxx.com/api/11/storage/keys/project_name/gitlab?authtoken=FdMORu02flT2R5zI' \
--header 'Content-type: application/x-rundeck-data-password' \
--header 'Cookie: AWSALB=D6Kpid4U/o7uHy9G0Pg40uvILs1toq367tPzPiCskEha7YGM3eCJldNnKyMFYBrkwOXIyvVmKAsIe9yIRm/8xOX/0mj4LIRy2wMl3qYpOvXKw3x9e+rXnjd8gEjX; AWSALBCORS=D6Kpid4U/o7uHy9G0Pg40uvILs1toq367tPzPiCskEha7YGM3eCJldNnKyMFYBrkwOXIyvVmKAsIe9yIRm/8xOX/0mj4LIRy2wMl3qYpOvXKw3x9e+rXnjd8gEjX' \
--data-raw 'XKmB1wkjsdfikjkHkKwCEW'
I try to add SCM with the key generated but still is not working. However, when I create manually the key with the same name and value the SCM import is working.
I have the same error with ansible with URI.
I deleted before the key (manually)
I create a playbook to access rundeck API
- name: "Create Keys {{ project_name }} - gitlab"
uri:
url: "{{ RD_URL }}{{ API_11 }}/storage/keys/{{ project_name }}/gitlab?authtoken={{ RD_TOKEN }}"
method: POST
body_format: raw
validate_certs: no
status_code: [201, 409]
return_content: true
headers:
Content-Type: application/x-rundeck-data-password
X-Rundeck-Auth-Token: "{{ RD_TOKEN }}"
body: '{{ GITLAB_TOKEN }}'
You can create the password via API 40 and Rundeck 3.4.9 with the following call:
#!/bin/sh
# protocol
protocol="http"
# basic rundeck info
rdeck_host="localhost"
rdeck_port="4440"
rdeck_api="40"
rdeck_token="hcFPFbJHFIqerwaRsWtuhPnPAIUoY4Kt"
# api call
curl --location --request POST "$protocol://$rdeck_host:$rdeck_port/api/$rdeck_api/storage/keys/mypass" \
--header "X-Rundeck-Auth-Token: $rdeck_token" \
--header "Content-Type: application/x-rundeck-data-password" \
--data-raw "12345"
Also, I created a Job example to test the password content:
- defaultTab: nodes
description: ''
executionEnabled: true
id: 5658bb13-f9e9-494b-839c-d18f25057a4e
loglevel: INFO
name: HelloWorld
nodeFilterEditable: false
options:
- name: opt1
secure: true
storagePath: keys/mypass
valueExposed: true
plugins:
ExecutionLifecycle: null
scheduleEnabled: true
sequence:
commands:
- exec: echo ${option.opt1}
keepgoing: false
strategy: node-first
uuid: 5658bb13-f9e9-494b-839c-d18f25057a4e
Running the job, you can see the password value.
Also tested using the SCM config.
So, make sure that you're pointing to the right key in your SCM config and ensure to use the latest API version (40 at this moment) in your API Call.

How to disable Ansible's password obfuscating feature

I have a simple Ansible playbook to
Fetch a database connection config from an RestAPI,
Extract the config object from the payload,
Using the config JSON (as request body) to create a PUT request to another RestAPI.
At the 3rd stage I found that the database username and password combination is wrong. Later, while I print the outputs, I have found that the password has been replaced with a string named "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER".
After some googling, I found that this is a security feature by Ansible. Unfortunately, I haven't found any configuration or something like this to disable this feature. Is it possible to disable this feature? Or any other workaround?
---
- name: my-playbook
gather_facts: no
hosts: all
vars_files:
- secret
tasks:
- name: Fetch the config payload from the API
uri:
url: "{{get_config}}"
method: GET
user: "{{username}}"
password: "{{password}}"
validate_certs: no
return_content: yes
status_code: 200
body_format: json
register: config
- name: Extract the config object
set_fact:
config_raw: "{{ config.json | json_query(jmesquery) }}"
vars:
jmesquery: '{{name}}.config'
- name: print the config
debug:
msg: "{{config_raw}}"
- name: Creating object using config
uri:
url: "{{create_ocject}}"
method: PUT
user: "{{username}}"
password: "{{password}}"
validate_certs: no
body: "{{config_raw}}"
body_format: json
return_content: yes
status_code: 200
headers:
Content-Type: "application/json"
register: test_res
- name: output value
debug:
msg: "{{test_res.json}}"

Display authorization header when logging in to api

I am using OpenApi do document an API, i have a path /Login which lets me specify Username and Password as parameters and returns a Bearer style JWT in the "authorization" header in the response.
For further use in the API this JWT is used with the "Authorize" mechanism so the other paths can be accessed.
Sadly the authorization header is only visible if i open the network tracing in my browser.
Therefore my question, can i somehow tweak my OpenAPI YAML so the value is displayed and can then be copy/pasted to the authorization for the rest of the API. I am using the latest swagger editor (https://editor.swagger.io/)
This is what i have specified
paths:
/Login:
post:
servers:
- url: ....
description: Local test server
tags:
- Login
summary: Logs into the API and returns the authorization.
description: Username and Password are passed in header
security: []
#- bearerAuth: []
parameters:
- $ref: '#/components/parameters/Username'
- $ref: '#/components/parameters/Password'
responses:
200:
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/Login'
headers:
authorization:
$ref: '#/components/headers/authorization'
parameters:
Username:
name: Username
in: header
description: Username to authorize with
schema:
type: string
Password:
name: Password
in: header
description: Password to authorize with
schema:
type: string
headers:
authorization:
schema:
type: string
description: Authorization token.