การ integrate Ansible กับ CI/CD pipeline อย่าง GitHub Actions ต้องจัดการ vault password อย่างระมัดระวัง — บน local เราใส่ password ใน file ที่ไม่ได้ commit แต่ใน CI/CD environment ไม่มี file นั้น ต้องส่ง vault password ผ่าน environment variables หรือ secret store ของ CI/CD แทน
บทความนี้ครอบคลุมวิธีใช้ Ansible Vault ใน GitHub Actions อย่างปลอดภัย: การตั้งค่า GitHub Secrets, 3 วิธีส่ง vault password ให้ ansible-playbook, การใช้ ansible-vault สำหรับ secrets ใน pipeline, การ cache dependencies, และ best practices สำหรับ CI/CD secrets management
หลักการจัดการ Vault Password ใน CI/CD
ใน CI/CD pipeline ต้องส่ง vault password ให้ Ansible โดยไม่ hardcode ใน workflow file และไม่เก็บใน repository — GitHub Actions Secrets คือที่เหมาะสมที่สุดสำหรับเก็บ vault password
# หลักการพื้นฐาน
# 1. vault password เก็บใน GitHub Secrets (ไม่ใช่ repository)
# 2. Workflow อ่าน secret ผ่าน env variable
# 3. ส่ง vault password ให้ ansible-playbook 3 วิธี:
# a. --vault-password-file (ใช้ temp file)
# b. ANSIBLE_VAULT_PASSWORD_FILE environment variable
# c. stdin (pipe password)
# ตัวอย่าง: ส่ง vault password ผ่าน stdin
echo "$VAULT_PASSWORD" | ansible-playbook site.yml --vault-password-file /dev/stdin
# ตัวอย่าง: ใช้ temp file (ปลอดภัยกว่า stdin)
echo "$VAULT_PASSWORD" > /tmp/vault_pass
chmod 600 /tmp/vault_pass
ansible-playbook site.yml --vault-password-file /tmp/vault_pass
rm -f /tmp/vault_pass
ตั้งค่า GitHub Secrets
ก่อนเขียน workflow ต้องตั้งค่า secrets ใน GitHub repository ก่อน — ไปที่ Settings → Secrets and variables → Actions → New repository secret
# GitHub Secrets ที่ต้องตั้งค่า
# ANSIBLE_VAULT_PASSWORD — vault password สำหรับ decrypt vault files
# ตั้งค่าที่: Settings → Secrets → New repository secret
# Name: ANSIBLE_VAULT_PASSWORD
# Value: [vault_password_ที่ใช้ encrypt ไฟล์]
# สำหรับ multiple environments — ตั้งแยกตาม environment:
# ANSIBLE_VAULT_PASSWORD_PRODUCTION
# ANSIBLE_VAULT_PASSWORD_STAGING
# SSH private key สำหรับ connect ไป managed nodes
# ANSIBLE_SSH_PRIVATE_KEY
# Value: [private key content]
# ข้อควรระวัง:
# - ห้ามใส่ค่า secret ใน workflow YAML โดยตรง
# - ห้าม print หรือ echo secrets ใน workflow
# - ใช้ environment ต่างกันสำหรับ production/staging
GitHub Actions Workflow — Deploy ด้วย Ansible Vault
ตัวอย่าง workflow สมบูรณ์สำหรับ deploy ด้วย Ansible Playbook ที่ใช้ vault secrets — รองรับทั้ง staging และ production ด้วย vault password แยกกัน
# .github/workflows/deploy.yml
name: Deploy with Ansible
on:
push:
branches:
- main # deploy production เมื่อ merge ไป main
- staging # deploy staging เมื่อ push ไป staging
env:
ANSIBLE_HOST_KEY_CHECKING: false
ANSIBLE_STDOUT_CALLBACK: yaml
jobs:
deploy:
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: pip
- name: Install Ansible
run: |
pip install ansible==9.0.0
- name: Install Ansible requirements
run: |
ansible-galaxy install -r requirements.yml
- name: Set up SSH key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.ANSIBLE_SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H ${{ secrets.TARGET_HOST }} >> ~/.ssh/known_hosts
- name: Set vault password file
run: |
echo "${{ secrets.ANSIBLE_VAULT_PASSWORD }}" > /tmp/vault_pass
chmod 600 /tmp/vault_pass
- name: Run Ansible Playbook
run: |
ansible-playbook \
-i inventory/production \
--vault-password-file /tmp/vault_pass \
site.yml
- name: Clean up vault password
if: always()
run: rm -f /tmp/vault_pass
Multi-Environment Workflow
workflow ที่รองรับหลาย environment พร้อม vault password แยกกัน — ใช้ GitHub Environments feature สำหรับ approval workflow และ environment-specific secrets
# .github/workflows/deploy-multi-env.yml
name: Deploy Multi-Environment
on:
workflow_dispatch:
inputs:
environment:
description: 'Target environment'
required: true
default: 'staging'
type: choice
options:
- staging
- production
jobs:
deploy:
runs-on: ubuntu-22.04
environment: ${{ github.event.inputs.environment }}
steps:
- uses: actions/checkout@v4
- name: Install Ansible
run: pip install ansible==9.0.0
- name: Set vault password
run: |
echo "${{ secrets.VAULT_PASSWORD }}" > /tmp/vault_pass
chmod 600 /tmp/vault_pass
# secrets.VAULT_PASSWORD มาจาก GitHub Environment secrets
# production environment มี VAULT_PASSWORD ต่างจาก staging
- name: Deploy to ${{ github.event.inputs.environment }}
run: |
ansible-playbook \
-i inventory/${{ github.event.inputs.environment }} \
--vault-password-file /tmp/vault_pass \
site.yml \
-e "target_env=${{ github.event.inputs.environment }}"
- name: Cleanup
if: always()
run: rm -f /tmp/vault_pass
Dry Run ก่อน Deploy จริง
เพิ่ม dry run step ก่อน deploy จริง — ใช้ --check flag เพื่อให้ Ansible จำลองการทำงานโดยไม่เปลี่ยนแปลงอะไรจริง ตรวจสอบว่า playbook parse ได้และ vault decrypt ได้ก่อน
# .github/workflows/deploy-with-check.yml
jobs:
validate:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Install Ansible
run: pip install ansible==9.0.0
- name: Set vault password
run: |
echo "${{ secrets.ANSIBLE_VAULT_PASSWORD }}" > /tmp/vault_pass
chmod 600 /tmp/vault_pass
- name: Syntax check
run: |
ansible-playbook \
--vault-password-file /tmp/vault_pass \
--syntax-check \
site.yml
- name: Dry run (check mode)
run: |
ansible-playbook \
-i inventory/staging \
--vault-password-file /tmp/vault_pass \
--check \
site.yml
- name: Cleanup
if: always()
run: rm -f /tmp/vault_pass
deploy:
needs: validate # รอ validate ผ่านก่อน
runs-on: ubuntu-22.04
environment: production
steps:
- uses: actions/checkout@v4
- name: Install Ansible
run: pip install ansible==9.0.0
- name: Set vault password
run: |
echo "${{ secrets.ANSIBLE_VAULT_PASSWORD }}" > /tmp/vault_pass
chmod 600 /tmp/vault_pass
- name: Deploy
run: |
ansible-playbook \
-i inventory/production \
--vault-password-file /tmp/vault_pass \
site.yml
- name: Cleanup
if: always()
run: rm -f /tmp/vault_pass
ใช้ ansible-vault ตรวจสอบใน CI
เพิ่ม CI job สำหรับตรวจสอบว่า vault files ทั้งหมดถูก encrypt และ decrypt ได้จริง — ช่วยป้องกัน vault files ที่ encrypt ด้วย password ผิด หรือ plain-text secrets ที่หลุดเข้า repository
# .github/workflows/vault-check.yml
name: Vault Integrity Check
on:
pull_request:
paths:
- '**/vault.yml'
- 'requirements.yml'
jobs:
vault-check:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Install Ansible
run: pip install ansible==9.0.0
- name: Check vault files are encrypted
run: |
FAILED=0
find . -name "vault.yml" -not -path "./.git/*" | while read f; do
if ! head -1 "$f" | grep -q '^\$ANSIBLE_VAULT'; then
echo "ERROR: $f is NOT encrypted!"
FAILED=1
else
echo "OK: $f"
fi
done
exit $FAILED
- name: Verify vault files can be decrypted
run: |
echo "${{ secrets.ANSIBLE_VAULT_PASSWORD }}" > /tmp/vault_pass
chmod 600 /tmp/vault_pass
find . -name "vault.yml" -not -path "./.git/*" | while read f; do
ansible-vault view --vault-password-file /tmp/vault_pass "$f" > /dev/null
echo "Decryption OK: $f"
done
rm -f /tmp/vault_pass
Cache Ansible Dependencies
Cache pip packages และ Ansible roles เพื่อลดเวลา CI/CD pipeline — สำคัญเมื่อมีหลาย Galaxy roles ที่ต้องดาวน์โหลดทุก run
# ตัวอย่าง: cache pip และ ansible roles
steps:
- uses: actions/checkout@v4
- name: Set up Python with cache
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: pip
cache-dependency-path: requirements-ansible.txt
- name: Cache Ansible roles
uses: actions/cache@v4
with:
path: ~/.ansible/roles
key: ansible-roles-${{ hashFiles('requirements.yml') }}
restore-keys: |
ansible-roles-
- name: Install Ansible
run: pip install -r requirements-ansible.txt
- name: Install Ansible requirements
run: ansible-galaxy install -r requirements.yml
# requirements-ansible.txt
# ansible==9.0.0
# boto3==1.34.0 (สำหรับ AWS dynamic inventory)
ป้องกัน Secret Leaking ใน Logs
GitHub Actions จะ mask secrets โดยอัตโนมัติใน logs แต่ก็ยังต้องระวังไม่ให้ vault content ถูก print ออกมา — ใช้ no_log: true ใน tasks ที่อาจแสดง sensitive values
# ansible tasks ที่จัดการ secrets ต้องใช้ no_log
- name: Create database with password
community.postgresql.postgresql_user:
name: appuser
password: "{{ vault_db_password }}"
priv: "appdb.*:ALL"
no_log: true # ป้องกัน password แสดงใน Ansible output
- name: Deploy app with secret config
template:
src: config.j2
dest: /etc/myapp/config.yml
mode: '0600'
no_log: true # ป้องกัน template content แสดงใน diff output
# GitHub Actions: ห้ามใช้ debug: msg สำหรับ vault variables
- name: Debug app config
debug:
msg: "App is configured" # ✅ ไม่แสดง secret
# msg: "{{ vault_app_secret_key }}" ❌ อย่าทำ!
# ถ้าต้องการ debug ให้ใช้ conditional
- name: Show config status (not secret)
debug:
msg: "vault_db_password is {{ 'defined' if vault_db_password is defined else 'MISSING' }}"
Ansible Vault + HashiCorp Vault ใน CI/CD
สำหรับ production enterprise environment ที่มีความต้องการความปลอดภัยสูง สามารถดึง vault password จาก HashiCorp Vault แทนการเก็บใน GitHub Secrets ตรงๆ — ทำให้ rotate vault password ได้โดยไม่ต้องแก้ GitHub Secrets
# .github/workflows/deploy-with-hcvault.yml
# ดึง Ansible vault password จาก HashiCorp Vault
jobs:
deploy:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
# authenticate กับ HashiCorp Vault ด้วย GitHub OIDC
- name: Import Secrets from HashiCorp Vault
uses: hashicorp/vault-action@v3
with:
url: https://vault.company.com
method: jwt
role: github-actions-deploy
secrets: |
secret/data/ansible/vault password | ANSIBLE_VAULT_PASSWORD ;
secret/data/ansible/ssh private_key | ANSIBLE_SSH_KEY
- name: Install Ansible
run: pip install ansible==9.0.0
- name: Set vault password
run: |
echo "$ANSIBLE_VAULT_PASSWORD" > /tmp/vault_pass
chmod 600 /tmp/vault_pass
echo "$ANSIBLE_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
- name: Deploy
run: |
ansible-playbook \
-i inventory/production \
--vault-password-file /tmp/vault_pass \
site.yml
- name: Cleanup
if: always()
run: rm -f /tmp/vault_pass ~/.ssh/id_rsa
สรุป
การใช้ Ansible Vault ใน CI/CD ต้องการแนวทางที่ชัดเจน: เก็บ vault password ใน GitHub Secrets หรือ secret manager ไม่ใช่ใน repository, ส่ง vault password ผ่าน temp file ที่ลบหลังใช้งาน, เพิ่ม vault integrity check ใน PR pipeline เพื่อตรวจสอบก่อน merge และใช้ no_log: true กับ tasks ที่จัดการ sensitive data
Pattern ที่แนะนำ: ทำ dry run ก่อน deploy จริงเสมอ, แยก GitHub Environment secrets สำหรับ production/staging, และเพิ่ม if: always() ใน cleanup step เพื่อให้ลบ vault password file แม้ workflow fail กลางทาง

