ansible: lineinfile for several lines?

https://stackoverflow.com/questions/24334115/ansible-lineinfile-for-several-lines

Asked 11 years, 6 months ago Modified 1 year, 10 months ago Viewed 295k times 217

The same way there is a module lineinfile to add one line in a file, is there a way to add several lines?

I do not want to use a template because you have to provide the whole file. I just want to add something to an existing file without necessarily knowing what the file already contains so a template is not an option.

ansible Share Improve this question Follow edited Sep 7, 2018 at 15:56 Baptiste Mille-Mathias’s user avatar Baptiste Mille-Mathias 2,19744 gold badges3131 silver badges3939 bronze badges asked Jun 20, 2014 at 19:10 Michael’s user avatar Michael 8,8782222 gold badges6464 silver badges9494 bronze badges I understand you don’t want to use template, but using lineinfile is an antipattern. It’s also a strong red flag that you “don’t know what is in the file”, which leads to substantial risk of unknown failures. – 300D7309EF17 CommentedJun 20, 2014 at 21:22 58 It’s not an anti-pattern. The point of lineinfile is to support multiple sources managing the same file, which is sometimes unavoidable. Most configuration files have a fixed format and logic to avoid conflicts is usually not too substantial. – Doug F CommentedNov 20, 2014 at 20:26 1 I don’t know what’s in the vast majority of files on my PC; doesn’t mean I want to nuke them all! – DylanYoung CommentedNov 15, 2018 at 16:24 You could also use “\n” – John Ronald CommentedSep 1, 2022 at 9:35 Add a comment 11 Answers Sorted by:

Highest score (default) 297

You can use the lineinfile built-in in a loop. Here’s an example:

  • name: Set some kernel parameters lineinfile: dest: /etc/sysctl.conf regexp: “{{ item.regexp }}” line: “{{ item.line }}” loop:
    • { regexp: ‘^kernel.shmall’, line: ‘kernel.shmall = 2097152’ }
    • { regexp: ‘^kernel.shmmax’, line: ‘kernel.shmmax = 134217728’ }
    • { regexp: ‘^fs.file-max’, line: ‘fs.file-max = 65536’ } Share Improve this answer Follow edited Jun 15, 2021 at 22:10 answered Jun 21, 2014 at 21:09 Ben Whaley’s user avatar Ben Whaley 34.6k88 gold badges9797 silver badges9292 bronze badges Sign up to request clarification or add additional context in comments.

8 Comments

JDS Over a year ago MAKE SURE you have the argument to line= and regexp= in quotes. I did not, and I kept getting msg: this module requires key=value arguments. The example given does have this correct – I just didn’t follow the example.

tdihp Over a year ago May I ask how to do a single backup before the the first change? maybe item.backup? :D

kkurian Over a year ago This was probably voted up before Ansible 2.0. A better answer is now: stackoverflow.com/a/28306576/972128

ndtreviv Over a year ago @kkurian Surely only if you’re inserting, not if you’re replacing?

absurd Over a year ago @kkurian The blockinfile solution will not work if you e.g. need to add some lines to a json file and do not want any markers. While you can set markers to “”, ansible blockinfile will still look for markers, not find any, and insert the block again. Thus, blockinfile without markers is not idempotent, lineinfile with a loop is. Add a comment | Show 3 more comments 258

You can try using blockinfile instead.

You can do something like

  • blockinfile: | dest=/etc/network/interfaces backup=yes content=”iface eth0 inet static address 192.168.0.1 netmask 255.255.255.0” Share Improve this answer Follow edited Oct 21, 2020 at 0:18 Boden Garman’s user avatar Boden Garman 2,56522 gold badges2121 silver badges1818 bronze badges answered Feb 3, 2015 at 18:53 Soichi Hayashi’s user avatar Soichi Hayashi 3,64422 gold badges2424 silver badges1515 bronze badges 9 Comments

