Ansible: Lineinfile with items in front of the other in same line - ansible

I want to know if it's possible to insert two or more items in the same line with module lineinfile or other modules.
Like that example:
- name: Insert content in the File
lineinfile:
dest: /tmp/file.txt
line: 'Groups = {{ item }}'
create: yes
with_items:
- "{{ group_ad }}"
group_ad:
- LINUX
- DBA
In the file stay that after executing the playbook:
Groups = LINUX
Groups = DBA
But I want to stay like below:
Groups = LINUX, DBA
Who I make that?

You're iterating over the list, but you want to add the list. So it's easier to use join here
- name: Insert content in the File
lineinfile:
dest: /tmp/file.txt
line: 'Groups = {{ group_ad | join(', ') }}'
create: yes

Related

Ansible: Sort output

I've got an Ansible playbook that pulls interface descriptions from two routers and writes the results to a CSV file. When it iterates through the interfaces it writes one interface per router to the file
---
- name: Cisco get ints
hosts: test
gather_facts: false
connection: local
become: false
vars:
csv_path: /tmp
csv_filename: int_audit.csv
headers: Hostname,Interface,Description
tasks:
- name: Save CSV headers
ansible.builtin.lineinfile:
dest: "{{ csv_path }}/{{ csv_filename }}"
line: "{{ headers }}"
create: true
state: present
delegate_to: localhost
run_once: true
- name: run show inventory on remote device
iosxr_facts:
gather_subset: interfaces
register: output
- name: Write int desc to csv file
loop: "{{ output.ansible_facts.ansible_net_interfaces | dict2items }}"
lineinfile:
dest: "{{ csv_path }}/{{ csv_filename }}"
line: "{{ output.ansible_facts.ansible_net_hostname }},{{ item.key }},{{ item.value.description }}"
create: true
state: present
delegate_to: localhost
so I end up with a list that has no order.
$ cat /tmp/int_audit.csv
Hostname,Interface,Description
RTR1.LAB1,BVI13,LOCAL:RTR2.LAB1:[L3]
RTR1.LAB1,Bundle-Ether1100.128,LOCAL:RTR2.LAB1:BUNDLE1100:20GE[UTIL]
RTR2.LAB1,Bundle-Ether1100.128,LOCAL:RTR1.LAB1:BUNDLE1100:20GE[UTIL]
RTR1.LAB1,Loopback0,LOOP:LOOP0-RTR1.LAB1:[N/A]
RTR2.LAB1,Loopback0,LOOP:LOOP0-RTR2.LAB1:[N\A]
I'd like to have it sort the list by router name.
Any help is appreciated.
You could in example achieve you goal by simply post-processing the file on the Control Node.
For the test file
cat test.csv
Hostname,Interface,Description
RTR1.LAB1,BVI13,LOCAL:RTR2.LAB1:[L3]
RTR1.LAB1,Bundle-Ether1100.128,LOCAL:RTR2.LAB1:BUNDLE1100:20GE[UTIL]
RTR2.LAB1,Bundle-Ether1100.128,LOCAL:RTR1.LAB1:BUNDLE1100:20GE[UTIL]
RTR1.LAB1,Loopback0,LOOP:LOOP0-RTR1.LAB1:[N/A]
RTR2.LAB1,Loopback0,LOOP:LOOP0-RTR2.LAB1:[N\A]
the sort command will result into
sort -k1 -n -t, test.csv
Hostname,Interface,Description
RTR1.LAB1,Bundle-Ether1100.128,LOCAL:RTR2.LAB1:BUNDLE1100:20GE[UTIL]
RTR1.LAB1,BVI13,LOCAL:RTR2.LAB1:[L3]
RTR1.LAB1,Loopback0,LOOP:LOOP0-RTR1.LAB1:[N/A]
RTR2.LAB1,Bundle-Ether1100.128,LOCAL:RTR1.LAB1:BUNDLE1100:20GE[UTIL]
RTR2.LAB1,Loopback0,LOOP:LOOP0-RTR2.LAB1:[N\A]
Similar Q&A
Sort CSV file based on first column
How to sort CSV by specific column
and more
Sort CSV file by multiple columns using the sort command
Thanks all, I've written a perl script (which I call in ansible) to do the sort after it's stored in the csv file

ansible lineinfile how add multiple lines with multiple dest?

