ansibleで特定の文字列があれば書き換え、なければ追記する方法

# はじめに
ansibleで一括でミドルウェアの設定ファイルを修正しようとしたところ、インストールされているバージョンによってはデフォルトで設定が入っている場合があった。
末尾に追加のような書き方をしていると、あるオプションについてyesとnoの両方を定義してしまったり、冪等性がなくなってしまうため、「特定の文字列があれば書き換え、なければ追記する方法」を確認する。

# 結論
lineinfileにregexpを定義するだけでよい。
```
  - name: "insert or replace hoge"
    lineinfile:
      path: { 修正したいファイルのパス }
      regexp: { 修正or追記したい文字列に対応する正規表現 }
      line: { 修正or追記したい文字列 }
      backrefs: false
```
※ `backrefs: false`はデフォルトのため省略可。  
※ 逆に`backrefs: true` とすると、なければ追記されなくなり、書き換えだけになる。
※ lineinfileモジュールは複数行には使用できないため、複数行変更したい場合はreplaceモジュールも必要

# 背景
ChatGPTに聞いたところ、一度grepした結果をregisterに入れてwhenで対応するという冗長な解決策が提案されたため、他の方法を調べたくなった。

# 検証
以下のファイルを用意し、特定の文字列がある状態とない状態でansibleを実行する。
``` test.txt 
Listen 80
Include conf.modules.d/*.conf
ServerName www.example.com:80
```
使用するplaybook
```
---
- hosts: all
  tasks:
  - name: "insert or replace ServerAdmin"
    lineinfile:
      path: /path/to/test.txt
      regexp: "ServerAdmin\s.*"
      line: "ServerAdmin www-admin@foo.example.com"
```

##### 1. 特定の文字列がない場合: OK
```
TASK [insert or replace ServerAdmin] *****************************************************************************************************************************************************************************************************************************************
--- before: /path/to/test.txt (content)
+++ after: /path/to/test.txt (content)
@@ -1,3 +1,4 @@
 Listen 80
 Include conf.modules.d/*.conf
 ServerName www.example.com:80
+ServerAdmin www-admin@foo.example.com
```

##### 2. 特定の文字列があった場合（`ServerAdmin admin@hoge.example.com`があった場合）: OK
```
TASK [insert or replace ServerAdmin] *****************************************************************************************************************************************************************************************************************************************
--- before: /path/to/test.txt (content)
+++ after: /path/to/test.txt (content)
@@ -1,4 +1,4 @@
 Listen 80
 Include conf.modules.d/*.conf
 ServerName www.example.com:80
-ServerAdmin admin@hoge.example.com
+ServerAdmin www-admin@foo.example.com
```

##### 3. 二回実行した場合: OK(変更なし)
```
TASK [insert or replace ServerAdmin] *****************************************************************************************************************************************************************************************************************************************
ok: [13.210.98.174]
```
###### 4. ※`backrefs: true`の場合
``` test.txt
Listen 80
Include conf.modules.d/*.conf
ServerName www.example.com:80
```
- playbook
``` 
---
- hosts: all
  tasks:
  - name: "insert or replace ServerAdmin"
    lineinfile:
      path: /path/to/test.txt
      regexp: "ServerAdmin.*"
      line: "ServerAdmin www-admin@foo.example.com"
      backrefs: true
```
- 実行結果: 変更なし(追記されない)
``` 
TASK [insert or replace ServerAdmin] *****************************************************************************************************************************************************************************************************************************************
ok: [13.210.98.174]
```

# 参考
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/lineinfile_module.html

https://zenn.dev/y_mrok/books/ansible-no-module-no-tsukaikata/viewer/ansible_builtin_lineinfile 
  
https://tekunabe.hatenablog.jp/entry/2019/02/24/ansible_lineinfile_intro

# おわりに
「なければ追記」ということで、`insertafter:EOF`が必要かと思ったが不要だった（[参考3](https://tekunabe.hatenablog.jp/entry/2019/02/24/ansible_lineinfile_intro)のregexpの説明に記載）
背景のChatGPTの回答は3日ほど前のものだが、今再度質問すると返答が改善されていた。
```
> ansibleのテキスト操作で、該当業がある場合は内容を変更、該当行がない場合は追記とすることはできますか

はい、Ansibleを使用してファイルのテキストを操作し、該当行がある場合は内容を変更し、該当行がない場合は追記することは可能です。これを達成するためには、lineinfileモジュールを使用できます。
以下は、この目的の例です。
-----------------------------------------------------------------------------------------
yaml
Copy code
- name: Modify or append a line in a file
  hosts: your_target_hosts
  tasks:
    - name: Modify or append a line
      lineinfile:
        path: /path/to/your/file.txt  # 対象のファイルのパスを指定
        line: 'your_desired_line'     # 追記または変更したい行の内容を指定
        regexp: 'your_pattern'        # 既存の行を検索するための正規表現パターンを指定
        state: present                # presentなら行が存在しなければ追記、存在すれば変更

# 例: 既存のファイルに"example_line"という行がある場合、その行を"modified_line"に変更する
# なければファイルの最後に"example_line"を追加する
-----------------------------------------------------------------------------------------
上記の例では、pathで対象のファイルのパスを指定し、lineで変更または追記したい行の内容を指定します。regexpでは既存の行を検索するための正規表現パターンを指定し、stateがpresentの場合は行が存在しなければ追記され、行が存在すれば内容が変更されます。

これにより、Ansibleを使用して指定された条件に基づいてファイルのテキストを変更または追記できます。
```