Ansible lineinfile module ใช้แก้ไขไฟล์ text โดยเพิ่ม, แก้ไข, หรือลบบรรทัดเดียวตาม pattern ที่กำหนด เหมาะสำหรับกรณีที่ต้องการแก้ค่า config เดียวในไฟล์ขนาดใหญ่ เช่น เปลี่ยน parameter ใน /etc/ssh/sshd_config หรือเพิ่ม entry ใน /etc/hosts โดยไม่ต้อง overwrite ไฟล์ทั้งหมด
บทความนี้อธิบายการใช้ lineinfile module ตั้งแต่การเพิ่ม/แก้ไข/ลบบรรทัด, regex matching, insertbefore/insertafter, และ blockinfile สำหรับการแทรกหลายบรรทัด
เพิ่มและแก้ไขบรรทัด
Parameter หลักคือ path (ไฟล์ที่แก้ไข), regexp (pattern ค้นหา), และ line (บรรทัดที่ต้องการให้มีในไฟล์) ถ้า regexp พบ match จะแทนด้วย line ถ้าไม่พบจะ append ท้ายไฟล์
---
- name: Configure SSH with lineinfile
hosts: all
become: true
tasks:
- name: Disable root login via SSH
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#?PermitRootLogin'
line: 'PermitRootLogin no'
notify: restart sshd
- name: Set SSH port
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#?Port '
line: 'Port 2222'
notify: restart sshd
- name: Disable password authentication
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#?PasswordAuthentication'
line: 'PasswordAuthentication no'
notify: restart sshd
handlers:
- name: restart sshd
service:
name: sshd
state: restarted
regexp: '^#?PermitRootLogin' จะ match ทั้งบรรทัดที่ comment ไว้ (#PermitRootLogin) และที่ไม่ได้ comment (PermitRootLogin) ทำให้ idempotent — รันซ้ำก็ได้ผลเดิม
ลบบรรทัดด้วย state: absent
state: absent ลบบรรทัดที่ match regexp ออกจากไฟล์ ถ้าไม่มี match จะไม่ทำอะไร (idempotent)
---
- name: Clean up config file
hosts: all
become: true
tasks:
- name: Remove obsolete config line
lineinfile:
path: /etc/app/app.conf
regexp: '^old_setting='
state: absent
- name: Remove specific host entry
lineinfile:
path: /etc/hosts
regexp: '192\.168\.1\.100'
state: absent
- name: Remove commented debug lines
lineinfile:
path: /etc/nginx/nginx.conf
regexp: '^#.*debug.*$'
state: absent
insertafter และ insertbefore
insertafter แทรกบรรทัดใหม่หลัง pattern ที่กำหนด และ insertbefore แทรกก่อน pattern เหมาะสำหรับการเพิ่ม config ในตำแหน่งที่ถูกต้องในไฟล์
---
- name: Insert lines at specific positions
hosts: all
become: true
tasks:
- name: Add config after [database] section
lineinfile:
path: /etc/app/app.conf
insertafter: '^\[database\]'
line: 'connection_pool = 10'
- name: Add include before end of http block
lineinfile:
path: /etc/nginx/nginx.conf
insertbefore: '^}'
line: ' include /etc/nginx/conf.d/*.conf;'
- name: Add entry at beginning of file
lineinfile:
path: /etc/environment
insertbefore: BOF # Beginning Of File
line: '# Managed by Ansible'
- name: Add entry at end of file
lineinfile:
path: /etc/environment
insertafter: EOF # End Of File
line: 'APP_ENV=production'
สร้างไฟล์ถ้ายังไม่มี
create: yes สร้างไฟล์ใหม่ถ้ายังไม่มีอยู่ก่อนที่จะ insert บรรทัด ป้องกัน error เมื่อไฟล์ปลายทางยังไม่ถูกสร้าง
---
- name: Manage environment variables
hosts: all
become: true
tasks:
- name: Set environment variable
lineinfile:
path: /etc/environment
regexp: '^APP_VERSION='
line: 'APP_VERSION=2.5.0'
create: yes # สร้างไฟล์ถ้าไม่มี
owner: root
mode: '0644'
- name: Add PATH to profile
lineinfile:
path: /etc/profile.d/custom.sh
regexp: '^export PATH=.*myapp'
line: 'export PATH=$PATH:/opt/myapp/bin'
create: yes
mode: '0755'
backrefs — ใช้ Captured Group จาก Regex
backrefs: yes ทำให้ line ใช้ \1, \2 อ้างอิง captured groups จาก regexp เหมาะสำหรับแก้ค่าใน pattern เดิมโดยเก็บ prefix/suffix ไว้
---
- name: Use backrefs for precise editing
hosts: all
become: true
tasks:
# เปลี่ยนค่า max_connections แต่เก็บ format เดิม
- name: Update max_connections value
lineinfile:
path: /etc/mysql/mysql.conf.d/mysqld.cnf
regexp: '^(max_connections\s*=\s*)\d+'
line: '\g<1>200'
backrefs: yes
# uncomment บรรทัดที่ถูก comment ไว้
- name: Uncomment listen directive
lineinfile:
path: /etc/nginx/nginx.conf
regexp: '^#(listen 80;)'
line: '\1'
backrefs: yes
blockinfile สำหรับหลายบรรทัด
เมื่อต้องแทรกหลายบรรทัดพร้อมกัน ใช้ blockinfile แทน lineinfile module นี้เพิ่ม marker comments รอบ block เพื่อให้ Ansible รู้ว่าส่วนไหนที่จัดการอยู่ ทำให้ idempotent แม้รันซ้ำ
---
- name: Add configuration blocks
hosts: all
become: true
tasks:
- name: Add SSH authorized keys block
blockinfile:
path: /etc/ssh/sshd_config
block: |
# Custom security settings
AllowUsers deploy admin
MaxAuthTries 3
LoginGraceTime 30
marker: "# {mark} ANSIBLE MANAGED BLOCK"
- name: Add hosts entries
blockinfile:
path: /etc/hosts
block: |
192.168.10.10 app-server-01
192.168.10.11 app-server-02
192.168.10.20 db-primary
marker: "# {mark} APPLICATION SERVERS"
- name: Remove managed block
blockinfile:
path: /etc/ssh/sshd_config
marker: "# {mark} ANSIBLE MANAGED BLOCK"
state: absent
marker ใช้ {mark} เป็น placeholder ที่จะถูกแทนด้วย BEGIN และ END ทำให้ block มีขอบเขตชัดเจน เปลี่ยน marker ถ้าต้องการ block หลายอันในไฟล์เดียวกัน
Pattern: Harden SSH Configuration
ตัวอย่าง Playbook สำหรับ harden SSH configuration โดยใช้ lineinfile แก้ค่า parameter หลักๆ และ blockinfile เพิ่ม settings กลุ่ม
---
- name: Harden SSH Configuration
hosts: all
become: true
tasks:
- name: Set SSH hardening parameters
lineinfile:
path: /etc/ssh/sshd_config
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
loop:
- { regexp: '^#?PermitRootLogin', line: 'PermitRootLogin no' }
- { regexp: '^#?PasswordAuthentication', line: 'PasswordAuthentication no' }
- { regexp: '^#?X11Forwarding', line: 'X11Forwarding no' }
- { regexp: '^#?MaxAuthTries', line: 'MaxAuthTries 3' }
- { regexp: '^#?ClientAliveInterval', line: 'ClientAliveInterval 300' }
- { regexp: '^#?ClientAliveCountMax', line: 'ClientAliveCountMax 2' }
notify: restart sshd
- name: Add ciphers block
blockinfile:
path: /etc/ssh/sshd_config
block: |
Ciphers [email protected],[email protected]
MACs hmac-sha2-512,hmac-sha2-256
KexAlgorithms curve25519-sha256,diffie-hellman-group14-sha256
marker: "# {mark} ANSIBLE SSH CRYPTO SETTINGS"
notify: restart sshd
- name: Validate SSH config before restart
command: sshd -t
changed_when: false
handlers:
- name: restart sshd
service:
name: sshd
state: restarted
สรุป
lineinfile module เหมาะกับการแก้ไข config file ทีละบรรทัดโดยไม่ต้อง overwrite ทั้งไฟล์ Pattern ที่ควรจำ: ใช้ regexp: '^#?PARAM' เพื่อ match ทั้ง commented และ uncommented lines, ใช้ backrefs: yes เมื่อต้องการแก้เฉพาะค่าแต่เก็บ format เดิม, และใช้ blockinfile เมื่อต้องแทรกหลายบรรทัดพร้อม marker สำหรับ idempotency
ข้อแตกต่างสำคัญ: lineinfile จัดการหนึ่งบรรทัด, blockinfile จัดการหลายบรรทัดเป็น block, ส่วน template หรือ copy เหมาะกว่าเมื่อต้องการ manage ทั้งไฟล์

