Ansible Lookups: ดึงข้อมูลจากไฟล์ Database หรือ API

Lookup Plugins คือกลไกที่ใช้ดึงข้อมูลจากแหล่งภายนอก Playbook ณ เวลาที่รัน ไม่ว่าจะเป็นไฟล์ในระบบ, environment variables, password ที่สร้างแบบ dynamic หรือผลลัพธ์จาก command แล้วนำมาใช้เป็น variable โดยตรง

บทความนี้อธิบาย lookup plugins ที่ใช้บ่อยที่สุดในทางปฏิบัติ ตั้งแต่ file, env, password, template, ไปจนถึง dict และ items พร้อมวิธีใช้งานที่ถูกต้อง

ไวยากรณ์พื้นฐานของ Lookup

Lookup เรียกใช้ผ่าน lookup('plugin_name', 'argument') หรือ query('plugin_name', 'argument') ความต่างคือ lookup() return string ส่วน query() return list เสมอ

---
- name: Basic lookup syntax
  hosts: localhost
  tasks:
    - name: Read file content
      debug:
        msg: "{{ lookup('file', '/etc/hostname') }}"

    - name: Get environment variable
      debug:
        msg: "{{ lookup('env', 'HOME') }}"

    - name: Use query() for list result
      debug:
        msg: "{{ query('fileglob', '/etc/*.conf') }}"

file Lookup: อ่านเนื้อหาจากไฟล์

file lookup อ่านเนื้อหาไฟล์บน control node (เครื่องที่รัน playbook) และนำมาใช้เป็น variable

---
- name: File lookup examples
  hosts: all
  tasks:
    - name: Read SSH public key
      authorized_key:
        user: deploy
        key: "{{ lookup('file', '~/.ssh/id_ed25519.pub') }}"
        state: present

    - name: Read multi-line config file
      debug:
        msg: "{{ lookup('file', 'files/app.conf') }}"

    - name: Read multiple files
      debug:
        msg: "{{ lookup('file', 'file1.txt', 'file2.txt') }}"
      # return ค่าจากทั้งสองไฟล์รวมกัน

env Lookup: ดึง Environment Variable

env lookup ดึงค่า environment variable จาก control node มีประโยชน์มากใน CI/CD pipelines ที่ inject secrets ผ่าน environment

---
- name: Environment variable lookup
  hosts: all
  vars:
    db_password: "{{ lookup('env', 'DB_PASSWORD') }}"
    api_key: "{{ lookup('env', 'API_KEY') | default('', true) }}"
  tasks:
    - name: Configure app with env vars
      template:
        src: templates/app.conf.j2
        dest: /etc/myapp/app.conf
      vars:
        password: "{{ db_password }}"

    - name: Fail if required env var missing
      fail:
        msg: "DB_PASSWORD environment variable is not set"
      when: db_password == ""

ข้อควรระวัง: env lookup อ่านค่าจาก control node เท่านั้น ไม่ใช่จาก remote host ถ้าต้องการ environment variable จาก remote host ต้องใช้ shell module แทน

password Lookup: สร้าง Password แบบ Random

password lookup สร้าง random password และบันทึกลงไฟล์อัตโนมัติ ครั้งต่อไปที่รันจะอ่านค่าเดิมจากไฟล์ ทำให้ password ไม่เปลี่ยนทุกครั้ง

---
- name: Password lookup examples
  hosts: dbservers
  tasks:
    - name: Generate and store MySQL root password
      mysql_user:
        name: root
        password: "{{ lookup('password', '/etc/ansible/passwords/mysql_root length=20 chars=ascii_letters,digits') }}"
        host: localhost
      no_log: true

    - name: Create app database user
      mysql_user:
        name: myapp
        password: "{{ lookup('password', '/etc/ansible/passwords/myapp_db') }}"
        priv: "myapp.*:ALL"
      no_log: true

ไฟล์ที่ระบุจะถูกสร้างบน control node ใน path ที่กำหนด ถ้าไฟล์มีอยู่แล้วจะอ่านค่าเดิม ถ้ายังไม่มีจะสร้าง password ใหม่และบันทึกไว้

