5

I am starting to use Ansible in order to write a playbook that will deploy a staging environment for our application. I'm trying to understand if the best practice for applying changes to files are to change them locally on the control machine and then propagate them into the remote server, or whether the files should be manipulated inside the playbook.

Manipulating the files through the playbook seems better in terms of readability and documentation, as well as keeping the entire configuration process to one tool for the entire configuration process. On the other hand, changing the files on the local server is easier and faster.

What is the best way to approach these problems?

Thanks, Yaron.

Yaron Idan
  • 6,207
  • 5
  • 44
  • 66

4 Answers4

5

Welcome to Ansible!

I think you should have a look at Templates. Never change Files locally and deploy them. Your deployment should not change any file locally!

If you have a config file, write a template for this file and render some variables inside to change the config for the deployed application. The template module will render the file directly to your server.

flxPeters
  • 1,476
  • 12
  • 21
  • 1
    In this case I'm trying to change the /etc/hosts file, is that file also a good candidate for Templates? Thanks! – Yaron Idan Feb 04 '16 at 13:50
  • Do you change the hosts file on your local or the remote maschine? On the remote maschine you should use ´lineinfile´ for this task. Template is a good solution if you want to replace the whole file. But i think you want to add contents – flxPeters Feb 04 '16 at 16:02
  • I find `lineinfile` is difficult to get right for many kinds of changes, e.g. modifying list values, or retaining the file unchanged if `regexp` matches. For the latter, you can use `backrefs`, but that breaks if `regexp` doesn't match. It's better in many ways than templates for simpler cases, though, IMHO. – orodbhen May 07 '19 at 17:39
2

Use lineinfile and blockinfile (ansible > 2.0). It is cleaner, portable and can be run from any control machine. But there are exception when the block is huge.

helloV
  • 50,176
  • 7
  • 137
  • 145
2

As the other answers already told you, the copy and template modules are the preferred way to manipulate configuration files with Ansible.

They allow you to prepare the entire file upfront and then move it to your server (the difference between them is the template module allows you to use variables while the copy module copies the file as it is).

Since you have complete control over the file, there are no surprises.

However, there are (more often than not) circumstances which prohibit the use of these modules:

  • Different roles manipulating the same file
  • Dealing with legacy systems
  • Needing different versions of a file for different servers (to some extent)

In this case, I like to use the lineinfile module in a bulletproof way:

  • First remove all ocurrences of a directive except the one you want to add
  • Then add the line at a specific place

As an example, take the sshd_config file. You may want to make sure that your server listens only to the IP address 1.2.3.4:

- name: Remove lines with unwanted occurrences of ListenAddress
  lineinfile: dest=/etc/ssh/sshd_config
              regexp="^ListenAddress (?!1\.2\.3\.4)"
              state=absent

- name: Listen on 1.2.3.4
  lineinfile: dest=/etc/ssh/sshd_config
              line="ListenAddress 1.2.3.4"
              insertafter="^#?AddressFamily"

The first task removes every occurrence of ListenAddress which does not specify the IP we want (using a regular expression construct called negative lookahead).

The second task then inserts the proper directive (ListenAddress 1.2.3.4) directly after the line that begins with AddressFamily (comment or not).

This way, your tasks stay idempotent and you can be sure that there are no ListenAddress directives in the file you do not know about.

If you need more detail, I wrote an article about this topic. And in case the application you are trying to deploy is written in Rails, you may be interested in Efficient Rails DevOps, a book I wrote about this topic.

Michael Trojanek
  • 1,813
  • 17
  • 15
1

Templates should be your go-to. They're simpler to work with and you know the host will have the accurate configuration once the playbook is run.

I find myself reaching for lineinfile or blockinfile as a fallback when I have a legacy system with many changes to the file on many hosts which I need to retain.

Whenever possible, render a template to a configuration directory on linux. E.g. don't use lineinfile on /etc/sudoers to add admin accounts, render a template with the accounts to /etc/sudoers.d/administrators

Dave Snigier
  • 2,574
  • 3
  • 21
  • 29