I currently pass parameters to my script which an ansible tower job template/role calls. In order to make it more user friendly, I have decided to use a survey to do this. The script takes on filenames as parameters.
The script accepts the parameters in this format
'file1.txt','file2.txt','file3.txt'
However, here is the problem
The file could be one, it could be 2 or three up to possibly 5.
I have thought about a solution and I think the best design is to have a comma delimited list of files coming from ansible survey, for example file1, file2, file3
How can I have a logic whereby with the list of files, they can be split and a loop used to copy them one by one if there are more than one file provided in the list, then have a variable that will add single quotes and a comma to the file list. For example in the survey the values provided such as file1.txt, file2.txt, file3.txt will then be transformed into a variable which contains the following
'file1.txt','file2.txt','file3.txt'
The other issue is this.
The ansible role copies the given file name onto a directory, I know the split function can be used to split a comma separated list, how can I then copy them onto a folder in a loop ? If we look at the example below, it only works for a single file.
EDIT.
I have looked at the split function and combining it with a loop. I get an error when I run it. Template error while templating string
---
- name: Set file name
set_fact:
file1: "file1.txt"
file_list: "file1.txt"
- name: Set working directory
set_fact:
standard_path: "{{ansible_user_dir}}\\execution"
content_file: "{{standard_path}}\\{{file1}}"
- name: Copy file to working directory
win_copy:
src: "file1.txt"
dest: "{{content_file}}"
- name: Set parameters for script
set_fact:
params: "-filenames '{{content_file}}'"
- name: Run a loop to copy the files.
win_copy:
src: "{{ item }}"
dest: "{{standard_path \\ item }}"
with_items: "{{file_list.split(',') }}"
The section within the code that was throwing an exception has now been fixed.
The jinja2 standard requires the variables to be in separate {{}} as seen below.
- name: Run a loop to copy the files.
win_copy:
src: "{{ item }}"
dest: "{{standard_path}}\\{{item }}"
with_items: "{{file_list.split(',') }}"
Related
I know how to use the acl module in ansible. It's working like a charm but not exactly like I want.
I have a log_dir variable with the exact path to log files. My goal is to set an ACL to the files and only to the parent directories up to a base directory.
For example:
Log file: /some/highly/fancy/secured/file
Log path: /some/highly/fancy/secured
Now I want an ACL up to /some but not to (for example):
/some/otherDirectory or /some/highly/fancy/A/file
Do you know how to handle this?
Feels super hacky, but something like this would work. I do hope there's a more elegant solution though.
vars:
file: /some/highly/fancy/secured/file
tasks:
- acl:
path: "/{{ file.split('/')[1:index+2] | join('/') }}"
# <snip>
loop: "{{ file.split('/')[1:] }}"
loop_control:
index_var: index
Basic idea is to use the file path split into a list to figure out how many times to loop. Then inside the loop once again split the file path into a list, and slice it from the base folder up to the loop index, and join it again into a file path. We skip the first entry in the list because it is blank, so need to adjust the index value in the list slice.
I have a file which contains different filnames. What filenames are in that file, changes every time i run the playbook.
Though I found only ways to either copy all files in a directory or copy certain files that are defined static.
filenames.txt:
file1
file4
Directory that contains the files:
file1
file2
file3
file4
file5
My Plan was to create a variable in my vars sheet in which I save the file and then use it in my role.
copy-files: path/filenames.txt
Role to copy the files:
---
- name: Copy Files
copy:
src: "{{ item }}"
dest: "{{path2}}"
with_fileglob:
- "/pathtofiles/{{copy-files}}"
Sadly this doesen't work. Does somebody else know a different approach?
When I understand you right, you want to copy the files, listed in path/filenames.txt. You can do it with the following task:
- name: Copy Files to /tmp
copy:
src: "{{ item }}"
dest: "{{ hqlname_sys2 }}"
loop: "{{ lookup('file', 'path/filenames.txt').split('\n') }}"
The contents of path/filenames.txt is read with the lookup file plugin into a string and that string is splitted by the split function on the '\n' delimiter, so that you get an array with of filenames which is passed to the loop.
I am trying to convert a yml file into json. I need to pick the list of yml files from file1.txt and convert all those files to json.
Below is the code that I am using
- hosts: localhost
tasks:
- name: convert yml to json
shell: cat /home/testadmin/{{ item }}.yml
register: result
- copy:
dest: ./{{ item }}.json
content: "{{ result.stdout | from_yaml | to_nice_json }}"
with_lines: cat file1.txt
The code should pick up the filename from file1.txt and then convert the file 1 by 1. I would like to know how to put all these commands to convert yml to json in a loop.
The actual result should replace all the .yml files in file1.txt and convered into the json format with the same name
A loop only works on the task that it is attached to. To wrap multiple tasks in a loop, you need to split them out to another file, use an include statement to load them, and then attach the loop to that include statement.
In your case tho, none of that is required. I think this should do what you are looking for, presuming that file1.txt contains a list of file names, one per line & without file extension:
- host: localhost
connection: local
tasks:
- name: Convert each file listed in file1.txt
copy:
dest: "./{{ item | trim }}.json"
content: "{{ lookup('file', item + '.yml') | from_yaml | to_nice_json }}"
with_lines: cat ./file1.txt
connection: local stops Ansible opening a SSH connection to the localhost
{{ item | trim }} takes each item from the list and trims any leading or trailing whitespace
lookup('file', item + '.yml') reads a file. item is the default variable name used in loops to contain the contents of each element of the loop
with_lines only works locally, so if you need to run this remotely, you will need to modify this
I'm writing an Ansible role where I have some templates that must be present multiple times with different names in a single destination directory. In order not to have to handle each of these files separately I would need to be able to apply templating or some other form of placeholder substitution also to their names. To give a concrete example, I might have a file named
{{ Client }}DataSourceContext.xml
which I need to change into, say,
AcmeDataSourceContext.xml
I have many files of this kind that have to be installed in different directories, but all copies of a single file go to the same directory. If I didn't need to change their names or duplicate them I could handle a whole bunch of such files with something like
- name: Process a whole subtree of templates
template:
src: "{{ item.src }}"
dest: "/path/to/{{ item.path }}"
with_filetree: ../templates/my-templates/
when: item.state == 'file'
I guess what I'd like is a magic consider_filenames_as_templates toggle that turned on filename preprocessing. Is there any way to approximate this behaviour?
Pretty much anywhere you can put a literal value in Ansible you can instead substitute the value of a a variable. So for example, you could do something like this:
- template:
src: sometemplate.xml
dest: "/path/to/{{ item }}DataSourceContext.xml"
loop:
- client1
- client2
This would end up creating templates
/path/to/client1DataSourceContext.xml and
/path/to/client2DataSourceContext.xml.
Update 1
For the question you've posed in your update:
I guess what I'd like is a magic consider_filenames_as_templates toggle that turned on filename preprocessing. Is there any way to approximate this behaviour?
It seems like you could just do something like:
- name: Process a whole subtree of templates
template:
src: "{{ item.src }}"
dest: "/path/to/{{ item.path.replace('__client__', client_name) }}"
with_filetree: ../templates/my-templates/
when: item.state == 'file'
That is, replace the string __client__ in your filenames with the
value of the client_name variable.
I am looking for something that would be similar to with_items: but that would get the list of items from a file instead of having to include it in the playbook file.
How can I do this in ansible?
I managed to find an easy alternative:
- debug: msg="{{item}}"
with_lines: cat files/branches.txt
Latest Ansible recommends loop instead of with_something. It can be used in combination with lookup and splitlines(), as Ikar Pohorský pointed out:
- debug: msg="{{item}}"
loop: "{{ lookup('file', 'files/branches.txt').splitlines() }}"
files/branches.txt should be relative to the playbook
Lets say you have a file like
item 1
item 2
item 3
And you want to install these items. Simply get the file contents to a variable using register. And use this variable for with_items. Make sure your file has one item per line.
---
- hosts: your-host
remote_user: your-remote_user
tasks:
- name: get the file contents
command: cat /path/to/your/file
register: my_items
- name: install these items
pip: name:{{item}}
with_items: my_items.stdout_lines
I am surprised that nobody mentioned the ansible Lookups, I think that is exactly what you want.
It reads contents that you want to use in your playbook but do not want to include inside the playbook from files, pipe, csv, redis etc from your local control machine(not from remote machine, that is important, since in most cases, these contents are alongside your playbook on your local machine), and it works with ansible loops.
---
- hosts: localhost
gather_facts: no
tasks:
- name: Loop over lines in a file
debug:
var: item
with_lines: cat "./files/lines"
with_lines here is actually loop with lines lookup, to see how the lines lookup works, see the code here, it just runs any commands you give it(so you can give it any thing like echo, cat etc), then split the output into lines and return them.
There are many powerful lookups, to get the comprehensive list, check out the lookup plugins folder.