template Lookup: Render Template เป็น String

template lookup render Jinja2 template และ return ผลลัพธ์เป็น string แทนที่จะเขียนลงไฟล์โดยตรง ใช้เมื่อต้องการ inline config string

---
- name: Template lookup example
  hosts: all
  vars:
    app_name: myservice
    app_port: 8080
  tasks:
    - name: Set config from template string
      set_fact:
        nginx_config: "{{ lookup('template', 'templates/nginx_block.j2') }}"

    - name: Use rendered template in another task
      debug:
        msg: "{{ nginx_config }}"

csvfile Lookup: อ่านข้อมูลจาก CSV

csvfile lookup ค้นหาแถวในไฟล์ CSV และดึงค่าจาก column ที่ระบุ เหมาะสำหรับ mapping ข้อมูลเช่น server → IP หรือ user → role

# ไฟล์ servers.csv:
# hostname,ip,role
# web01,192.168.1.10,webserver
# db01,192.168.1.20,database

---
- name: CSV lookup example
  hosts: all
  tasks:
    - name: Get server IP from CSV
      debug:
        msg: "IP for web01: {{ lookup('csvfile', 'web01 file=servers.csv delimiter=, col=1') }}"

    - name: Get role from CSV
      set_fact:
        server_role: "{{ lookup('csvfile', inventory_hostname + ' file=servers.csv delimiter=, col=2') }}"

pipe Lookup: ดึงผลลัพธ์จาก Command

pipe lookup รัน shell command บน control node และ return stdout เป็น string ใช้ดึงข้อมูล dynamic ที่ไม่มี lookup plugin รองรับโดยตรง

---
- name: Pipe lookup examples
  hosts: all
  vars:
    git_commit: "{{ lookup('pipe', 'git rev-parse HEAD') }}"
    current_user: "{{ lookup('pipe', 'whoami') }}"
    date_stamp: "{{ lookup('pipe', 'date +%Y%m%d') }}"
  tasks:
    - name: Tag deployment with git commit
      debug:
        msg: "Deploying commit: {{ git_commit[:8] }}"

    - name: Create versioned backup
      file:
        path: "/backup/{{ date_stamp }}"
        state: directory

ข้อควรระวัง: pipe lookup รัน command บน control node เท่านั้น และรันตอน Playbook กำลัง parse variables ไม่ใช่ตอน task execute จึงไม่ควรใช้กับ command ที่มี side effects

Lookup กับ loop: ใช้ with_* Syntax

Lookup plugins หลายตัวถูกออกแบบมาใช้คู่กับ loop หรือ with_* syntax เพื่อวน iterate ข้อมูล

---
- name: Lookup with loop
  hosts: all
  tasks:
    # วนอ่านหลายไฟล์
    - name: Deploy multiple config files
      copy:
        content: "{{ lookup('file', item) }}"
        dest: "/etc/myapp/{{ item | basename }}"
      loop:
        - files/app.conf
        - files/db.conf
        - files/cache.conf

    # ใช้ with_fileglob แทน loop + fileglob
    - name: Deploy all .conf files
      copy:
        src: "{{ item }}"
        dest: /etc/myapp/
      with_fileglob:
        - "files/*.conf"

สรุป

Lookup plugins เป็นสะพานเชื่อมระหว่าง Playbook กับแหล่งข้อมูลภายนอก ทำให้ไม่ต้อง hardcode ค่าสำคัญลงใน variable files โดยตรง Pattern ที่ควรจำ: ใช้ file สำหรับอ่านไฟล์ key หรือ cert, ใช้ env ใน CI/CD pipeline, ใช้ password สำหรับ random credentials ที่ต้องการ consistency, และใช้ pipe สำหรับ dynamic values จาก command

Lookup ทั้งหมดรันบน control node เสมอ ถ้าต้องการข้อมูลจาก remote host ต้องใช้ register + fact แทน