Ansible Tower server claims it was sent a bad request - ansible

I have written a playbook to create credentials with custom credentials, below is the ansible playbook
---
- name: Trigger an Atower API
hosts: localhost
connection: local
tasks:
- name: Create a valid SCM credential from a private_key file
command: tower-cli credential create --organization "Default" --name "DevOps User" --credential-type "csa-test2" --inputs "{'user':'devops', 'stg01_ssh_key':\"$( sed -z 's/\n/\\n/g' test.pem )\" }"
no_log: false
I am getting this error related to quotaions
name", "DevOps User", "--credential-type", "csa-test2", "--inputs", "{'user':'devops', 'stg01_ssh_key':\"$( sed -z 's/\\n/\\n/g' test.pem )\" }"], "delta": "0:00:01.319114", "end": "2021-01-07 16:00:22.763388", "msg": "non-zero return code", "rc": 40, "start": "2021-01-07 16:00:21.444274", "stderr": "Error: The Tower server claims it was sent a bad request.\n\nPOST http://x.x.x.x:13080/api/v2/credentials/\nParams: None\nData: {\"inputs\": {\"stg01_ssh_key\": \"$( sed -z 's/\\n/\\n/g' test.pem )\", \"user\": \"devops\"}, \"credential_type\": 36, \"organization\": 1, \"name\": \"DevOps User\"}\n\nResponse: {\"inputs\":{\"stg01_ssh_key\":[\"Invalid certificate or key: $( sed -z 's/\\n/\\n/g' test.pem )...\"]}}", "stderr_lines": ["Error: The Tower server claims it was sent a bad request.", "", "POST http://x.x.x.x:13080/api/v2/credentials/", "Params: None", "Data: {\"inputs\": {\"stg01_ssh_key\": \"$( sed -z 's/\\n/\\n/g' test.pem )\", \"user\": \"devops\"}, \"credential_type\": 36, \"organization\": 1, \"name\": \"DevOps User\"}", "", "Response: {\"inputs\":{\"stg01_ssh_key\":[\"Invalid certificate or key: $( sed -z 's/\\n/\\n/g' test.pem )...\"]}}"], "stdout": "", "stdout_lines": []}
Also, when i try manually "tower-cli create" command, it is working fine. Can anyone help me in this. I dunno what is wrong here.

try to use shell instead of command, documentation says
The command(s) will not be processed through the shell, so variables like $HOSTNAME and operations like "*", "<", ">", "|", ";" and "&" will not work. Use the ansible.builtin.shell module if you need these features.
source: Ansible Documentation

Related

How can I get the private IP put into conf file ansible?