Jay Taylor Over a year ago The blockinfile module has worked out wonderfully every time I’ve chosen to use it. I especially love the intuitive behaviour of the insertafter/insertbefore options.

Willem van Ketwich Over a year ago The highest-voted answer was probably before Ansible 2.0, but this is the more correct answer now.

ceving Over a year ago Blockinfile requires markers. This is sometimes no option.

pkaramol Over a year ago Are we able to overwrite content with blockinfile?

xjcl Over a year ago Good link, but really bad example Add a comment | Show 4 more comments 33

Here is a noise-free version of the solution which is to use with_items:

  • name: add lines lineinfile: dest: fruits.txt line: ‘{{ item }}’ with_items:
    • ‘Orange’
    • ‘Apple’
    • ‘Banana’ For each item, if the item exists in fruits.txt no action is taken.

If the item does not exist it will be appended to the end of the file.

Easy-peasy.

Share Improve this answer Follow edited Nov 28, 2017 at 8:22 Joshua Grigonis’s user avatar Joshua Grigonis 77811 gold badge55 silver badges1818 bronze badges answered May 12, 2016 at 4:51 Rick O’Shea’s user avatar Rick O’Shea 1,4591919 silver badges1616 bronze badges 2 Comments

ceving Over a year ago This can not be combined with insertafter.

MUY Belgium Over a year ago If multiple line are missing, i would like the item to appears in an order. How can I be sure of the order in which items are appended? 25

If you need to configure a set of unique property=value lines, I recommend a more concise loop. For example:

  • name: Configure kernel parameters lineinfile: dest: /etc/sysctl.conf regexp: “^{{ item.property | regex_escape() }}=” line: “{{ item.property }}={{ item.value }}” with_items:
    • { property: ‘kernel.shmall’, value: ‘2097152’ }
    • { property: ‘kernel.shmmax’, value: ‘134217728’ }
    • { property: ‘fs.file-max’, value: ‘65536’ } Using a dict as suggested by Alix Axel and adding automatic removing of matching commented out entries,
  • name: Configure IPV4 Forwarding lineinfile: path: /etc/sysctl.conf regexp: “^#? *{{ item.key | regex_escape() }}=” line: “{{ item.key }}={{ item.value }}” with_dict: ‘net.ipv4.ip_forward’: 1 Share Improve this answer Follow edited Jun 18, 2018 at 19:46 answered Feb 14, 2017 at 20:32 Nicholas Sushkin’s user avatar Nicholas Sushkin 14k33 gold badges3737 silver badges2020 bronze badges 1 Comment

Alix Axel Over a year ago If you use with_dict it would be more concise. 5

It’s not ideal, but you’re allowed multiple calls to lineinfile. Using that with insert_after, you can get the result you want:

  • name: Set first line at EOF (1/3) lineinfile: dest=/path/to/file regexp=”^string 1” line=”string 1”
  • name: Set second line after first (2/3) lineinfile: dest=/path/to/file regexp=”^string 2” line=”string 2” insertafter=”^string 1”
  • name: Set third line after second (3/3) lineinfile: dest=/path/to/file regexp=”^string 3” line=”string 3” insertafter=”^string 2” Share Improve this answer Follow answered Jun 20, 2014 at 20:32 Ramon de la Fuente’s user avatar Ramon de la Fuente 8,44333 gold badges3434 silver badges3131 bronze badges 1 Comment

Michael Over a year ago yes but it’s still one line at a time. If I have 15 lines, I would prefer add them with only one command. It does not seem to be possible. 5

I was able to do that by using \n in the line parameter.

It is specially useful if the file can be validated, and adding a single line generates an invalid file.

In my case, I was adding AuthorizedKeysCommand and AuthorizedKeysCommandUser to sshd_config, with the following command:

  • lineinfile: dest=/etc/ssh/sshd_config line=’AuthorizedKeysCommand /etc/ssh/ldap-keys\nAuthorizedKeysCommandUser nobody’ validate=’/usr/sbin/sshd -T -f %s’ Adding only one of the options generates a file that fails validation.

