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.
Related
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
Im trying to schedule an ansible playbook to re-index Jira, Id also like our dev to be able to kick this off ad-hoc is needed which they would be able to do via AWX, I am able to start a re-index but would like a notification when the re-index is complete.
Here are my CLI commands and outputs:
[root#devjira02 ~]# curl -u admin:admin123! -X GET https://devjira.comapny.com/rest/api/2/reindex/progress
{"progressUrl":"/secure/admin/jira/IndexProgress.jspa?taskId=12720","currentProgress":35,"currentSubTask":"Currently working on: Issue index","type":"BACKGROUND","submittedTime":"2019-04-01T14:23:50.971+0100","startTime":"2019-04-01T14:23:50.973+0100","success":false}
[root#devjira02 ~]# curl -u admin:admin123! -X GET https://devjira.company.com/rest/api/2/reindex/progress
{"progressUrl":"/secure/admin/jira/IndexProgress.jspa?taskId=12720","currentProgress":100,"type":"BACKGROUND","submittedTime":"2019-04-01T14:23:50.971+0100","startTime":"2019-04-01T14:23:50.973+0100","finishTime":"2019-04-01T14:24:33.922+0100","success":true}
My Playbook:
- name: DEV Jira Re-index
become: true
become_method: sudo
become_user: root
hosts: DEVJIRA02
gather_facts: yes
tasks:
- name: Jira Re-index
uri:
url: https://devjira.company.com/rest/api/2/reindex?type=BACKGROUND_PREFERRED
method: POST
user: admin
password: admin123!
force_basic_auth: yes
status_code: [201, 202]
- name: Wait until re-index completes
uri:
url: https://devjira.company.com/rest/api/2/reindex/progress
method: GET
user: admin
password: admin123!
force_basic_auth: yes
status_code: [201, 202]
return_content: yes
body_format: json
register: result
until: result.json.success == true
retries: 180
delay: 30
Id like the second part to retry until success == true at which point AWX can send a notification email. Running the playbook manually will start the re-index but the retry will just retry even though the re-index has finished
Edit after user comment:
Although my below proposition is still good practice and will prevent some possible future headaches, it proved not to be the issue in this case.
I didn't pay attention at first that your are requesting the url call to return a 201 or 202 status while the jira documentation specifies you will receive a 200.
You should either:
Change the expected return code: status: 200
Drop the status option completely and change your condition: until: response.status == 200 and (result.json.success | bool)
Original proposition (does not fix but still good practice)
It is generally a bad idea in ansible to test against a literal true or false because ansible/yaml is quite permissive about the values you can use for that (yes, true, Y, 1, 234, no, false, 0, ...) and also because the values can sometimes be interpreted as pure strings rather that booleans (try with vars_prompt to understant the problem if you have some time).
Ansible provides a filter to test the boolean representation of a value. I'm not 100% sure this will fix your current problem but I would try with until: result.json.success | bool
I trying to create a simple paybook with a common role. Unfortunately I get stymied by ansible. I have looked up and down the internet for solution for this error.
The setup:
I am running ansible 2.7.4 on Ubuntu 18.04
directory structure:
~/Ansible_Do
playbook.yml
inventory (hosts file)
/roles
/common
/defaults
main.yml (other variables)
/tasks
main.yml
richard_e.yml
/vars
vars_and_stuff.yml (vault)
I have a simple playbook.yml
---
# My playbook 1
- hosts: test
- name: Go to common role to run tasks.
roles:
- common
tasks:
- name: echo something
shell: echo $(ip addr | grep inet)
...
I run this command to start the playbook:
~/Ansible_Do$ ansible-playbook -vv --vault-id #prompt -i ~/Ansible_Do/inventory playbook.yml
I enter the vault password continuing the playbook.
The playbook starts pulls facts from the test group of servers. Then reads the role and works to /roles/common. That calls the /common/tasks/main.yml file. This is where the error happens.
The error appears to have been in '/home/~/Ansible_Do/roles/common/tasks/main.yml': line 8, column 3
# Common/tasks file
---
- name: Bring variable from vault
include_vars:
file: vars_and_stuff.yml
name: My_password
- name: Super Richard <====== Error
become: yes
vars:
ansible_become_pass: "{{ My_password }}"
- import_tasks: ./roles/common/tasks/ricahrd_e.yml
...
The ./roles/common/tasks/ricahrd_e.yml is a simple testing task.
---
- name: say hi
debug:
msg: "Active server."
...
The error on "- name". I have checked online and in the Ansible docs to see if there is a key I'm missing. I found an example for include_vars in a /role/tasks (https://gist.github.com/halberom/ef3ea6d6764e929923b0888740e05211) showing proper syntax (I presume) in a simple role. The code works as parts, but not together.
I have reached what I can understand. I feel that is error is utterly simple and I am missing something (forest for the trees).
The error means exactly what it says, except the "module name" is not misspelled in your case, but missing altogether.
This...
- name: Super Richard <====== Error
become: yes
vars:
ansible_become_pass: "{{ My_password }}"
... is not a valid task definition, it does not declare an action.
An action in Ansible is a call to a module, hence "misspelled module name".
The error comes after name, because that's where Ansible expects the name of the "module" that you want to call, e.g. shell in your first example.
You are probably assuming that become is a "module", but it is not.
It is a "playbook keyword", in this case applied on the task level, which has the effect that you become another user for this task only.
But as the task has no action, you get this error.
See docs:
Playbook keywords
Understanding privilege escalation
After a bit of work I got the playbook to work. Knowing that 'become' is not a task was the start. I also found out how to pull the proper vars from the vault.
# My first playbook 1
- hosts: test
become: yes
vars_files:
- ./roles/common/vars/vars_and_stuff.yml
vars:
ansible_become_pass: "{{ My_password }}"
roles:
- common
tasks:
- name: echo something
shell: echo $(ip addr | grep inet)
The vars file access the vault and then vars: pulls the password used by become. With become in force I ran the other tasks in the common role with a last standalone task. Lastly, don't try to - name: at the top level of the playbook as it trigger a hosts undefined error.
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
I'm trying to spin up an AWS deployment environment in Ansible, and I want to make it so that if something fails along the way, Ansible tears down everything on AWS that has been spun up so far. I can't figure out how to get Ansible to throw an error within the role
For example:
<main.yml>
- hosts: localhost
connection: local
roles:
- make_ec2_role
- make_rds_role
- make_s3_role
2. Then I want it to run some code based on that error here.
<make_rds_role>
- name: "Make it"
- rds:
params: etc <-- 1. Let's say it fails in the middle here
I've tried:
- name: this command prints FAILED when it fails
command: /usr/bin/example-command -x -y -z
register: command_result
failed_when: "'FAILED' in command_result.stderr"
As well as other things on within the documentation, but what I really want is just a way to use something like the "block" and "rescue" commands , but as far as I can tell that only works within the same book and on plays, not roles. Does anyone have a good way to do this?
Wrap tasks inside your roles into block/rescue thing.
Make sure that rescue block has at least one task – this way Ansible will not mark the host as failed.
Like this:
- block:
- name: task 1
... # something bad may happen here
- name: task N
rescue:
- assert: # we need a dummy task here to prevent our host from being failed
that: ansible_failed_task is defined
Recent versions of Ansible register ansible_failed_task and ansible_failed_result when hit rescue block.
So you can do some post_tasks in your main.yml playbook like this:
post_tasks:
- debug:
msg: "Failed task: {{ ansible_failed_task }}, failed result: {{ ansible_failed_result }}"
when: ansible_failed_task is defined
But be warned that this trick will NOT prevent other roles from executing.
So in your example if make_rds_role fails ansible will apply make_s3_role and run your post_tasks afterwards.
If you need to prevent it, add some checking for ansible_failed_task fact in the beginning of each role or something.