I have many lines need to add, like
today
is
a
good
day
if just one dest, will be
- name: add line
lineinfile:
dest: "/tmp/aaa.txt"
line: "{{ item }}"
with_items:
- "toady"
- "is"
- "a"
- "good"
- "day"
and then, also have many files need to add, like
aaa.txt
bbb.txt
ccc.txt
if just one line, will be
- name: add line
lineinfile:
dest: "{{ item }}"
line: "today"
with_items:
- "/tmp/aaa.txt"
- "/tmp/bbb.txt"
- "/tmp/ccc.txt"
Now I need mix them, both have all dest and all line, but I can't try it success.
Both of them are an array or object, I try many method still fail.
Helppppppp please :(
Thanks everyone
Althought this looks a bit weird to me and that I think you should probably consider using blockinfile or even better a template, there is a solution to your exact question. One possibility here with the product filter:
- name: Add several lines to several files
vars:
lines:
- today
- is
- a
- good
- day
files:
- a.txt
- b.txt
- c.txt
lineinfile:
line: "{{ item.0 }}"
dest: "{{ item.1 }}"
loop: "{{ lines | product(files) }}"
Use nested. For example
- hosts: localhost
vars:
files: [aaa.txt, bbb.txt, ccc.txt]
lines: [today, good, day]
tasks:
- lineinfile:
create: true
dest: "/tmp/{{ item.0 }}"
line: "{{ item.1 }}"
with_nested:
- "{{ files }}"
- "{{ lines }}"
gives
shell> cat /tmp/aaa.txt
today
good
day
shell> cat /tmp/bbb.txt
today
good
day
shell> cat /tmp/ccc.txt
today
good
day

Use Ansible to ensure a file exists, ignoring any extra lines

I'm trying to update the sssd.conf file on about 200 servers with a standardized configuration file, however, there is one possible exception to the standard. Most servers will have a config that looks like this:
[domain/domainname.local]
id_provider = ad
access_provider = simple
simple_allow_groups = unixsystemsadmins, datacenteradmins, sysengineeringadmins, webgroup
default_shell = /bin/bash
fallback_homedir = /export/home/%u
debug_level = 0
ldap_id_mapping = false
case_sensitive = false
cache_credentials = true
dyndns_update = true
dyndns_refresh_interval = 43200
dyndns_update_ptr = true
dyndns_ttl = 3600
ad_use_ldaps = True
[sssd]
services = nss, pam
config_file_version = 2
domains = domainname.local
[nss]
[pam]
However, on some servers, there's an additional line after simple_allow_groups called simple_allow_users, and each server that has this line has it configured for specific users to be allowed to connect without being a member of an LDAP group.
My objective is to replace the sssd.conf file on all servers, but not to remove this simple_allow_users line, if it exists. I looked into lineinfile and blockinfile, but neither of these seems to really handle this exception. I'm thinking I'm going to have to check the file for the existance of the line, store it to a variable, push the new file, and then add the line back, using the variable afterwards, but I'm not entirely sure if this is the best way to handle it. Any suggestions on the best way to accomplish what I'm looking to do?
Thanks!
I would do the following
See if the simple_allow_users exists in the current sssd.conf file
Change your model configuration to add the current value of the line simple_allow_users is exists
overwrite the sssd.conf file with the new content
You can use jinja2 conditional to achieve step 2 https://jinja2docs.readthedocs.io/
I beleive the above tasks will solve what you need, just remember to test on a simngle host and backup the original file just for good measure ;-)
- shell:
grep 'simple_allow_users' {{ sssd_conf_path }}
vars:
sssd_conf_path: /etc/sssd.conf
register: grep_result
- set_fact:
configuration_template: |
[domain/domainname.local]
id_provider = ad
access_provider = simple
simple_allow_groups = unixsystemsadmins, datacenteradmins, sysengineeringadmins, webgroup
{% if 'simple_allow_users' in grep_result.stdout %}
{{ grep_result.stdout.rstrip() }}
{% endif %}
default_shell = /bin/bash
..... Rest of your config file
- copy:
content: "{{ configuration_template }}"
dest: "{{ sssd_conf_path }}"
vars:
sssd_conf_path: /etc/sssd.conf
I used Zeitounator's tip, along with this question Only check whether a line present in a file (ansible)
This is what I came up with:
*as it turns out, the simple_allow_groups are being changed after the systems are deployed (thanks for telling the admins about that, you guys... /snark for the people messing with my config files)
---
- name: Get Remote SSSD Config
become: true
slurp:
src: /etc/sssd/sssd.conf
register: slurpsssd
- name: Set simple_allow_users if exists
set_fact:
simpleallowusers: "{{ linetomatch }}"
loop: "{{ file_lines }}"
loop_control:
loop_var: linetomatch
vars:
- decode_content: "{{ slurpsssd['content'] | b64decode }}"
- file_lines: "{{ decode_content.split('\n') }}"
when: '"simple_allow_users" in linetomatch'
- name: Set simple_allow_groups
set_fact:
simpleallowgroups: "{{ linetomatch }}"
loop: "{{ file_lines }}"
loop_control:
loop_var: linetomatch
vars:
- decode_content: "{{ slurpsssd['content'] | b64decode }}"
- file_lines: "{{ decode_content.split('\n') }}"
when: '"simple_allow_groups" in linetomatch'
- name: Install SSSD Config
copy:
src: etc/sssd/sssd.conf
dest: /etc/sssd/sssd.conf
owner: root
group: root
mode: 0600
backup: yes
become: true
- name: Add simple_allow_users back to file if it existed
lineinfile:
path: /etc/sssd/sssd.conf
line: "{{ simpleallowusers }}"
insertafter: "^simple_allow_groups"
when: simpleallowusers is defined
become: true
- name: Replace simple allow groups with existing values
lineinfile:
path: /etc/sssd/sssd.conf
line: "{{ simpleallowgroups }}"
regexp: "^simple_allow_groups"
backrefs: true
when: simpleallowgroups is defined
become: true

