Ansible find replace is not working for 2 lines - ansible

- name: Cassandra cassandra.yaml
lineinfile:
dest: "{{ home}}/conf/cassandra.yaml"
state: present
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
# backup: yes
with_items:
- { regexp: "data_file_directories:", line: "data_file_directories: \n - {{ data_directory }}" }
in the first file first run it looks good but in the second run it keeps adding new line. what needs to be done in order to the second run also looks good.
data_file_directories:
- /mount1/data
second run
data_file_directories:
- /mount1/data
- /mount1/data
- /mount1/data
I want some thing like
data_file_directories:
- /mount1/data

lineinfile ― as its name suggest ― is a module to ensure a certain line is in the target file; not lines.
To deal with multiple lines either:
use regexp module with matchgroup and backreference ― the exact implementation is dependent on other constructs in the target file,
use blockinfile module ― the exact implementation is dependent on other constructs in the target file,
or (preferably) use template module.

Related

How can I put the discovered values into loop variables so that they are on one line

How can I put the discovered values into loop variables so that they are on one line using Ansible task? I have now task like this
- name: Updating test.conf
lineinfile:
path: "/root/test.conf"
regexp: "test="
line: "test={{ hostvars[item]['ansible_env'].SSH_CONNECTION.split(' ')[2] }}"
state: present
with_nested:
- "{{groups['app']}}"
It needs that when invoking the job, it takes the IP addresses from the servers that are in the app group and puts them on a single line. Currently, it performs such a substitution twice with which they are replaced and finally there is only one address in the test parameter.
I need format after task like this:
test=1.1.1.1, 2.2.2.2
While jinja2 is heavily inspired from python, its does not allow you to do all the same operations. To join a list your would have to do something like:
- debug:
msg: "{{ myvar | join(',') }}"
vars:
myvar:
- foo
- bar
When in doubt, always use a simple playwook with a debug task to validate your jinja code.

Ansible add a line in file

I am trying to add a new line in a file through ansible lineinfile module. Below is the task defined in the playbook.
lineinfile:
path: /etc/httpd/file.conf
line: myfilecontent
It works fine and add a new line in the file. But when i modify the line content to some another value i.e mynewfilecontent it adds another line instead of updating it.
lineinfile:
path: /etc/httpd/file.conf
line: mynewfilecontent
Help is appreciated.
Thanks
Use the state parameter of lineinfile module and create the structure below
my_lines:
- line: myfilecontent
state: absent
- line: mynewfilecontent
state: present
The state parameter controls if the line is present, or absent in the file. See the example below
- hosts: localhost
vars:
my_lines:
- line: myfilecontent
state: absent
- line: mynewfilecontent
state: present
tasks:
- lineinfile:
path: /tmp/test.conf
create: yes
line: "{{ item.line }}"
state: "{{ item.state }}"
loop: "{{ my_lines }}"
Note 1.
The structure can be simplified by adding the state parameter only if the line is absent
my_lines:
- line: myfilecontent
state: absent
- line: mynewfilecontent
Declare default state present in the loop
state: "{{ item.state|default('present') }}"
Note 2.
The default state, defined in the module, is present, hence the state parameter may be omitted if not present in the data structure
state: "{{ item.state|default(omit) }}"
All 3 variations above are functionally equivalent.
If you want your line to potentially replace an existing line in the file then you need to provide a regexp argument to the lineinfile module.
The regular expression to look for in every line of the file.
For state=present, the pattern to replace if found. Only the last line found will be replaced.
For state=absent, the pattern of the line(s) to remove.
If the regular expression is not matched, the line will be added to the file in keeping with insertbefore or insertafter settings.
When modifying a line the regexp should typically match both the initial state of the line as well as its state after replacement by line to ensure idempotence.
The last line which matches your regexp will be replaced with your line value; if there is no match then a new line will be added.
Without a regexp argument the module will just check for an exact match to your line value.
See https://docs.ansible.com/ansible/latest/modules/lineinfile_module.html.

Ansible - only last with_items being updated in file

