Skrip yang berjalan di laptop bisa menjadi bencana di produksi. Episode ini membahas standar menulis skrip PowerShell profesional: organisasi kode, konvensi penamaan, comment-based help, penanganan error, efisiensi pipeline, dan keamanan kredensial.

Di episode 21 kalian mengelola jaringan dengan rangkaian perintah yang rapi. Tapi perhatikan: setiap perintah yang kalian ketik langsung di konsol adalah satu kali pakai. Ketika perintah itu menjadi skrip — dan skrip itu dipakai tim, dijadwalkan oleh Task Scheduler, atau dijalankan oleh CI/CD — standar mulai berlaku. Skrip yang berjalan mulus di laptop kalian bisa menjadi bencana di produksi.
Bayangkan perbedaan antara menulis catatan untuk diri sendiri dan menulis manual operasional untuk tim. Catatan diri boleh berantakan; manual operasional harus bisa dibaca, diuji, diubah, dan diandalkan oleh orang lain — termasuk kalian sendiri enam bulan kemudian. Episode ini adalah tentang menjadikan skrip kalian manual operasional: organisasi kode yang rapi, penamaan yang konsisten, dokumentasi otomatis, penanganan error yang matang, efisiensi, dan keamanan.
Skrip yang baik punya struktur yang bisa diprediksi. Pola standar yang disarankan:
C:\Scripts\
module\ # fungsi-fungsi yang bisa dipakai ulang
main.ps1 # entry point: memanggil fungsi
config.json # konfigurasi terpisah dari kode
log\ # output logPrinsipnya sederhana: konfigurasi terpisah dari kode, log terpisah dari skrip, dan fungsi terpisah dari alur utama. Skrip utama (entry point) seharusnya pendek — ia hanya mengatur urutan pemanggilan fungsi, bukan berisi ribuan baris logika.
Inilah jantung organisasi kode: satu fungsi, satu tanggung jawab. Fungsi yang melakukan dua hal (misalnya "validasi data lalu kirim email") sulit diuji dan sulit dipakai ulang. Pecah menjadi Test-InputData dan Send-ReportEmail.
Alur utama skrip yang baik terlihat seperti daftar isi:
# main.ps1
$config = Get-Content "C:\Scripts\config.json" | ConvertFrom-Json
$data = Get-RevenueData -Server $config.DatabaseServer
$report = Build-RevenueReport -Data $data
Send-RevenueReport -Report $report -Recipients $config.EmailRecipientsSetiap baris adalah satu langkah yang jelas. Kalau satu langkah gagal, kalian langsung tahu harus membenahi fungsi yang mana — bukan mengurai spaghetti 500 baris.
Penamaan variabel yang konsisten membuat skrip terbaca tanpa komentar. Aturan umum komunitas PowerShell:
| Konvensi | Contoh | Keterangan |
|---|---|---|
| camelCase | $userName, $lastLogon | Variabel biasa |
| PascalCase | $HostName, $ReportPath | Variabel global yang disengaja |
| Jangan pakai | $x, $data2, $temp | Tidak deskriptif |
Hindari nama variabel yang menyerupai cmdlet ($Get-Process), kata yang mirip cmdlet, atau singkatan yang hanya kalian mengerti. $al tidak ada artinya; $activeList jelas.
Fungsi wajib mengikuti pola Verb-Noun dengan verb dari daftar resmi PowerShell. Ini bukan sekadar gaya — pola ini membuat fungsi kalian bekerja dengan Get-Help, autocomplete, dan Get-Command seperti cmdlet bawaan:
# Salah: kata kerja tidak dikenal, tidak konsisten
function AmbilData { ... }
function process_user { ... }
# Benar: Verb-Noun, PascalCase
function Get-UserData { ... }
function Export-UserReport { ... }Kata kerja yang sering dipakai: Get, Set, New, Remove, Start, Stop, Test, Convert, Export, Import. Cek daftar lengkapnya dengan Get-Verb.
Komentar hanya menjelaskan mengapa, bukan apa — karena "apa" sudah dijawab oleh nama fungsi yang baik. Untuk dokumentasi yang benar-benar berguna, gunakan comment-based help: blok komentar khusus yang dikenali PowerShell sebagai bantuan resmi fungsi.
<#
.SYNOPSIS
Menghasilkan laporan user aktif di Active Directory.
.DESCRIPTION
Mengambil semua user berstatus aktif dan menuliskannya ke file CSV.
.PARAMETER OutputPath
Path file CSV tujuan.
.EXAMPLE
Export-ActiveUsers -OutputPath "C:\Data\active-users.csv"
.EXAMPLE
Get-ADUser -Filter "Enabled -eq 'true'" |
Export-ActiveUsers -OutputPath "C:\Data\active-users.csv"
.NOTES
Author: Arman Dwi Pangestu
Versi: 1.0
#>
function Export-ActiveUsers {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$OutputPath
)
process {
Get-ADUser -Filter "Enabled -eq 'true'" |
Select-Object SamAccountName, DisplayName, LastLogonDate |
Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
}
}Karena comment-based help terintegrasi dengan mesin bantuan PowerShell, kalian mendapat dokumentasi otomatis:
Get-Help Export-ActiveUsers -Full
Get-Help Export-ActiveUsers -ExamplesGet-Help membaca blok .SYNOPSIS, .DESCRIPTION, .PARAMETER, .EXAMPLE, dan .NOTES langsung dari fungsi — dokumentasi yang tidak akan ketinggalan zaman karena hidup berdampingan dengan kode.
Tip
Menulis .EXAMPLE bukan untuk estetika — ini adalah dokumentasi yang bisa diuji. Kalian bisa menyalin contoh dari Get-Help -Examples dan menjalankannya. Contoh yang berfungsi adalah spesifikasi sekaligus bukti bahwa skrip bekerja.
Skrip production tidak boleh diam-diam melanjutkan eksekusi setelah kegagalan. Pola standar: -ErrorAction Stop pada operasi yang kritis, lalu tangkap dengan try-catch:
try {
$response = Invoke-WebRequest -Uri "https://api.example.com/data" -ErrorAction Stop
Write-Output "Berhasil: $($response.StatusCode)"
} catch {
Write-Error "Gagal mengambil data: $($_.Exception.Message)"
exit 1
}exit 1 di akhir blok catch memberitahu pemanggil (Task Scheduler, CI/CD) bahwa skrip gagal — jangan pernah keluar dengan 0 padahal proses gagal, karena itu kebohongan yang meracuni monitoring.
Skrip produksi dijalankan oleh orang yang mungkin bukan penulis skrip. Pesan error harus menjawab tiga pertanyaan: apa yang gagal, kenapa, dan apa yang harus dilakukan:
catch {
Write-Error "Backup gagal: file sumber tidak ditemukan. Periksa path di config.json"
Write-Error "Detail teknis: $($_.Exception.Message)"
}Baris pertama untuk manusia, baris kedua untuk debugging. Jangan memaksa user membaca stack trace mentah sebagai satu-satunya penjelasan.
PowerShell terkenal lambat bila dipakai salah. Perbedaan paling berpengaruh: foreach (statement) vs ForEach-Object (cmdlet pipeline):
# Lambat: setiap objek melewati pipeline satu per satu
Get-Content "C:\Data\ips.txt" |
ForEach-Object { Test-Connection -ComputerName $_ -Count 1 }
# Cepat: kumpulkan dulu, proses dalam blok tunggal
$ips = Get-Content "C:\Data\ips.txt"
foreach ($ip in $ips) {
Test-Connection -ComputerName $ip -Count 1
}Untuk 10 item perbedaannya tak terasa; untuk 100 ribu item, foreach bisa beberapa kali lebih cepat karena tidak ada overhead pipeline per objek. Pilih ForEach-Object saat streaming penting (proses data yang tidak bisa dimuat penuh ke memori); pilih foreach untuk kecepatan.
Prinsip paling berdampak: filter di sumber, bukan di akhir. Mengambil 10 ribu objek lalu menyaring di Where-Object membuang waktu di setiap langkah. Cmdlet AD bahkan mendukung filter di sisi server:
# Buruk: bawa semua, saring di akhir
Get-ADUser -Filter * | Where-Object { $_.Enabled }
# Baik: server yang menyaring
Get-ADUser -Filter "Enabled -eq 'true'"Pola yang sama berlaku untuk Get-Service -Name "*sql*" ketimbang menyaring semua service secara manual. Semakin sedikit data yang melewati pipeline, semakin cepat skripnya.
Aturan absolut: jangan pernah menaruh password mentah di dalam skrip atau file skrip. Password dalam teks terbuka adalah bom waktu — satu file ter-exfiltrasi, seluruh sistem terbuka. Dua pola yang benar:
$cred = Get-Credential -UserName "svc-backup"
# Simpan terenkripsi untuk sesi berikutnya
$cred | Export-CliXml -Path "C:\Secure\cred.xml"
$cred = Import-CliXml -Path "C:\Secure\cred.xml"Get-Credential meminta user mengisi kredensial lewat dialog aman. Export-CliXml menyimpannya ke file yang dienkripsi dengan kunci mesin (DPAPI) — hanya bisa dibaca oleh user dan mesin yang sama, dan tidak pernah menyimpan password sebagai teks terbuka.
Read-Host -AsSecureString untuk input password interaktif tanpa menampilkan ke layar.-SkipCertificateCheck tanpa alasan kuat, karena itu membuka pintu untuk serangan man-in-the-middle.Important
SecureString bukan enkripsi — ia hanyalah penyembunyian di memori. Keamanan sebenarnya datang dari tiga hal: tidak menaruh secret di dalam kode, membatasi siapa yang punya akses ke file secret, dan memastikan secret disimpan dengan mekanisme yang dirancang untuk itu (Credential Manager, vault, atau CI/CD secret store).
Di episode 22 ini kalian telah belajar menulis skrip yang layak produksi: organisasi kode dengan fungsi satu-tanggung-jawab dan pemisahan konfigurasi dari logika; konvensi penamaan variabel camelCase dan fungsi Verb-Noun; dokumentasi lewat comment-based help yang otomatis tersedia di Get-Help; penanganan error dengan try-catch dan pesan yang ramah; optimasi performa dengan pipeline efisien dan filter sedini mungkin; serta keamanan kredensial dengan Get-Credential, Export-CliXml, dan larangan keras password hardcoded.
Poin kunci yang harus kalian bawa:
exit 1, bukan diam-diam sukses.foreach untuk kecepatan, ForEach-Object untuk streaming.Skrip kalian kini rapi, cepat, aman, dan terdokumentasi. Tapi ada satu pertanyaan yang belum terjawab: bagaimana kalian tahu skrip itu benar-benar bekerja saat masalah terjadi? Di episode 23 selanjutnya kita akan membahas Logging & Debugging: menulis log yang informatif, memakai breakpoint dan stepping di debugger, debugging di VS Code, transcript sesi, hingga profiling dengan Measure-Command dan performance counter. Sampai jumpa!