who i can remplace multiple line in a cfg

I would like some help to be able to replace several lines in a cfg file with the ansible module lineinfile
- name: "[MODIFY /etc/zabbix/zabbix.agentd.conf]
lineinfile:
path: /etc/zabbix/zabbix.agentd.conf]
insertafter: "{{ item}}"
with_items:
- line 99
- line 77
line: "{{ item }}"
with_items:
- set number
- :colorschem murphy
become: yes
become_method: sudo
A: It's not possible to use two loops (with_items) in one task.
Put the configuration data into one loop. Try the task below
- name: MODIFY /etc/zabbix/zabbix.agentd.conf
lineinfile:
path: /etc/zabbix/zabbix.agentd.conf
line: "{{ item.line }}"
insertafter: "{{ item.after }}"
loop:
- line: 'line 99'
after: 'set number'
- line: 'line 77'
after: 'colorschem murphy'
become: yes
become_method: sudo
Q: I want replaces line 99 by set number
A: Use replace module.
- name: MODIFY /etc/zabbix/zabbix.agentd.conf
replace:
path: /etc/zabbix/zabbix.agentd.conf
regexp: "{{ item.regexp }}"
replace: "{{ item.replace }}"
loop:
- regexp: 'line 99'
replace: 'set number'
become: yes
become_method: sudo
(not tested)
This might be a good time to use templates:
- name: MODIFY /etc/zabbix/zabbix.agentd.conf
template:
src: template/zabbix.agentd.conf.j2
dest: /etc/zabbix/zabbix.agentd.conf
The file zabbix.agentd.conf.j2 should be a copy of the file you want to copy at the dest path. Your variable goes in the j2 file and can be change at each interaction.
Example:
vi zabbix.agentd.conf.j2
line 1 my file
line 2
line 3
.
.
line 99 {{ set number }}

Ansible script is executing but loop is failing

I am trying to write a script (adduser.yml) to create users in a linux machine which imports users list from userlist.csv file. When I executed adduser.yml, the loop failed creating only 1st user. Can someone help me understand what mistake I am doing and how to correct it?
userlist.csv:
id,username,password,sudo
1,ansible1,ansible#123,yes
2,ansible2,ansible#123,no
3,ansible3,ansible#123,yes
4,ansible4,ansible#123,yes
adduser.yml:
---
## executed but until loop failed; check
- hosts: 192.168.0.3
vars:
count: "{{COUNT}}"
x: "1"
uname: "{{ lookup('csvfile', '{{x}} file=userlist.csv delimiter=, col=1') }}"
password: "{{ lookup('csvfile', '{{x}} file=userlist.csv delimiter=, col=2') }}"
sudo: "{{ lookup('csvfile', '{{x}} file=userlist.csv delimiter=, col=3') }}"
tasks:
name: "user add"
action:
- user:
x: "x+=1"
name: "{{ uname }}"
password: "{{ password }}"
state: present
register: x
until: "{{x > count}}"
name: "add to sudoers"
when: sudo == "yes"
lineinfile:
dest: /etc/sudoers
There are quite some things that will not work as you expected them. First thing, your loop is defined for your first task. That means only the user task will be repeated. Neither the sudo task nor the vars definition at the top will be looped. But don't try to re-define your loop, this is not going to work with vars.
Ansible has no build-in way to read vars from a csv file other than the csv lookup which will read exactly one line. But as said, you can not combine that with a loop.
I see two options you have:
Do not use csv. Ansible is mostly bound to yaml. If you'd had a yaml definition of your users, you simply could use the include_vars module to load these vars.
If you are bound to csv, you could try to use this includecsv module. (I have no experience with it and can not tell if it actually works)
Now, let's assume you have loaded your users into a list either from yaml or from csv with mentioned module. Then you'd simply loop with with_items:
tasks:
- name: "user add"
user:
name: "{{ item['uname'] }}"
password: "{{ item['password'] }}"
state: present
with_items: users_you_loaded
- name: "add to sudoers"
when: "{{ item['sudo'] }} == 'yes'"
...
with_items: users_you_loaded

Resources