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 }}'
Related
I'm trying to copy many files using ansible.
this is my playbook :
- name: Copy the scenario test
copy:
src: files/{{ scenario_name }}
dest: /home/{{ user }}/scenario_creation
mode: '0644'
run_once: true
loop: "{{ scenario_name }}"
tags:
- user
- scenario
and this is my roles/scenario_test/defaults/main.yml
scenario_name: ['topup-scenario.json', 'test.json']
when I execute my playbook it says:
"msg": "Could not find or access 'files/[u'topup-scenario.json', u'test.json']'\nSearched in:\n\t/home/path/ansible/plays/files/[u'topup-scenario.json', u'test.json']\n\t/home/path/ansible/plays/files/[u'topup-scenario.json', u'test.json'] on the Ansible Controller.\nIf you are using a module and expect the file to exist on the remote, see the remote_src option"
}
any help ?
Change:
src: files/
to
src: ./files/
You need to change your code to this:
- name: Copy the scenario test
copy:
src: files/{{ item }}
dest: /home/{{ user }}/scenario_creation
mode: '0644'
run_once: true
loop: "{{ scenario_name }}"
tags:
- user
- scenario
The loop iterates the list to the term 'item', unless you redefine it with the loop_var option. So when you call scenario_name in your src line, you are actually calling the entire list, not an iteration of it.
I am new to ansible and have exhausted my forum searches. I cannot seem to find an answer for this issue I am having with with_items and when. The playbook as it is now will run, but it results in failed messages "src file does not exist" for every path in the list that does not exist on that machine.
Since, this is being run against several machines, that's a lot of failed (red) messages that mean nothing. I thought the when statement would only run the task IF the statresult existed. This is not the case.
Basically what I am trying to do is check several machines to see if these two paths exist. If they do, create a symlink for each one. All the paths to check in are different. Right now I have:
---
- hosts: all
become: yes
gather_facts: no
tasks:
- name: Check that domains exist
stat:
path: '/path/to/the/domain/{{ item.domainpath }}'
get_attributes: no
get_checksum: no
get_md5: no
get_mime: no
register: item.statresult
with_items:
- { domainpath: 'path1/', statresult: 'stat_result_path1' }
- { domainpath: 'path2/', statresult: 'stat_result_path2' }
- { domainpath: 'path3/', statresult: 'stat_result_path3' }
- name: Create symlink for bin on existing domain machines
file:
src: '/path/to/the/domain/{{ item.srcbin }}'
dest: /path/new/symlink_bin_link
state: link
with_items:
- { srcbin: 'path1/bin/', domainexists: 'stat_result_path1.stat.exists' }
- { srcbin: 'path2/bin/', domainexists: 'stat_result_path2.stat.exists' }
- { srcbin: 'path3/bin/', domainexists: 'stat_result_path3.stat.exists' }
when: item.domainexists
ignore_errors: yes
- name: Create symlink for config on existing domain machines
file:
src: '/path/to/the/domain/{{ item.srcconfig }}'
dest: /path/new/symlink_config_link
state: link
with_items:
- { srcconfig: 'path1/config/', domainexists: 'stat_result_path1.stat.exists' }
- { srcconfig: 'path2/config/', domainexists: 'stat_result_path2.stat.exists' }
- { srcconfig: 'path3/config/', domainexists: 'stat_result_path3.stat.exists' }
when: item.domainexists
ignore_errors: yes
I have to use ignore_errors because otherwise it will not go to the second task. I have tried to use when: item.domainexists == true but that results in the task getting skipped even when it matches a path that exists.
Even if the when statement iterates over every with_items, it should not matter because as long as it matches one, it should do the task correctly?
This is how your playbook should look like:
---
- hosts: all
become: yes
gather_facts: no
tasks:
- name: Check that domains exist
stat:
path: /path/to/the/domain/{{ item }}
loop:
- path1
- path2
- path3
register: my_stat
- name: Ensure symlinks are created for bin on existing domain machines
file:
src: /path/new/symlink_bin_link
dest: /path/to/the/domain/{{ item }}/bin
state: link
loop: "{{ my_stat.results | selectattr('stat.exists') | map(attribute='item') | list }}"
- name: Ensure symlinks are created for config on existing domain machines
file:
src: /path/new/symlink_config_link
dest: /path/to/the/domain/{{ item }}/config
state: link
loop: "{{ my_stat.results | selectattr('stat.exists') | map(attribute='item') | list }}"
Explanation:
register: item.statresult is a nonsensical construct in Ansible, you should provide a name of a variable as a parameter
that variable will contain a list of results for any task running in a loop
you should process that list (see this answer to learn about selectattr and map) to get a list of the paths which exist (only)
you should loop over that filtered-and-mapped list
Also: src and dest should be defined the other way round for symlinks than in your code.
Further, you can combine the last two tasks into one by adding a product filter to the iterable definition.
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 }}
I want to create the folder: temp2 which is able to store all the symlinks of the subfolders/files of other foder: temp1. with_items can help complete this task, but it needs to list down all the folder/file name as below script shown:
- name: "create folder: temp2 to store symlinks"
file:
path: "/etc/temp2"
state: directory
- name: "create symlinks & store in temp2"
file:
src: "/etc/temp1/{{ item.src }}"
dest: "/etc/temp2/{{ item.dest }}"
state: link
force: yes
with_items:
- { src: 'BEAM', dest: 'BEAM' }
- { src: 'cfg', dest: 'cfg' }
- { src: 'Core', dest: 'Core' }
- { src: 'Data', dest: 'Data' }
It is not flexible as the subfolders/files under temp1 would be added or removed, and I need to update above script frequently to keep the symlinks as updated
Is there any way to detect all the files/folder under temp1 automatically instead of maintaining the with_items list?
The following code works under Ansible-2.8:
- name: Find all files in ~/commands
find:
paths: ~/commands
register: find
- name: Create symlinks to /usr/local/bin
become: True
file:
src: "{{ item.path }}"
path: "/usr/local/bin/{{ item.path | basename }}"
state: link
with_items: "{{ find.files }}"
You can create a list of files using find module:
Return a list of files based on specific criteria. Multiple criteria are AND’d together.
You'll likely need to leave recurse set to false (default) since you assume subfolders might exist.
You need to register the results of the module with register declaration:
register: find
In the next step you need to iterate over the files list from the results:
with_items: "{{ find.results.files }}"
and refer to the value of the path key. You already know how to do it.
You will also need to extract the filename from the path, so that you can append it to the destination path. Use basename filter for that.
I have a play as follows
- name: create the unison preference file
template:
src: default.prf.j2
dest: /root/.unison/{{ item }}.prf
with_items: groups['ndeployslaves']
The contents of the default.prf.j2 file is as follows
root = /home
root = ssh://root#{{ item }}//home
ignore = Path virtfs
ignore = Path */mail
The item variable is not working in the template and I am getting the error
TASK [unison_master : create the unison prefrence file] ************************
fatal: [127.0.0.1]: FAILED! => {"failed": true, "msg": "'item' is undefined"}
How do I reference an item inside a template used in a play?
Since it's not letting you use {{item}} in the template, you could do this:
- name: create the unison preference file
copy:
src: default.prf
dest: "/root/.unison/{{ item }}.prf"
force: no
with_items: "{{ groups['ndeployslaves'] }}"
- name: edit preference file
lineinfile:
dest: "/root/.unison/{{ item }}.prf"
line: "root = ssh://root#{{item}}//home"
regexp: '^root = ssh://'
with_items: "{{ groups['ndeployslaves'] }}"
The contents of default.prf on your local host should be:
root = /home
root = ssh://
ignore = Path virtfs
ignore = Path */mail
However I have {{item}} working in a template. Are you sure your whitespace is correct? src and dest need to be indented one level deeper than template, but with_items needs to be on the same level as template.
- name: create the unison preference file
template:
src: default.prf.j2
dest: "/root/.unison/{{ item }}.prf"
with_items: "{{ groups['ndeployslaves'] }}"
The error was caused by an indentation error.
The with_items: groups['ndeployslaves'] was indented a level deeper than it should have.