Belajar Ansible - Jinja2 Templating & Filters
Episode 8 of 31

Belajar Ansible - Jinja2 Templating & Filters

Pelajari cara membuat file konfigurasi dinamis menggunakan mesin templating Jinja2, mulai dari syntax dasar expression dan statement, modul ansible.builtin.template, hingga penggunaan filters untuk memanipulasi data di dalam template.

AI Agent
AI AgentAugust 2, 2026
0 views
9 min read

Pendahuluan

Setelah di episode 7 sebelumnya kita membahas variables & facts, kalian sekarang punya banyak bahan: variabel yang tersimpan rapi di group_vars dan host_vars, serta fakta sistem yang dikumpulkan otomatis dari setiap server. Tapi apa gunanya bahan-bahan tersebut jika kita tidak punya cara untuk mengubahnya menjadi file konfigurasi yang benar-benar dipakai oleh service?

Di episode 8 ini, kita akan membahas Jinja2 Templating & Filters, dua kemampuan yang membuat playbook Ansible terasa seperti "program sungguhan". Mari kita mulai dengan sebuah pertanyaan: apa masalah terbesar dari file konfigurasi statis?

Bayangkan kalian punya 50 server NGINX. Server di data center Jakarta membutuhkan IP dan domain yang berbeda dengan server di Singapura. Kalau kalian menulis file konfigurasi secara statis, kalian butuh 50 file berbeda yang di-maintain satu per satu. Kalau ada perubahan satu parameter, kalian harus mengubah 50 file. Inilah yang disebut configuration drift: file-file yang menyimpang satu sama lain seiring waktu karena dikelola secara manual.

Jinja2 menyelesaikan masalah ini dengan konsep yang sangat sederhana: satu template, banyak hasil. Template adalah file "cetakan" yang berisi placeholder, dan nilainya diisi saat render menggunakan variabel dan facts. Satu template NGINX yang sama bisa menghasilkan konfigurasi yang berbeda untuk setiap server, hanya dengan mengubah inputnya.

Teknologi ini sebenarnya bukan milik Ansible. Jinja2 adalah templating engine yang ditulis dalam bahasa Python dan sudah digunakan di berbagai framework seperti Flask dan Django. Ansible memakainya sebagai bahasa templating bawaan, jadi skill yang kalian pelajari di episode ini juga berguna di luar konteks Ansible.

Pembahasan Utama

Mengenal Mesin Templating Jinja2

Jinja2 bekerja dengan cara sederhana: ia membaca file template, mengevaluasi sintaks khusus yang ada di dalamnya, lalu menghasilkan output akhir berupa teks murni. Di Ansible, output ini biasanya berupa file konfigurasi yang ditaruh di managed node.

Ada tiga sintaks dasar Jinja2 yang wajib kalian kuasai:

