Ansible Lineinfile: 9 Ways to Use It to Improve Your Playbooks

8 min read

Lines with characters and numbers can be used to describe the beauties of this world and can also be used to describe the configuration of your web server. It's a simple concept with infinite forms and uses. When you're handling files with lines in Ansible, there's one go-to module that covers most use cases. The lineinfile module is a simple yet powerful tool that you can use in a variety of ways to improve your playbooks. This article lists nine of the best ways it can be used.

Getting Started

Lineinfile is a task module, so we need a playbook to put it in. If you’re following along, you can use this playbook to get you started: 

---
- name: Lineinfile Examples
  hosts: localhost
  tasks:

You can copy and paste this into a directory of your choosing. The name doesn’t matter, so feel free to go for something like main.yml. To run the playbook, you run "ansible-playbook main.yml". All of the examples below are able to run locally just fine, but you can use them for remote machines in the same way. 

Placing a Line in a File

As the name of this module suggests, the lineinfile module is surprisingly good at placing a line in a file. The most basic example of this would be using only the two parameters, the line of content and the path of the file that this line needs to get. 

    - name: Place line
      lineinfile:
        line: Hello World
        path: hello.txt
        create: true

If you don’t provide the create parameter, this task will fail if the file does not exist. In most cases, you want to leave this out. This way, you can be alerted when you want to change a file that is expected to exist. 

Remove a Line From a File

Handling lines in files also includes removing lines. With the lineinfile module, this is easy to do. To ensure a specific line is absent in the file, you can use the state parameter. 

    - name: Remove line
      lineinfile:
        line: Hello World
        path: hello.txt
        state: absent

By default, the state parameter is set to "present." That means that if you're adding or changing lines, then setting this parameter is not required. 

Changing a Line

Adding or removing plain lines has a limited set of real-life use cases. In most situations, you need a bit more finesse. A very common use case is changing configuration files. To define which line you want to change, you can use the regexp parameter, which takes regular expressions. If you search for the key you want to set, you can prevent having the key defined twice. If you have the "PermitEmptyPasswords yes" line, and you only define "line: PermitEmptyPasswords no", you’ll end up with a completely new line at the end of the file. To actually change the existing line, using the regular expression "^PermitEmptyPasswords" makes sense. The ^ character makes sure the line you’re changing starts with that key. This way you don’t accidentally change comments instead, for example. 

    - name: Change SSH daemon configuration
      lineinfile:
        line: PermitEmptyPasswords no
        regexp: ^PermitEmptyPasswords
        path: /etc/ssh/sshd_config

Note that for changing the file "/etc/ssh/sshd_config", you need the right permissions. In most cases, you would need to add "become: true’" on the play or task level for this task to be successful. 

Changing Multiple Lines

If you want to set multiple configuration items, using a loop makes this very straightforward: 

    - name: Change SSH daemon configuration
      lineinfile:
        line: "{{ item.line }}"
        regexp: "{{ item.regexp }}"
        path: /etc/ssh/sshd_config
      loop:
        - line: GatewayPorts no
          regexp: ^GatewayPorts
        - line: X11Forwarding yes
          regexp: ^X11Forwarding

Adding a Line to a Specific Location

For some configuration files, it doesn’t really matter where you put a certain configuration item. In other cases, location is essential. The lineinfile module provides the tools to define the context in which a line needs to be present by using the insertbefore and insertafter parameters. You can set these parameters to EOF or BOF, respectively, to place the configuration item at the end or at the beginning of the file. The other way of using these parameters is by using a regular expression to which the reference line should adhere. Let’s say you have the following configuration file: 

# ansible.cfg
[defaults]
<----------- Desired location of config line *
gathering = implicit

[colors]
highlight = white
<----------- EOF and default location of new line *

If you set a configuration item without specifying the location, it will by default place the item at the end of the file. In this case, that means you will find it under the colors category. To place a line in the defaults category, you would provide the insertafter parameter: 

    - name: Set the remote_port value in defaults category
      lineinfile:
        insertafter: \\[defaults\\]
        line: remote_port = 22
        path: ansible.cfg

For some configuration file formats, like the ini format, there are other modules that might provide better support, like the ini_file module. 

Writing to Log Files

You can use the lineinfile module for more than configuration files. You can also use it for appending lines to log files, for example. Of course, you can save the complete Ansible logs too, but having granular and explicit logging can help a lot when debugging or auditing. This is how you could do that in your playbook: 

    - name: Write message to logging
      lineinfile:
        path: application.log
        line: "{{ ansible_date_time.iso8601 }} - Application successfully installed."
        create: true

It results in a log file that looks like this: 

2022-01-21T14:03:23Z - Application successfully installed.
2022-01-21T14:09:31Z - Application successfully installed.
2022-01-21T14:10:23Z - Application successfully installed.
2022-01-21T14:10:51Z - Application successfully installed.

You can use variables to make log lines like this even more useful.

Backing Up Files You Change

Changing files can be, or feel, dangerous. The lineinfile module can help provide you peace of mind by backing up the file that you’re changing. To ensure a backup is made, you'll want to set the backup parameter to "true": 

    - name: Set the remote_port value in defaults category
      lineinfile:
        insertafter: \\[defaults\\]
        line: remote_port = 22
        path: ansible.cfg
        backup: true

After the task has been executed, you’ll find a file with a name similar to "ansible.cfg.12578.2022-01-21@15:00:09~" in the same directory as the changed file. The backup only happens when the file is actually changed, so rerunning the task will not fill up the directory with backups. 

Verifying Changes

Some applications or systems will not work correctly if the configuration file has invalid entries. For example, it can be risky to change entries in the sudoers file, as this can lead to a broken system. To prevent situations like this, the lineinfile module has the validate parameter. You can use it to provide a command to verify the file you’re changing: 

    - name: Enable passwordless sudo
      lineinfile:
        path: sudoers
        line: 'myusername ALL=(ALL) NOPASSWD: ALL'
        create: true
        validate: /usr/sbin/visudo -cf %s

Since this is an example, this task is just changing the sudoers file in the playbook directory. To actually change the sudoers configuration, you’d need to change the path to "/etc/sudoers", or, preferably, to "/etc/sudoers.d/myusername". 

Checking the Presence of a Line in a File

If you want to check if a certain line is present in a file, you can use the check_mode attribute. Note that this is not a parameter of the module, but of the task itself. This can be useful if you want to run some tasks based on whether a line is present in a file. 

      - name: Check for line
        lineinfile:
          line: Hello World
          path: hello.txt
       check_mode: true
       register: line_check

      - name: Print message
        debug:
          msg: The line "Hello World" was not found in hello.txt.
        when: line_check.changed

Using the check_mode parameter ensures that the task does not actually change the file, but instead it reports what it would do. You can then check for the return key "changed" in order to see if it would have been changed. If it would, the line is not present in the file. 

Wrapping Things Up

The possibilities of using lineinfile are virtually endless. By combining the simplicity of a plain text line with the power of regular expressions, the lineinfile module makes it possible to do both simple and complex manipulations of plain text files. It’s typically a module where you’ll find fresh ways of using it, even after years of experience with it. Hopefully, these nine examples will help you make your playbooks a little bit better. 

To learn more about Ansible, including how to keep your playbooks secure, check out this post

This post was written by Ruben Jongejan. Automation is Ruben’s thing. He likes to be on the cutting edge—the place where infrastructure, automation, and application development touch. That's why he spends most of his days in the cloud.

Stay up to date

We'll never share your email address and you can opt out at any time, we promise.