I have this role task file in roles/make_elasticsearch_conf/tasks/main.yml:
---
# tasks file for make_elasticsearch_conf
#
- name: Get private IP address
command:
cmd: "hostname -I | awk '{print $2}'"
register: "cluster_ip"
- name: Create /etc/elasticsearch/elasticsearch.yml File
ansible.builtin.template:
src: elasticsearch.yml.j2
dest: /etc/elasticsearch/elasticsearch.yml
I also have a template in roles/make_elasticsearch_conf/templates/elasticsearch.yml.j2:
cluster.name: {{ ansible_host }}
node.name: {{ ansible_host }}
network.host: {{ cluster_ip }}
I use it in this make_elastic_search_conf.yml playbook:
---
- name: Make Elastic Search Config.
hosts: all
become: True
gather_facts: True
roles:
- roles/make_elasticsearch_conf
When I run my playbook I get this error:
FAILED! => {"changed": true, "cmd": ["hostname", "-I", "|", "awk", "{print $2}"], "delta": "0:00:00.006257", "end": "2022-12-06 21:54:47.612238", "msg": "non-zero return code", "rc": 255, "start": "2022-12-06 21:54:47.605981", "stderr": "Usage: hostname [-b] {hostname|-F file} set host name (from file)\n hostname [-a|-A|-d|-f|-i|-I|-s|-y] display formatted name\n hostname display host name\n\n {yp,nis,}domainname {nisdomain|-F file} set NIS domain name (from file)\n {yp,nis,}domainname display NIS domain name\n\n dnsdomainname display dns domain name\n\n hostname -V|--version|-h|--help print info and exit\n\nProgram name:\n {yp,nis,}domainname=hostname -y\n dnsdomainname=hostname -d\n\nProgram options:\n -a, --alias alias names\n -A, --all-fqdns all long host names (FQDNs)\n -b, --boot set default hostname if none available\n -d, --domain DNS domain name\n -f, --fqdn, --long long host name (FQDN)\n -F, --file read host name or NIS domain name from given file\n -i, --ip-address addresses for the host name\n -I, --all-ip-addresses all addresses for the host\n -s, --short short host name\n -y, --yp, --nis NIS/YP domain name\n\nDescription:\n This command can get or set the host name or the NIS domain name. You can\n also get the DNS domain or the FQDN (fully qualified domain name).\n Unless you are using bind or NIS for host lookups you can change the\n FQDN (Fully Qualified Domain Name) and the DNS domain name (which is\n part of the FQDN) in the /etc/hosts file.", "stderr_lines": ["Usage: hostname [-b] {hostname|-F file} set host name (from file)", " hostname [-a|-A|-d|-f|-i|-I|-s|-y] display formatted name", " hostname display host name", "", " {yp,nis,}domainname {nisdomain|-F file} set NIS domain name (from file)", " {yp,nis,}domainname display NIS domain name", "", " dnsdomainname display dns domain name", "", " hostname -V|--version|-h|--help print info and exit", "", "Program name:", " {yp,nis,}domainname=hostname -y", " dnsdomainname=hostname -d", "", "Program options:", " -a, --alias alias names", " -A, --all-fqdns all long host names (FQDNs)", " -b, --boot set default hostname if none available", " -d, --domain DNS domain name", " -f, --fqdn, --long long host name (FQDN)", " -F, --file read host name or NIS domain name from given file", " -i, --ip-address addresses for the host name", " -I, --all-ip-addresses all addresses for the host", " -s, --short short host name", " -y, --yp, --nis NIS/YP domain name", "", "Description:", " This command can get or set the host name or the NIS domain name. You can", " also get the DNS domain or the FQDN (fully qualified domain name).", " Unless you are using bind or NIS for host lookups you can change the", " FQDN (Fully Qualified Domain Name) and the DNS domain name (which is", " part of the FQDN) in the /etc/hosts file."], "stdout": "", "stdout_lines": []}
I have tried all sorts of ways to get the private ip of the host but nothing I have tried gave the expected result.
The problem is caused by |.
As per the documentation of the command module:
If you want to run a command through the shell (say you are using <,> >, |, and so on), you actually want the ansible.builtin.shell module instead. Parsing shell metacharacters can lead to unexpected commands
being executed if quoting is not done correctly so it is more secure
to use the command module when possible.
You might want to use shell module. Or better get the IP address from the ansible facts.

I'm trying to unseal vault using ansible. But I'm getting connection refused error

