ansible: lineinfile pour plusieurs lignes?

162

De la même manière, il existe un module lineinfilepour ajouter une ligne dans un fichier, existe-t-il un moyen d'ajouter plusieurs lignes?

Je ne souhaite pas utiliser de modèle car vous devez fournir le fichier complet. Je veux juste ajouter quelque chose à un fichier existant sans nécessairement savoir ce que le fichier contient déjà donc un modèle n'est pas une option.

Michael
la source
Je comprends que vous ne voulez pas utiliser template, mais utiliser lineinfileest un anti-modèle . C'est aussi un signal d'alarme fort indiquant que vous «ne savez pas ce qu'il y a dans le fichier», ce qui entraîne un risque important d'échecs inconnus.
tedder42
39
Ce n'est pas un anti-modèle. Le but de lineinfile est de prendre en charge plusieurs sources gérant le même fichier, ce qui est parfois inévitable. La plupart des fichiers de configuration ont un format fixe et la logique pour éviter les conflits n'est généralement pas trop importante.
Doug F
Je ne sais pas ce que contient la grande majorité des fichiers sur mon PC; ne veut pas dire que je veux tous les détruire!
DylanYoung

Réponses:

222

Vous pouvez utiliser une boucle pour le faire. Voici un exemple utilisant une with_itemsboucle:

- name: Set some kernel parameters
  lineinfile:
    dest: /etc/sysctl.conf
    regexp: "{{ item.regexp }}"
    line: "{{ item.line }}"
  with_items:
    - { regexp: '^kernel.shmall', line: 'kernel.shmall = 2097152' }
    - { regexp: '^kernel.shmmax', line: 'kernel.shmmax = 134217728' }
    - { regexp: '^fs.file-max', line: 'fs.file-max = 65536' }
Ben Whaley
la source
ASSUREZ-VOUS que vous avez l'argument de line = et regexp = entre guillemets . Je ne l'ai pas fait et j'ai continué à le faire msg: this module requires key=value arguments. L'exemple donné a cela correct - je n'ai tout simplement pas suivi l'exemple.
JDS
1
Puis-je demander comment faire une seule sauvegarde avant le premier changement? peut-être item.backup? : D
tdihp
6
Cela a probablement été voté avant Ansible 2.0. Une meilleure réponse est maintenant: stackoverflow.com/a/28306576/972128
kkurian
@kkurian Sûrement seulement si vous insérez, pas si vous remplacez?
ndtreviv
7
@kkurian La solution blockinfile ne fonctionnera pas si vous avez par exemple besoin d'ajouter des lignes à un fichier json et que vous ne voulez pas de marqueurs. Bien que vous puissiez définir des marqueurs sur "", ansible blockinfile cherchera toujours des marqueurs, n'en trouvera pas et réinsérera le bloc. Ainsi, blockinfile sans marqueurs n'est pas idempotent, lineinfile avec une boucle l'est.
absurde le
176

Vous pouvez essayer d'utiliser à la blockinfileplace.

Vous pouvez faire quelque chose comme

- blockinfile: |
    dest=/etc/network/interfaces backup=yes
    content="iface eth0 inet static
        address 192.168.0.1
        netmask 255.255.255.0"
Soichi Hayashi
la source
8
Le blockinfilemodule a fonctionné à merveille chaque fois que j'ai choisi de l'utiliser. J'aime particulièrement le comportement intuitif des options insertafter/ insertbefore.
Jay Taylor du
9
La réponse la plus votée était probablement antérieure à Ansible 2.0, mais c'est la réponse la plus correcte maintenant.
Willem van Ketwich
11
Blockinfile nécessite des marqueurs. Ce n'est parfois pas une option.
ceving le
1
Pouvons-nous écraser le contenu avec blockinfile?
pkaramol
1
C'est une bonne façon de le faire, je pense. docs.ansible.com/ansible/blockinfile_module.html
Paulo Victor
20

Si vous avez besoin de configurer un ensemble de lignes propriété = valeur uniques, je recommande une boucle plus concise. Par exemple:

- 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' }

Utiliser un dict comme suggéré par Alix Axel et ajouter la suppression automatique des entrées commentées correspondantes,

- 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
Nicolas Sushkin
la source
2
Si vous utilisez with_dict, ce serait plus concis.
Alix Axel
18

Voici une version sans bruit de la solution qui doit utiliser with_items:

- name: add lines
  lineinfile: 
    dest: fruits.txt
    line: '{{ item }}'
  with_items:
    - 'Orange'
    - 'Apple'
    - 'Banana' 

Pour chaque élément, si l'élément existe dans fruits.txt, aucune action n'est entreprise.

Si l'élément n'existe pas, il sera ajouté à la fin du fichier.

Peasy facile.

Rick O'Shea
la source
Cela ne peut pas être combiné avec insertafter.
ceving le
S'il manque plusieurs lignes, je souhaite que l'article apparaisse dans une commande. Comment puis-je être sûr de l'ordre dans lequel les éléments sont ajoutés?
MUY Belgium
5

Ce n'est pas idéal, mais vous êtes autorisé à plusieurs appels lineinfile. En utilisant cela avec insert_after, vous pouvez obtenir le résultat souhaité:

- 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"
Ramon de la Fuente
la source
5
oui mais c'est toujours une ligne à la fois. Si j'ai 15 lignes, je préférerais les ajouter avec une seule commande. Cela ne semble pas possible.
Michael
1
Merci. Il semble que ce soit toujours la seule façon de faire plusieurs lignes avec insertion après / avant.
timss
5

J'ai pu le faire en utilisant \nle paramètre de ligne.

C'est particulièrement utile si le fichier peut être validé et que l'ajout d'une seule ligne génère un fichier invalide.

Dans mon cas, j'ajoutaisAuthorizedKeysCommand et AuthorizedKeysCommandUserà sshd_config , avec la commande suivante:

- lineinfile: dest=/etc/ssh/sshd_config line='AuthorizedKeysCommand /etc/ssh/ldap-keys\nAuthorizedKeysCommandUser nobody' validate='/usr/sbin/sshd -T -f %s'

L'ajout d'une seule des options génère un fichier dont la validation échoue.

Penz
la source
12
Cela créera la ligne une fois de plus chaque fois que le playbook est exécuté - il ne reconnaît pas correctement que la ligne existe déjà. Au moins, c'est le cas pour moi sur Ansible 1.7.1
David
1
J'ai signalé un bug , mais les gars d'Ansible n'ont aucun intérêt à le corriger.
ceving le
1
Il existe un nouveau module blockinfile qui devrait être meilleur que cette solution maintenant. ( docs.ansible.com/ansible/blockinfile_module.html )
Penz
1

Pour ajouter plusieurs lignes, vous pouvez utiliser 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'

pour ajouter une ligne, vous pouvez utiliser lininfile:

- name: server.example.com in /etc/hosts
  lineinfile:
    path: /etc/hosts
    line: '192.0.2.42 server.example.com server'
    state: present
Ahmed Taha
la source
1

Pour ajouter plusieurs lignes, vous pouvez utiliser le lineinfilemodule avec with_itemségalement une variable varsici pour le rendre 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}}"' }
mail2sandeepd
la source