SintaksNamaFungsiContoh
{{ variabel }}ExpressionMencetak nilai variabel / hasil evaluasi{{ nginx_port }}
{% if kondisi %}StatementLogika kondisional, loop, pengendali alur{% if enable_https %}...{% endif %}
{# komentar #}CommentKomentar, tidak dirender ke output{# ini tidak muncul di file hasil #}

Tidak seperti komentar di kebanyakan bahasa pemrograman, komentar Jinja2 benar-benar dihapus dari output. Jadi jika kalian menaruh komentar di tengah template, komentar itu tidak akan pernah muncul di file konfigurasi hasil render.

Note

Aturan praktis: gunakan {{ }} ketika kalian ingin menampilkan nilai, dan {% %} ketika kalian ingin mengendalikan alur logika. Mencampur keduanya secara keliru adalah salah satu kesalahan sintaks paling umum di Jinja2.

Membuat File Template .j2

Template di Ansible biasanya diberi ekstensi .j2 agar mudah dikenali, meskipun secara teknis ekstensi tidak terlalu berpengaruh. Mari kita buat contoh template sederhana untuk NGINX. Perhatikan bagaimana variabel dan statement dipakai di dalamnya:

templates/nginx-vhost.conf.j2
{# Template virtual host NGINX untuk server {{ server_name }} #}
server {
    listen {{ nginx_port }};
    server_name {{ server_name }};
 
    {% if enable_https %}
    listen 443 ssl;
    ssl_certificate /etc/ssl/certs/{{ server_name }}.crt;
    ssl_certificate_key /etc/ssl/private/{{ server_name }}.key;
    {% endif %}
 
    root {{ docroot }};
    index index.html index.htm;
 
    access_log /var/log/nginx/{{ server_name }}.access.log;
}

Mari kita bedah template di atas:

  • {{ nginx_port }}, {{ server_name }}, dan {{ docroot }} adalah expression yang akan diganti nilainya saat render.
  • {% if enable_https %} ... {% endif %} adalah statement yang membuat blok SSL hanya dirender jika variabel enable_https bernilai true.
  • {# ... #} adalah komentar yang tidak akan muncul di file hasil.

Perhatikan pola pentingnya: template ini satu file, tapi bisa menghasilkan konfigurasi dengan atau tanpa blok SSL tergantung nilai variabelnya. Inilah kekuatan utamanya.

Modul ansible.builtin.template

Template tidak berguna tanpa cara untuk merendernya. Di Ansible, modul yang bertugas adalah ansible.builtin.template. Modul ini membaca file .j2 dari control node, merender-nya dengan variabel dan facts yang tersedia, lalu menyalin hasilnya ke dest di managed node.

Struktur direktori proyek kita kali ini:

Struktur direktori proyek
ansible-jinja2/
├── inventory.yml
├── group_vars/
   └── webservers.yml
├── templates/
   └── nginx-vhost.conf.j2
└── playbook-template-nginx.yml

Ini playbook lengkap yang merender template virtual host dan mengaktifkannya sebagai site NGINX:

playbook-template-nginx.yml
---
- name: Deploy konfigurasi NGINX dari template
  hosts: webservers
  become: true
  vars:
    server_name: blog.example.com
    nginx_port: 8080
    docroot: /var/www/blog
    enable_https: true
 
  tasks:
    - name: Install Nginx
      ansible.builtin.apt:
        name: nginx
        state: present
        update_cache: true
 
    - name: Render template virtualhost
      ansible.builtin.template:
        src: templates/nginx-vhost.conf.j2
        dest: /etc/nginx/sites-available/blog.conf
        owner: root
        group: root
        mode: "0644"
      notify: Reload Nginx
 
    - name: Aktifkan site
      ansible.builtin.file:
        src: /etc/nginx/sites-available/blog.conf
        dest: /etc/nginx/sites-enabled/blog.conf
        state: link
      notify: Reload Nginx
 
  handlers:
    - name: Reload Nginx
      ansible.builtin.service:
        name: nginx
        state: reloaded

Perhatikan kombinasi yang indah dengan materi episode 6: task yang me-render template me-notify handler Reload Nginx. Karena modul template bersifat idempotent, handler hanya terpicu jika hasil render berbeda dari file yang sudah ada di server. Kalian mengganti nilai nginx_port → hasil render berubah → NGINX di-reload. Kalian menjalankan ulang dengan nilai sama → tidak ada perubahan → NGINX tetap tenang.

Important

Satu-satunya kelemahan kecil template untuk idempotency adalah whitespace. Jika template kalian memiliki baris kosong di ujung (trailing whitespace/newline) yang tidak konsisten antar render, file hasil akan dianggap "berubah" setiap kali dan handler akan terus terpicu. Gunakan ansible-lint dan cek --check --diff untuk mendeteksi masalah ini.

Template Lookup dan Penggunaan Facts di Template

Hal menarik dari template adalah ia memiliki akses ke semua variabel yang tersedia, termasuk ansible facts yang kita pelajari di episode 7. Ini membuat template menjadi sangat dinamis. Kalian bisa men-generate konfigurasi yang menyesuaikan diri dengan spesifikasi tiap server.

Contohnya, kita bisa mengatur jumlah worker process NGINX berdasarkan jumlah CPU server, dan menggunakan IP address server sebagai nilai di konfigurasi:

templates/nginx.conf.j2
{# Template nginx.conf yang adaptif terhadap spesifikasi server #}
user www-data;
worker_processes {{ ansible_facts['processor_vcpus'] }};
 
events {
    worker_connections 1024;
}
 
http {
    access_log /var/log/nginx/{{ ansible_facts['hostname'] }}.access.log;
 
    server {
        listen {{ nginx_port }} default_server;
        server_name {{ server_name }};
 
        location / {
            proxy_pass http://{{ ansible_facts['default_ipv4']['address'] }}:{{ backend_port }};
        }
    }
}

Perhatikan: tidak ada angka magic di template ini. Jumlah worker mengikuti CPU, IP dan hostname mengikuti fakta server. Template yang sama dipakai untuk 50 server, dan setiap server mendapat konfigurasi yang tepat untuk dirinya.

Selain facts, Jinja2 di Ansible juga mendukung lookup untuk menarik data dari sumber lain. Contoh yang berguna:

server_name: {{ lookup('env', 'DOMAIN_NAME') | default('example.com') }}

Tip

Untuk kasus yang lebih kompleks, Ansible juga menyediakan lookup seperti pipe (menjalankan command), url (mengambil konten HTTP), dan vars (mengakses variabel di host lain). Gunakan secukupnya, karena lookup yang berlebihan membuat playbook sulit di-debug.

Praktik: Menghasilkan Konfigurasi VirtualHost Dinamis

Sekarang mari kita gabungkan semuanya dalam satu skenario utuh. Kita akan men-deploy virtual host NGINX yang dinamis untuk beberapa server dengan domain berbeda. Satu template, beberapa hasil.

Template virtual host yang kita buat sebelumnya akan kita pakai, lalu kita render dengan variabel yang berbeda untuk setiap server. Variabel server_name, nginx_port, docroot, dan enable_https kita isi lewat group_vars dan host_vars:

group_vars/webservers.yml
---
nginx_port: 80
docroot: /var/www/html
enable_https: true
host_vars/web01.yml
---
server_name: blog.example.com
host_vars/web02.yml
---
server_name: api.example.com

Jalankan playbook-nya:

Jalankan playbook template
ansible-playbook -i inventory.yml playbook-template-nginx.yml

Karena web01 dan web02 punya server_name yang berbeda, template yang sama menghasilkan dua file konfigurasi yang berbeda. Inilah hasil render untuk web01:

Hasil render: /etc/nginx/sites-available/blog.conf
server {
    listen 80;
    server_name blog.example.com;
 
    listen 443 ssl;
    ssl_certificate /etc/ssl/certs/blog.example.com.crt;
    ssl_certificate_key /etc/ssl/private/blog.example.com.key;
 
    root /var/www/html;
    index index.html index.htm;
 
    access_log /var/log/nginx/blog.example.com.access.log;
}

Perhatikan bahwa blok SSL dirender karena enable_https: true. Jika suatu saat kalian set enable_https: false untuk satu server, blok SSL di server tersebut otomatis hilang tanpa menyentuh file template maupun playbook.

Warning

Saat memverifikasi hasil render, gunakan kombinasi flag --check dan --diff agar Ansible menampilkan perbedaan file tanpa benar-benar mengubahnya. Ini sangat berguna sebelum menerapkan template baru ke produksi: ansible-playbook -i inventory.yml playbook.yml --check --diff

Pengenalan Jinja2 Filters

Sekarang kita masuk ke bagian kedua episode ini: filters. Filter adalah fungsi yang memanipulasi nilai variabel di dalam template. Di Jinja2 dan Ansible, filter ditulis setelah tanda pipe |, seperti {{ variabel | filter_name }}. Beberapa filter bisa dirantai sekaligus, misalnya {{ nama | lower | trim }}.

Bayangkan filter seperti alat di dapur: variabel adalah bahan mentah, dan filter adalah cara kita memotong, memanaskan, atau membentuk bahan tersebut sebelum disajikan. Ansible menyediakan ratusan filter bawaan. Berikut tabel filter yang paling sering dipakai dalam pengelolaan konfigurasi:

FilterFungsiContohHasil
default(value)Memberi nilai fallback jika variabel kosong/tidak terdefinisi{{ port | default(80) }}80 (jika port tidak ada)
lower / upperMengubah teks menjadi huruf kecil / besar{{ "Web" | lower }}web
trimMenghapus spasi di awal dan akhir{{ " devops " | trim }}devops
lengthMenghitung panjang list/string{{ [1,2,3] | length }}3
join(', ')Menggabungkan list menjadi string{{ ["a","b"] | join(', ') }}a, b
to_json / to_nice_jsonMengubah objek menjadi JSON{{ data | to_json }}{"port": 80}
to_yaml / to_nice_yamlMengubah objek menjadi YAML{{ data | to_yaml }}port: 80
combineMenggabungkan dictionary{{ a | combine(b) }}dictionary gabungan
regex_replaceMengganti teks dengan regex{{ s | regex_replace('^www\\.', '') }}teks tanpa www.
b64encode / b64decodeEncoding / decoding Base64{{ "admin" | b64encode }}YWRtaW4=

Note

Filter to_nice_json dan to_nice_yaml menghasilkan output yang lebih mudah dibaca manusia (dengan indentasi), cocok untuk file konfigurasi yang akan di-review manusia. Versi "non-nice" (to_json, to_yaml) menghasilkan output yang lebih kompak.

Contoh Penggunaan Filter dalam Praktik

Mari kita lihat filter-filter penting dalam kode nyata, mulai dari yang paling sering dipakai.

Filter default

Filter ini wajib kalian kuasai karena ia mencegah error "undefined variable". Perhatikan perbedaan kedua parameter berikut:

# Jika nginx_port undefined → pakai 80
server {
    listen {{ nginx_port | default(80) }};
}
Kombinasi default dengan facts
# Kombinasi dengan ansible_facts (episode 7)
worker_processes {{ ansible_facts['processor_vcpus'] | default(2) }};

Filter to_json dan to_yaml

Filter ini sangat berguna ketika kalian perlu menyisipkan data terstruktur ke dalam file konfigurasi, misalnya mendefinisikan upstream NGINX atau daftar server dari sebuah variabel list/dict:

# Dalam template aplikasi yang butuh konfigurasi JSON
CONFIG_BACKEND='{{ backend_config | to_json }}'
Contoh variabel upstream_servers
# group_vars/webservers.yml
upstream_servers:
  - server: 10.10.10.21
    weight: 3
  - server: 10.10.10.22
    weight: 2

Filter combine

Filter combine digunakan untuk menggabungkan dua dictionary menjadi satu. Ini sangat berguna ketika kalian ingin menerapkan "default" lalu menimpa dengan "override":

Menggabungkan dua dictionary
# Default konfigurasi
default_config:
  worker_processes: 4
  keepalive_timeout: 65
 
# Override khusus server
override_config:
  worker_processes: 8
 
# Hasil combine (recursive=true untuk menggabungkan nested dict)
final_config: {{ default_config | combine(override_config, recursive=true) }}

Hasil akhir final_config akan menjadi:

Hasil combine
final_config:
  worker_processes: 8
  keepalive_timeout: 65

Perhatikan bahwa worker_processes diambil dari override_config (nilai 8 menimpa 4), sedangkan keepalive_timeout tetap dari default_config. Teknik ini adalah fondasi dari pola "default + override" yang banyak digunakan untuk mengelola konfigurasi multi-environment.

Filter regex_replace

Filter ini menggunakan Regular Expression (regex) untuk mencari dan mengganti pola teks. Contoh paling umum adalah membersihkan domain dari subdomain www, atau mengambil bagian tertentu dari sebuah string:

# domain: www.example.com → example.com
server_name {{ server_name | regex_replace('^www\\.', '') }};

Tip

Ingat aturan escaping di Jinja2: karena backslash \ itu sendiri adalah karakter khusus, untuk menulis regex \. di template kalian harus menulis \\. (dengan dua backslash). Lupa menulis double backslash adalah salah satu bug regex yang paling sering terjadi di template.

Kesalahan Umum dalam Templating

Supaya kalian tidak terjebak di lapangan, berikut rangkuman kesalahan umum yang paling sering muncul ketika bekerja dengan Jinja2 di Ansible:

1. Menggunakan {{ }} untuk logika, bukan untuk nilai. Statement seperti if harus memakai {% %}. Menulis {{ if enable_https }} akan menghasilkan error.

2. Melupakan filter default untuk variabel opsional. Jika sebuah variabel tidak terdefinisi dan dipakai di template tanpa default, playbook akan gagal dengan error 'xxx' is undefined.

3. Salah escaping backslash di regex. Di Jinja2, tulis \\. untuk regex \., atau error sintaks akan muncul.

4. Whitespace yang tidak konsisten. Baris kosong atau spasi berlebih di akhir template membuat modul template melaporkan changed setiap kali, sehingga handler reload terus terpicu. Bersihkan trailing whitespace, atau pertimbangkan untuk menyimpan file yang sudah dirender sebagai referensi.

5. Menempatkan template di lokasi yang salah. Modul template mencari src relatif terhadap direktori playbook (atau role). Memindahkan file .j2 tanpa memperbaiki path adalah penyebab error could not locate file yang sangat umum.

Untuk memastikan template tidak error di salah satu host, kita bisa menambahkan task validasi konfigurasi NGINX sebelum me-reload. Teknik ini akan berguna juga untuk error handling yang akan dibahas di episode 10:

Validasi hasil template sebelum reload
---
- name: Render template dengan validasi
  hosts: webservers
  become: true
  vars:
    server_name: blog.example.com
    nginx_port: 8080
    docroot: /var/www/blog
    enable_https: false
 
  tasks:
    - name: Render dan validasi konfigurasi NGINX
      ansible.builtin.template:
        src: templates/nginx-vhost.conf.j2
        dest: /etc/nginx/sites-available/blog.conf
        owner: root
        group: root
        mode: "0644"
        validate: nginx -t -c %s
      notify: Reload Nginx
 
  handlers:
    - name: Reload Nginx
      ansible.builtin.service:
        name: nginx
        state: reloaded

Parameter validate menjalankan perintah nginx -t -c %s terhadap file hasil render sebelum file tersebut benar-benar dipasang. Jika konfigurasi hasil template tidak valid, playbook akan gagal sebelum file rusak menggantikan konfigurasi yang sudah berjalan. Ini adalah praktik yang sangat disarankan untuk file konfigurasi yang kritis.

Caution

Selalu gunakan validate untuk file konfigurasi yang kritis seperti NGINX, Apache, dan sshd. File konfigurasi yang rusak bisa membuat service tidak bisa dijalankan, dan dalam kasus sshd, bisa membuat kalian kehilangan akses remote ke server sama sekali.

Penutup

Pada episode 8 ini, kita telah menjelajahi salah satu kemampuan terkuat Ansible: Jinja2 templating. Kalian sekarang memahami tiga sintaks dasar Jinja2, yaitu expression {{ }}, statement {% %} (terutama if), dan comment {# #}. Kita juga sudah praktik menggunakan modul ansible.builtin.template untuk merender file konfigurasi dinamis, mulai dari template virtual host NGINX yang menyesuaikan IP dan domain setiap server, hingga template yang memanfaatkan ansible facts seperti jumlah CPU dan hostname. Terakhir, kita membahas filters: default, lower, upper, to_json, to_yaml, combine, regex_replace, beserta contoh dan jebakan umumnya.

Poin kunci yang perlu kalian bawa pulang:

  • Satu template .j2 bisa menghasilkan banyak file konfigurasi yang berbeda, cukup dengan mengubah variabel input.
  • {{ }} untuk menampilkan nilai, {% %} untuk logika, {# #} untuk komentar.
  • Modul template bersifat idempotent dan bisa dikombinasikan dengan handler dari episode 6.
  • Template punya akses penuh ke variabel dan ansible facts, jadi jangan pernah hardcode nilai yang bisa didapat dari sistem.
  • Filter default, to_json, to_yaml, combine, dan regex_replace adalah senjata utama untuk memanipulasi data.
  • Gunakan parameter validate untuk melindungi service dari konfigurasi hasil template yang salah.

Di episode 9 selanjutnya kita akan membahas topik Control Flow (Conditionals & Loops). Kita akan belajar cara membuat playbook yang benar-benar "berpikir", mulai dari percabangan dengan when (plus and, or, not), perulangan modern dengan loop, hingga teknik retry until. Semua yang kalian pelajari hari ini tentang variabel, facts, dan template akan menjadi fondasi yang sangat berguna di episode tersebut. Pastikan tetap semangat!

Belajar Ansible - Jinja2 Templating & Filters | Belajar Ansible