It worked a few days back, i even checked similar problems like here
I tried to add the environment variables and everything, my hcl file aslo is not a problem as far as i know
hcl file is
storage "file" {
path = "/home/***/vault/"
}
listener "tcp" {
address = "127.0.0.1:8200"
tls_disable = 1
}
my unseal.yml looks like this
---
- name: Removing login and putting to another file
shell: sed -n '7p' keys.txt > login.txt
- name: Remove all lines other than the keys
shell: sed '6,$d' keys.txt > temp.txt
- name: Extracting the keys
shell: cut -c15- temp.txt > unseal_keys.txt
- name: Deleting unnecessary files
shell: rm temp.txt
- name: Unsealing the vault
environment:
VAULT_ADDR: http://127.0.0.1:8200
shell: vault operator unseal $(awk 'NR==1' unseal_keys.txt)
- name: Unsealing the vault
environment:
VAULT_ADDR: http://127.0.0.1:8200
shell: vault operator unseal $(awk 'NR==2' unseal_keys.txt)
- name: Unsealing the vault
environment:
VAULT_ADDR: http://127.0.0.1:8200
shell: vault operator unseal $(awk 'NR==3' unseal_keys.txt)
register: check
- debug: var=check.stdout_lines
- name: Login
environment:
VAULT_ADDR: http://127.0.0.1:8200
shell: vault login $(sed 's/Initial Root Token://; s/ //' login.txt)
register: checkLogin
- debug: var=checkLogin.stdout_lines
My start-server.yml looks like this
---
#- name: Disable mlock
# shell: sudo setcap cap_ipc_lock=+ep $(readlink -f $(which vault))
# shell: LimitMEMLOCK=infinity
- name: Start vault service
systemd:
state: started
name: vault
daemon_reload: yes
environment:
VAULT_ADDR: http://127.0.0.1:8200
become: true
- pause:
seconds: 15
This the error shown.
fatal: [europa]: FAILED! => {"changed": true, "cmd": "vault operator unseal $(awk 'NR==1' unseal_keys.txt)", "delta": "0:00:00.049258", "end": "2019-09-17 12:25:48.987789", "msg": "non-zero return code", "rc": 2, "start": "2019-09-17 12:25:48.938531", "stderr": "Error unsealing: Put http://127.0.0.1:8200/v1/sys/unseal: dial tcp 127.0.0.1:8200: connect: connection refused", "stderr_lines": ["Error unsealing: Put http://127.0.0.1:8200/v1/sys/unseal: dial tcp 127.0.0.1:8200: connect: connection refused"], "stdout": "", "stdout_lines": []}
This is the main error
"Error unsealing: Put http://127.0.0.1:8200/v1/sys/unseal: dial tcp 127.0.0.1:8200: connect: connection refused", "stderr_lines": ["Error unsealing: Put http://127.0.0.1:8200/v1/sys/unseal: dial tcp 127.0.0.1:8200: connect: connection refused"
"Error unsealing: Put http://127.0.0.1:8200/v1/sys/unseal: dial tcp 127.0.0.1:8200: connect: connection refused"
As it is showing connection refused, most probably your vault service is not running.
Other thing which I can suggest is that you can make a script named unseal_vault.sh and can use that script to unseal your vault instead of repeating same tasks in your playbook.
Below is a script which I use in my setup to unseal vault.
#!/bin/bash
# Assumptions: vault is already initialized
# Fetching first three keys to unseal the vault
KEY_1=$(cat keys.log | grep 'Unseal Key 1' | awk '{print $4}')
KEY_2=$(cat keys.log | grep 'Unseal Key 2' | awk '{print $4}')
KEY_3=$(cat keys.log | grep 'Unseal Key 3' | awk '{print $4}')
# Unseal using first key
curl --silent -X PUT \
http://192.*.*.*:8200/v1/sys/unseal \
-H 'cache-control: no-cache' \
-H 'content-type: application/json' \
-d '{
"key": "'$KEY_1'"
}'
# Unseal using second key
curl --silent -X PUT \
http://192.*.*.*:8200/v1/sys/unseal \
-H 'cache-control: no-cache' \
-H 'content-type: application/json' \
-d '{
"key": "'$KEY_2'"
}'
# Unseal using third key
curl --silent -X PUT \
http://192.*.*.*:8200/v1/sys/unseal \
-H 'cache-control: no-cache' \
-H 'content-type: application/json' \
-d '{
"key": "'$KEY_3'"
}'
And you can run this script using a single task in ansible.

shell script gives incorrect output when invoked using Ansible shell module

