ansible playbook / bash script - automation

bonjour / hello
First of all sorry for my very poor english, I'm french and I used google translator.
I'm using an ansible playbook to run a bash script. In my test environment it works, but not in my production environment and I don't understand why.
It's a script that allows to scan for violations, check the status of the services and at the end it generates a report.
In ansible the task is accomplished but the report is not generated, whereas when I run the script directly from the terminal of my vm (hosted by aws) the script works and generates the report for me.
Can you help me please ?
- name: run the scan to generate deviation report
become: yes
command: sh /<path>
I tried several commands but I always have the same result. I tried the ansible script module and launched it in debug mode with bash -x

i tested like below
script:
root#swarm01:/myworkspace/ansible# cat hello.sh
#!/usr/bin/sh
touch abc
Playbook.
root#swarm01:/myworkspace/ansible# cat test.yml
- name: simple script
hosts: localhost
tasks:
- name: simple script
command: sh hello.sh
root#swarm01:/myworkspace/ansible# ansible-playbook test.yml
PLAY [simple script] ***********************************************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************************************************
ok: [localhost]
TASK [simple script] ***********************************************************************************************************************************
changed: [localhost]
PLAY RECAP *********************************************************************************************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
It worked for me, i see a abc file got created.

Suggest using the script module
This will allow the PB to run the script on remote systems as well as localhost. Probably best to make sure you have the sh-bang on the first line of the script.
- name: simple script
hosts: localhost
tasks:
- name: simple script
script:
cmd: hello.sh

Related

How can I print all extra-vars before playbook is started

I have wired problem with running ansible
I'd like to print to log or anywhere else the extra-vars I'm executing the playbook with.
I am running the following command:
ansible-playbook /foo/main.yml --extra-vars "main_playbook=app_install start_path=work" --extra-vars {"db_config":{"db_multi_config":["value1","value2"]}}'
with the following main.yml playbook
- name: start playbook
import_playbook: "{{ playbook_dir }}/{{ main_playbook}}/{{ start_path}}.yml"
I'd like to print the values of all extra-vars passed on the command line before it gets to main.yml. Is there any way to do this?
You can get this information in the output running ansible in full debug mode:
Given the following test.yml playbook:
- hosts: localhost
gather_facts: false
and running it with the command:
ansible-playbook test.yml -vvvv -e toto=bla -e '{"test1":2}'
I get the following result (see the debug lines for playbook before play starts)
ansible-playbook [core 2.11.3]
config file = None
configured module search path = ['/home/user/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/local/lib/python3.8/dist-packages/ansible
ansible collection location = /home/user/.ansible/collections:/usr/share/ansible/collections
executable location = /usr/local/bin/ansible-playbook
python version = 3.8.10 (default, Jun 2 2021, 10:49:15) [GCC 9.4.0]
jinja version = 2.11.3
libyaml = True
No config file found; using defaults
The vault password file /home/user/bin/vault-keyring-client is a client script.
Executing vault password client script: /home/user/bin/vault-keyring-client --vault-id avaultid1
The vault password file /home/user/bin/vault-keyring-client is a client script.
Executing vault password client script: /home/user/bin/vault-keyring-client --vault-id avaultid2
The vault password file /home/user/bin/vault-keyring-client is a client script.
Executing vault password client script: /home/user/bin/vault-keyring-client --vault-id avaultid3
setting up inventory plugins
host_list declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
script declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
auto declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
Parsed /etc/ansible/hosts inventory source with ini plugin
Loading callback plugin default of type stdout, v2.0 from /usr/local/lib/python3.8/dist-packages/ansible/plugins/callback/default.py
Skipping callback 'default', as we already have a stdout callback.
Skipping callback 'minimal', as we already have a stdout callback.
Skipping callback 'oneline', as we already have a stdout callback.
PLAYBOOK: test.yml **********************************************************************************************************************
Positional arguments: test.yml
verbosity: 4
connection: smart
timeout: 10
become_method: sudo
tags: ('all',)
inventory: ('/etc/ansible/hosts',)
extra_vars: ('toto=bla', '{"test1":2}')
forks: 5
1 plays in test.yml
PLAY [all] ******************************************************************************************************************************
skipping: no hosts matched
PLAY RECAP ******************************************************************************************************************************
You can't separate extra vars from all other vars, because they are merged. But you can see all variables for the host. (except for setup facts, which you can browse separately with -m setup).
The trick is to print all variables from hostvars group for 'itself', ansible_host:
ansible -i inventory -e extra_vars_here -m debug -a 'msg={{hostvars[inventory_hostname]}}' all
(replace all with specific hostname if you wish).
If you are overwhelmed, you can filter it with .keys(): hostvars[inventory_hostname].keys().

Ansible ssh fails with error: Data could not be sent to remote host

I have an ansible playbook that executes a shell script on remote host "10.8.8.88" as many times as the number files provided as parameter
ansible-playbook test.yml -e files="file1,file2,file3,file4"
playbook looks like below:
- name: Call ssh
shell: ~./execute.sh {{ item }}
with_items: {{ files.split(',') }}
This works fine for fewer files say 10 to 15 files. But I happen to have 145 files in the argument.
This is when the execution broke and play failed mid-way with below error message:
TASK [shell] *******************************************************************
[WARNING]: conditional statements should not include jinja2 templating
delimiters such as {{ }} or {% %}. Found: entrycurrdb.stdout.find("{{ BASEPATH
}}/{{ vars[(item | splitext)[1].split('.')[1]] }}/{{ item | basename }}") == -1
and actualfile.stat.exists == True
[WARNING]: sftp transfer mechanism failed on [10.8.8.88]. Use ANSIBLE_DEBUG=1
to see detailed information
[WARNING]: scp transfer mechanism failed on [10.8.8.88]. Use ANSIBLE_DEBUG=1
to see detailed information
fatal: [10.8.8.88]: UNREACHABLE! => {"changed": false, "msg": "Data could not be sent to remote host \"10.8.8.88\". Make sure this host can be reached over ssh: ", "unreachable": true}
NO MORE HOSTS LEFT *************************************************************
PLAY RECAP *********************************************************************
10.8.8.88 : ok=941 changed=220 unreachable=1 failed=0 skipped=145 rescued=0 ignored=0
localhost : ok=7 changed=3 unreachable=0 failed=0 skipped=3 rescued=0 ignored=0
Build step 'Execute shell' marked build as failure
Finished: FAILURE
I have the latest Ansible and the "pipeline" and "ssh" settings in ansible.cfg are as defaults.
I have the following questions.
How can I resolve the above issue?
I guess this could be due to a network issue. Is it possible to run infinite ssh ping to the remote server for testing purposes to see if the ansible command-line breaks? It will help me prove my case. A sample command that keeps pinging the remote using ssh is what I'm looking for.
It is possible to force ansible to retry ssh connection a few times in case of such failures so that it may connect in during retries. If so, I would appreciate where and how that can be set in ansible-playbook code as vars variable and not in ansible.cfg file?
https://docs.ansible.com/ansible/2.4/intro_configuration.html#retries
Something similar to:
vars:
ansible_ssh_private_key_file: "{{ key1 }}"
Many Thanks !!

