Link to home
Start Free TrialLog in
Avatar of Henry
HenryFlag 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
Avatar of simon3270
simon3270
Flag of United Kingdom of Great Britain and Northern Ireland image

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.
Avatar of 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
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'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!
Avatar of 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
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.
ASKER CERTIFIED SOLUTION
Avatar of simon3270
simon3270
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Henry

ASKER

Thanks for your advices