On remote host I have many files under /tmp with name like EM_Prereq*, I want to copy all those to my ansible server under current ansible working directory or /tmp/results directory .
I am using below code and is working fine but its creating files in different path than I expected.
ansbile creating file in path /tmp/EM_Prereq_testbafffmqygx_root_Warning_20180311202123.txt/test.host.com/tmp/<actual file name>
But i want file to be created as /tmp/results/<file name>
---
- name: 'vij'
hosts: 'all'
gather_facts: 'false'
tasks:
- name: 'ls files'
shell: "ls -l /tmp/EM_Prereq_*|awk '{print $(NF)}'"
register: 'filetocopy'
- name: 'fetch files'
fetch :
src: '{{ item }}'
dest: '{{ item }}'
with_items: '{{ filetocopy.stdout_lines }}'
Output is below
changed: [test.host.com] => (item=/tmp/EM_Prereq_testbafffmqygx_root_Warning_20180311202123.txt) => {
"changed": true,
"checksum": "1f7edc7c9704add9f3b191c70a6eb81aa4ff3e14",
"dest": "/tmp/EM_Prereq_testbafffmqygx_root_Warning_20180311202123.txt/oc-129-158-67-48.compute.oraclecloud.com/tmp/EM_Prereq_testbafffmqygx_root_Warning_20180311202123.txt",
"item": "/tmp/EM_Prereq_testbafffmqygx_root_Warning_20180311202123.txt",
"md5sum": "de1bcca72d0c391f203d2956e672f51d",
"remote_checksum": "1f7edc7c9704add9f3b191c70a6eb81aa4ff3e14",
"remote_md5sum": null
}
Appreciate your inputs
See: http://docs.ansible.com/ansible/latest/fetch_module.html
You need: flat: yes as the documentation says: Allows you to override the default behavior of appending hostname/path/to/file to the destination. If dest ends with '/', it will use the basename of the source file, similar to the copy module. Obviously this is only handy if the filenames are unique.
- name: 'fetch files'
fetch :
src: '{{ item }}'
dest: '/tmp/results/'
flat: yes
with_items: '{{ filetocopy.stdout_lines }}'
edit: you also want '/tmp/results/' for the destination directory, and not {{ item }}
Related
I am running into a silly issue when i try to delete some folders with win_file
first i copy some folders on the remote itself from one dir to another
- name: copy folders first
win_copy:
src: '{{ item }}'
dest: 'C:\folders\to\copy'
remote_src: yes
loop: '{{ paths_to_copy }}'
register: copied_folders
then i filter only the 'path' of those folders to be deleted later in the play after executng some other tasks.
- name: filter paths to be deleted after some tasks
set_fact:
paths_to_delete: "{{ copied_folders | json_query('results[*].dest') }}"
i get this results:
ok: [<computer>] => {
"ansible_facts": {
"paths_to_delete": [
"C:\\folders\\to\\copy\\1",
"C:\\folders\\to\\copy\\2",
"C:\\folders\\to\\copy\\3",
"C:\\folders\\to\\copy\\4"
]
},
"changed": false
}
all seems good but the playbook is failing when i loop over 'paths_to_delete' because it returns with all those 4 paths as ONE path.
- name: clean up temporary copied directories
win_file:
path: '{{ item }}'
state: absent
loop:
- '{{ paths_to_delete }}'
"msg": "Get-AnsibleParam: Parameter 'path' has an invalid path '['C:\\\\folders\\\\to\\\\copy\\\\1','C:\\\\folders\\\\to\\\\copy\\\\2','C:\\\\folders\\\\to\\\\copy\\\\3','C:\\\\folders\\\\to\\\\copy\\\\4'] specified."
why it is not looping over this list and deletes them one by one?
i am using the same mechanism in the first copy task, looping over a list and it DOES copy the folder one by one without any issue.
Any help would be much appreciated.
Your loop syntax is incorrect.
loop:
- '{{ paths_to_delete }}'
This nests the list inside another list with a single element. What you want to do is loop over the original list:
loop: '{{ paths_to_delete }}'
I'm trying to get an output to a file using a Jinja2 template. However the last task 'Write to the file' fails. The idea is to create a dynamic folder based on m-d-yyyy--Hr:Min as the task is running on the same set of devices. Therefore they will be saved to folders based on date and time. The folder gets created. But the file is not. Also if I go to a static folder and use it in a "dest" it also works. Can someone please provide a direction? Thank you in advance.
- name: Create directory
file:
path: ./config-backups/{{ lookup('pipe', 'date +%m-%d-%Y--%H:%M') }}
mode: 0755
recurse: yes
register: folder_name_with_date
- name: Write to the file
ansible.builtin.template:
src: ./templates/show_run.j2
dest: ./{{folder_name_with_date}}/{{ inventory_hostname }}-running-config-{{ lookup('pipe', 'date +%m-%d-%Y--%H:%M') }}.txt
newline_sequence: '\r\n'
Error
fatal: [10.6.66.66]: FAILED! => {"changed": false, "checksum": "699bd34165f85658300254daf40e5322cf7faaa", "msg": "Destination directory ./{'path': './config-backups/03-17-2021--09:58', 'changed': True, 'diff': {'before': {'path': './config-backups/03-17-2021--09:58', 'state': 'absent', 'mode': '0775'}, 'after': {'path': './config-backups/03-17-2021--09:58', 'state': 'directory', 'mode': '0755'}}, 'uid': 1000, 'gid': 1000, 'owner': 'xxxx', 'group': 'xxxx', 'mode': '0755', 'state': 'directory', 'size': 4096, 'failed': False} does not exist"}
Q: "Destination directory ./{'path': './config-backups/03-17-2021--09:58' ... does not exist"}"
A: If you want to create a directory it's necessary to set the attribute state=directory. The default state is file. Create the variable with the name of the directory first. Then use it in the playbook. Given the template
shell> cat templates/show_run.j2
This is show_run.j2
the playbook
- hosts: localhost
tasks:
- name: Set name of the directory
set_fact:
folder_name_with_date: "{{ lookup('pipe', 'date +%m-%d-%Y--%H:%M') }}"
- name: Create directory
file:
state: directory
path: ./config-backups/{{ folder_name_with_date }}
mode: 0755
- name: Write to the file
ansible.builtin.template:
src: ./templates/show_run.j2
dest: "./config-backups/{{ folder_name_with_date }}/{{ inventory_hostname }}-running-config-{{ lookup('pipe', 'date +%m-%d-%Y--%H:%M') }}.txt"
newline_sequence: '\r\n'
gives
shell> tree config-backups
config-backups
└── 03-17-2021--16:42
└── localhost-running-config-03-17-2021--16:42.txt
shell> cat config-backups/03-17-2021--16\:42/localhost-running-config-03-17-2021--16\:42.txt
This is show_run.j2
First, the variable you are registering as folder_name_with_date is a list of items, not just a string. You need to get just the path variable from your folder_name_with_date list. Call it via folder_name_with_date.path to get the path you are looking for.
Also, because you are trying to concatenate variables with a string, I would recommend encapsulating your entire statement with "'s, like so: dest: "./{{folder_name_with_date.path}}/{{ inventory_hostname }}-running-config-{{ lookup('pipe', 'date +%m-%d-%Y--%H:%M') }}.txt"
Not entirely sure whether adding the "'s or not is necessary, but be sure to get the folder_name_with_date's .path instead.
EDIT: For the future, I would recommend outputting what your variables look like after you create them, so you can see how to use them in future tasks. Technically, Ansible will output what the variable is when it is registered, but I also like to include a quick debug like this to view my variables:
name: what is my variables structure??
debug:
msg: "{{ folder_name_with_date }}"
Debug documentation: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/debug_module.html
Ansible find module isn't working as expected.
So i have three instances
One is test node , second controller node and third is from where i am running my ansible playbook
I am trying to generate ssh-keys on test_nodes and then fetching the public keys from those nodes. This is working fine.
Then I am trying to appending these public keys in the authorized_keys file of a different host(controller_node). For this, I am using the find module to get list of files and then loop over these files in authorized_key module.
I was using :
- name: Set authorized key file taken from file
authorized_key:
user: absrivastava
key: "{{ lookup('file','item') }}"
state: present
#with_file:
- "/home/absrivastava/ANSIBLE/ssh-keys/*/home/ribbon/.ssh/id_rsa.pub" This didnt work
#with_filetree:
- "/home/absrivastava/ANSIBLE/ssh-keys/*/home/ribbon/.ssh/id_rsa.pub" This was not appending data
But it didnt seem to work. So i am using find to get list of files and then iterate over them.
- name: Generate ssh keys
hosts: media_nodes
gather_facts: false
tasks:
- name: key generation
openssh_keypair:
path: ~/.ssh/id_ssh_rsa
force: True
register: public_key
- debug:
var: public_key.public_key
- name: fetch public key from all nodes
fetch:
src: ~/.ssh/id_ssh_rsa.pub
dest: ssh-keys/
- name: Controller play
hosts: controller
gather_facts: false
tasks:
- name: Find list of public key files
find:
paths: /home/abhilasha/ANSIBLE/ssh-keys/
file_type: file
recurse: yes
patterns: ".*pub"
use_regex: yes
register: files_matched
- name: debug files matched
debug:
var: files_matched.files
- name: Debug files_matched loop
debug:
var: item.path
loop: "{{ files_matched.files|flatten(levels=1) }}"
loop_control:
label: "{{ item.path }}"
- name: Set authorized key file taken from file
authorized_key:
key: "{{ lookup('file','item') }}"
state: present
with_file:
- "{{ files_matched.files }}"
- name: Find list of public key files
This play is not working giving error
TASK [Find list of public keys] *****************************************************************************************************************************************************************************************************************
ok: [test_controller] => {"changed": false, "examined": 0, "files": [], "matched": 0, "msg": "/home/abhilasha/ANSIBLE/ssh-keys/ was skipped as it does not seem to be a valid directory or it cannot be accessed\n"}
Okay so i got the issue , i was using hosts: controller for this play but the files are on my test VM instance .
But I am not sure how to still solve my problem. I want to use publoc keys on my local and then append it to controller server
- name: Fetch public key files from localhost
gather_facts: false
hosts: 127.0.0.1
connection: local
tasks:
- name: Find list of public keys
find:
paths: ssh-keys/
file_type: file
recurse: yes
patterns: "pub"
use_regex: yes
hidden: yes
register: files_matched
- name: Debug files_matched loop
debug:
var: item.path
loop: "{{ files_matched.files|flatten(levels=1) }}"
loop_control:
label: "{{ item.path }}"
- name: Add Public keys to controller authorized keys
hosts: controller
gather_facts: false
tasks:
- name: Set authorized key file taken from file
authorized_key:
key: "{{ lookup('file','item') }}"
state: present
with_file:
- "{{ files_matched.files }}"
I am unable to use files_matched variable outside the scope of that play. How can i make this work. Thanks in advance
Q: "msg": "/home/abhilasha/ANSIBLE/ssh-keys/ was skipped as it does not seem to be a valid directory or it cannot be accessed\n"
A: Take a look at the directory ssh-keys/ at controller and check the content. Instead of
paths: /home/abhilasha/ANSIBLE/ssh-keys/
find it in the same path
path: ssh-keys/
it has been fetch to
dest: ssh-keys/
Can you change the paths as below and try
- name: fetch public key from all nodes
fetch:
src: ~/.ssh/id_ssh_rsa.pub
dest: /tmp/ssh-keys/
- name: Find list of public key files
find:
paths: /tmp/ssh-keys/
file_type: file
recurse: yes
patterns: ".*pub"
use_regex: yes
register: files_matched
If you are trying to copy/find files from your local machine to the remote, by default the find module will run on the remote, not your local machine. As a result, the error will be thrown if those directories don't exist on your remote.
So you just tell it to "find" on your local machine by specifying delegate_to: localhost and it should work.
tasks:
- find:
paths:
- local_dir1
- local_dir2
file_type: file
patterns: '*.tgz'
register: files_output
# Execute task on this host instead of the target (inventory_hostname).
delegate_to: localhost
- block:
- set_fact:
files: "{{ files_output.files | map(attribute='path') }}"
- set_fact:
files: '{{ files + more }}'
vars:
more:
- '{{playbook_dir}}/remote.sh'
- debug:
msg: '{{ item }}'
loop: '{{ files }}'
How can I take filename from a specific directory and use it as a variable?
Then I'll use this variable to copy a new file with variable filename
For example: In the directory /home/user1/test/ there is always only one file named test1 or some other name.
I need to extract the filename (test1) from this path to some variable
- hosts: linux
become: yes
tasks:
- name: Ansible find file examples
find:
paths: /home/user1/test/
register: files_matched
- debug:
msg: "{{ files_matched.files }}"
- name: "Save find results to file"
copy:
content: "{{ files_matched.files }}"
dest: "/tmp/find_result.txt"
Then I've should get "test1" as variable and use it in the new filename in this code:
copy:
src: /home/myuser/myfile
dest: "{{ item.dest }}"
owner: root
group: root
mode: 0666
with_items:
- { dest: '/home/user2/test/{{ files_matched }}' }
As result of first script i've got:
: 0, "ischr": false, "wusr": true, "xoth": false, "islnk": false, "nlink": 1, "issock": false, "rgrp": true, "gr_name": "root", "path": "/home/user1/test/test1", "xusr":
false, "atime": 1564553299.6092095, "isdir": false, "ctime": 1564553304.7172158, "isblk": false, "xgrp": false, "dev": 2050, "wgrp": false, "isfifo": false, "mode": "0644
", "rusr": true}]
But i need only test1 part as result, not this all.
Thank you!
Q: "How can I take filename from a specific directory and use it as a variable?'
A: Given the tree
$ tree /home/user1/test/
/home/user1/test/
├── test1
├── test2
└── test3
the tasks below
- find:
paths: /home/user1/test/
register: files_matched
- debug:
msg: "{{ '/home/user2/test/' ~ item|basename }}"
loop: "{{ files_matched.files|json_query('[*].path') }}
give
"msg": "/home/user2/test/test1"
"msg": "/home/user2/test/test3"
"msg": "/home/user2/test/test2"
I hope you are expecting something like this. this will save the list of files under the specified directory into the /tmp/find_result.txt. I hope from here you can proceed.
---
- name: find file
hosts: linux
tasks:
- name: Ansible find file examples
find:
paths: /home/user1/test/
register: files_matched
- name: "Save find results to file"
lineinfile:
line: "{{ item.path }}"
path: "/tmp/find_result.txt"
create: yes
loop: "{{ files_matched.files | flatten }}"
note: make sure that you delete the /tmp/find_result.txt once your task completed.
if you want to store only the filename not the entire path, then replace line: "{{ item.path }}" with line: "{{ item.path | basename }}"
I am trying to use the mv module on Ansible but I am not having luck.
In my initial attempt I did the following:
- name: changing the name of the file
shell: mv /tmp/bundle /opt/Rocket.Chat
And I get the following error:
FAILED! => {"changed": true, "cmd": "mv /tmp/bundle /opt/Rocket.Chat", "delta": "0:00:00.033553", "end": "2019-02-11 06:06:43.273787", "msg": "non-zero return code", "rc": 1, "start": "2019-02-11 06:06:43.240234", "stderr": "mv: cannot move ‘/tmp/bundle’ to ‘/opt/Rocket.Chat/bundle’: File exists", "stderr_lines": ["mv: cannot move ‘/tmp/bundle’ to ‘/opt/Rocket.Chat/bundle’: File exists"], "stdout": "", "stdout_lines": []}
So, I changed it to:
- name: create directory
file:
state: directory
path: "/opt/Rocket.Chat"
- name: copy the files
copy:
src: "/tmp/bundle"
dest: "/opt/Rocket.Chat"
remote_src: yes
- name: delete the other files
file: path=/tmp/bundle state=absent
My new error is:
FAILED! => {"changed": false, "msg": "Remote copy does not support recursive copy of directory: /tmp/bundle"}
Seems that the "copy module to work with recursive and remote_src" does not work yet, but will be supported from May 2019
Here is a workaround, edit the folder names to your setup.
# Copy all files and directories from /usr/share/easy-rsa to /etc/easy-rsa
- name: List files in /usr/share/easy-rsa
find:
path: /usr/share/easy-rsa
recurse: yes
file_type: any
register: find_result
- name: Create the directories
file:
path: "{{ item.path | regex_replace('/usr/share/easy-rsa','/etc/easy-rsa') }}"
state: directory
mode: "{{ item.mode }}"
with_items:
- "{{ find_result.files }}"
when:
- item.isdir
- name: Copy the files
copy:
src: "{{ item.path }}"
dest: "{{ item.path | regex_replace('/usr/share/easy-rsa','/etc/easy-rsa') }}"
remote_src: yes
mode: "{{ item.mode }}"
with_items:
- "{{ find_result.files }}"
when:
- item.isdir == False