BitBucket deployment using SSH keys to remote server

I am trying to write a YAML pipeline script to deploy files that have been altered from my bitbucket repository to my remote server using ssh keys. The document that I have in place at the moment was copied from bitbucket itself and has errors:
pipelines:
default:
- step:
name: Deploy to test
deployment: test
script:
- pipe: atlassian/sftp-deploy:0.3.1
- variables:
USER: $USER
SERVER: $SERVER
REMOTE_PATH: $REMOTE_PATH
LOCAL_PATH: $LOCAL_PATH
I am getting the following error
Configuration error
There is an error in your bitbucket-pipelines.yml at [pipelines > default > 0 > step > script > 1]. To be precise: Missing or empty command string. Each item in this list should either be a single command string or a map defining a pipe invocation.
My ssh public and private keys are setup in bitbucket along with the fingerprint and host. The variables have also been setup.
How do I go about setting up my YAML deploy script to connect to my remote server via ssh and transfer the files?
Try to update the variables section become:
- variables:
- USER: $USER
- SERVER: $SERVER
- REMOTE_PATH: $REMOTE_PATH
- LOCAL_PATH: $LOCAL_PATH
Here is am example about how to set variables: https://confluence.atlassian.com/bitbucket/configure-bitbucket-pipelines-yml-792298910.html#Configurebitbucket-pipelines.yml-ci_variablesvariables
Your directive - step has to be intended.
I have bitbucket-pipelines.yml like that (using rsync instead of ssh):
# This is a sample build configuration for PHP.
# Check our guides at https://confluence.atlassian.com/x/e8YWN for more examples.
# Only use spaces to indent your .yml configuration.
# -----
# You can specify a custom docker image from Docker Hub as your build environment.
image: php:7.2.1-fpm
pipelines:
default:
- step:
script:
- apt-get update
- apt-get install zip -y
- apt-get install unzip -y
- apt-get install libgmp3-dev -y
- curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
- composer install
- cp .env.example .env
#- vendor/bin/phpunit
- pipe: atlassian/rsync-deploy:0.2.0
variables:
USER: $DEPLOY_USER
SERVER: $DEPLOY_SERVER
REMOTE_PATH: $DEPLOY_PATH
LOCAL_PATH: '.'
I suggest to use their online editor in repository for editing bitbucket-pipelines.yml, it checks all formal yml structure and you can't commit invalid file.
Even if you check file on some other yaml editor, it may look fine, but not necessary according to bitbucket specification. Their online editor does fine job.
Also, I suggest to visit their community on atlasian community as it's very active, sometimes their staff members are providing answers.
However, I struggle with plenty dependencies needed to run tests properly. (actual bitbucket-pipelines.yml is becoming bigger and bigger).
Maybe there is some nicely prepared Docker image for this job.

