Ansible replace line in file - Ansible Replace Examples | Devops Junction

This article is about "how to replace a line in file using ansible and seeing various other examples of ansible replace module".  Ansible facilitates us with a dedicated module named as replace The working principle of this module is more like find and replace in your favourite editor and it also supports regular expressions.

There is one major difference between Ansible replace and Line in file.

The Ansible REPLACE module will replace all the instances of a matching string in a file, while Line in file module replaces a single instance,

If you are looking for changing a single instance of your match string consider using lineinfile module. Refer this  Ansible Lineinfile examples for more information

Shortly put, Ansible REPLACE is to globally replace all the matching lines in a file with the String you have provided, on other hand LINEINFILE is to replace a matching string in a Single line Mostly First or Last appearance

In this post, we are going to see various examples of ansible replace module framed as playbooks and we have also given the execution outputs of the same.

 

Ansible Replace module - A Quick Summary

Ansible Replace module

  • Used to replace ALL instances of a matching String in a file ( Can be used to modify a Single Instance if you are sure that the destination file have only one matching string)
  • More like Find All and Replace All
  • Backup Option is available Before modifying the file
  • Support Regular Expressions to Find a String
  • Supports replacing before and after a Matching String/line
  • Supports Replacing between two matching Strings or lines or two expressions using before and after at the same time.
  • Back References (Backrefs) are supported
  • Fail if the file is not available 
  • Support for Validating the changes

 

Ansible replace in Action

In this chapter, we are going to take a quick overview of replace module and how it works and how it can be used for your requirements, it can be considered as more of an elevator pitch. In this we will cover the basic but indispensable key elements of ansible replace module

You can find more examples of ansible replace in the next chapter.

For now. Lets march on.

A quick introduction to Regular Expressions and Why it is necessary

If you are any of Dev or Ops person. I bet you might have come across these term called Regular expressions.

as per Wikipedia Regular Expression is