I have check.sh script that I wish to run on the target nodes:
cat check.sh
str=`echo $1 | sed -e 's#[\][\]n# #g'`
echo $str>check.row
It is suppose to replace \n with a single white space from the argument and save it in check.row file.
When I run it manually on the target server i get good output results as shown below:
bash -x ./check.sh '/fin/app/01/scripts\\n/fin/app/01/sql'
++ echo '/fin/app/01/scripts\\n/fin/app/01/sql'
++ sed -e 's#[\][\]n# #g'
+ str='/fin/app/01/scripts /fin/app/01/sql'
+ echo /fin/app/01/scripts /fin/app/01/sql
The check.row generated looks good as below:
[user1#remotehost1 ~]$ cat check.row
/fin/app/01/scripts /fin/app/01/sql
However, when i run the same using ansible shell or command module I do not get the expected results.
Below is my playbook:
tasks:
- copy:
src: "{{ playbook_dir }}/files/check.sh"
dest: "~/"
mode: 0754
- set_fact:
install_dir: "{{ hostvars[\'localhost\'][\'command_result\'].stdout.split('\t')[2] }}"
- shell: "bash -x ~/check.sh '{{ install_dir }}' > ~/check_rollback.log"
See ansible's debug output below:
changed: [10.8.44.55] => {
"changed": true,
"cmd": "bash -x ~/check.sh '/fin/app/01/scripts\\n/fin/app/01/sql' > ~/check_rollback.log",
"delta": "0:00:00.118943",
"end": "2019-09-04 10:50:16.503745",
"invocation": {
"module_args": {
"_raw_params": "bash -x ~/check.sh '/fin/app/01/scripts\\n/fin/app/01/sql' > ~/check_rollback.log",
"_uses_shell": true,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"stdin_add_newline": true,
"strip_empty_ends": true,
"warn": true
}
},
"rc": 0,
"start": "2019-09-04 10:50:16.384802",
"stderr": "++ echo '/fin/app/01/scripts\\n/fin/app/01/sql'\n++ sed -e 's#[\\][\\]n# #g'\n+ str='/fin/app/01/scripts\\n/fin/app/01/sql'\n+ echo '/fin/app/01/scripts\\n/fin/app/01/sql'",
"stderr_lines": [
"++ echo '/fin/app/01/scripts\\n/fin/app/01/sql'",
"++ sed -e 's#[\\][\\]n# #g'",
"+ str='/fin/app/01/scripts\\n/fin/app/01/sql'",
"+ echo '/fin/app/01/scripts\\n/fin/app/01/sql'"
],
"stdout": "",
"stdout_lines": [] }
And here is the check.row file output from ansible's run:
[user1#remotehost1 ~]$ cat check.row
/fin/app/01/scripts\\n/fin/app/01/sql
As you can instead of single whitespace it is now printing \n.
I am on the latest version of ansible.
One can replicate this issue easily. Can you please suggest why am I getting this issue and how to fix this?
First of all, you are using the shell module in which only the shell command is specified, you have incorrectly used bash in it.
shell: "bash -x ~/check.sh '{{ install_dir }}' > ~/check_rollback.log"
Secondly, as you can see your task has resulted in an error as seen in your attached output.
Stdout is empty and we can see an error in stderr.
Thirdly, If you want to use bash you can use command module, as shown below,
- command: "bash -x ~/check.sh '{{ install_dir }}' > ~/check_rollback.log"
I also suggest the following changes in your check.sh script,
#!/bin/bash
echo $1 # You can check the value that is passed to the script
str=$(echo "$1" | sed -e 's/\\n/ /g') # Use quotes around your variable
echo "$str" > check.row
And it is working fine.

How to pass multiple executable in ansible within a shell module?

I am trying to pass a prompt y in ansible when it executes command.
When i do manually on server it asks for a prompt.
The issue is for command to run i need to pass the executable /bin/bash
command: source /etc/profile.d/tableau_server.sh && tsm pending-changes apply for expect command to run i need to pass /usr/bin/expect .
My question, how can i pass 2 executable in ansible such that for command it uses /bin/bash and for expect prompt it should use /usr/bin/expect and the error is because i am using source, what is an alternative i can use?
Update: I dont know why but i am not able to pass --ignore-prompt , It gives an error
ubuntu#ip-xx-xxx-xx-xx:~$ tsm pending-changes apply --ignore-prompt
Unrecognized option: --ignore-prompt
Please help me with a solution!
ubuntu#ip-xx-xxx-xx-xx:~$ tsm pending-changes apply
This operation will perform a server restart. Are you sure you wish to continue?
(y/n):
My ansible script:
shell: |
source /etc/profile.d/tableau_server.sh && tsm pending-changes apply
expect "This operation will perform a server restart. Are you sure you wish to continue?\n(y/n):"
send "y\n"
exit 0
args:
executable: /usr/bin/expect
args:
executable: /bin/bash/expect
when: inventory_hostname == "xx.xxx.xx.xx"
ERROR:
changed: [xx.xxx.xxx.xx] => {
"changed": true,
"cmd": "source /etc/profile.d/tableau_server.sh && tsm pending-changes apply\n expect \"This operation will perform a server restart. Are you sure you wish to continue?\\n(y/n):\"\n send \"y\\n\"\n exit 0",
"delta": "0:00:00.034824",
"end": "2018-08-20 17:29:41.457700",
"invocation": {
"module_args": {
"_raw_params": "source /etc/profile.d/tableau_server.sh && tsm pending-changes apply\n expect \"This operation will perform a server restart. Are you sure you wish to continue?\\n(y/n):\"\n send \"y\\n\"\n exit 0",
"_uses_shell": true,
"argv": null,
"chdir": null,
"creates": null,
"executable": "/usr/bin/expect",
"removes": null,
"stdin": null,
"warn": true
}
},
"rc": 0,
"start": "2018-08-20 17:29:41.422876",
"stderr": "wrong # args: should be \"source ?-encoding name? fileName\"\n while executing\n\"source /etc/profile.d/tableau_server.sh && tsm pending-changes apply\"",
"stderr_lines": [
"wrong # args: should be \"source ?-encoding name? fileName\"",
" while executing",
"\"source /etc/profile.d/tableau_server.sh && tsm pending-changes apply\""
],
"stdout": "",
"stdout_lines": []
I would say you are doing far too much with bash commands and '&&' inside command, none of this feels idempotent.
Can I recommend going back to the drawing board with this. I would recommend creating the command using the 'creates' parameter so it can tell if it needs to run.
https://docs.ansible.com/ansible/2.6/modules/command_module.html
Or alternatively check before hand which will then see if the command needs running using register.
In this instance of your issue with the:
tsm pending-changes apply
should support as per https://onlinehelp.tableau.com/current/server-linux/en-us/cli_pending-changes.htm
tsm pending-changes apply --ignore-prompt
which will then not prompt for a yes and will not need the expect module.
I solved my issue by passing an -r option.
- name: Initialize and Start Tableau Server
shell: source /etc/profile.d/tableau_server.sh && tsm pending-changes apply -r -u ubuntu -p '{{ tableau_server_admin_password }}'
args:
executable: /bin/bash
when: inventory_hostname == "xx.xxx.xx.xx"

Chaining a star sign in Ansible playbook

Lately, I've started to use Ansible, and i need some help with a playbook.
I'm trying to get a device details as a parameter, and then chain a star sign for it, in order to run a shell command. For example, if the device is /etc/sda
, the shell command will be ls -l /dev/sda*.
- name: Get Hitachi Devices device details
shell: lsscsi | grep HITACHI | awk '{print $6}'
register: hitachiDevice
- name: Check if the volume is partitioned
shell: ls -l "{{ hitachiDevice.rc }}"* | wc -l
failed_when: hitachiDevice.rc != 1
Here is the error i get after running the script:
{
"changed": true,
"cmd": "ls -l \"0\"* | wc -l",
"delta": "0:00:00.027338",
"end": "2016-08-10 14:15:12.200415",
"failed": true,
"failed_when_result": true,
"rc": 0,
"start": "2016-08-10 14:15:12.173077",
"stderr": "ls: cannot access 0*: No such file or directory",
"stdout": "0",
"stdout_lines": [
"0"
],
"warnings": []
}
Any one know what is the issue and how can i fix it?
hitachiDevice.rc contains result code of previous command.
I bet it is always 0 if grep is successful.
So you next command will almost always will be ls -l 0* | wc -l which gives you the error
ls: cannot access 0*: No such file or directory
I think you need something like hitachiDevice.stdout, depending on what you want to achieve.

Resources