Ansible error change handler is not defined

I'm trying to run my first playbook to install Java on four servers and subsequently define a JAVA_HOME environment variable.
ansible-playbook site.yml --check
PLAY [crave_servers] **********************************************************
GATHERING FACTS ***************************************************************
ok: [54.174.151.196]
ok: [54.174.197.35]
ok: [54.174.207.83]
ok: [54.174.208.240]
TASK: [java | install Java JDK] ***********************************************
changed: [54.174.197.35]
changed: [54.174.151.196]
changed: [54.174.208.240]
changed: [54.174.207.83]
ERROR: change handler (setvars) is not defined
I've placed my site.yml under /etc/ansible
---
- hosts: crave_servers
remote_user: ubuntu
sudo: yes
roles:
- java
I've placed main.yml under /etc/ansible/java/tasks
---
- name: install Java JDK
apt: name=default-jdk state=present
notify:
- setvars
I've placed main.yml under /etc/ansible/handlers
---
- name: setvars
shell: echo "JAVA_HOME=\"/usr/lib/jvm/java-7-openjdk-amd64\"" >> /etc/environment
Now I'm not sure if the syntax is structure of my handlers is correct. But it's obvious from the output that Ansible is able to find the correct role and execute the correct task. But the task can't find the handler.
Nobody else seems to have the same problem. And I don't really know how to debug it because my ansible version seems to be missing the config file.
You should put your handler to /etc/ansible/java/handlers/main.yml
As handlers are part of a role.
Remarks:
You should not use your handler as it would paste the line into /etc/environment each time you call this playbook. I would recommend the lineinefile module.
You should reconsider your decision to put ansible playbooks into /etc

Ansible - Define Inventory at run time

