Ansible lineinfile module could be the saviour of your day when you want to work with files and especially modify their content on the run, like adding a new line in the file or updating a line in the file or replace a line in the file when certain text is found and much more.
lineinfile has a various set of examples and it provides many parameters to get your job done easily. In this post, we are going to see the ansible lineinfile module in action with examples.
The Ansible lineinfile module
Ansible lineinfile module is helpful when you want to add, remove, modify a single line in a file. You can also use conditions to match the line before modifying or removing using the regular expressions. You can reuse and modify the matched line using the back reference parameter.
Consider yourself having any of these following requirement
- Add a line when it is not already present.
- Change the port number in the configuration file
- Disable the SSL when the SSL is enabled
- Add a new entry in the /etc/hosts file
- Upgrade the package version when the installed version is matching your regular expression
- Remove a username from /etc/passwd file using regex
Hope this sets the context. Before we proceed with the examples Something to be highlighted.
Ansible Lineinfile can be used only for working a single line in a file. If you want to replace mutiple lines try replace module or use blockinfile if you want to insert/update/remove a block of lines in a file.
If you want to explore more about the Ansible Replace module you can refer this post.
Ansible replace line in file – Ansible Replace Examples | Devops Junction
Ansible lineinfile examples
We have gathered various examples of ansible lineinfile here. These are examples we have covered in this post. you can choose to read all or any specific example.
- Validate if a line is present without any modification
- Validate if a line is present in the file and add if it does not exist
- Replace a Line in a file If it is found with ansible lineinfile
- Remove a line from the file if it is found - All the instances
- Insert before a matching line using insertbefore parameter
- Insert after the matching line using insertafter parameter
- validate the changes are correct before saving
Example 1: Validate if a line is present in the file without any modification
This is just to validate if a line is present in the file or not. It will not modify the file irrespective of whatsoever the result is. this is just like running the quick find command
The Example given below is to find whether or not the String "LogLevel debug" is found in the remote apache web server's httpd.conf file.
As mentioned earlier. There would be no action taken whatsoever
In this example we are going to check if the LogLevel is Debug and print the message If it is there or not and take no action. This is being done with the help of checkmode=yes
---
- name: Examples of lineinfile
hosts: web
tasks:
- name: "Example1: Validate if a String or line is present in the file"
become: yes
become_user: root
tags: example1
lineinfile:
path: /etc/httpd/conf/httpd.conf
line: "LogLevel debug"
state: present
backup: yes
check_mode: yes
register: example1out
Though this playbook would report that there is a change made by marking it as changed=1
but this would not do any modification on the file as we ran the task in Check mode.
Here is the Quick ad-hoc command to check what is the actual LogLevel in the remote httpd.conf file
$ ansible web -m shell -a "grep -i LogLevel /etc/httpd/conf/httpd.conf" -i ansible_hosts
mwiweb02 | CHANGED | rc=0 >>
# LogLevel: Control the number of messages logged to the error_log.
LogLevel warn
It is recommended to always have the
backup: yes
parameter in your playbook when you are using the lineinfile. This would make sure the file is backed up before any changes are made. This would help in case if you want to roll back.
Example2: Validate if a String or line is present in the file and add if it does not exist
In the same playbook, we have just seen if we remove the check mode it would be a valid playbook which searches for a line and adds it when there are no matches found
But there is a problem here the line you are mentioning to be added would be added only at the End Of File or Last line. This can be controlled with insert_before and insert_before directives which will be discussed later in this article.
Now the playbook
---
- name: Examples of lineinfile
hosts: web
tasks:
- name: "Example2: Add the line if it does not exist"
become: yes
become_user: root
tags: example2
lineinfile:
path: /etc/httpd/conf/httpd.conf
line: "LogLevel debug"
state: present
backup: yes
register: example2out
The result of this would result in a invalid configuration file as the entry would be added at the End Of File.
$ ansible web -m shell -a "grep -in LogLevel /etc/httpd/conf/httpd.conf"
mwiweb02 | CHANGED | rc=0 >>
185:# LogLevel: Control the number of messages logged to the error_log.
189:LogLevel warn
354:LogLevel debug
Which can be controlled with Insert after and insert before which we will later in this post.
Example3: Replace a line in a file with ansible lineinfile.
In the example2 we have seen how to add a new line with lineinfile module. Now we are going to see how to replace a line when a Certain line is found.
Though you can use the ansible replace module to replace. The Lineinfile module can also be used to replace a line.
Now in this example, we are going to change the LogLevel debug
to LogLevel Debug
with a capital D
We are going to use the Ansible Lineinfile with Regular Expressions
to Search for a line and the line element would contain the line
argument would hold the line to replace with
Look at the following Playbook and you can easily understand it.
---
- name: Examples of lineinfile
hosts: web
tasks:
- name: "Example1: Validate if a String or line is present in the file"
become: yes
become_user: root
tags: example1
lineinfile:
path: /etc/httpd/conf/httpd.conf
# The String to Search
regexp: "LogLevel warn"
# The String to Replace
line: "LogLevel debug"
state: present
backup: yes
register: example1out
The Result of the file would be something like this
$ ansible web -m shell -a "grep -in LogLevel /etc/httpd/conf/httpd.conf" -i ansible_hosts
mwiweb02 | CHANGED | rc=0 >>
185:# LogLevel: Control the number of messages logged to the error_log.
189:LogLevel debug
Would it Replace all the Matching Lines? What if there are More than one Matches
If there are more than one matches in the file. Ansible Lineinfile would replace only the last line matched or found.
If you would like to replace all the occurrences, you must consider using the replace module and not lineinfile.
Example 4: Remove a line from the file, if it is found ( All the Instances )
Now I want to remove the Line LogLevel Debug
which we have added earlier as it is in the wrong place and also we already have theLogLevel warn
present in the same file.
Here is the ansible playbook to remove the Line from the file and we can use some Regular expressions here to find both debug
and Debug
---
- name: Examples of lineinfile
hosts: web
tasks:
- name: "Example1: Validate if a String or line is present in the file"
become: yes
become_user: root
tags: example1
lineinfile:
path: /etc/httpd/conf/httpd.conf
# String to Search
regexp: "LogLevel [Dd]ebug"
# State is set to Absent to remove if the Searching Line is found
state: absent
backup: yes
register: example1out
Here we are setting the state parameter as absent which will remove if the Search is Success. the line
parameter is not used as it is not necessary
As mentioned earlier, this would remove all the matching lines from the file, in other words, all the appearance of a Search Line.
Example5: Insert After a matching line using insertafter parameter
Since we have taken Apache httpd.conf file as our base file in this post, Let us take some requirement we used to do often in Apache, which is adding a new listener
First Let us see what is there in the file already
$ ansible web -m shell -a "grep -in Listen /etc/httpd/conf/httpd.conf" -i ansible_hosts
mwiweb02 | CHANGED | rc=0 >>
34:# Listen: Allows you to bind Apache to specific IP addresses and/or
38:# Change this to Listen on specific IP addresses as shown below to
41:#Listen 12.34.56.78:80
42:Listen 80
here 42 is the line number and you can see that we have only one Listen statement/directive as of now. Now in order to make Apache Listen to 443 we need to add Listen 443
right beneath the Listen 80
Let us see how to Insert after some line using insertafter parameter.
Here is the playbook.
---
- name: Examples of lineinfile
hosts: web
tasks:
- name: "Example1: Validate if a String or line is present in the file"
become: yes
become_user: root
tags: lineinfileexample
lineinfile:
path: /etc/httpd/conf/httpd.conf
insertafter: "^Listen [0-9]+"
line: "Listen 443"
firstmatch: yes
state: present
register: lineinfileexample
Here you can notice that there is no REGEX parameter. we do not need this as the insertafter parameter itself can have the Search String with the Regular Expression Syntax
$ ansible web -m shell -a "grep -in Listen /etc/httpd/conf/httpd.conf" -i ansible_hosts
mwiweb02 | CHANGED | rc=0 >>
34:# Listen: Allows you to bind Apache to specific IP addresses and/or
38:# Change this to Listen on specific IP addresses as shown below to
41:#Listen 12.34.56.78:80
42:Listen 80
43:Listen 443
If you notice the Line numbers in the Output, You can see the Listen 443
has been added right beneath the Listen 80
When there are multiple entries of the searching line is present in the file. The last matched line would be considered.
Example6: Insert Before a matching line using insertbefore parameter
To Test the insert before parameter, Let us take the same httpd.conf file as a base file and this time we are going to update the ServerAdmin
Email ID from the default ServerAdmin root@localhost
to Server Admin [email protected]
Before any modification
aksarav@middlewareinventory:/apps/vagrant/webinfra$ ansible web -m shell -a "grep -in ServerAdmin /etc/httpd/conf/httpd.conf" -i ansible_hosts
mwiweb02 | CHANGED | rc=0 >>
83:# ServerAdmin: Your address, where problems with the server should be
87:ServerAdmin root@localhost
But we need to Disable/Comment the Existing ServerAdmin line and add a new entry, Here is the playbook to do both these tasks.
---
- name: Examples of lineinfile
hosts: web
tasks:
- name: "Example6: Comment the Exisiting ServerAdmin Line"
become: yes
become_user: root
tags: lineinfileexample6_1
lineinfile:
path: /etc/httpd/conf/httpd.conf
regexp: '(^ServerAdmin .*)'
line: '# \1'
backrefs: yes
state: present
register: lineinfileexample6_1
- name: "Example6: Add a New ServerAdmin Before the Commented Line"
become: yes
become_user: root
tags: lineinfileexample6_2
lineinfile:
path: /etc/httpd/conf/httpd.conf
insertbefore: '# ServerAdmin .*'
line: "ServerAdmin [email protected]"
state: present
register: lineinfileexample6_2
The Expected result would be something like follows.
aksarav@middlewareinventory:/apps/vagrant/webinfra$ ansible web -m shell -a "grep -in ServerAdmin /etc/httpd/conf/httpd.conf" -i ansible_hosts
mwiweb02 | CHANGED | rc=0 >>
83:# ServerAdmin: Your address, where problems with the server should be
87:ServerAdmin aksarav@middlewareinventory.com
88:# ServerAdmin root@localhost
You can see the Line Number 88 is commented now, it was the older Server Admin and line number 87 is our new ServerAdmin
Example 7: Validate the Changes Before Saving/Committing
This is a Nice feature of Ansible lineinfile which will let you run a shell command to validate if the modified file is really OK or if there are any issues. Since our base file is a configuration file httpd.conf
it has some syntax and If we miss adhering to it, we will leave the entire website/infra at stake.
So let us validate it before saving the file.
Apache HTTPD server has a command to perform the Syntax check on the httpd.conf file which is httpd -t
When invoked and with no syntax issues. It would print the message Syntax OK like given below and return a Zero Return Code which is all it matters to Ansible. Ansible relies on the Validation Command's Return Code.
[aksarav@mwiweb02 ~]$ httpd -t
Syntax OK
If it is NON-ZERO the Changes would not be committed and the task will fail.
Here is the playbook to Update the ServerName Directive of Apache.
---
- name: Examples of lineinfile
hosts: web
tasks:
- name: "Example7: Update the ServerName"
become: yes
become_user: root
lineinfile:
path: /etc/httpd/conf/httpd.conf
insertafter: '#ServerName www.example.com:80'
line: "ServerName www.middlewareinventory.com:80"
state: present
# Command to Validate the Configuration and %s is a working copy of the file
validate: "httpd -t -f %s"
Here the %s
is the working copy of the actual file. Ansible would always copy the file and keep it as a working copy and make the changes and finally copy it over to the Destined location and replace the original file.
Quick Ad Hoc command to validate
$ ansible web -m shell -a "grep -in ServerName /etc/httpd/conf/httpd.conf" -i ansible_hosts
mwiweb02 | CHANGED | rc=0 >>
92:# ServerName gives the name and port that the server uses to identify itself.
98:#ServerName www.example.com:80
99:ServerName www.middlewareinventory.com:80
You can see the ServerName directive has been added.
We have come to the End of the Article.
Hope it helps
Rate this article [ratings]
Cheers
Sarav AK
Follow me on Linkedin My Profile Follow DevopsJunction onFacebook orTwitter For more practical videos and tutorials. Subscribe to our channel
Signup for Exclusive "Subscriber-only" Content