regular expressionregex or regexp (sometimes called a rational expressionis a sequence of characters that define a search pattern. Usually this pattern is used by string searching algorithms for "find" or "find and replace" operations on strings, or for input validation. It is a technique that developed in theoretical computer science and formal language theory.

Whenever you want to Search something programmatically, There is 90% of chances that you might need a Simple to Complex regular expressions.

Some of the Simple Regular Expressions and their purposes are given below.

click to expand
Regular Expression Test String Match/No Match Explanation
^ServerName ServerName www.example.com Match represents the start of a line and ServerName is a literal String to search
^ServerName$ ServerName www.example.com No Match Here represents end of a line. As per the regex, the line should immediately end of a String ServerName which is not true in Test String.
^ServerName.* ServerName www.example.com Match Here .* is the newly added expression, The . (period/dot) matches any character/number and matches Zero or more of the preceding expression. Means anything after ServerName ( but in the same line)

The reason why we are insisting on the regular expression is. A Good Regular Expression can get you the String what you are searching for.  It is a must that we know HOW TO SEARCH before searching for something, It is true if we are searching for  A word or a string or a line or something in life 🙂

Regular Expression is the tool when ansible replace is your workshop.

Now the question is where will go and learn and test regular expressions. Is there any better way than wandering around and failing multiple times to get a perfect regex expression.

yes, there is.

Write and Test your Regular Expressions on the Go

Meet REGEX 101 A website to write and test a regular expression like a PRO but at free of cost

Regex 101 have been my life saviour for years now. If you are not already aware, go ahead and try it now. You would blame yourself for not knowing this so far.

A Simple Screenshot that explains all.   You can see everything,  what you are looking for.

Back to the track.

 

Sample Playbook with replace module to replace a line in file

This is the playbook we are going to use and add more features and changes as we proceed.  This is to change the domain name of the Apache Virtual host from old domain to a new domain.

---
  - name: Replace line in file examples
    hosts: web
    tasks:
    - name: "Replace line in file examples"
      become: yes
      become_user: root
      replace: 
        path: /etc/httpd/conf/httpd.conf
        regexp: '(^ServerName\s)(.*)$'
        replace: '\1www.newdomain.com'
        backup: yes

 

Explanation of playbook

path:   A file name in which we are going to find and replace

regexp: the regular expression to search the string you want

In our case,

() - Grouping of Regular Expression and to create backreferences, which will make it easy to perform backreference while replacing.
^ - Matches the Start of a Line
ServerName - Matches a Literal String
\s - Matches a whitespace
.* - matches one or more of any character/numeral ( not new line)
$ - matches the end of line

 

replace:  A string, with which you want to replace the matched String

In our case,

\1 - backreference indicator and represents the first group we have matched. in our case, it is the result of  (^ServerName\s)

backup:  To tell ansible to backup the file before making any changes.

 

Validating the Changes before committing

Replace module bundled with a cool feature with that you can actually validate the changes before committing the changes onto the file.

Consider the same example we have given above which is changing the domain name.  Let us see how can we validate it before making the changes in to /etc/httpd/conf file.

If you are not aware, Ansible would not directly work with the destination file. It will clone it and keep the file in ansible_temp directory once the changes are completed it will copy the file over and replace the destination file with this working copy.

With that said, Now you know, how it is possible to validate your changes before it actually goes into your destination file.

This is the same playbook with validate parameter added

---
  - name: Replace line in file examples
    hosts: web
    tasks:
    - name: "Replace line in file apache conf"
      become: yes
      become_user: root
      replace: 
        path: /etc/httpd/conf/httpd.conf
        regexp: '(^ServerName\s)(.*)$'
        replace: '\1www.newdomain.com'
        backup: yes 
        validate: apachectl -f %s -t

Here %s represents the temp or working copy of the destination file
Let us make some mistake intentionally to examine how this validate is working. In order to do that, I am changing a Single line from the preceding playbook

# Final String is "ServerName www.newdomain.com" 
replace: '\1www.newdomain.com' 
to
# Final String is "www.newdomain.com"
replace: 'www.newdomain.com'  

In the replace field, we are taking the reference indicator out. which will remove the String ServerName from the config file and ansible is supposed to throw an error and the playbook will fail if the validate command is returning a NON ZERO return code.

$ ansible-playbook AnsibleWorkSpace/replace-ex1.yml -i ansible_hosts

PLAY [Replace line in file examples] ********************************************************************************************************

TASK [Gathering Facts] **********************************************************************************************************************
ok: [mwiweb02]

TASK [Replace line in file apache conf] ********************************************************************************************************
fatal: [mwiweb02]: FAILED! => {"changed": false, "msg": "failed to validate: 
rc:1 error:AH00526: Syntax error on line 95 of 
/home/vagrant/.ansible/tmp/ansible-tmp-1549278281.07-220158736541166/tmp6LJx9x:\n
Invalid command 'www.newdomain.com', perhaps misspelled or defined by a module not included 
in the server configuration\n"}
        to retry, use: – limit @/apps/vagrant/webinfra/AnsibleWorkSpace/replace-ex1.retry

PLAY RECAP **********************************************************************************************************************************
mwiweb02                   : ok=1    changed=0    unreachable=0    failed=1

You can see the temp file name with the validation failure note /home/vagrant/.ansible/tmp/ansible-tmp-1549278281.07-220158736541166/tmp6LJx9x 

 

Ansible Replace Examples

  1. Replace all the Instances of a Matching String in a file
  2. Replacing string AFTER the Matching Expression till the End Of File
  3. Start Replacing BEFORE the Matching Expression up to the Beginning of File
  4. Replacing the String Between matching Expressions (or) Using BEFORE and AFTER together in ansible replace

 

Setting the Context

Before going to the examples,  we need some common file which we can use for all the upcoming examples. So I  have taken the famous FOX and GRAPE story and placed it as a web page in default doc root/var/www/html of apache web server.

Now here is how it looks like at the browser

The Html page - Click to expand
<html>
<head>
<title>TheStoryPage</title>
</head>
<body style="
    font-family: serif;
    font-size: 26px;
">

<h2> The Fox and Grape Story </h2>
<p style="
    text-indent: 60px;
    letter-spacing: 1.5px;
    font-size: 26px;
">

A fox was once very hungry and went in search of some food. He searched everywhere, but he couldn’t find anything that he could eat. Finally, with his stomach rumbling, he came upon a farmer’s wall. On top of the wall were the biggest, juiciest grapes the fox had ever seen. The rich purple color told the fox that they were ready to be eaten. The fox jumped high in the air to catch the grapes in his mouth, but he missed. He tried again and missed again. He tried a few more times but missed each time. Finally, the fox decided to go home all the while muttering, ‘I’m sure the grapes were sour anyway’.
</p>


</body>
</html>

As we have set the plot lets begin the play.

Replace all the Instances of a Matching String in a file

we are going to take the same FOX and GRAPE Story html page for this example and let us replace all the instances of the word "fox" with "raccoon"

Come, let us create a new Story. The Racoon and Grape !.

Let us see how to do the same with ansible replace module.  If you are following along, you know that I am using the apache web server and the story is inside the html page at /var/www/html/index.html

 

The Playbook

In the following playbook, we have used the Handlers to restart the apache upon the replace task completion. It is more like a function.

You can relate it like this, When Handlers is a function definition, notify is a function invocation or function call.

---
  - name: Replace line in file examples
    hosts: web

    handlers:
      - name: restart apache
        become: yes
        become_user: root
        service: name=httpd state=restarted

    tasks:
    - name: "Replace line in file examples"
      become: yes
      become_user: root
      replace: 
        path: /var/www/html/index.html
        regexp: '[F|f]ox'
        replace: 'racoon'
        backup: yes 
      notify: restart apache

In the regexp section, we have used [F|f]ox which is to match both Fox and the fox. The usual case sensitive stuff.

 

The Execution Output

 

The HTML Page result after the Replacements

From the preceding Snapshot of the web page. you can understand how the ansible replace module is working. It finds all and replaces them all, as simple as that.

Before proceeding to the next example. Let me roll back my changes and make the racoon story as a Fox Story. A Quick Reset. I can do it either with replacing module as well (racoon to fox) or in the host, I can take the backed up file and copy it over the current.

Replacing string AFTER the Matching Expression till the End Of File

We need a new plan or a requirement, As this is a little tricky but the most usual requirement.

We are going to rewrite the story once again and going to name the fox as fofo.  The following Snapshot could help you understand the same, I hope.

As you can see we are going to do two things to get the expected story at the bottom

  1.  Adding a new Hero introduction lines at the start of story (Highlighted in Blue background)
  2. Replace all the presence of fox with fofo
  3. While doing this, we have to leave the title untouched. It should remain having a fox not fofo.
  4. Should replace "The fofo" with "fofo" so it sounds grammatically correct.

Complex,  is not it? Not if we split it into small tasks, after all thats what ansible suggests us to do.

Here is the Playbook to accomplish the same

---
  - name: Replace line in file examples
    hosts: web

    handlers:
      - name: restart apache
        become: yes
        become_user: root
        service: name=httpd state=restarted

    tasks:

    - name: "Replacing all fox with fofo - Leaving the title untouched"
      become: yes
      become_user: root
      replace: 
        path: /var/www/html/index.html
        regexp: 'fox'
        replace: 'fofo'
        after: "The fox and Grape Story"
        backup: yes 
    
    - name: "Naming our Hero and introduction line"
      become: yes
      become_user: root
      replace: 
        path: /var/www/html/index.html
        regexp: 'A fofo was once'
        replace: 'Once upon a time, There was a fox living in a forest named "fofo". One day fofo was'
        backup: yes    
    
    - name: "Grammatical correction - The fofo to fofo"
      become: yes
      become_user: root
      replace: 
        path: /var/www/html/index.html
        regexp: '[T|t]he fofo'
        replace: 'fofo'
        backup: yes    
      notify: restart apache

In the preceding playbook, You can see we have left the title unchanged with the help of after parameter in the replace module ( highlighted in yellow font colour)

One more reset back to the Normal Fox and Grape Story before the further example.

Start Replacing BEFORE the Matching Expression up to the Beginning of File

For this example also we need a new requirement and here is what I have come up with.

We are going to change the Headline of the Story and add some color formatting to it. The following diagram would help you understand our requirement

We are basically going to replace the <h2> tag and add some <span> elements to it to change the color of the words Fox and Grape

But our Search String remains just the word Fox and Grape

Here is the playbook to accomplish the same and we are going to use before in this example ( Just to change the color of the words Fox and Grape only in the headline, Not in the Story)

 

The Playbook

---
  - name: Replace line in file examples
    hosts: web

    handlers:
      - name: restart apache
        become: yes
        become_user: root
        service: name=httpd state=restarted

    tasks:

    - name: "Replacing the Fox with Formatting only in headline before closing of H2"
      become: yes
      become_user: root
      replace: 
        path: /var/www/html/index.html
        regexp: '[F|f]ox'
        replace: '<span style="color:crimson">Fox</span>'
        before: "</h2>"
        backup: yes

    - name: "Replacing the Grape with Formatting only in headline before closing of H2"
      become: yes
      become_user: root
      replace: 
        path: /var/www/html/index.html
        regexp: '[G|g]rape'
        replace: '<span style="color:purple"> Grape </span>'
        before: "</h2>"
        backup: yes   
      notify: restart apache

You can see the before parameter is having the closing element of the H2 tag. In this way we are Restricting the change to happen only in the headline.

Anyways for our next example, we are going to need all of the Fox and Grape be replaced with the color formatting.

To do this, You just have to remove the before parameter from the Playbook. It will replace all the instance across the HTML page. I will let you do it, so you will get some exposure. Your end result should be like the following.

In the preceding Snapshot, you can see all the instances of the word Fox and Grape has been replaced with Color formatting

 

Replacing the String Between matching Expressions (or) Using BEFORE and AFTER together in ansible replace

As the title of this example states, we are going to use Before and After Together in the same replace task to achieve replacing between two matching expressions.

Let us set some requirement first, The Requirement for this example is to underline the word Fox only in the Story but not in the Headline.

So we are going to set the opening of <p> tag as our start line(after) and the closing tag of </p>(before) as our end line

The preceding illustration could help you understand it even better

 

The Playbook

---
  - name: Replace line in file examples
    hosts: web

    handlers:
      - name: restart apache
        become: yes
        become_user: root
        service: name=httpd state=restarted

    tasks:
    - name: "Replacing the Fox in the Story with Underline formatting"
      become: yes
      become_user: root
      replace: 
        path: /var/www/html/index.html
        regexp: '[F|f]ox'
        replace: '<u>Fox</u>'
        # Matches the Start of P tag
        after: '<p\s[.|\w|=|\"|\r|\n|\s|\-|:|;]*>'
        # Matches the End of P tag
        before: '</p>'
        backup: yes  
      notify: restart apache

 

Hope you would find this article helpful.  Feel free to contact me for help and support over the comments section.

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

Buy Me a Coffee at ko-fi.com

Signup for Exclusive "Subscriber-only" Content

Loading