Batch Peserta: Tiga Project
Panduan ini dipakai pemegang satu akun master untuk menyiapkan tiga project kelompok dan memasang akses peserta secara manual, tanpa berkas perantara.
Bacaan ini untuk instruktur dan asisten instruktur. Peserta tidak membacanya, dan tidak ada satu pun langkah di sini yang boleh mengandalkan peserta membaca aturan apa pun.
Akun master │ ├── geoportal-kelompok-a-xxxxx peserta 1 sampai 4 ├── geoportal-kelompok-b-xxxxx peserta 5 sampai 8 └── geoportal-kelompok-c-xxxxx peserta 9 sampai 12Jumlah akun, project, dan peserta per project dibekukan di docs-md/00-INDEX.md bagian
Struktur batch yang dibekukan: 4 akun master, 3 project per akun, 4 peserta per project.
Untuk pekerjaan satu project saja, termasuk alasan di balik setiap pilihan, rujukannya panduan setup project.
1. Siapa mengerjakan apa
Section titled “1. Siapa mengerjakan apa”| Pekerjaan | Master | Peserta | Instruktur |
|---|---|---|---|
| Buat 3 project, tautkan Billing Account | ✅ | ||
| Aktifkan API, firewall, Artifact Registry, budget | ✅ | ||
| Pasang role IAM peserta | ✅ | ||
| Cocokkan IP dengan daftar alamat reserved | ✅ | ||
Tambah record A di DNS domain |
✅ | ||
| Buat VM dalam project sendiri | ✅ | ||
| Kunci alamat IP statis | ✅ | ||
| Laporkan nama, email GCP, IP statis, subdomain | ✅ | ||
| Domain, DNS, dan keputusan sertifikat | ✅ |
2. Sebelum mulai
Section titled “2. Sebelum mulai”# 1. Akun yang aktif harus akun master yang benar.gcloud auth list
# 2. Billing Account yang akan dipakai.gcloud billing accounts list
# 3. Berapa project yang sudah menempel di Billing Account itu.BILLING_ACCOUNT_ID="BILLING_ACCOUNT_ID"gcloud billing projects list --billing-account="$BILLING_ACCOUNT_ID"Batasnya tiga project berbayar per Billing Account, dihitung termasuk project bawaan yang sudah menempel di sana sebelum batch ini.
2.1 Lepaskan slot dari project yang tidak dipakai
Section titled “2.1 Lepaskan slot dari project yang tidak dipakai”Jalankan hanya kalau hasil perintah nomor tiga menunjukkan slot tidak cukup.
PROJECT_LAMA="PROJECT_ID"
gcloud billing projects describe "$PROJECT_LAMA" \ --format="value(billingAccountName,billingEnabled)"
gcloud billing projects unlink "$PROJECT_LAMA"2.2 Kalau isinya masih dibutuhkan, tukar tautannya
Section titled “2.2 Kalau isinya masih dibutuhkan, tukar tautannya”gcloud billing projects link menggantikan tautan lama tanpa memerlukan unlink lebih
dahulu, dan tanpa mematikan apa pun.
gcloud billing projects link "PROJECT_ID" \ --billing-account="BILLING_ACCOUNT_LAIN"2.3 Buat ketiga project untuk akun master baru
Section titled “2.3 Buat ketiga project untuk akun master baru”Lewati bagian ini kalau projectnya sudah ada.
BILLING_ACCOUNT_ID="BILLING_ACCOUNT_ID"BATCH="batch-1"KODE_BIAYA="big-webgis-2026"
SUFFIX="$(openssl rand -hex 3 | cut -c1-5)"PROJECT_A="geoportal-kelompok-a-${SUFFIX}"PROJECT_B="geoportal-kelompok-b-${SUFFIX}"PROJECT_C="geoportal-kelompok-c-${SUFFIX}"
for PROJECT_ID in "$PROJECT_A" "$PROJECT_B" "$PROJECT_C"; do echo "== $PROJECT_ID" if gcloud projects describe "$PROJECT_ID" >/dev/null 2>&1; then echo " sudah ada, dilewati" else gcloud projects create "$PROJECT_ID" \ --labels="batch=${BATCH},billing-code=${KODE_BIAYA}" fi gcloud billing projects link "$PROJECT_ID" \ --billing-account="$BILLING_ACCOUNT_ID"done
# Dipakai oleh seluruh perintah di bagian berikutnya.PROJECT_IDS=( "$PROJECT_A" "$PROJECT_B" "$PROJECT_C" )Nilai BATCH dan KODE_BIAYA wajib huruf kecil. Label GCP dengan huruf besar ditolak dengan
pesan yang menyebut nilainya, bukan nama variabelnya.
Bloknya aman diulang: project yang sudah ada dilewati, dan penautan billing tidak error.
Verifikasi. Ketiganya harus True:
for PROJECT_ID in "${PROJECT_IDS[@]}"; do printf '%-34s %s\n' "$PROJECT_ID" \ "$(gcloud billing projects describe "$PROJECT_ID" --format='value(billingEnabled)')"done3. Aktifkan API bersama
Section titled “3. Aktifkan API bersama”Sekali per project. Peserta tidak mengulanginya.
REGION="asia-southeast2"
PROJECT_IDS=( "geoportal-kelompok-a-xxxxx" "geoportal-kelompok-b-xxxxx" "geoportal-kelompok-c-xxxxx")
APIS=( compute.googleapis.com cloudbuild.googleapis.com artifactregistry.googleapis.com iap.googleapis.com iam.googleapis.com iamcredentials.googleapis.com dns.googleapis.com secretmanager.googleapis.com)
for PROJECT_ID in "${PROJECT_IDS[@]}"; do echo "== $PROJECT_ID" gcloud services enable "${APIS[@]}" --project="$PROJECT_ID"doneiam.googleapis.com wajib ada di daftar itu. Tanpa API tersebut, peserta yang membuat
Service Account sendiri akan gagal dengan PERMISSION_DENIED walaupun role-nya lengkap, dan
pesan errornya tidak menyebut API sama sekali.
domains.googleapis.com sengaja tidak diaktifkan. Pelatihan ini memakai subdomain dari domain
milik instruktur, dan domain itu tidak didaftarkan lewat Cloud Domains.
4. Resource bersama
Section titled “4. Resource bersama”Nama resource berikut sama di semua project, dan itu tidak masalah karena setiap project
memisahkan dirinya sendiri. Yang berbahaya adalah peserta yang membuatnya ulang di dalam
project yang sama dan menerima ALREADY_EXISTS.
for PROJECT_ID in "${PROJECT_IDS[@]}"; do echo "== $PROJECT_ID"
gcloud artifacts repositories create katalog-images \ --repository-format=docker \ --location="$REGION" \ --project="$PROJECT_ID" \ --description="Docker image bersama pelatihan WebGIS" || true
gcloud compute firewall-rules create allow-webgis-http \ --network=default --direction=INGRESS --action=ALLOW \ --rules=tcp:80,tcp:443 --source-ranges=0.0.0.0/0 \ --target-tags=webgis-http --project="$PROJECT_ID" || true
gcloud compute firewall-rules create allow-webgis-iap-ssh \ --network=default --direction=INGRESS --action=ALLOW \ --rules=tcp:22 --source-ranges=35.235.240.0/20 \ --target-tags=webgis-iap-ssh --project="$PROJECT_ID" || truedone|| true membuat pengulangan perintah aman. Tanpa itu, perintah kedua pada project yang sama
menghentikan skrip di tengah jalan.
4.1 Label batch
Section titled “4.1 Label batch”Label memisahkan biaya per batch di Billing Reports. Tanpanya, biaya hanya bisa dipisah per project, dan itu tidak cukup ketika project yang sama dipakai batch kedua.
Label hanya bisa dipasang saat project dibuat lewat gcloud. gcloud projects update tidak
menerima flag label sama sekali; --update-labels ditolak dengan unrecognized arguments.
Project yang terlanjur dibuat tanpa label: periksa dengan
gcloud projects describe PROJECT_ID --format="value(labels)". Keluaran kosong berarti belum
berlabel, dan menambahkannya lewat Console pada IAM & Admin, Manage Resources, bagian
Labels. Console satu-satunya jalur yang pasti tersedia tanpa memasang komponen alpha.
Label tidak wajib untuk menjalankan pelatihan, tetapi label yang tidak ada tidak memunculkan error apa pun; ia hanya membuat pemisahan biaya per batch menjadi tidak mungkin.
4.2 Akses GitHub connection
Section titled “4.2 Akses GitHub connection”Cloud Build menyimpan token GitHub di Secret Manager. Sebelum peserta membuat GitHub connection, identitas layanan Cloud Build harus ada dan punya akses ke sana. Langkah ini dijalankan untuk ketiga project, karena setiap project punya Service Agent sendiri.
for PROJECT_ID in "${PROJECT_IDS[@]}"; do echo "== $PROJECT_ID"
gcloud beta services identity create \ --service=cloudbuild.googleapis.com \ --project="$PROJECT_ID"
NOMOR="$(gcloud projects describe "$PROJECT_ID" \ --format="value(projectNumber)")"
gcloud projects add-iam-policy-binding "$PROJECT_ID" \ --member="serviceAccount:service-${NOMOR}@gcp-sa-cloudbuild.iam.gserviceaccount.com" \ --role="roles/secretmanager.admin" \ --condition=NonedoneVerifikasi. Setiap project harus menghasilkan satu alamat Service Agent:
for PROJECT_ID in "${PROJECT_IDS[@]}"; do NOMOR="$(gcloud projects describe "$PROJECT_ID" --format="value(projectNumber)")" printf '%-34s %s\n' "$PROJECT_ID" "service-${NOMOR}@gcp-sa-cloudbuild.iam.gserviceaccount.com"doneBiarkan role ini selama peserta membuat GitHub connection. Setelah semua connection berstatus
COMPLETE, cabut kembali dari ketiga project:
for PROJECT_ID in "${PROJECT_IDS[@]}"; do NOMOR="$(gcloud projects describe "$PROJECT_ID" --format="value(projectNumber)")"
gcloud projects remove-iam-policy-binding "$PROJECT_ID" \ --member="serviceAccount:service-${NOMOR}@gcp-sa-cloudbuild.iam.gserviceaccount.com" \ --role="roles/secretmanager.admin" \ --condition=NonedonePerintah gcloud beta services identity create memerlukan komponen beta. Cloud Shell sudah
memasangnya. Kalau dijalankan di terminal sendiri, pasang lebih dahulu dengan
gcloud components install beta.
4.3 Budget alert
Section titled “4.3 Budget alert”Buat dari akun master melalui Billing, lalu Budgets & alerts. Konfigurasi dan nilai ambangnya ada di panduan setup project bagian 9, termasuk budget per project dan budget seluruh Billing Account sebagai pengaman kedua.
Bedanya untuk batch: scope-nya tiga project sekaligus, bukan satu. Supaya tetap berguna, buat budget Billing Account dengan tiga project itu sebagai cakupannya, karena budget per project tidak memunculkan peringatan ketika totalnya melewati batas gabungan.
Budget alert hanya mengirim notifikasi. Ia tidak menghentikan VM, Cloud Build, pembelian domain, atau billing. Akun master tetap harus memeriksa Billing Reports secara berkala.
5. Role IAM peserta
Section titled “5. Role IAM peserta”5.1 Daftar role
Section titled “5.1 Daftar role”ROLES=( roles/editor roles/resourcemanager.projectIamAdmin roles/serviceusage.serviceUsageAdmin roles/iam.serviceAccountAdmin roles/iam.serviceAccountUser roles/compute.osAdminLogin roles/iap.tunnelResourceAccessor roles/cloudbuild.connectionAdmin roles/logging.viewer)Blok di atas adalah sumber tunggal daftar role peserta. Dokumen lain merujuk ke sini, bukan menyalinnya, supaya daftarnya tidak bisa berbeda antar berkas.
Sembilan binding per peserta, dikali dua belas peserta, berarti sembilan puluh enam binding per akun master.
add-iam-policy-binding bersifat menambah, bukan mengganti. Menjalankannya dua kali untuk email
dan role yang sama tidak menghasilkan error dan tidak menggandakan binding.
5.2 Fungsi setiap role
Section titled “5.2 Fungsi setiap role”| Role | Fungsi |
|---|---|
roles/editor |
Membuat dan mengelola sebagian besar resource project |
roles/resourcemanager.projectIamAdmin |
Memberi role deployment kepada Service Account, dan mengerjakan langkah IAM Modul 5.4 |
roles/serviceusage.serviceUsageAdmin |
Mengaktifkan API yang terlewat |
roles/iam.serviceAccountAdmin |
Membuat dan mengelola Service Account deployment |
roles/iam.serviceAccountUser |
Memakai Service Account pada VM dan trigger |
roles/compute.osAdminLogin |
Login administratif ke VM lewat OS Login |
roles/iap.tunnelResourceAccessor |
Membuka SSH lewat Identity-Aware Proxy |
roles/cloudbuild.connectionAdmin |
Membuat GitHub connection dan linked repository |
roles/logging.viewer |
Membaca log Cloud Build saat build gagal |
roles/editor dan roles/resourcemanager.projectIamAdmin memberi akses luas, termasuk
mengubah IAM dan melihat resource peserta lain. Keduanya dipertahankan karena peserta harus
dapat membuat Service Account sendiri dan mengerjakan langkah IAM pada Modul 5.4.
Risikonya satu: projectIamAdmin memungkinkan peserta memberi dirinya sendiri role yang tidak
kamu berikan. Mencabut role dari daftar tidak menutup celah itu selama role ini ada. Penutup
sungguhnya adalah pre-grant dari akun master lalu menghapus langkah 11 Modul 5.4 dari sesi.
Analisis lengkapnya di docs-md/infra-gcp.md bagian 3.3 dan 3.4.
5.3 Dua role yang sengaja tidak diberikan
Section titled “5.3 Dua role yang sengaja tidak diberikan”| Role | Alasan |
|---|---|
roles/domains.admin |
Peserta dapat membeli domain atas nama Billing Account penyelenggara. Komitmen satu tahun, terikat pada project, tidak dapat dibatalkan. |
roles/dns.admin |
Pelatihan memakai subdomain dari domain instruktur, dan record DNS ditambahkan penyelenggara di luar GCP. Cloud DNS per project tidak dipakai. |
roles/editor juga bisa menimbulkan tagihan, misalnya lewat Cloud NAT atau load balancer. Yang
membuat roles/domains.admin berbeda bukan kemampuan membelanjakan uang, melainkan sifat
belanjanya: satu klik salah tidak bisa diperbaiki dengan mematikan VM keesokan harinya.
5.4 Role pada Service Account
Section titled “5.4 Role pada Service Account”Dua binding berikut dikerjakan peserta pada bagian 9 panduan deployment. Akun master tidak perlu mengerjakannya.
| Binding | Untuk |
|---|---|
roles/artifactregistry.reader pada Compute Engine default Service Account |
Docker di VM menarik image |
roles/iam.serviceAccountUser dari Service Account build ke Service Account VM |
Cloud Build melakukan SSH ke VM |
6. Pasang peserta ke project
Section titled “6. Pasang peserta ke project”6.1 Daftar peserta dan projectnya
Section titled “6.1 Daftar peserta dan projectnya”Satu peserta satu project. Isi PESERTA dengan pasangan email dan Project ID.
declare -A PESERTA=( ["asisten01@example.com"]="geoportal-kelompok-a-xxxxx" ["asisten02@example.com"]="geoportal-kelompok-a-xxxxx" ["asisten03@example.com"]="geoportal-kelompok-a-xxxxx" ["asisten04@example.com"]="geoportal-kelompok-a-xxxxx" ["asisten05@example.com"]="geoportal-kelompok-b-xxxxx" ["asisten06@example.com"]="geoportal-kelompok-b-xxxxx" ["asisten07@example.com"]="geoportal-kelompok-b-xxxxx" ["asisten08@example.com"]="geoportal-kelompok-b-xxxxx" ["asisten09@example.com"]="geoportal-kelompok-c-xxxxx" ["asisten10@example.com"]="geoportal-kelompok-c-xxxxx" ["asisten11@example.com"]="geoportal-kelompok-c-xxxxx" ["asisten12@example.com"]="geoportal-kelompok-c-xxxxx")Isi ROLES lebih dahulu dengan blok pada bagian 5.1, karena blok di bawah memakainya. Kalau
Cloud Shell sudah ditutup sejak bagian itu, ROLES kosong dan loop-nya selesai tanpa memasang
apa pun.
Maksimal empat peserta per project. Kuota alamat IPv4 publik berbatas empat per project per
region, dan peserta yang perlu subdomain tidak dapat dibuatkan record A tanpa alamat IP
publik. Peserta kelima harus dipindahkan ke project lain, bukan ke region lain.
6.2 Pastikan setiap email adalah akun Google
Section titled “6.2 Pastikan setiap email adalah akun Google”GCP menolak memberikan role kepada alamat yang belum menjadi akun Google, dengan pesan
INVALID_ARGUMENT: User <alamat> does not exist. Kalimat does not exist menyesatkan: ia bukan
berarti salah ketik, melainkan alamat itu belum terdaftar sebagai akun Google saat perintahnya
dijalankan.
Penyebabnya salah satu dari tiga: peserta belum membuat akun Gmail-nya, alamatnya salah tulis, atau alamatnya milik Google Workspace dan bukan Gmail biasa.
Pemeriksaan ini mencoba satu role ringan ke setiap email, melaporkan mana yang valid dan mana yang tidak, dan tidak berhenti ketika menemukan yang gagal. Jalankan sebelum memasang sembilan role, supaya kegagalan tidak menghentikan proses di tengah.
GAGAL=()
echo "Memeriksa ${#PESERTA[@]} email..."echo
for EMAIL in $(printf '%s\n' "${!PESERTA[@]}" | sort); do PROJECT_ID="${PESERTA[$EMAIL]}"
# Pesan galatnya disimpan, bukan dibuang. Tanpa itu, "alamat salah ketik" dan # "akun belum ada" terlihat sama, padahal tindakannya berbeda. PESAN="$(gcloud projects add-iam-policy-binding "$PROJECT_ID" \ --member="user:$EMAIL" \ --role="roles/editor" \ --condition=None --quiet 2>&1)" KODE=$?
if [[ "$KODE" -eq 0 ]]; then printf ' OK %s\n' "$EMAIL" continue fi
printf ' GAGAL %s\n' "$EMAIL" # "Policy modification failed" sengaja tidak diteruskan: itu pengantar yang # menyesatkan pada pesan gcloud, dan sebab sebenarnya ada di baris berikutnya. printf '%s\n' "$PESAN" | grep -oiE "does not exist|permission denied|invalid argument[^.]*" | sort -u | while read -r BARIS; do printf ' %s\n' "$BARIS"; done GAGAL+=("$EMAIL")done
echoif [[ "${#GAGAL[@]}" -eq 0 ]]; then echo "Semua email valid. Lanjut ke bagian 6.4."else echo "${#GAGAL[@]} email bermasalah, role lain belum dipasang untuk mereka:" for G in "${GAGAL[@]}"; do echo " $G"; done echo echo "Alamat dengan 'does not exist' belum jadi akun Google. Minta peserta" echo "membuka accounts.google.com dengan alamat itu, lalu ulangi bagian 6.2." echo "Dua sebab yang paling sering: akun belum pernah dibuat, atau alamat" echo "baru dibuat beberapa jam sebelumnya sehingga belum terbaca IAM."fi6.3 Lihat rencananya lebih dahulu
Section titled “6.3 Lihat rencananya lebih dahulu”Blok berikut memuat dua jalur sekaligus, dan UJI yang memilih. Dengan UJI=1 ia hanya
mencetak rencana. Setelah rencananya benar, bagian 6.4 memakai blok yang sama dengan UJI=0.
UJI=1 # 1 = cetak rencana saja, 0 = benar-benar memasang
for EMAIL in $(printf '%s\n' "${!PESERTA[@]}" | sort); do PROJECT_ID="${PESERTA[$EMAIL]}"
if [[ "$UJI" -eq 1 ]]; then printf 'RENCANA %-30s -> %-32s %s role\n' "$EMAIL" "$PROJECT_ID" "${#ROLES[@]}" continue fi
printf '\n[%s]\n' "$EMAIL" for ROLE in "${ROLES[@]}"; do gcloud projects add-iam-policy-binding "$PROJECT_ID" \ --member="user:$EMAIL" \ --role="$ROLE" \ --condition=None \ --quiet donedoneBaca keluarannya lebih dahulu. Yang diperiksa: setiap email menuju project yang benar, dan tidak ada project yang menerima lebih dari empat peserta.
6.4 Terapkan
Section titled “6.4 Terapkan”Jalankan ulang blok pada bagian 6.3 dengan UJI=0.
declare -A memerlukan bash 4 atau lebih baru. Cloud Shell memakainya, jadi blok ini aman di
sana. Terminal macOS bawaan memakai bash 3.2 dan akan menolaknya, jadi jalankan blok ini di
Cloud Shell.
6.5 Tambah satu peserta susulan
Section titled “6.5 Tambah satu peserta susulan”Tambahkan satu baris pada PESERTA, lalu jalankan ulang blok 6.3 dan 6.4. Peserta yang sudah
terpasang tidak dipasangi dua kali, karena add-iam-policy-binding bersifat menambah.
Untuk satu orang saja, tanpa menyentuh peta PESERTA:
PROJECT_ID="geoportal-kelompok-c-xxxxx"EMAIL="asisten13@example.com"
gcloud projects get-iam-policy "$PROJECT_ID" \ --flatten="bindings[].members" \ --filter="bindings.members~user:$EMAIL" \ --format="value(bindings.role)"
for ROLE in "${ROLES[@]}"; do gcloud projects add-iam-policy-binding "$PROJECT_ID" \ --member="user:$EMAIL" \ --role="$ROLE" \ --condition=None \ --quietdonePerintah get-iam-policy di awal mencetak role yang sudah dimiliki email itu, sehingga kamu
bisa membedakan yang baru ditambahkan dari yang sudah ada sejak awal.
6.6 Kalau project sudah penuh
Section titled “6.6 Kalau project sudah penuh”Dua pilihan, dan hanya dua:
- Pindahkan ke project yang masih punya slot. Ini yang benar bila orang itu perlu subdomain.
- Terima tanpa IP publik. Orang itu bekerja lewat terowongan IAP dan tidak diberi subdomain. Ini hanya sah bila ia benar-benar melepas tuntutan subdomain, bukan menundanya.
Project keempat bukan pilihan selama Billing Account masih berhenti di batas tiga project berbayar.
7. Periksa hasilnya
Section titled “7. Periksa hasilnya”Jalankan blok di bawah untuk melihat siapa punya akses di ketiga project sekaligus. Blok ini tidak memakai filter gcloud, karena operator filter berubah perilaku antar versi dan pernah melaporkan hasil kosong tanpa error.
PROJECT_IDS=( "geoportal-kelompok-a-xxxxx" "geoportal-kelompok-b-xxxxx" "geoportal-kelompok-c-xxxxx")
for PROJECT_ID in "${PROJECT_IDS[@]}"; do echo "==================================================" echo "PROJECT: $PROJECT_ID" echo gcloud projects get-iam-policy "$PROJECT_ID" --format=json 2>&1 | tr -d ' \n' | sed 's/},{/}\n{/g' | awk ' { if (!match($0, /"role":"[^"]+"/)) next role = substr($0, RSTART + 8, RLENGTH - 9) if (!match($0, /"members":\[[^]]*\]/)) next isi = substr($0, RSTART, RLENGTH) n = split(isi, bagian, ",") for (i = 1; i <= n; i++) if (match(bagian[i], /user:[^"]+/)) { printf "%s|%s\n", substr(bagian[i], RSTART + 5, RLENGTH - 5), role } }' | sort | awk -F'|' ' BEGIN { n = split("editor resourcemanager.projectIamAdmin serviceusage.serviceUsageAdmin iam.serviceAccountAdmin iam.serviceAccountUser compute.osAdminLogin iap.tunnelResourceAccessor cloudbuild.connectionAdmin logging.viewer", a, " ") for (i = 1; i <= n; i++) perlu["roles/" a[i]] = 1 } { email = $1; role = $2 jumlah[email]++ if (role in perlu) punya[email, role] = 1 else if (role == "roles/owner") owner[email] = 1 else lebih[email] = lebih[email] "\n " role } END { if (length(jumlah) == 0) { print " Project terbaca, tetapi belum ada user yang di-assign."; exit } printf " %-32s %-4s %s\n", "EMAIL", "ROLE", "STATUS" printf " %-32s %-4s %s\n", "--------------------------------", "----", "--------" for (email in jumlah) { kurang = "" for (r in perlu) if (!((email, r) in punya)) kurang = kurang "\n " r if (email in owner) { kurang = ""; status = (lebih[email] == "") ? "pemilik" : "pemilik+lebih" } else if (kurang != "" && lebih[email] != "") status = "TERTUKAR" else if (kurang != "") status = "KURANG" else if (lebih[email] != "") status = "berlebih" else status = "lengkap" printf " %-32s %-4s %s\n", email, jumlah[email], status if (kurang != "") print " kurang:" kurang if (lebih[email] != "") print " berlebih:" lebih[email] } }' echodoneCara membaca hasilnya:
| Status | Arti | Tindakan |
|---|---|---|
lengkap |
Sembilan role yang benar, satu per satu | tidak ada |
pemilik |
Hanya roles/owner |
tidak ada, itu akun pemilik project |
pemilik+lebih |
roles/owner plus role lain |
hapus tambahannya, owner sudah mencakup semuanya |
KURANG |
Ada role belum terpasang | lihat bagian 7.1 |
berlebih |
Ada role di luar sembilan | lihat bagian 7.1 |
TERTUKAR |
Ada yang kurang dan berlebih sekaligus | lihat bagian 7.1 |
TERTUKAR yang paling perlu diwaspadai. Jumlahnya bisa tampak benar, misalnya tetap sembilan,
padahal susunannya salah: satu role yang dibutuhkan hilang dan tertutup oleh role sisa skema
lama. Akibatnya baru terasa ketika peserta memakai role yang ternyata tidak dimilikinya.
Keluaran ini tidak memuat Service Account, karena yang dicari adalah email peserta. Ia juga tidak menunjukkan siapa yang sudah login; untuk itu buka IAM & Admin, IAM, kolom Last activity.
7.1 Melengkapi role yang kurang atau berlebih
Section titled “7.1 Melengkapi role yang kurang atau berlebih”Skrip ini mencocokkan daftar role yang benar dengan yang terpasang, mencetak selisihnya, dan baru
mengubah apa pun setelah kamu mengetik LANJUT.
PROJECT_ID="geoportal-kelompok-a-xxxxx"EMAIL="asisten01@example.com"
SELISIH="$( gcloud projects get-iam-policy "$PROJECT_ID" --format=json 2>/dev/null | tr -d ' \n' | sed 's/},{/}\n{/g' | awk ' { if (!match($0, /"role":"[^"]+"/)) next role = substr($0, RSTART + 8, RLENGTH - 9) if (!match($0, /"members":\[[^]]*\]/)) next isi = substr($0, RSTART, RLENGTH) n = split(isi, bagian, ",") for (i = 1; i <= n; i++) if (match(bagian[i], /user:[^"]+/)) { printf "%s|%s\n", substr(bagian[i], RSTART + 5, RLENGTH - 5), role } }' | sort | awk -F'|' -v target="$EMAIL" ' BEGIN { n = split("editor resourcemanager.projectIamAdmin serviceusage.serviceUsageAdmin iam.serviceAccountAdmin iam.serviceAccountUser compute.osAdminLogin iap.tunnelResourceAccessor cloudbuild.connectionAdmin logging.viewer", a, " ") for (i = 1; i <= n; i++) perlu["roles/" a[i]] = 1 } { semua[$1] = 1; if ($1 == target) punya[$2] = 1 } END { if (!(target in semua)) { print "TIDAK ADA DI POLICY"; exit } for (r in perlu) if (!(r in punya)) print "add|" r for (r in punya) if (!(r in perlu) && r != "roles/owner") print "remove|" r }')"
if [[ "$SELISIH" == "TIDAK ADA DI POLICY" ]]; then echo "$EMAIL tidak muncul di policy project ini." exit 1fi
if [[ -z "$SELISIH" ]]; then echo "$EMAIL sudah lengkap dan tidak ada role berlebih." exit 0fi
echo "$EMAIL:"while IFS= read -r BARIS; do case "${BARIS%%|*}" in add) echo " TAMBAH ${BARIS##*|}" ;; remove) echo " CABUT ${BARIS##*|}" ;; esacdone <<< "$SELISIH"
echoread -r -p "Ketik LANJUT untuk mengubah: " JAWABAN[[ "$JAWABAN" == "LANJUT" ]] || { echo "Dibatalkan."; exit 0; }
while IFS= read -r BARIS; do AKSI="${BARIS%%|*}" ROLE="${BARIS##*|}" if [[ "$AKSI" == "add" ]]; then printf ' menambah %s ... ' "$ROLE" gcloud projects add-iam-policy-binding "$PROJECT_ID" \ --member="user:$EMAIL" --role="$ROLE" --condition=None --quiet >/dev/null else printf ' mencabut %s ... ' "$ROLE" gcloud projects remove-iam-policy-binding "$PROJECT_ID" \ --member="user:$EMAIL" --role="$ROLE" --condition=None --quiet >/dev/null || true fi echo "selesai"done <<< "$SELISIH"
echoecho "Jalankan blok pemeriksaan di bagian 7 sekali lagi."Kalau yang kurang hanya satu role dan kamu sudah tahu role-nya, blok berikut lebih cepat:
PROJECT_ID="geoportal-kelompok-a-xxxxx"
for EMAIL in asisten01@example.com asisten02@example.com; do echo "menambah untuk $EMAIL" gcloud projects add-iam-policy-binding "$PROJECT_ID" \ --member="user:$EMAIL" \ --role="roles/serviceusage.serviceUsageAdmin" \ --condition=None \ --quietdone8. Setelah peserta menerima akses
Section titled “8. Setelah peserta menerima akses”8.1 Yang dikirim ke peserta
Section titled “8.1 Yang dikirim ke peserta”Project ID: geoportal-kelompok-a-xxxxxRegion: asia-southeast2Zone: asia-southeast2-bSlug kamu: dhany (kamu pilih sendiri, huruf kecil, maksimal 18 karakter)Subdomain kamu: dhany.<domain-instruktur>Artifact Registry bersama: katalog-imagesApp directory: /opt/webgis/appPeserta menentukan slug-nya sendiri. Satu syarat yang tidak bisa ditawar: slug harus unik di dalam projectnya, karena ia menjadi nama VM, static IP, dan Service Account. Dua peserta dengan slug sama di satu project akan membuat VM dengan nama yang sama.
Kirim juga dua hal berikut, karena keduanya menentukan apakah record DNS-nya bisa dibuat:
- Kunci alamat IP statis sebelum melaporkan IP-nya. Alamat sementara berubah saat VM
di-stop lalu di-start ulang, dan record
Ayang menunjuk ke alamat lama membuat situsnya mati sekaligus membuat sertifikat gagal diterbitkan. - Pakai network tag
webgis-httppada VM, karena firewall bersama menargetkan tag itu.
Kirimkan juga perintah ini supaya peserta dapat memastikan sendiri bahwa aksesnya siap:
PROJECT_ID="geoportal-kelompok-a-xxxxx"EMAIL="$(gcloud config get-value account 2>/dev/null)"
gcloud projects get-iam-policy "$PROJECT_ID" \ --flatten="bindings[].members" \ --filter="bindings.members=user:$EMAIL" \ --format="value(bindings.role)" | sortKeluarannya harus berisi sembilan baris. Peserta yang melihat jumlah berbeda, atau menerima
PERMISSION_DENIED, melapor lebih dahulu. Keduanya berarti aksesnya belum siap, dan itu lebih
murah diperbaiki sebelum sesi daripada di tengah praktik.
Peserta yang lebih nyaman tanpa terminal dapat memeriksanya di Console pada IAM & Admin, IAM, dan mencari namanya sendiri. Kalau namanya tidak muncul, penyebab paling sering adalah project yang dibuka bukan project kelompoknya.
8.2 Verifikasi IP statis
Section titled “8.2 Verifikasi IP statis”Jangan percaya laporan “sudah statis”. Cocokkan setiap IP yang dilaporkan dengan daftar alamat yang benar-benar di-reserve:
PROJECT_ID="geoportal-kelompok-a-xxxxx"REGION="asia-southeast2"
gcloud compute addresses list \ --project="$PROJECT_ID" \ --regions="$REGION" \ --format="table(name,address,status,users)"Kolom status harus RESERVED atau IN_USE, dan address harus sama persis dengan yang
dilaporkan peserta. IP yang tidak muncul pada daftar itu adalah alamat sementara. Peserta yang
belum mengunci alamatnya dicatat, lalu kamu yang menguncinya:
VM="<nama-vm>"SLUG="<slug>"
VM_ZONE="$(gcloud compute instances describe "$VM" \ --project="$PROJECT_ID" --format='value(zone)' | sed 's|.*/||')"
gcloud compute addresses create "webgis-ip-${SLUG}" \ --project="$PROJECT_ID" --region="$REGION"
gcloud compute instances delete-access-config "$VM" \ --access-config-name="external-nat" \ --zone="$VM_ZONE" --project="$PROJECT_ID"
gcloud compute instances add-access-config "$VM" \ --access-config-name="external-nat" \ --address="$(gcloud compute addresses describe "webgis-ip-${SLUG}" \ --project="$PROJECT_ID" --region="$REGION" --format='value(address)')" \ --zone="$VM_ZONE" --project="$PROJECT_ID"Urutannya mengikat: alamat statis dibuat lebih dahulu, baru dipasang. Menghapus konfigurasi akses tanpa menyiapkan penggantinya membuat VM kehilangan IP publiknya.
8.3 Serah terima ke instruktur
Section titled “8.3 Serah terima ke instruktur”| Subdomain | IP statis | Project | |
|---|---|---|---|
dhany.<domain> |
34.x.x.x |
geoportal-kelompok-a-xxxxx |
dhany@example.com |
Aturan yang menyertai tabel: IP harus sudah statis sebelum tabel dikirim, dan record A
dibuat sebelum peserta menjalankan Certbot. Urutan terbalik membuat sertifikat gagal
diterbitkan dengan pesan yang tidak menyebut sebabnya.
Daftar subdomain yang sudah terpakai disimpan di satu tempat terpusat, bukan di dalam salah satu akun master.
9. Checklist penyerahan
Section titled “9. Checklist penyerahan”- Billing Account aktif dan tiga project tertaut padanya.
- Sembilan role terlihat untuk setiap email peserta, sesuai nama role, bukan hanya jumlahnya.
- API aktif, termasuk
iam.googleapis.com. - Artifact Registry dan dua firewall rule tersedia di ketiga project.
- Ketiga GitHub connection berstatus
COMPLETE, danroles/secretmanager.adminsudah dicabut dari Cloud Build Service Agent di ketiga project (bagian 4.2). - Budget alert aktif untuk setiap project dan untuk Billing Account.
- Label
batchdanbilling-codeterpasang pada ketiga project. - Setiap project berisi paling banyak empat peserta ber-IP publik.
- Setiap peserta menerima Project ID, region, zone, slug, dan subdomainnya.
- Akses diberikan minimal satu hari sebelum sesi, supaya layar persetujuan layanan selesai sebelum peserta mulai bekerja.
- Daftar subdomain terpusat sudah diperiksa terhadap subdomain yang sudah dipakai.
- Tabel serah terima
subdomain → IP statissudah dikirim ke instruktur.
10. Setelah batch selesai
Section titled “10. Setelah batch selesai”Prosedur membongkar tiga project sekaligus ada di
panduan flushing akun master. Yang tidak boleh ikut
terhapus di antara batch: Artifact Registry katalog-images, dua firewall rule, API, dan
tautan Billing Account.
Dua skrip di repo tidak dipakai pada alur ini dan tidak perlu disalin ke Cloud Shell:
gcp-setup/scripts/batch-peserta.sh untuk impor dari berkas CSV, dan
gcp-setup/scripts/cek-peran.py untuk memeriksa role lewat JSON. Keduanya disimpan untuk
keperluan lain.