Ansible stat Module: ตรวจสอบ File Status และ Permissions

Ansible stat module ใช้ตรวจสอบสถานะของ filesystem object บน remote server ก่อนดำเนินการอื่น — เช่น ตรวจว่ามีไฟล์อยู่หรือไม่, ขนาด, permissions, owner, checksum หรือว่าเป็น symlink โดยไม่ต้องรัน command: ls -la ตรง ๆ

บทความนี้ครอบคลุม parameters หลัก, การอ่านค่าจาก stat result, การใช้ร่วมกับ when conditional เพื่อสร้าง idempotent tasks, การตรวจสอบ checksum และ pattern สำหรับ pre-deploy verification

stat Module พื้นฐาน

stat module ต้องใช้คู่กับ register เสมอ เพราะข้อมูลทั้งหมดจะถูกเก็บไว้ใน registered variable ไม่ได้ทำการเปลี่ยนแปลงอะไรบน server — changed จะเป็น false เสมอ

---
- name: Basic stat module usage
  hosts: all
  tasks:
    # ตรวจสอบว่าไฟล์มีอยู่หรือไม่
    - name: Check if config file exists
      stat:
        path: /etc/myapp/app.conf
      register: config_stat

    # ดูข้อมูลทั้งหมดที่ได้
    - name: Show file info
      debug:
        var: config_stat.stat

    # ใช้ when conditional ตามผล stat
    - name: Create config if not exists
      template:
        src: app.conf.j2
        dest: /etc/myapp/app.conf
      when: not config_stat.stat.exists

    # ตรวจว่าเป็น directory
    - name: Check if directory exists
      stat:
        path: /opt/myapp/data
      register: data_dir

    - name: Create directory if missing
      file:
        path: /opt/myapp/data
        state: directory
        owner: appuser
        mode: '0755'
      when: not data_dir.stat.exists

Fields ที่ได้จาก stat

เมื่อ stat.exists เป็น true จะมี fields เพิ่มเติมให้ใช้ได้ หาก exists เป็น false จะมีแค่ stat.exists เท่านั้น

---
- name: Reading stat fields
  hosts: all
  tasks:
    - name: Get file stats
      stat:
        path: /opt/myapp/bin/myapp
      register: app_bin

    - name: Check various stat fields
      debug:
        msg:
          - "Exists: {{ app_bin.stat.exists }}"
          - "Is file: {{ app_bin.stat.isreg | default(false) }}"
          - "Is directory: {{ app_bin.stat.isdir | default(false) }}"
          - "Is symlink: {{ app_bin.stat.islnk | default(false) }}"
          - "Size: {{ app_bin.stat.size | default(0) }} bytes"
          - "Owner: {{ app_bin.stat.pw_name | default('N/A') }}"
          - "Group: {{ app_bin.stat.gr_name | default('N/A') }}"
          - "Mode: {{ app_bin.stat.mode | default('N/A') }}"
          - "Executable: {{ app_bin.stat.executable | default(false) }}"
          - "Writeable: {{ app_bin.stat.writeable | default(false) }}"
      when: app_bin.stat.exists

Checksum — ตรวจสอบความถูกต้องของไฟล์

ใช้ checksum_algorithm เพื่อคำนวณ hash ของไฟล์ ช่วยตรวจสอบว่าไฟล์ถูกแก้ไขหรือ download ครบถ้วนหรือไม่

---
- name: Checksum verification
  hosts: all
  tasks:
    # คำนวณ SHA256 checksum
    - name: Get file checksum
      stat:
        path: /opt/myapp/bin/myapp
        checksum_algorithm: sha256
      register: bin_stat

    - name: Verify checksum matches expected
      assert:
        that:
          - bin_stat.stat.checksum == "{{ expected_sha256 }}"
        fail_msg: "Checksum mismatch! File may be corrupted."
        success_msg: "Checksum verified OK."
      when: bin_stat.stat.exists

    # MD5 checksum (เร็วกว่า SHA256 แต่ไม่ปลอดภัยเท่า)
    - name: Get MD5 checksum
      stat:
        path: /etc/myapp/app.conf
        checksum_algorithm: md5
      register: conf_stat

    - name: Show MD5
      debug:
        msg: "MD5: {{ conf_stat.stat.checksum }}"
      when: conf_stat.stat.exists

ตรวจสอบ Symlink

stat module ช่วยตรวจสอบ symlink ได้อย่างละเอียด — ทั้งว่าเป็น symlink หรือไม่, link ชี้ไปที่ไหน และ target ยังมีอยู่จริงหรือไม่