Using blockinfile: which is using with_items from an exernal file. When I run the playbook I can see all itmes being processed but the resulting end file only has the last item updated.
Apologies, bit of a noob to this, so might be missing something obvious.
Tried various permutations
I have an external yaml config file with following contents - which is included as include_vars:
yaml props file:
ds_props:
- prop: dataSource.initialSize=8
- prop: dataSource.maxActive=50
- prop: dataSource.maxIdle=20
- prop: dataSource.minIdle=5
- prop: dataSource.maxWait=1000
Ansible tasks:
- name: update DS settings
blockinfile:
path: /app.properties
insertafter: "##### Data Source Properties"
block: |
"{{ item.prop }}"
with_items: "{{ ds_props }}"
Expected:
##### Data Source Properties #####
# BEGIN ANSIBLE MANAGED BLOCK
dataSource.initialSize=8
dataSource.maxActive=50
dataSource.maxIdle=20
dataSource.minIdle=5
dataSource.maxWait=1000
# END ANSIBLE MANAGED BLOCK
Actual:
##### Data Source Properties #####
# BEGIN ANSIBLE MANAGED BLOCK
dataSource.maxWait=1000
# END ANSIBLE MANAGED BLOCK
blockinfile uses a marker to keep track of the blocks it manages in a file. By default, this marker is ANSIBLE MANAGED BLOCK.
What happens in your case since you use the default marker is that a block is created after the line "##### Data Source Properties" for the first item then edited for the next items.
One solution would be to change the marker for each item. An other is to use lineinfile as reported by #Larsk
I would prefer in this case creating the full block at once:
- name: update DS settings
blockinfile:
path: /app.properties
insertafter: "##### Data Source Properties"
marker: "Custom ds props - ansible managed"
block: "{{ ds_props | json_query('[].prop') | join('\n') }}"
If your intent is to do more complicated stuff with your config file, follow #Larsk advice and use a template.
blockinfile is behaving exactly as designed: it adds a block of text to a target file, and will remove the matching block before adding a modified version. So for every iteration of your loop, blockinfile is removing the block added by the previous iteration and adding a new one.
Given that you're adding single lines to the file, rather than a block, you're probably better off using the lineinfile module, as in:
---
- hosts: localhost
gather_facts: false
vars:
ds_props:
- prop: dataSource.initialSize=8
- prop: dataSource.maxActive=50
- prop: dataSource.maxIdle=20
- prop: dataSource.minIdle=5
- prop: dataSource.maxWait=1000
tasks:
- name: update DS settings using lineinfile
lineinfile:
path: /app.properties-line
line: "{{ item.prop }}"
insertafter: "##### Data Source Properties"
with_items: "{{ ds_props }}"
While this works, it's still problematic: if you change the value of one of your properties, you'll end up with multiple entries in the file. E.g, if we were to change dataSource.maxWait from 1000 to 2000, we would end up with:
dataSource.maxWait=1000
dataSource.maxWait=2000
We can protect against that using the regexp option to the lineinfile module, like this:
- name: update DS settings using lineinfile
lineinfile:
path: /app.properties-line
line: "{{ item.prop }}"
insertafter: "##### Data Source Properties"
regexp: "{{ item.prop.split('=')[0] }}"
with_items: "{{ ds_props }}"
This will cause the module to remove any existing lines for the particular property before adding the new one.
Incidentally, you might want to consider slightly restructuring your data, using a dictionary rather than a list of "key=value" strings, like this:
---
- hosts: localhost
gather_facts: false
vars:
ds_props:
dataSource.initialSize: 8
dataSource.maxActive: 50
dataSource.maxIdle: 20
dataSource.minIdle: 5
dataSource.maxWait: 1000
tasks:
- name: update DS settings using lineinfile
lineinfile:
path: /app.properties-line
line: "{{ item.key }}={{ item.value }}"
insertafter: "##### Data Source Properties"
regexp: "{{ item.key }}"
with_items: "{{ ds_props|dict2items }}"
And lastly, rather than using lineinfile or blockinfile, you might want to consider using ansible's template module to create your /app.properties file rather than trying to edit it.

Ansible Lineinfile insert after syntax issues

Having some issues with my code and can't figure out the syntax error.
I'm trying to take output from a firewall and put it into an html file but i keep getting stuck. The command in its own separate file outputs a couple of lines of text. Note destination changed to protect it.
- hosts: firewall
gather_facts: no
tasks:
- name: Hardware Info
raw: show asset all
register: output
- local_action: lineinfile dest=dest destination regexp="{{item}}" insertafter="test" line={{item}}
with_items: output.stdout_lines
Error:
local_action: lineinfile dest=dest insertafter="test"
line='{{item}}'
^ here We could be wrong, but this one looks like it might be an issue with missing quotes. Always quote template expression brackets
when they start a value. For instance:
with_items:
{{ foo }}
Should be written as:
with_items:
- "{{ foo }}"
Change this line:
- local_action: lineinfile dest=dest destination regexp="{{item}}" insertafter="test" line={{item}}
To this:
- local_action: lineinfile dest=dest destination regexp="{{item}}" insertafter="test" line="{{item}}"
The extra quotes should help.
Note lineinfile is terrible from a configuration management point of view, I predict you'll pull your hair out in the future.

Ansible escaping a string issue on mac vs ubuntu?

With the following code:
- name: Add the gii config for main.php on staging
blockinfile:
dest: "{{ www_path }}/protected/config/main.php"
marker: "//Gii"
insertafter: "\'modules\'=>array\\("
block: "{{ gii_content }}"
I'm getting a syntax error on OSX:
ERROR! Syntax Error while loading YAML.
The offending line appears to be:
marker: "//Gii"
insertafter: "\'modules\'=>array\\("
^ here
Yet on ubuntu, no such issue and the playbook runs seamlessly. Any ideas?
If you run this through a YAML parser it will tell you it discovered an unknown escape character, so OS X is right there. What is the purpose of \'? If the idea was to match that string and the backslash appears in the file like this, you should put two backslashes there:
- name: Add the gii config for main.php on staging
blockinfile:
dest: "{{ www_path }}/protected/config/main.php"
marker: "//Gii"
insertafter: "\\'modules\\'=>array\\("
block: "{{ gii_content }}"
If there are two backslashes right after array you would need to have 4 of them there, just for escaping. Though since insertafter takes a regular expression and ( has a special meaning in regular expressions, it might be necessary to actually put 6 of them there.
- name: Add the gii config for main.php on staging
blockinfile:
dest: "{{ www_path }}/protected/config/main.php"
marker: "//Gii"
insertafter: "\\'modules\\'=>array\\\\\\("
block: "{{ gii_content }}"

Resources