How to copy file to local directory using Ansible? - ansible

I'm trying to set up a playbook that will configure my development system. I'd like to copy the /etc/hosts file from my playbooks "files" directory to the /etc directory on my system. Currently I'm doing the following:
# main.yml
- hosts: all
- tasks:
- copy: src=files/hosts
dest=/etc/hosts
owner=root
group=wheel
mode=0644
backup=true
become: true
# inventory
localhost ansible_connection=local
When I run the playbook I'm getting this error:
fatal: [localhost]: FAILED! => {... "msg": Failed to get information on remote file (/etc/hosts): MODULE FAILURE"}
I believe this is because copy is supposed to be used to copy a file to a remote file system. So how do you copy a file to your local management system? I did a Google Search and everything talks about doing the former. I didn't see this addressed in the Ansible docs.

Your task is ok.
You should add --ask-sudo-pass to the ansible-playbook call.
If you run with -vvv you can see the command starts with sudo -H -S -n -u root /bin/sh -c echo BECOME-SUCCESS-somerandomstring (followed by a call to the Python script). If you execute it yourself, you'll get sudo: a password is required message. Ansible quite unhelpfully replaces this error message with its own Failed to get information on remote file (/etc/hosts): MODULE FAILURE.

Related

Ansible, copy script, then execute in remote machine

I want to copy a script to a remote server and then execute it. I can copy it to the directory /home/user/scripts, but when I run ansible script, it returns Could not find or access '/home/user/scripts/servicios.sh'
The full error output is:
fatal: [192.168.1.142]: FAILED! => {"changed": false, "msg": "Could not find or access '/home/user/scripts/servicios.sh'"}
Here is the ansible playbook
- name: correr script
hosts: all
tasks:
- name: crear carpeta de scripts
file:
path: /home/user/scripts
state: directory
- name: copiar el script
copy:
src: /home/local/servicios.sh
dest: /home/user/scripts/servicios.sh
- name: ejecutar script como sudo
become: yes
script: /home/user/scripts/servicios.sh
You don’t need to create a directory and copy the script to target (remote node), the script module does that for you. It takes the script name followed by a list of space-delimited arguments. The local script at path will be transferred to the remote node and then executed. The script will be processed through the shell environment on the remote node. You were getting the error because script module expects the path /home/user/scripts/servicios.sh on your Ansible controller (the node where you are running the playbook from). To make it work you can specify correct path (/home/local/servicios.sh) in script task instead of /home/user/scripts/servicios.sh which is the path on the remote node. So you can change the playbook like this: You can also register the result of that command as a variable if you would like to see that.
---
- name: correr script
hosts: all
become: yes
tasks:
- name: ejecutar script como sudo
script: /home/local/servicios.sh
register: console
- debug: msg="{{ console.stdout }}"
- debug: msg="{{ console.stderr }}"
What if don’t want to go for script module and you are interested in creating a directory and copy the script to target (remote node) explicitly, and run it? No worries, you can still use the command module like this:
---
- name: correr script
hosts: all
become: yes
tasks:
- name: crear carpeta de scripts
file:
path: /home/user/scripts
state: directory
- name: copiar el script
copy:
src: /home/local/servicios.sh
dest: /home/user/scripts/servicios.sh
- name: ejecutar script como sudo
command: bash /home/user/scripts/servicios.sh
register: console
- debug: msg="{{ console.stdout }}"
- debug: msg="{{ console.stderr }}"
But I strongly recommend to go for script module.
The script tag itself transfers the script from the local machine to remote machine and executes it there.
So, the path specified in the script module is of the local machine and not the remote machine i.e., /home/local/servicios.sh instead of /home/user/scripts/servicios.sh
As you have specified the path which is supposed to be on the remote machine, ansible is unable to find that script on local machine at the given path which results in the given error.
Hence, update the path in the task to local path as shown below,
- name: ejecutar script como sudo
become: yes
script: /home/local/servicios.sh
So scripts cant be executed inside the remote server and should be
executed via local machine to the remote?
#thrash3d No, it is not like that. When you use script tag the script is transferred to the remote machine and then it is executed there. If there is a script which you don't want to put on your remote machine and just want to execute it then you can use script tag.
If you want that script on your remote machine then you can first copy your script on remote machine and then execute it there.
Both ways are correct and it is up to you which case suits you better.

Ansible Synchronize not able to create directory as root

I'm working on building an Ansible playbook and I'm using Vagrant as a test platform before I apply the playbook to a remote server.
I'm having issues getting Synchronize to work. I have some files that I need to move up to the server as part of the deployment.
Here's my playbook. I put the shell: whoami in there to make sure commands were running as root.
---
- hosts: all
sudo: yes
tasks:
- name: who am I
shell: whoami
- name: Sync up www folder
synchronize: src=www dest=/var
When I run this I get this:
failed: [default] => {"cmd": "rsync --delay-updates -FF --compress --archive --rsh 'ssh -i /Users/dan/.vagrant.d/insecure_private_key -o StrictHostKeyChecking=no -o Port=2222' --out-format='<<CHANGED>>%i %n%L' www vagrant#127.0.0.1:/var", "failed": true, "rc": 23}
msg: rsync: recv_generator: mkdir "/var/www" failed: Permission denied (13)
*** Skipping any contents from this failed directory ***
rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1236) [sender=3.1.1]
FATAL: all hosts have already failed -- aborting
If I'm supplying sudo: yes shouldn't all commands be run as root, including Synchronize?
The Ansible Synchronize module page has some big hairy warnings:
The remote user for the dest path will always be the remote_user, not
the sudo_user.
There's a suggestion to wrap rsync with sudo like this:
# Synchronize using an alternate rsync command
synchronize: src=some/relative/path dest=/some/absolute/path rsync_path="sudo rsync"
There's also a suggestion to use verbosity to debug what's really going on. In this case, it means adding -vvv or even -vvvv to your ansible-playbook commandline execution.
Finally, this is a great time to use proper permissions, especially for non-system files like a www dir. This will solve your problem in the process.
# don't use recurse here unless you are confident how it works with directories.
- file: dest=/var/www state=directory owner=www-data group=www-data mode=0755
- synchronize: src=www dest=/var

Ansible and Wget

I am trying to wget a file from a web server from within an Ansible playbook.
Here is the Ansible snippet:
---
- hosts: all
sudo: true
tasks:
- name: Prepare Install folder
sudo: true
action: shell sudo mkdir -p /tmp/my_install/mysql/ && cd /tmp/my_install/mysql/
- name: Download MySql
sudo: true
action: shell sudo wget http://{{ repo_host }}/MySQL-5.6.15-1.el6.x86_64.rpm-bundle.tar
Invoking it via:
ansible-playbook my_3rparties.yml -l vsrv644 --extra-vars "repo_host=vsrv656" -K -f 10
It fails with the following:
Cannot write to `MySQL-5.6.15-1.el6.x86_64.rpm-bundle.tar' (Permission denied).
FATAL: all hosts have already failed -- aborting
PLAY RECAP ********************************************************************
to retry, use: --limit #/usr2/ihazan/vufroria_3rparties.retry
vsrv644 : ok=2 changed=1 unreachable=0 failed=1
When trying to do the command that fail via regular remote ssh to mimic what ansible would do, it doesn't work as follows:
-bash-4.1$ ssh ihazan#vsrv644 'cd /tmp/my_install/mysql && sudo wget http://vsrv656/MySQL-5.6.15-1.el6.x86_64.rpm-bundle.tar'
Enter passphrase for key '/usr2/ihazan/.ssh/id_rsa':
sudo: sorry, you must have a tty to run sudo
But I can solve it using -t as follows:
-bash-4.1$ ssh -t ihazan#vsrv644 'cd /tmp/my_install/mysql && sudo wget http://vsrv656/MySQL-5.6.15-1.el6.x86_64.rpm-bundle.tar'
Then it works.
Is there a way to set the -t (pseudo tty option) on ansible?
P.S: I could solve it by editing the sudoers file as others propose but that is a manual step I am trying to avoid.
Don't use shell-module when there is specialized modules available. In your case:
Create directories with file-module:
- name: create project directory {{ common.project_dir }}
file: state=directory path={{ common.project_dir }}
Download files with get_url-module:
- name: download sources
get_url: url={{ opencv.url }} dest={{ common.project_dir }}/{{ opencv.file }}
Note the new module call syntax in the examples above.
If you have to use sudo with password remember to give --ask-sudo-pass when needed (see e.g. Remote Connection Information).
In Ansible:
file to manage files/directories
get_url to download what you need
become:yes to use sudo priviledges
See ansible documentation:
https://docs.ansible.com/ansible/latest/modules/modules_by_category.html

Sudo does not work with Ansible AWX

I have set sudo_user in /etc/ansible/ansible.cfg.
In my playbook I have set remote_user, sudo_user and sudo:yes (also tried sudo:True) on the same level as hosts:
I then use a role that does:
shell: cp -f src /usr/local/bin/dest
sudo: yes
and get
stderr: cp: cannot create regular file `/usr/local/bin/dest': Permission denied
The credentials in AWX are set correctly - I am able to manually log in as the desired user on the remote machine and copy the file with sudo cp. I have no idea what I'm doing wrong.
What's your sudo user? Your base playbook should look something like this:
# Base System: this is my base playbook
- name: Base Playbook
hosts: all
user: myuser
sudo: True

How to execute a shell script on a remote server using Ansible?

I am planning to execute a shell script on a remote server using Ansible playbook.
blank test.sh file:
touch test.sh
Playbook:
---
- name: Transfer and execute a script.
hosts: server
user: test_user
sudo: yes
tasks:
- name: Transfer the script
copy: src=test.sh dest=/home/test_user mode=0777
- name: Execute the script
local_action: command sudo sh /home/test_user/test.sh
When I run the playbook, the transfer successfully occurs but the script is not executed.
you can use script module
Example
- name: Transfer and execute a script.
hosts: all
tasks:
- name: Copy and Execute the script
script: /home/user/userScript.sh
local_action runs the command on the local server, not on the servers you specify in hosts parameter.
Change your "Execute the script" task to
- name: Execute the script
command: sh /home/test_user/test.sh
and it should do it.
You don't need to repeat sudo in the command line because you have defined it already in the playbook.
According to Ansible Intro to Playbooks user parameter was renamed to remote_user in Ansible 1.4 so you should change it, too
remote_user: test_user
So, the playbook will become:
---
- name: Transfer and execute a script.
hosts: server
remote_user: test_user
sudo: yes
tasks:
- name: Transfer the script
copy: src=test.sh dest=/home/test_user mode=0777
- name: Execute the script
command: sh /home/test_user/test.sh
It's better to use script module for that:
http://docs.ansible.com/script_module.html
For someone wants an ad-hoc command
ansible group_or_hostname -m script -a "/home/user/userScript.sh"
or use relative path
ansible group_or_hostname -m script -a "userScript.sh"
Contrary to all the other answers and comments, there are some downsides to using the script module. Especially when you are running it on a remote(not localhost) host. Here is a snippet from the official ansible documentation:
It is usually preferable to write Ansible modules rather than pushing
scripts. Convert your script to an Ansible module for bonus points!
The ssh connection plugin will force pseudo-tty allocation via -tt
when scripts are executed. Pseudo-ttys do not have a stderr channel
and all stderr is sent to stdout. If you depend on separated stdout
and stderr result keys, please switch to a copy+command set of tasks
instead of using script.
If the path to the local script contains spaces, it needs to be
quoted.
This module is also supported for Windows targets.
For example, run this script using script module for any host other than localhost and notice the stdout and stderr of the script.
#!/bin/bash
echo "Hello from the script"
nonoexistingcommand
echo "hello again"
You will get something like the below; notice the stdout has all the stderr merged.(ideally line 6: nonoexistingcommand: command not found should be in stderr) So, if you are searching for some substring in stdout in the script output. you may get incorrect results.:
ok: [192.168.122.83] => {
"script_out": {
"changed": true,
"failed": false,
"rc": 0,
"stderr": "Shared connection to 192.168.122.83 closed.\r\n",
"stderr_lines": [
"Shared connection to 192.168.122.83 closed."
],
"stdout": "Hello from the script\r\n/home/ps/.ansible/tmp/ansible-tmp-1660578527.4335434-35162-230921807808160/my_script.sh: line 6: nonoexistingcommand: command not found\r\nhello again\r\n",
"stdout_lines": [
"Hello from the script",
"/home/ps/.ansible/tmp/ansible-tmp-1660578527.4335434-35162-230921807808160/my_script.sh: line 6: nonoexistingcommand: command not found",
"hello again"
]
}
}
The documentation is not encouraging users to use the script module; consider converting your script into an ansible module; here is a simple post by me that explains how to convert your script into an ansible module.
You can use template module to copy if script exists on local machine to remote machine and execute it.
- name: Copy script from local to remote machine
hosts: remote_machine
tasks:
- name: Copy script to remote_machine
template: src=script.sh.2 dest=<remote_machine path>/script.sh mode=755
- name: Execute script on remote_machine
script: sh <remote_machine path>/script.sh
Since nothing is defined about "the script", means complexity, content, runtime, runtime environment, size, tasks to perform, etc. are unknown, it might be possible to use an unrecommended approach like in "How to copy content provided in command prompt with special chars in a file using Ansible?"
---
- hosts: test
become: false
gather_facts: false
tasks:
- name: Exec sh script on Remote Node
shell:
cmd: |
date
ps -ef | grep ssh
echo "That's all folks"
register: result
- name: Show result
debug:
msg: "{{ result.stdout }}"
which is a multi-line shell command only (annot.: ... just inline code) and resulting into an output of
TASK [Show result] ****************************************************
ok: [test.example.com] =>
msg: |-
Sat Sep 3 21:00:00 CEST 2022
root 709 1 0 Aug11 ? 00:00:00 /usr/sbin/sshd -D
root 123456 709 14 21:00 ? 00:00:00 sshd: user [priv]
user 123456 123456 1 21:00 ? 00:00:00 sshd: user#pts/0
root 123456 123456 0 21:00 pts/0 00:00:00 grep ssh
That's all folks
One could just add more lines, complexity, necessary output, etc.
Because of script module – Runs a local script on a remote node after transferring it - Notes
It is usually preferable to write Ansible modules rather than pushing scripts.
I also recommend to get familar with writing an own module and as already mentioned in the answer of P....
You can execute local scripts at ansible without having to transfer the file to the remote server, this way:
ansible my_remote_server -m shell -a "`cat /localpath/to/script.sh`"

Resources