Avatar of Henry
Henry
Flag for France asked on

Ansible lineinfile and loop

Hi,

Something wrong with this ansible script :

hosts:
[test-server]
ip1    (for hostname1)
ip2    (for hostname2)
ip3    (for hostname3)
ip4    (for hostname4)

test.yml :
---
- hosts: all
  become: yes
  vars:
     hostname:
       - hostname1
       - hostname2
       - hostname3
       - hostname4
  - name: The following setting are changed
    lineinfile:
      path: /etc/rsyslog.d/test.conf
      state: present
      regexp: "{{ item.From }}"
      line: "{{ item.To }}"
    with_items:
      - { From: '^.*target.*$', To: 'target="{{ item }}"' }
      - { From: '^.*tls.myprivkey.*$', To: 'tls.myprivkey="/etc/pki/tls/private/{{ item }}.key"' }
      - { From: '^.*tls.mycert.*$', To: 'tls.mycert="/etc/pki/tls/certs/{{ item }}.crt"' }
    loop: "{{ hostname }}"
    when: inventory_hostname in groups['test-server'] == hostname

Thanks for any help
Linux* Ansible

Avatar of undefined
Last Comment
Henry

8/22/2022 - Mon
simon3270

Couple of things to change:
  • "-" is not valid in group names - use "_" instead.
  • "loop" and "with_items" are alternatives for loop control - you use one or the other, not both
  • Your "when" clause is odd. If you just want all hosts in the "test_server" group, then use one of these fixes
    • Just process the hosts in the test_server group, and delete the "when" clause
      • hosts: test_server
    • Use "hosts: all", then limit the hosts in this task to ones where "test_server" is one of the groups that the current host is in
      • when: "'test_server' in group_names"

You can use nested loops if the inner task is in a separate file, and you "include_tasks: inner_loop.yml".

You've also got a problem with both loops using "item" as a loop value - which "item" is which? To fix this, in the "loop:" section, have this, and use "{{ hostitem }}" when referring to the outer loop variable:
  loop: "{{hostname}}"
  loop_control:
    loop_var: hostitem

Open in new window


In fact, all of your problems disappear if you use "hosts: test_server" - you don't need the outer loop at all. Just use "{{ inventory_hostname }}" or "{{ ansible_hostname }}" to get the name of the host you are processing.
Henry

ASKER
Hi simon3270,

I'm writing a Ansible playbook whose job is to configure rsyslog client server to transmit logs via SSL/TLS. I need to configure on each server, the name of the certificate corresponding to the name of the server.
I used some things you advised me, but it still doesn't work.
Can you try other solutions by testing some linux vm on virtualbox?

hosts:
[test_server]
ip1    (for hostname1)
ip2    (for hostname2)
ip3    (for hostname3)
ip4    (for hostname4)

test.yml :
  - name: The following setting are changed
    lineinfile:
      path: /etc/rsyslog.d/test.conf
      state: present
      regexp: "{{ item.From }}"
      line: "{{ item.To }}"
    with_items:
      - { From: '^.*target.*$', To: 'target="{{ hostitem }}"' }
      - { From: '^.*tls.myprivkey.*$', To: 'tls.myprivkey="/etc/pki/tls/private/{{ hostitem }}.key"' }
      - { From: '^.*tls.mycert.*$', To: 'tls.mycert="/etc/pki/tls/certs/{{ hostitem }}.crt"' }
    loop: "{{ groups['test_server'] }}"
    loop_control:
      loop_var: hostitem
    when: inventory_hostname in groups['test_server']

Thanks for any help
simon3270

As I said, you can't use "with_items" and "loop" at the same time. Can you just use "hosts: test_server"?

And no, I can't "try other solutions". I suggest things, you try them. If I have time, and I decide I want to, I will dig around for solutions (see my recent testimonials). But at the moment I don't have the time.
I started with Experts Exchange in 2004 and it's been a mainstay of my professional computing life since. It helped me launch a career as a programmer / Oracle data analyst
William Peck
simon3270

I've been thinking about your script. I think you have had a problem, and tried to fix it by adding more and more code. A better fix would be to make it simpler!

Ansible's job is to run a series of commands on multiple hosts. You don't need to add a 'host' loop, because it already does that loop for you.

You should only put hosts in your inventory, not in the playbook. The playbook then refers to groups of hosts.

We have two options. If this playbook is only processing the hosts in the test_sever group, then use "hosts: test_server". If it is also doing other tasks which have to process other hosts in other groups, you can use "hosts: all", and use my "when" clause to select only hosts in the specific group.

Now remove the "hostname" variable, and all of your "loop:" and "loop_control:" statements.

In your remaining code (from the "-name" to the "with_items" bit), use {{ ansible_hostname}} where you want that host's name. "ansible_hostname" is the hostname of the node that Ansible is currently processing. "inventory_hostname" is the name taken from the inventory file. The inventory file could, for example, use IP addresses.

Hope this helps!
Henry

ASKER
Hi simon3270,

I'm writing a Ansible playbook whose job is to configure rsyslog client server to transmit logs via SSL/TLS.
I need to configure on each server, the name of the certificate corresponding to the hostname of the server.

For example:

hosts:
[test_server]
ip1   (for hostname1)
ip2   (for hostname2)

On the certificate directory path of each server :

ip1 (for hostname1):
target=hostname1
tls.myprivkey="/etc/pki/tls/private/hostname1.key
tls.mycert="/etc/pki/tls/certs/hostname1.crt

ip2 (for hostname2):
target=hostname2
tls.myprivkey="/etc/pki/tls/private/hostname2.key
tls.mycert="/etc/pki/tls/certs/hostname2.crt

So here's the script :
The problem is when I run the script I don't have any error but I don't have also the expected result because nothing is done?

hosts:
[test_server]
ip1    (for hostname1)
ip2    (for hostname2)

test.yml :
  - name: The following setting are changed
    lineinfile:
      path: /etc/rsyslog.d/test.conf
      state: present
      regexp: "{{ item.From }}"
      line: "{{ item.To }}"
    with_items:
      - { From: '^.*target.*$', To: 'target="{{ hostitem }}"' }
      - { From: '^.*tls.myprivkey.*$', To: 'tls.myprivkey="/etc/pki/tls/private/{{ hostitem }}.key"' }
      - { From: '^.*tls.mycert.*$', To: 'tls.mycert="/etc/pki/tls/certs/{{ hostitem }}.crt"' }
    loop: "{{ hostname }}"
    loop_control:
      loop_var: hostitem
    when: inventory_hostname in groups['test_server'] == hostname
 
Thanks for any help
simon3270

I say again, DO NOT USE "with_items" AND "loop" TOGETHER!

Ansible's job is to loop over a list of hosts - you don't have to do it yourself.

Take out the 3 "loop" lines ("loop", "loop_control" and "loop_var")

Replace your "when" clause with
 when: "'test_server' in group_names"

Open in new window


In your "with_items" lines, use the "ansible_hostname" variable to get the current host's name.
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
ASKER CERTIFIED SOLUTION
simon3270

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
GET A PERSONALIZED SOLUTION
Ask your own question & get feedback from real experts
Find out why thousands trust the EE community with their toughest problems.
Henry

ASKER
Thanks for your advices