Share Improve this answer Follow answered Aug 18, 2014 at 22:51 Penz’s user avatar Penz 5,70855 gold badges3434 silver badges3232 bronze badges 3 Comments

David Over a year ago This will create the line an additional time each time the playbook is run–it doesn’t correctly recognise that the line already exists. At least, that’s the case for me on Ansible 1.7.1

ceving Over a year ago I reported a bug, but the Ansible guys have no interest to fix it.

Penz Over a year ago There is a new blockinfile module that should be better than that solution now. (docs.ansible.com/ansible/blockinfile_module.html) Add a comment 2

To add multiple lines you can use blockfile:

  • name: Add mappings to /etc/hosts blockinfile: path: /etc/hosts block: | ‘10.10.10.10 server.example.com’ ‘10.10.10.11 server1.example.com’ to Add one line you can use lininfile:

  • name: server.example.com in /etc/hosts lineinfile: path: /etc/hosts line: ‘192.0.2.42 server.example.com server’ state: present Share Improve this answer Follow answered Oct 23, 2019 at 0:19 Ahmed Taha’s user avatar Ahmed Taha 2111 bronze badge Comments

2

To add multiple lines you can use lineinfile module with with_items also including variable vars here to make it simple :)


  • hosts: localhost #change Host group as par inventory gather_facts: no become: yes vars: test_server: “10.168.1.1” test_server_name: “test-server” file_dest: “/etc/test/test_agentd.conf”

    • name: configuring test.conf lineinfile: dest: “{{ item.dest }}” regexp: “{{ item.regexp }}” line: “{{ item.line }}” with_items:
      • { dest: ‘”{{ file_dest }}”’, regexp: ‘Server=’, line: ‘Server=”{{test_server}}”’ }
      • { dest: ‘”{{ file_dest }}”’, regexp: ‘ServerActive=’, line: ‘ServerActive=”{{test_server}}”’ }
      • { dest: ‘”{{ file_dest }}”’, regexp: ‘Hostname=’, line: ‘Hostname=”{{test_server_name}}”’ } Share Improve this answer Follow answered Jan 30, 2020 at 11:20 mail2sandeepd’s user avatar mail2sandeepd 2111 bronze badge Comments

1

To add multiple Lines in a configuration file you can use “ “ instead of ‘ ‘ and escape sequence \n for the new line in lineinfile ansible module:

  • name: Configure core-site.xml lineinfile: path: /etc/hadoop/core-site.xml insertafter: ‘^' line: "Line 1 \n Line 2 \n Line 3" Share Improve this answer Follow edited Dec 22, 2020 at 8:01 MattAllegro's user avatar MattAllegro 7,65355 gold badges5050 silver badges5959 bronze badges answered Dec 22, 2020 at 7:38 Kethavath Siva Naik's user avatar Kethavath Siva Naik 5722 bronze badges Comments

1

You might consider using ansible.builtin.replace if you want to replace multiple lines which match one regex.

Share Improve this answer Follow answered Mar 4, 2024 at 12:33 Zaphrim’s user avatar Zaphrim 1111 bronze badge Comments

0

The solution that fit my use-case (network automation):

  • name: “create block in own file per host” blockinfile: path: “/dir/logs/{{ inventory_hostname }}.txt” create: true block: “some commands” …

  • name: “add more line in block (even if already executed on another host)” lineinfile: line: “{{ item }}” … loop: “{{ more_commands_1 | default([]) + more_commands_2 | default([]) }}”

  • name: “assemble all files in one” assemble: src: “/dir/logs/” dest: “/dir/logs/all_hosts.txt” … note: I used those modules with “check_mode: false” and “delegate_to: localhost”

Would be glad to use a more clever solution if it exists.

Share Improve this answer Follow edited Jan 21, 2022 at 16:23 answered Jan 21, 2022 at 16:13 tintin’s user avatar tintin 113

Updated: