akishin999の日記

調べた事などを書いて行きます。

Ansible で複数行の文字列置換

ansible で設定ファイル内の文字列を置換したかったので、 lineinfile モジュールを試してみました。

lineinfile
http://www.ansibleworks.com/docs/modules.html#lineinfile

対象サーバに以下のようなファイルを用意。

  • /tmp/test.conf
foo
bar
baz
bar
bar
bar
foo

このファイル内の文字列を置換してみます。

以下のような Playbook を作成しました。

  • lineinfile.yml
- hosts: all
  user: root
  vars:
    target_file: /tmp/test.conf
  tasks:
    - name: lineinfile example task
      lineinfile: dest=${target_file} state=present regexp='^bar$' line=BAR=99999

ここで lineinfile モジュールに指定しているパラメータは以下のような意味です。

  • dest ・・・ 編集する対象ファイルのパス
  • regexp ・・・ 対象行を指定する正規表現
  • line ・・・ 置き換える文字列

実行してみます。

$ ansible-playbook lineinfile.yml -i hosts -k

確認してみると以下のように、最後の "bar" だけが置換されている事がわかります。

# cat /tmp/test.conf
foo
bar
baz
bar
bar
BAR=99999
foo

複数行まとめて置換したい場合はどうやるんだろう?と思ってドキュメントを確認したところ、以下のように書いてありました。

only the last line found will be replaced.

http://www.ansibleworks.com/docs/modules.html#lineinfile

どうやら lineinfile モジュールでは、複数行マッチした場合は最後にマッチした行のみ置換されるようです。
他にも同じくドキュメントに

This is primarily useful when you want to change a single line in a file only. For other cases, see the copy or template modules.

http://www.ansibleworks.com/docs/modules.html#lineinfile

と書いてあったりするので、どうやら今のところ複数行の置換には対応していない模様(バージョン 1.2.2 で確認)。
とはいえ template を用意するほど変更箇所が多いわけでもないし・・・。

というわけで、結局 command モジュール + sed で以下のようにしました。

- hosts: all
  user: root
  vars:
    target_file: /tmp/test.conf
  tasks:
    - name: find and replace using sed
      command: sed -i "s/^bar$/BAR=99999/g" ${target_file}

変更後に再度実行すると全て置換されている事が分かります。

# cat /tmp/test.conf
foo
BAR=99999
baz
BAR=99999
BAR=99999
BAR=99999
foo

置換系は sed 使えば確かに簡単なので、当面これでいいような気がしました。
マッチしなければ何も起きないので、冪等性が壊れることも多分無さそうだし。

参考:

lineinfile モジュール
http://yteraoka.github.io/ansible-tutorial/ansible-in-detail.html#module-lineinfile

ちょうど今月の Software Designsed 特集みたいです。