---
- name: Symlink inspection
  hosts: all
  tasks:
    - name: Check current symlink
      stat:
        path: /opt/myapp/current
      register: current_link

    - name: Show symlink info
      debug:
        msg:
          - "Is symlink: {{ current_link.stat.islnk }}"
          - "Points to: {{ current_link.stat.lnk_target | default('N/A') }}"
          - "Target exists: {{ current_link.stat.lnk_source | default('N/A') }}"
      when: current_link.stat.exists and current_link.stat.islnk

    # ตรวจสอบก่อน overwrite symlink
    - name: Check if symlink needs update
      debug:
        msg: "Symlink already points to {{ current_link.stat.lnk_target }}"
      when:
        - current_link.stat.exists
        - current_link.stat.islnk
        - current_link.stat.lnk_target == "/opt/myapp/releases/v2.5.0"

ตรวจสอบ Permissions และ Ownership

ใช้ stat ตรวจ permissions และ owner ก่อนแก้ไข เพื่อ skip task ที่ไม่จำเป็น หรือ fail early ถ้า permissions ไม่ถูกต้อง

---
- name: Permission and ownership checks
  hosts: all
  tasks:
    - name: Check config file permissions
      stat:
        path: /etc/myapp/secret.conf
      register: secret_stat

    # fail ถ้า permissions ไม่ถูกต้อง (ควรเป็น 0600)
    - name: Assert secret file has correct permissions
      assert:
        that:
          - secret_stat.stat.mode == "0600"
          - secret_stat.stat.pw_name == "root"
        fail_msg: "secret.conf permissions are wrong: mode={{ secret_stat.stat.mode }}, owner={{ secret_stat.stat.pw_name }}"
      when: secret_stat.stat.exists

    # แก้ permissions เฉพาะเมื่อไม่ถูกต้อง
    - name: Fix permissions if wrong
      file:
        path: /etc/myapp/secret.conf
        mode: '0600'
        owner: root
        group: root
      when:
        - secret_stat.stat.exists
        - secret_stat.stat.mode != "0600"

Pattern: Pre-Deploy Verification

ตัวอย่าง Playbook ตรวจสอบ prerequisites ก่อน deploy application — ตรวจว่า binary มีอยู่, config ถูกต้อง, directory พร้อม และ symlink ชี้ถูกต้อง

---
- name: Pre-deploy verification
  hosts: appservers
  vars:
    app_name: myapp
    app_version: "{{ deploy_version }}"
    release_path: "/opt/{{ app_name }}/releases/{{ app_version }}"

  tasks:
    # ตรวจ release directory
    - name: Check release directory
      stat:
        path: "{{ release_path }}"
      register: release_dir

    - name: Fail if release not found
      fail:
        msg: "Release directory {{ release_path }} not found — run deploy first"
      when: not release_dir.stat.exists or not release_dir.stat.isdir

    # ตรวจ binary executable
    - name: Check application binary
      stat:
        path: "{{ release_path }}/bin/{{ app_name }}"
      register: app_bin

    - name: Fail if binary missing or not executable
      fail:
        msg: "Application binary not found or not executable"
      when: not app_bin.stat.exists or not app_bin.stat.executable

    # ตรวจ config file
    - name: Check config file
      stat:
        path: "{{ release_path }}/config/app.conf"
        checksum_algorithm: sha256
      register: app_conf

    - name: Fail if config missing
      fail:
        msg: "Config file not found"
      when: not app_conf.stat.exists

    # ตรวจ log directory
    - name: Check log directory writable
      stat:
        path: "/var/log/{{ app_name }}"
      register: log_dir

    - name: Create log directory if missing
      file:
        path: "/var/log/{{ app_name }}"
        state: directory
        owner: appuser
        mode: '0755'
      when: not log_dir.stat.exists

    - name: Pre-deploy checks passed
      debug:
        msg: "All checks passed — proceeding with deployment"

สรุป

stat module เป็นเครื่องมือสำคัญสำหรับสร้าง idempotent Playbook รองรับการตรวจสอบ existence, type, permissions, owner, checksum และ symlink target ของ filesystem object โดยต้องใช้ร่วมกับ register เสมอ

Pattern ที่ควรจำ: ตรวจ stat.exists ก่อนเข้าถึง field อื่นเสมอ เพราะถ้า exists: false fields อื่นจะไม่มีค่า, ใช้ | default(false) เพื่อป้องกัน undefined variable error และใช้ assert ร่วมกับ stat เพื่อ fail early แทนการปล่อยให้ task ถัดไป error แบบไม่มี context