I am liitle new to ansible so bear with me if my questions are a bit basic.
Scenario:
I have a few group of Remote hosts such as [EPCs] [Clients] and [Testers]
I am able to configure them just the way I want them to be.
Problem:
I need to write a playbook, which when runs, asks the user for the inventory at run time.
As an example when a playbook is run the user should be prompted in the following way:
"Enter the number of EPCs you want to configure"
"Enter the number of clients you want to configure"
"Enter the number of testers you want to configure"
What should happen:
Now for instance the user enters 2,5 and 8 respectively.
Now the playbook should only address the first 2 nodes in the group [EPCs], the first 5 nodes in group [Clients] and the first 7 nodes in the group [Testers] .
I don't want to create a large number of sub-groups, for instance if I have 20 EPCs, then I don't want to define 20 groups for my EPCs, I want somewhat of a dynamic inventory, which should automatically configure the machines according to the user input at run time using the vars_prompt option or something similar to that
Let me post a partial part of my playbook for better understanding of what is to happen:
---
- hosts: epcs # Now this is the part where I need a lot of flexibility
vars_prompt:
name: "what is your name?"
quest: "what is your quest?"
gather_facts: no
tasks:
- name: Check if path exists
stat: path=/home/khan/Desktop/tobefetched/file1.txt
register: st
- name: It exists
debug: msg='Path existence verified!'
when: st.stat.exists
- name: It doesn't exist
debug: msg="Path does not exist"
when: st.stat.exists == false
- name: Copy file2 if it exists
fetch: src=/home/khan/Desktop/tobefetched/file2.txt dest=/home/khan/Desktop/fetched/ flat=yes
when: st.stat.exists
- name: Run remotescript.sh and save the output of script to output.txt on the Desktop
shell: cd /home/imran/Desktop; ./remotescript.sh > output.txt
- name: Find and replace a word in a file placed on the remote node using variables
shell: cd /home/imran/Desktop/tobefetched; sed -i 's/{{name}}/{{quest}}/g' file1.txt
tags:
- replace
#gli I tried your solution, I have a group in my inventory named test with two nodes in it. When I enter 0..1 I get:
TASK: [echo sequence] *********************************************************
changed: [vm2] => (item=some_prefix0)
changed: [vm1] => (item=some_prefix0)
changed: [vm1] => (item=some_prefix1)
changed: [vm2] => (item=some_prefix1)
Similarly when I enter 1..2 I get:
TASK: [echo sequence] *********************************************************
changed: [vm2] => (item=some_prefix1)
changed: [vm1] => (item=some_prefix1)
changed: [vm2] => (item=some_prefix2)
changed: [vm1] => (item=some_prefix2)
Likewise when I enter 4..5 (nodes not even present in the inventory, I get:
TASK: [echo sequence] *********************************************************
changed: [vm1] => (item=some_prefix4)
changed: [vm2] => (item=some_prefix4)
changed: [vm1] => (item=some_prefix5)
changed: [vm2] => (item=some_prefix5)
Any help would be really appreciated. Thanks!
You should use vars_prompt for getting information from user, add_host for updating hosts dynamically and with_sequence for loops:
$ cat aaa.yml
---
- hosts: localhost
gather_facts: False
vars_prompt:
- name: range
prompt: Enter range of EPCs (e.g. 1..5)
private: False
default: "1"
pre_tasks:
- name: Set node id variables
set_fact:
start: "{{ range.split('..')[0] }}"
stop: "{{ range.split('..')[-1] }}"
- name: "Add hosts:"
add_host: name="host_{{item}}" groups=just_created
with_sequence: "start={{start}} end={{stop}} "
- hosts: just_created
gather_facts: False
tasks:
- name: echo sequence
shell: echo "cmd"
The output will be:
$ ansible-playbook aaa.yml -i 'localhost,'
Enter range of EPCs (e.g. 1..5) [1]: 0..1
PLAY [localhost] **************************************************************
TASK: [Set node id variables] *************************************************
ok: [localhost]
TASK: [Add hosts:] ************************************************************
ok: [localhost] => (item=0)
ok: [localhost] => (item=1)
PLAY [just_created] ***********************************************************
TASK: [echo sequence] *********************************************************
fatal: [host_0] => SSH encountered an unknown error during the connection. We recommend you re-run the command using -vvvv, which will enable SSH debugging output to help diagnose the issue
fatal: [host_1] => SSH encountered an unknown error during the connection. We recommend you re-run the command using -vvvv, which will enable SSH debugging output to help diagnose the issue
FATAL: all hosts have already failed -- aborting
PLAY RECAP ********************************************************************
to retry, use: --limit #/Users/gli/aaa.retry
host_0 : ok=0 changed=0 unreachable=1 failed=0
host_1 : ok=0 changed=0 unreachable=1 failed=0
localhost : ok=2 changed=0 unreachable=0 failed=0
Here, it failed as host_0 and host_1 are unreachable, for you it'll work fine.
btw, I used more powerful concept "range of nodes". If you don't need it, it is quite simple to have "start=0" and ask only for "stop" value in the prompt.
I don't think you can define an inventory at run time. One thing you can do is, write a wrapper script over Ansible which will first prompt user for the hosts and then dynamically structure an ansible-playbook command.
I would prefer doing this using python, but you can use any language of your choice.
$ cat ansible_wrapper.py
import ConfigParser
import os
nodes = ''
inv = {}
hosts = raw_input("Enter hosts: ")
hosts = hosts.split(",")
config = ConfigParser.ConfigParser(allow_no_value=True)
config.readfp(open('hosts'))
sections = config.sections()
for i in range(len(sections)):
inv[sections[i]] = hosts[i]
for key, value in inv.items():
for i in range(int(value)):
nodes = nodes + config.items(key)[i][0] + ";"
command = 'ansible-playbook -i hosts myplaybook.yml -e "nodes=%s"' % (nodes)
print "Running command: ", command
os.system(command)
Note: I've tried running this script only using python2.7