Ansible Files For Beginners


In this video we are going to get our first introduction to how Ansible handles our Files.

Perhaps the biggest thing to understand with Files is that the file module sets attributes on files that exist on our remote sytems. The copy module is what actually transfers the files from your Ansible host to your deployment target / server.

When I first started using Ansible, I thought that Files would be quite a big deal. But it turned out that the more I've used Ansible, the times when I've needed to use the Files functionality have been few and far between.

The main reason for this would be that when I first started tinkering with Ansible, I had expected to go through a process of pulling the config files I already had created and was using on my live and dev servers, then put them into Ansible's Files folder, and have them pushed up to the server as required.

However, it turned out that Ansible provides a much more elegant and reusable solution to that problem - Templates - which we will come to in another video.

The second reason I had for using Files was that I envisaged copying over the VMWare VMTools file to each of my cloned and Ansibilised (not a real word, I'm sure) servers.

But even that never happened, as I was having so much trouble trying to get VMWare Fusion to share a folder with my Macbook (as you might have seen with Vagrant's Synced Folders) that I gave up in disgust and switched to using Oracle's VirtualBox, which it transpires does everything I need and has a more attractive pricing structure. In that it's free.

So the two big reasons I was likely to use the Files directory have turned out to be none-starters.

But that's not to say it's not going to be useful in some situations.

For example, sometimes you have collections of files that need to be on every server, and those files rarely change and don't require any specific config that Template variables would give you. Those kinds of files would make ideal candidates.

In the video we just use 10mb of dummy data, created by using this command:

dd if=/dev/zero of=10mb-file.bin bs=1024k count=10

(thanks Markus)

Directories Not Included

The first big gotcha that caught me out is that directories won't be created for you, if you happen to specify a dest path that doesn't exist.

Let's look at an example Ansible Playbook task that would make use of Files, and how this might cause us an unexpected problem:

---
# role/example/tasks/main.yml
- name: Copy our 10mb file to our server being managed by Ansible
  copy: src=10mb-file.bin dest=/home/codereview/10mb-file.bin

The big 'gotcha' is that if the absolute path of /home/codereview/ doesn't exist on our deployment target, this task will fail.

Ok, so far, so probably durrr, very obvious Chris.

I don't dispute this may be obvious, but what is less obvious is when you start managing an existing server with Ansible, and you forget about this trivial tidbit, and it's only at 3am on one rainy Thursday morning when your server died without rhyme or reason and you're stuck rebuilding that you will be caught out with things like this.

At least, that's how it usually works for me.

Rectifying Gotcha #1

In summary just be sure your target directory / path exists before trying to write to it. If in doubt, you can use the file module to create directories also. We touched on that in the video on Roles.

Just be sure the absolute path exists on the remote server before you try to write files to it. The smart thing to do here is to get Ansible to ensure that path is there first.

---
# role/example/tasks/main.yml
- name: Create our directory before writing to it later on
  file: path=/var/www/codereview
    state=directory
    owner={{user_name}}
    group={{user_name}}
    mode=0700

(More on Variables ({{user_name}}) here)

That's My File, How Dare You?

So we've covered 'gotcha' #1.

The second one to bite you will likely come before you suffer the first.

That is remembering to specify which owner and group's will have access to your files you copy across, once you have copied them to your deployment target.

This is the one you usually forget about the first few times you are working with the copy module, and like I say, it's pretty quick to spot. Easier than the other gotcha, that's for sure.

If we don't specify which user or group of users should have access to the file we are copying over, by default it will be assigned to the user with which Ansible is running its commands.

As with most of our videos so far, we have been running all our Playbooks against our own host (localhost). We've been specifying that we will run our commands with sudo, and providing the sudo password - even though the password is the same as the ssh password in our case.

Therefore, when our files are copied over, even though they are going to our own server (ourselves / localhost), they are going via ssh as our sudo user (root), and therefore will be assigned to the user of root and the root group. We could say that another way: root:root.

If we had sent the files over as a different user, perhaps by specifying the ansible_ssh_user=codereview in our hosts file as we will see in a future video, our file(s) would be copied with the user and group of codereview:codereview. Assuming that the codereview user and group existed on that system, of course ;)

Rectifying Gotcha #2

Fortunately, specifying our permissions is dead straight forward:

---
# role/example/tasks/main.yml
- name: Copy our 10mb file to our server being managed by Ansible
  copy: src=10mb-file.bin dest=/home/codereview/10mb-file.bin
    owner={{user_name}}
    group={{user_name}}
    mode=0644

Combining With with_items

Now we know how to use file and copy on a simple level, we can start combining them with other Ansible commands.

By combining file and copy with the with_items looping syntax, we can create one task that copies across multiple files or creates folder on our deployment target.

Which is kinda nice.

---
# role/example/tasks/main.yml
- name: Create our remote directory structure
  file: path=/home/{{user_name}}/{{ item }}
    owner={{user_name}}
    group={{user_name}}
    mode=0700
  with_items:
    - subdir
    - subdir/another-dir
    - third-dir

- name: Copy multiple files to our server
  copy: src={{ item }} dest=/home/codereview/{{ item }}
    owner={{user_name}}
    group={{user_name}}
    mode=0700
  with_items:
    - 10mb-file.bin
    - another-file.mpg
    - subdir/third-file.jpg

Once again, be a little careful here that subdir actually exists. We would also need to make sure we have a file named third-file.jpg in our Role's files/subdir directory.

But hopefully you can see how we can gain quite a neat set of instructions for the roles that will be carried out by our server inventory.

We can get fancier than this if we wish. But truthfully, there is no need to over complicate our builds at this stage. Keep things nice and simple, and you are more likely to keep coming back and using them more and more.

At least, that's how I see it.

Code For This Course

Get the code for this course.

Episodes