Google Cloud Beginner Guide

Google Cloud for beginnersGoogle Cloud สำหรับผู้เริ่มต้น

Google Cloud Beginner Guide

Google's cloud, tried with Google's own emulatorsคลาวด์ของ Google ทดลองด้วย emulator ของ Google เอง

Google Cloud runs on the same infrastructure as Google's own products, and it is strong in data and containers. This guide covers the services a typical app starts with: Cloud Storage for files, Pub/Sub for messages, the Firestore database, Cloud Run for containers and Cloud Run functions, plus IAM for access and Terraform for infrastructure as code. Google's gcloud CLI includes emulators for Pub/Sub and Firestore, and an open-source emulator covers Cloud Storage; this guide uses them, so you need no account and no billing. Google Cloud ทำงานบนโครงสร้างพื้นฐานเดียวกับผลิตภัณฑ์ของ Google เอง และโดดเด่นด้านข้อมูลและ container คู่มือนี้ครอบคลุมบริการที่แอปทั่วไปเริ่มใช้ ได้แก่ Cloud Storage สำหรับไฟล์ Pub/Sub สำหรับข้อความ ฐานข้อมูล Firestore Cloud Run สำหรับ container และ Cloud Run functions รวมถึง IAM สำหรับควบคุมสิทธิ์ และ Terraform สำหรับ infrastructure as code CLI gcloud ของ Google มี emulator ของ Pub/Sub และ Firestore มาด้วย และมี emulator แบบโอเพนซอร์สสำหรับ Cloud Storage คู่มือนี้ใช้ emulator เหล่านั้น จึงไม่ต้องมีบัญชีและไม่มีค่าใช้จ่าย

Every command marked with a terminal on this page was run for real against Google's Pub/Sub and Firestore emulators and fake-gcs-server, and the output is what it printed. Commands that need a real project are labelled "not run". The ideas behind regions and IAM are in Cloud Fundamentals. ทุกคำสั่งที่อยู่ในเทอร์มินัลในหน้านี้รันจริงกับ emulator ของ Pub/Sub และ Firestore ของ Google และ fake-gcs-server และผลลัพธ์คือสิ่งที่มันพิมพ์ออกมา คำสั่งที่ต้องใช้โปรเจกต์จริงจะมีป้าย "ไม่ได้รัน" แนวคิดเรื่อง region และ IAM อยู่ใน Cloud Fundamentals

  • Run withรันด้วย gcloud 587
  • Terraform 1.16
  • Node.js 22
  • Functions Framework 5.0

Lesson 1 · Setupบทที่ 1 · เตรียมพร้อม

Google Cloud and the labGoogle Cloud และแล็บ

Google Cloud organises everything in projects. Every resource belongs to exactly one project, and a project has its own permissions, its own list of switched-on APIs and a link to a billing account that pays for it. A project ID is unique across all of Google Cloud (for example shop-dev-4821). Companies group projects in folders under an organization. Resources also have a place: a region such as asia-southeast1 (Singapore) or asia-southeast2 (Jakarta), a zone inside it such as asia-southeast1-a, or global, as Pub/Sub topics are. Google Cloud จัดทุกอย่างไว้ใน โปรเจกต์ (project) ทรัพยากรทุกตัวอยู่ในโปรเจกต์เดียวเท่านั้น และแต่ละโปรเจกต์มีสิทธิ์ของตัวเอง รายการ API ที่เปิดใช้ของตัวเอง และเชื่อมกับ billing account ที่จ่ายเงินให้ ID ของโปรเจกต์ต้องไม่ซ้ำกันทั้ง Google Cloud (เช่น shop-dev-4821) บริษัทต่างๆ จัดกลุ่มโปรเจกต์ไว้ใน folder ใต้ organization ทรัพยากรยังมีตำแหน่งด้วย อาจเป็น region เช่น asia-southeast1 (สิงคโปร์) หรือ asia-southeast2 (จาการ์ตา) หรือเป็น zone ใน region นั้น เช่น asia-southeast1-a หรือเป็นแบบ global อย่าง topic ของ Pub/Sub

The lab: Google publishes the gcloud CLI with its Pub/Sub and Firestore emulators as a Docker image. Google has no emulator for Cloud Storage, so the lab uses fake-gcs-server, a widely used open-source one. Environment variables tell gcloud and the SDK where the emulators are, and give gcloud a dummy token, because emulators don't check who you are: แล็บนี้ใช้ Docker image ที่ Google เผยแพร่ ซึ่งมี CLI gcloud พร้อม emulator ของ Pub/Sub และ Firestore Google ไม่มี emulator ของ Cloud Storage แล็บจึงใช้ fake-gcs-server ซึ่งเป็นโอเพนซอร์สที่ใช้กันแพร่หลาย ตัวแปรสภาพแวดล้อมบอก gcloud และ SDK ว่า emulator อยู่ที่ไหน และให้โทเค็นปลอมกับ gcloud เพราะ emulator ไม่ตรวจว่าเราเป็นใคร

You can install gcloud on your computer, or do what this guide did: run it from Google's image with this shell function. It starts gcloud inside the network of the emulator it talks to, so localhost means that emulator, exactly as for an installed gcloud. It was loaded before every command on this page. เราติดตั้ง gcloud ในเครื่องได้ หรือทำแบบที่คู่มือนี้ทำ คือรันจาก image ของ Google ด้วยฟังก์ชัน shell นี้ ฟังก์ชันนี้เริ่ม gcloud ในเครือข่ายของ emulator ที่มันคุยด้วย localhost จึงหมายถึง emulator นั้น แบบเดียวกับ gcloud ที่ติดตั้งในเครื่อง ฟังก์ชันนี้ถูกโหลดก่อนทุกคำสั่งในหน้านี้

Terminal, in ~/gcp-labเทอร์มินัล ใน ~/gcp-labgcloud 587 · Terraform 1.16
$ docker run -d --name pubsub -p 8085:8085 gcr.io/google.com/cloudsdktool/google-cloud-cli:emulators gcloud beta emulators pubsub start --host-port=0.0.0.0:8085 --project=shop-dev
c3b179e5790af64c70fca9732acd56451551b428c3e31f783ec2990b66592b2a

$ docker run -d --name firestore -p 8086:8086 gcr.io/google.com/cloudsdktool/google-cloud-cli:emulators gcloud emulators firestore start --host-port=0.0.0.0:8086
80a1fd17c7bd4e87f3c262afaf79f493bf00808d8200eeaf0474cb0f609fd478

$ docker run -d --name gcs -p 4443:4443 fsouza/fake-gcs-server -scheme http -port 4443 -public-host localhost:4443
8c347b7d9d50b176e584f86926fe1d629dc7f3e19875ac88077fbf80931dccd4

$ docker logs pubsub 2>&1 | tail -n 2
[pubsub] Oct 05, 2026 7:44:19 PM com.google.cloud.pubsub.testing.v1.Main main
[pubsub] INFO: Server started, listening on 8085

$ gcloud version | head -n 1
Google Cloud SDK 587.0.0

$ gcloud config list
[api_endpoint_overrides]
pubsub = http://localhost:8085/
storage = http://localhost:4443/storage/v1/
[component_manager]
disable_update_check = true
[core]
disable_prompts = 1
disable_usage_reporting = true
project = shop-dev
[metrics]
environment = docker_image_emulator

Your active configuration is: [default]

gcloud config list shows the endpoint overrides from the environment, so every pubsub and storage command goes to the emulators. The [metrics] and [component_manager] lines come from the Docker image. shop-dev is a made-up project; emulators accept any name.gcloud config list แสดง endpoint ที่ถูกแทนที่จากตัวแปรสภาพแวดล้อม ทุกคำสั่ง pubsub และ storage จึงไปที่ emulator บรรทัด [metrics] และ [component_manager] มาจาก Docker image ส่วน shop-dev เป็นชื่อโปรเจกต์สมมติ emulator รับชื่อใดก็ได้

Real Google CloudGoogle Cloud จริง

With an account you run gcloud init to sign in and choose a project, then switch on each API before you use it: gcloud services enable pubsub.googleapis.com run.googleapis.com firestore.googleapis.com. New accounts get free trial credit; set a budget alert under Billing anyway.ถ้ามีบัญชี ให้รัน gcloud init เพื่อล็อกอินและเลือกโปรเจกต์ แล้วเปิดใช้ API แต่ละตัวก่อนใช้งาน: gcloud services enable pubsub.googleapis.com run.googleapis.com firestore.googleapis.com บัญชีใหม่จะได้เครดิตทดลองใช้ฟรี แต่ควรตั้งการแจ้งเตือนงบประมาณ (budget alert) ในหน้า Billing ไว้ด้วย

Lesson 2 · Dataบทที่ 2 · ข้อมูล

Cloud Storage: filesCloud Storage: ไฟล์

Google's object storage. Buckets hold objects (files). A bucket name is global, as on S3, and a bucket has a location: one region, two regions, or a multi-region such as ASIA. Each object has a storage class: Standard for data in use, then Nearline (read about once a month), Coldline (once a quarter) and Archive (once a year). Colder classes cost less to keep and more to read. Here cat.jpg is a small text file standing in for a photo. object storage ของ Google bucket เก็บ object (ไฟล์) ชื่อ bucket ต้องไม่ซ้ำกันทั้งโลกเหมือน S3 และ bucket มี location ซึ่งอาจเป็น region เดียว สอง region หรือ multi-region เช่น ASIA object แต่ละตัวมี storage class คือ Standard สำหรับข้อมูลที่ใช้อยู่ ตามด้วย Nearline (อ่านประมาณเดือนละครั้ง) Coldline (ไตรมาสละครั้ง) และ Archive (ปีละครั้ง) class ที่เย็นกว่าเก็บได้ถูกกว่าแต่อ่านแพงกว่า ในที่นี้ cat.jpg เป็นไฟล์ข้อความเล็กๆ ที่ใช้แทนรูปภาพ

Terminal, in ~/gcp-labเทอร์มินัล ใน ~/gcp-labgcloud 587 · Terraform 1.16
$ gcloud storage buckets create gs://shop-photos --location=asia-southeast1
Creating gs://shop-photos/...

$ gcloud storage cp cat.jpg gs://shop-photos/2026/cat.jpg
Copying file://cat.jpg to gs://shop-photos/2026/cat.jpg

$ gcloud storage ls -l 'gs://shop-photos/**'
        33  2026-10-05T19:44:27Z  gs://shop-photos/2026/cat.jpg
TOTAL: 1 objects, 33 bytes (33.00B)

$ gcloud storage cat gs://shop-photos/2026/cat.jpg
pretend this is a photo of a cat

$ gcloud storage objects describe gs://shop-photos/2026/cat.jpg --format='yaml(name,size,content_type,storage_class)'
content_type: image/jpeg
name: 2026/cat.jpg
size: 33
storage_class: STANDARD

** matches objects at any depth; like an S3 key, the 2026/ is just part of the object's name. gcloud set content_type from the .jpg extension, and --format picks the fields to print, here as YAML.** จับคู่ object ทุกระดับความลึก 2026/ เป็นแค่ส่วนหนึ่งของชื่อ object เหมือน key ของ S3 gcloud ตั้ง content_type จากนามสกุล .jpg และ --format เลือกฟิลด์ที่จะพิมพ์ ในที่นี้เป็น YAML

Sharingการแชร์

Buckets are private. To share one object for a while, use a signed URL: a link with an expiry, signed by a service account (gcloud storage sign-url). Keep public access prevention and uniform bucket-level access on, so access comes only from IAM (Lesson 7). fake-gcs-server covers the common calls but not all of them; it ignores a bucket's location, for example, which is why Lesson 8 uses Terraform with Pub/Sub only.bucket เป็นแบบส่วนตัว ถ้าต้องการแชร์ object หนึ่งชั่วคราว ให้ใช้ signed URL คือลิงก์ที่มีวันหมดอายุและลงนามด้วย service account (gcloud storage sign-url) ควรเปิด public access prevention และ uniform bucket-level access ไว้ เพื่อให้สิทธิ์มาจาก IAM เท่านั้น (บทที่ 7) fake-gcs-server รองรับคำสั่งที่ใช้บ่อย แต่ไม่ครบทุกอย่าง เช่น มันไม่สนใจ location ของ bucket บทที่ 8 จึงใช้ Terraform กับ Pub/Sub เท่านั้น

Lesson 3 · Dataบทที่ 3 · ข้อมูล

Pub/Sub: messagesPub/Sub: ข้อความ

Pub/Sub is Google's messaging service. A publisher sends a message to a topic. Every subscription on that topic gets its own copy (fan-out), and its subscriber acknowledges (acks) each message when the work is done. A message that isn't acked within the ack deadline is delivered again (Lesson 10). Next to its data, a message can carry attributes, small key-value labels. A subscription is pull (the worker asks for messages) or push (Pub/Sub calls an HTTPS URL, such as a Cloud Run service). Pub/Sub คือบริการส่งข้อความของ Google ผู้ส่ง (publisher) ส่งข้อความไปที่ topic ทุก subscription ของ topic นั้นจะได้สำเนาของตัวเอง (fan-out) และผู้รับของ subscription นั้น ยืนยัน (ack) ข้อความแต่ละข้อเมื่อทำงานเสร็จ ข้อความที่ไม่ถูก ack ภายใน ack deadline จะถูกส่งซ้ำ (บทที่ 10) นอกจากข้อมูลแล้ว ข้อความยังมี attribute ซึ่งเป็นป้ายกำกับแบบ key-value เล็กๆ ได้ subscription มีแบบ pull (ผู้รับเป็นฝ่ายขอข้อความ) และแบบ push (Pub/Sub เรียก URL แบบ HTTPS เช่น บริการบน Cloud Run)

Terminalเทอร์มินัลgcloud 587 · Terraform 1.16
$ gcloud pubsub topics create order-events
Created topic [projects/shop-dev/topics/order-events].

$ gcloud pubsub subscriptions create emails --topic=order-events
$ gcloud pubsub subscriptions create analytics --topic=order-events
Created subscription [projects/shop-dev/subscriptions/emails].
Created subscription [projects/shop-dev/subscriptions/analytics].

$ gcloud pubsub topics publish order-events --message="order 1001 paid" --attribute=type=paid
messageIds:
- '1'

$ gcloud pubsub subscriptions pull emails --auto-ack
┌─────────────────┬────────────┬──────────────┬────────────┬──────────────────┬────────────┐
│       DATA      │ MESSAGE_ID │ ORDERING_KEY │ ATTRIBUTES │ DELIVERY_ATTEMPT │ ACK_STATUS │
├─────────────────┼────────────┼──────────────┼────────────┼──────────────────┼────────────┤
│ order 1001 paid │ 1          │              │ type=paid  │                  │ SUCCESS    │
└─────────────────┴────────────┴──────────────┴────────────┴──────────────────┴────────────┘

$ gcloud pubsub subscriptions pull analytics --auto-ack --format='value(message.data, message.attributes.type)'
order 1001 paid	paid

One publish, two copies: emails and analytics each received the message. --auto-ack acks it straight away, and value(...) prints chosen fields instead of the table, separated by a tab: the data, then the type attribute. The emulator numbers messages from 1; real message IDs are long numbers.ส่งครั้งเดียวได้สองสำเนา ทั้ง emails และ analytics ต่างได้รับข้อความ --auto-ack ทำการ ack ทันที และ value(...) พิมพ์เฉพาะฟิลด์ที่เลือกแทนตาราง โดยคั่นด้วย tab คือข้อมูล ตามด้วย attribute type emulator นับเลขข้อความจาก 1 ส่วน ID ของข้อความจริงเป็นตัวเลขยาว

Lesson 4 · Dataบทที่ 4 · ข้อมูล

Firestore: a document databaseFirestore: ฐานข้อมูลเอกสาร

Firestore is a serverless document database. Documents are JSON-like records with an ID, kept in collections, and they can hold arrays and maps. Queries filter and sort on fields, and every query is answered from an index; Firestore creates an index for each single field by itself. There is no SQL and no join, so store data the way your screens read it. The Node.js SDK finds the emulator through FIRESTORE_EMULATOR_HOST from Lesson 1. Firestore คือฐานข้อมูลเอกสารแบบ serverless เอกสาร (document) คือระเบียนคล้าย JSON ที่มี ID เก็บอยู่ใน คอลเลกชัน (collection) และเก็บอาร์เรย์กับ map ได้ คิวรีกรองและเรียงตามฟิลด์ และทุกคิวรีตอบจาก ดัชนี (index) Firestore สร้างดัชนีของแต่ละฟิลด์ให้เอง ไม่มี SQL และไม่มี join จึงควรเก็บข้อมูลให้ตรงกับที่หน้าจอของแอปอ่าน SDK ของ Node.js หา emulator ผ่าน FIRESTORE_EMULATOR_HOST จากบทที่ 1

Terminal, in ~/gcp-labเทอร์มินัล ใน ~/gcp-labgcloud 587 · Terraform 1.16
$ cd sdk && npm install --no-fund --no-audit
npm warn deprecated node-domexception@1.0.0: Use your platform's native DOMException instead
npm warn deprecated glob@10.5.0: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me

added 117 packages in 4s

$ cd sdk && node firestore.mjs
(node:92304) MetadataLookupWarning: received unexpected error = All promises were rejected code = ETIMEDOUT, ENOTFOUND
(Use `node --trace-warnings ...` to show where the warning was created)
one document: 1002 { customer: 'mya', total: 45, items: [ 'coffee' ] }
mya's orders: 1001=120, 1002=45
after update: { customer: 'mya', items: [ 'coffee' ], paid: true, total: 50 }

The npm warnings are about packages the SDK uses internally; they aren't errors. The MetadataLookupWarning comes from Google's auth library: it asked the metadata server, which exists only on Google Cloud, whether the code runs there. With the emulator on a laptop it is harmless. FieldValue.increment(5) changed 45 to 50 on the server, without reading the document first.คำเตือนของ npm เกี่ยวกับแพ็กเกจที่ SDK ใช้ภายใน ไม่ใช่ข้อผิดพลาด ส่วน MetadataLookupWarning มาจากไลบรารี auth ของ Google ซึ่งถาม metadata server (มีอยู่บน Google Cloud เท่านั้น) ว่าโค้ดรันอยู่บน Google Cloud หรือไม่ เมื่อใช้ emulator บนโน้ตบุ๊กจึงไม่เป็นอันตราย FieldValue.increment(5) เปลี่ยน 45 เป็น 50 ที่ฝั่งเซิร์ฟเวอร์ โดยไม่ต้องอ่านเอกสารก่อน

Real Google CloudGoogle Cloud จริง

The query on line 15 filters on one field and sorts on another, so it needs a composite index. The emulator doesn't check indexes, but real Firestore fails the query with FAILED_PRECONDITION and a link that creates the index. Keep index definitions in code (firestore.indexes.json or Terraform) so every environment has them.คิวรีในบรรทัดที่ 15 กรองด้วยฟิลด์หนึ่งและเรียงด้วยอีกฟิลด์หนึ่ง จึงต้องใช้ composite index emulator ไม่ตรวจดัชนี แต่ Firestore จริงจะทำให้คิวรีล้มเหลวด้วย FAILED_PRECONDITION พร้อมลิงก์สำหรับสร้างดัชนี ควรเก็บนิยามดัชนีไว้ในโค้ด (firestore.indexes.json หรือ Terraform) เพื่อให้ทุกสภาพแวดล้อมมีดัชนีครบ

Lesson 5 · Run codeบทที่ 5 · รันโค้ด

Cloud Run: containers without serversCloud Run: container โดยไม่ต้องดูแลเซิร์ฟเวอร์

Cloud Run runs any container that answers HTTP on the port given in $PORT (8080 by default). It starts instances as requests arrive, sends each instance up to 80 requests at a time by default, and scales to zero when nothing comes in, so an idle service costs nothing. Every deploy creates a new revision, and traffic can be split between revisions. On your computer, the same container runs in Docker: Cloud Run รัน container ใดก็ได้ที่ตอบ HTTP บนพอร์ตที่ระบุใน $PORT (ค่าเริ่มต้นคือ 8080) มันเริ่ม instance เมื่อมีคำขอเข้ามา ส่งคำขอให้แต่ละ instance พร้อมกันได้สูงสุด 80 คำขอโดยค่าเริ่มต้น และลดเหลือศูนย์เมื่อไม่มีคำขอ บริการที่ไม่มีคนใช้จึงไม่มีค่าใช้จ่าย ทุกการ deploy สร้าง revision ใหม่ และแบ่ง traffic ระหว่าง revision ได้ บนเครื่องของเรา container เดียวกันรันใน Docker ได้

Terminal, in ~/gcp-labเทอร์มินัล ใน ~/gcp-labgcloud 587 · Terraform 1.16
$ cd run && docker build -q -t shop-run .
sha256:7373aa8ba385446a346f3bdceb5952d22511f57eb8e2cf31a1885d992e47ede2

$ docker run -d --name shop-run -p 8096:8080 -e PORT=8080 shop-run
2d45fd7425cfa1ca2e1edaa36dc04ae20be9007ce65ecab53ada5df5832475bd

$ curl -s localhost:8096/orders
{"message":"Hello from Cloud Run","revision":"local","path":"/orders"}

$ docker logs shop-run
listening on 8080

$ time docker stop shop-run
shop-run

real	0m0.170s
user	0m0.007s
sys	0m0.005s

Docker passes PORT=8080 the way Cloud Run does; on Cloud Run, revision would show the revision's name from K_REVISION. docker stop sends SIGTERM first. The server exits at once, so the stop took 0.17 s instead of Docker's 10-second wait. Cloud Run sends the same signal before it shuts an instance down.Docker ส่ง PORT=8080 แบบเดียวกับที่ Cloud Run ทำ บน Cloud Run ค่า revision จะเป็นชื่อ revision จาก K_REVISION docker stop ส่ง SIGTERM ก่อน เซิร์ฟเวอร์ออกทันที การหยุดจึงใช้เวลา 0.17 วินาที แทนที่จะรอ 10 วินาทีตามค่าของ Docker Cloud Run ก็ส่งสัญญาณเดียวกันก่อนปิด instance

Not run: needs a project with billingไม่ได้รัน: ต้องใช้โปรเจกต์ที่เปิด billing

--source . uploads the folder, builds it with Cloud Build (using the Dockerfile) and keeps the image in Artifact Registry; the command prints the service's HTTPS URL. Leave out --allow-unauthenticated for internal services: callers then need the Cloud Run Invoker role. --source . อัปโหลดโฟลเดอร์ build ด้วย Cloud Build (โดยใช้ Dockerfile) แล้วเก็บ image ไว้ใน Artifact Registry คำสั่งนี้พิมพ์ URL แบบ HTTPS ของบริการออกมา สำหรับบริการภายใน ให้ตัด --allow-unauthenticated ออก ผู้เรียกจะต้องมี role Cloud Run Invoker

Lesson 6 · Run codeบทที่ 6 · รันโค้ด

Cloud Run functionsCloud Run functions

For a single function you don't need a container: you write the function and Google builds the container. Cloud Functions was renamed Cloud Run functions in 2024, because functions now run on Cloud Run. On your computer, the Functions Framework, the same library Google runs your code with, serves the function. An HTTP function gets a request; an event function gets a CloudEvent, for example when a message reaches a Pub/Sub topic. ถ้ามีแค่ฟังก์ชันเดียวก็ไม่ต้องทำ container เราเขียนฟังก์ชัน แล้ว Google สร้าง container ให้ Cloud Functions ถูกเปลี่ยนชื่อเป็น Cloud Run functions ในปี 2024 เพราะตอนนี้ฟังก์ชันรันบน Cloud Run บนเครื่องของเรา Functions Framework ซึ่งเป็นไลบรารีเดียวกับที่ Google ใช้รันโค้ดของเรา จะให้บริการฟังก์ชันนั้น ฟังก์ชัน HTTP ได้รับคำขอ ส่วน ฟังก์ชันอีเวนต์ (event function) ได้รับ CloudEvent เช่น เมื่อมีข้อความเข้ามาที่ topic ของ Pub/Sub

Terminal, in ~/gcp-labเทอร์มินัล ใน ~/gcp-labgcloud 587 · Terraform 1.16
$ cd fn && npm install --no-fund --no-audit
npm warn deprecated uuid@8.3.2: uuid@10 and below is no longer supported.  For ESM codebases, update to uuid@latest.  For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).

added 128 packages in 6s
A second terminal, in ~/gcp-lab/fnเทอร์มินัลที่สอง ใน ~/gcp-lab/fngcloud 587 · Terraform 1.16
$ npx functions-framework --target=hello --port=8097
Serving function...
Function: hello
Signature type: http
URL: http://localhost:8097/
Terminal, in ~/gcp-labเทอร์มินัล ใน ~/gcp-labgcloud 587 · Terraform 1.16
$ curl -s "localhost:8097/?name=Mya"; echo
Hello, Mya!

Stop it with Ctrl+C and start the event function. The curl below sends the CloudEvent that Google's event service (Eventarc) sends when a message is published to order-events: the ce- headers describe the event, and the body holds the Pub/Sub message, with its data in base64. หยุดด้วย Ctrl+C แล้วเริ่มฟังก์ชันอีเวนต์ curl ด้านล่างส่ง CloudEvent แบบเดียวกับที่บริการอีเวนต์ของ Google (Eventarc) ส่ง เมื่อมีข้อความถูกส่งเข้า order-events header ที่ขึ้นต้นด้วย ce- อธิบายอีเวนต์ และ body มีข้อความของ Pub/Sub ซึ่งข้อมูลเข้ารหัสเป็น base64

The second terminal, in ~/gcp-lab/fnเทอร์มินัลที่สอง ใน ~/gcp-lab/fngcloud 587 · Terraform 1.16
$ npx functions-framework --target=onOrder --signature-type=cloudevent --port=8098
Serving function...
Function: onOrder
Signature type: cloudevent
URL: http://localhost:8098/
order event 41: order 1001 paid
Terminal, in ~/gcp-labเทอร์มินัล ใน ~/gcp-labgcloud 587 · Terraform 1.16
$ curl -s -o /dev/null -w "%{http_code}\n" localhost:8098 \
  -H "ce-id: 41" -H "ce-specversion: 1.0" -H "ce-source: //pubsub.googleapis.com/projects/shop-dev/topics/order-events" \
  -H "ce-type: google.cloud.pubsub.topic.v1.messagePublished" -H "Content-Type: application/json" \
  -d "{\"message\": {\"data\": \"$(printf 'order 1001 paid' | base64)\"}}"
204

204 means the function finished without an error. Its log line, order event 41: order 1001 paid, is the last line in the second terminal.204 หมายถึงฟังก์ชันทำงานเสร็จโดยไม่มีข้อผิดพลาด บรรทัด log ของมัน order event 41: order 1001 paid คือบรรทัดสุดท้ายในเทอร์มินัลที่สอง

Not run: needs a project with billingไม่ได้รัน: ต้องใช้โปรเจกต์ที่เปิด billing

Lesson 7 · Controlบทที่ 7 · การควบคุม

IAM and service accountsIAM และ service account

IAM decides who can do what. A principal (a user, a group, or a service account for software) gets a role, a set of permissions, through a binding on a resource: an organization, a folder, a project, or a single bucket or topic. Bindings apply to everything below where they are set. Avoid the broad basic roles (Owner, Editor, Viewer) and use predefined ones such as roles/pubsub.publisher or roles/storage.objectViewer. IAM ตัดสินว่าใครทำอะไรได้ principal (ผู้ใช้ กลุ่ม หรือ service account สำหรับซอฟต์แวร์) ได้รับ role ซึ่งเป็นชุดของสิทธิ์ ผ่าน binding บนทรัพยากร ซึ่งอาจเป็น organization, folder, โปรเจกต์ หรือ bucket หรือ topic เพียงตัวเดียว binding มีผลกับทุกอย่างที่อยู่ใต้จุดที่ตั้งไว้ ควรหลีกเลี่ยง role พื้นฐานที่กว้างเกินไป (Owner, Editor, Viewer) และใช้ role สำเร็จรูปที่เฉพาะเจาะจง เช่น roles/pubsub.publisher หรือ roles/storage.objectViewer

Code on Google Cloud runs as a service account. Give each service its own, attach it when you deploy, and the client libraries find it through Application Default Credentials (ADC). On your laptop, ADC uses your own login from gcloud auth application-default login. No key file is needed anywhere. These commands need a real project, so they were not run for this guide: โค้ดบน Google Cloud รันในนามของ service account ให้แต่ละบริการมี service account ของตัวเอง ผูกมันตอน deploy แล้วไลบรารีจะหามันเจอผ่าน Application Default Credentials (ADC) บนโน้ตบุ๊ก ADC ใช้การล็อกอินของเราเองจาก gcloud auth application-default login จึงไม่ต้องใช้ไฟล์กุญแจที่ไหนเลย คำสั่งเหล่านี้ต้องใช้โปรเจกต์จริง จึงไม่ได้รันในคู่มือนี้

Not run: needs a projectไม่ได้รัน: ต้องใช้โปรเจกต์จริง

Secret Manager

For secrets that must exist (a payment provider's API key, say), use Secret Manager and give the service account roles/secretmanager.secretAccessor on that one secret. CI systems such as GitHub Actions sign in with Workload Identity Federation, also without keys.สำหรับความลับที่จำเป็นต้องมี (เช่น API key ของผู้ให้บริการชำระเงิน) ให้ใช้ Secret Manager และให้ role roles/secretmanager.secretAccessor กับ service account บนความลับตัวนั้นตัวเดียว ส่วนระบบ CI อย่าง GitHub Actions ล็อกอินด้วย Workload Identity Federation ซึ่งก็ไม่ต้องใช้กุญแจเช่นกัน

Lesson 8 · Controlบทที่ 8 · การควบคุม

Terraform: infrastructure as codeTerraform: infrastructure as code

On Google Cloud, most teams describe their infrastructure with Terraform (or OpenTofu) and Google's provider; Google's own Infrastructure Manager runs Terraform too. This file describes the Pub/Sub setup from Lesson 3. The two marked lines point the provider at the emulator; against real Google Cloud you delete them. บน Google Cloud ทีมส่วนใหญ่อธิบายโครงสร้างพื้นฐานด้วย Terraform (หรือ OpenTofu) คู่กับ provider ของ Google และ Infrastructure Manager ของ Google เองก็รัน Terraform เช่นกัน ไฟล์นี้อธิบายการตั้งค่า Pub/Sub จากบทที่ 3 สองบรรทัดที่ไฮไลต์ไว้ชี้ provider ไปที่ emulator ถ้าใช้กับ Google Cloud จริงให้ลบออก

Terminal, in ~/gcp-lab, with a fresh Pub/Sub emulatorเทอร์มินัล ใน ~/gcp-lab กับ emulator ของ Pub/Sub ที่เริ่มใหม่gcloud 587 · Terraform 1.16
$ cd tf && terraform init -no-color
Initializing the backend...

Initializing provider plugins...
- Finding hashicorp/google versions matching "~> 7.0"...
- Installing hashicorp/google v7.46.1...
- Installed hashicorp/google v7.46.1 (signed by HashiCorp)

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!
⋯ 8 more lines ⋯⋯ อีก 8 บรรทัด ⋯

$ cd tf && terraform plan -no-color
Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:
⋯ 20 more lines ⋯⋯ อีก 20 บรรทัด ⋯
  # google_pubsub_subscription.emails will be created
  + resource "google_pubsub_subscription" "emails" {
      + ack_deadline_seconds       = 20
      + deletion_policy            = "DELETE"
      + effective_labels           = {
          + "goog-terraform-provisioned" = "true"
        }
      + id                         = (known after apply)
      + message_retention_duration = "604800s"
      + name                       = "emails"
      + project                    = "shop-dev"
      + terraform_labels           = {
          + "goog-terraform-provisioned" = "true"
        }
      + topic                      = (known after apply)

      + expiration_policy (known after apply)
    }
⋯ 17 more lines ⋯⋯ อีก 17 บรรทัด ⋯
Plan: 3 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + topic_id = (known after apply)

─────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't
guarantee to take exactly these actions if you run "terraform apply" now.

$ cd tf && terraform apply -auto-approve -no-color
⋯ 60 lines: the same plan as above ⋯⋯ 60 บรรทัด: แผนเดียวกับข้างบน ⋯
Plan: 3 to add, 0 to change, 0 to destroy.
⋯ 3 lines: the same plan as above ⋯⋯ 3 บรรทัด: แผนเดียวกับข้างบน ⋯
google_pubsub_topic.orders: Creating...
google_pubsub_topic.orders: Creation complete after 0s [id=projects/shop-dev/topics/order-events]
google_pubsub_subscription.analytics: Creating...
google_pubsub_subscription.emails: Creating...
google_pubsub_subscription.emails: Creation complete after 0s [id=projects/shop-dev/subscriptions/emails]
google_pubsub_subscription.analytics: Creation complete after 0s [id=projects/shop-dev/subscriptions/analytics]

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

Outputs:

topic_id = "projects/shop-dev/topics/order-events"

$ cd tf && terraform state list
google_pubsub_subscription.analytics
google_pubsub_subscription.emails
google_pubsub_topic.orders

$ gcloud pubsub subscriptions list --format="value(name.basename(), ackDeadlineSeconds, messageRetentionDuration)"
analytics	10	86400s
emails	20	604800s

$ cd tf && terraform plan -no-color | grep -E "^No changes|^Plan:"
No changes. Your infrastructure matches the configuration.

+ means create. Values marked (known after apply) come from Google once the resource exists. The provider adds the label goog-terraform-provisioned by itself, so you can see in the console which resources Terraform made. -no-color only keeps this recording plain; leave it off in your terminal.+ หมายถึงสร้างใหม่ ค่าที่มีป้าย (known after apply) จะได้จาก Google เมื่อทรัพยากรถูกสร้างแล้ว provider เพิ่มป้ายกำกับ goog-terraform-provisioned ให้เอง จึงดูใน console ได้ว่าทรัพยากรไหน Terraform สร้าง ส่วน -no-color ใช้แค่ให้การบันทึกนี้เป็นข้อความเรียบๆ ในเทอร์มินัลของเราไม่ต้องใส่

Now change ack_deadline_seconds = 20 to 30 in main.tf and plan again: ต่อไปแก้ ack_deadline_seconds = 20 เป็น 30 ใน main.tf แล้ว plan อีกครั้ง

Terminal, in ~/gcp-labเทอร์มินัล ใน ~/gcp-labgcloud 587 · Terraform 1.16
$ cd tf && terraform plan -no-color | grep -E "~|Plan:"
  ~ update in-place
  ~ resource "google_pubsub_subscription" "emails" {
      ~ ack_deadline_seconds         = 20 -> 30
Plan: 0 to add, 1 to change, 0 to destroy.

~ means update in place: the subscription stays and only the setting changes. The emulator can't apply updates (it rejects the request the provider sends), so this guide stops at the plan; on Google Cloud, terraform apply makes the change. Set it back to 20 before you go on.~ หมายถึงอัปเดตในที่เดิม subscription ยังอยู่ เปลี่ยนแค่ค่าตั้ง emulator ใช้การอัปเดตไม่ได้ (มันปฏิเสธคำขอที่ provider ส่งไป) คู่มือนี้จึงหยุดที่ plan บน Google Cloud จริง terraform apply จะทำการเปลี่ยนแปลงให้ แก้ค่ากลับเป็น 20 ก่อนทำต่อ

Terminal, in ~/gcp-labเทอร์มินัล ใน ~/gcp-labgcloud 587 · Terraform 1.16
$ cd tf && terraform destroy -auto-approve -no-color | tail -n 4
google_pubsub_topic.orders: Destroying... [id=projects/shop-dev/topics/order-events]
google_pubsub_topic.orders: Destruction complete after 0s

Destroy complete! Resources: 3 destroyed.
StateState

In a team, keep the state in a Cloud Storage bucket (backend "gcs") so everyone works from one copy, with locking. The state file can contain secrets, so never commit it to Git.เมื่อทำงานเป็นทีม ให้เก็บ state ไว้ใน bucket ของ Cloud Storage (backend "gcs") เพื่อให้ทุกคนใช้สำเนาเดียวกัน พร้อมระบบล็อก ไฟล์ state อาจมีความลับอยู่ จึงห้าม commit ลง Git

Lesson 9 · Your turnบทที่ 9 · ถึงตาคุณแล้ว

Practiceแบบฝึกหัด

Three tasks against the emulators. Each solution was run; the output is under it. งานสามข้อกับ emulator ทุกเฉลยรันจริงแล้ว ผลลัพธ์อยู่ด้านล่าง

1. A folder of photos1. โฟลเดอร์รูปภาพ

Make a local photos folder with a 2025 and a 2026 subfolder, upload it with one command, then list only the 2026 photos.สร้างโฟลเดอร์ photos ในเครื่อง ที่มีโฟลเดอร์ย่อย 2025 และ 2026 อัปโหลดด้วยคำสั่งเดียว แล้วแสดงเฉพาะรูปของปี 2026

Show solutionดูเฉลย
Terminal, in ~/gcp-labเทอร์มินัล ใน ~/gcp-labgcloud 587 · Terraform 1.16
$ gcloud storage buckets create gs://shop-practice --location=asia-southeast1
$ mkdir -p photos/2025 photos/2026 && echo dog > photos/2025/dog.jpg && echo cat > photos/2026/cat.jpg && echo bird > photos/2026/bird.jpg
$ gcloud storage cp -r photos gs://shop-practice
$ gcloud storage ls gs://shop-practice/photos/2026/
Creating gs://shop-practice/...
Copying file://photos/2025/dog.jpg to gs://shop-practice/photos/2025/dog.jpg
Copying file://photos/2026/bird.jpg to gs://shop-practice/photos/2026/bird.jpg
  
Copying file://photos/2026/cat.jpg to gs://shop-practice/photos/2026/cat.jpg


Average throughput: 3.4kiB/s
gs://shop-practice/photos/2026/bird.jpg
gs://shop-practice/photos/2026/cat.jpg

cp -r copies the folder and keeps the paths. Files are copied in parallel, so their order changes from run to run; the blank lines are left over from gcloud's progress display.cp -r คัดลอกทั้งโฟลเดอร์และคงพาธเดิมไว้ ไฟล์ถูกคัดลอกแบบขนาน ลำดับจึงเปลี่ยนไปในแต่ละครั้ง บรรทัดว่างเป็นส่วนที่เหลือจากการแสดงความคืบหน้าของ gcloud

2. Only the big orders2. เฉพาะคำสั่งซื้อใหญ่

Create a subscription that receives only the messages whose size attribute is big.สร้าง subscription ที่รับเฉพาะข้อความที่ attribute size เป็น big

Show solutionดูเฉลย
Terminal, with a fresh Pub/Sub emulatorเทอร์มินัล กับ emulator ของ Pub/Sub ที่เริ่มใหม่gcloud 587 · Terraform 1.16
$ gcloud pubsub topics create order-events
$ gcloud pubsub subscriptions create big-orders --topic=order-events --message-filter='attributes.size = "big"'
$ gcloud pubsub topics publish order-events --message="order 1004" --attribute=size=small
$ gcloud pubsub topics publish order-events --message="order 1005" --attribute=size=big
$ gcloud pubsub subscriptions pull big-orders --auto-ack --limit=10 --format='value(message.data)'
Created topic [projects/shop-dev/topics/order-events].
Created subscription [projects/shop-dev/subscriptions/big-orders].
messageIds:
- '1'
messageIds:
- '2'
order 1005

Both messages were published, but only order 1005 was delivered. A filter is set when the subscription is created and can't be changed afterwards.ข้อความทั้งสองถูกส่งแล้ว แต่มีเพียง order 1005 ที่ถูกส่งถึง ตัวกรองถูกกำหนดตอนสร้าง subscription และแก้ภายหลังไม่ได้

3. Who bought tea?3. ใครซื้อชา

Query Firestore for the orders whose items array contains "tea".คิวรี Firestore หาคำสั่งซื้อที่อาร์เรย์ items มี "tea"

Show solutionดูเฉลย
Terminal, in ~/gcp-labเทอร์มินัล ใน ~/gcp-labgcloud 587 · Terraform 1.16
$ cd sdk && node firestore.mjs > /dev/null 2>&1 && node tea.mjs 2>&1 | grep -v -i "warning"
[ '1001 (mya)' ]

The first script fills the collection; grep -v hides the MetadataLookupWarning lines from Lesson 4.สคริปต์แรกใส่ข้อมูลลงคอลเลกชัน ส่วน grep -v ซ่อนบรรทัด MetadataLookupWarning จากบทที่ 4

Lesson 10 · Avoiding troubleบทที่ 10 · หลีกเลี่ยงปัญหา

Common mistakesข้อผิดพลาดที่พบบ่อย

Creating by hand what Terraform managesสร้างด้วยมือในสิ่งที่ Terraform ดูแลอยู่

Someone made the topic by hand. Terraform doesn't know about it, tries to create it, and Google refuses with 409. Either delete the hand-made resource or import it into the state. After the import, Terraform manages the topic and only created the two subscriptions. (Terraform 1.5 and later can also do this with an import block in the code.)มีคนสร้าง topic ด้วยมือ Terraform ไม่รู้จักมัน จึงพยายามสร้างขึ้นมาใหม่ และ Google ปฏิเสธด้วย 409 ทางแก้คือลบทรัพยากรที่สร้างด้วยมือ หรือ import มันเข้าไปใน state หลัง import แล้ว Terraform ดูแล topic นั้นเอง และสร้างเพียง subscription สองตัว (Terraform 1.5 ขึ้นไปทำแบบนี้ได้ด้วยบล็อก import ในโค้ดเช่นกัน)

Terminal, in ~/gcp-lab, with a fresh Pub/Sub emulatorเทอร์มินัล ใน ~/gcp-lab กับ emulator ของ Pub/Sub ที่เริ่มใหม่gcloud 587 · Terraform 1.16
$ gcloud pubsub topics create order-events
Created topic [projects/shop-dev/topics/order-events].

$ cd tf && terraform apply -auto-approve -no-color 2>&1 | grep -E "Error|Creation complete"
Error: Error creating Topic: googleapi: Error 409: Topic already exists

$ cd tf && terraform import -no-color google_pubsub_topic.orders projects/shop-dev/topics/order-events | grep Import
google_pubsub_topic.orders: Importing from ID "projects/shop-dev/topics/order-events"...
google_pubsub_topic.orders: Import prepared!
Import successful!

$ cd tf && terraform apply -auto-approve -no-color | grep -E "Creation complete|Apply complete"
google_pubsub_subscription.emails: Creation complete after 0s [id=projects/shop-dev/subscriptions/emails]
google_pubsub_subscription.analytics: Creation complete after 0s [id=projects/shop-dev/subscriptions/analytics]
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Pulling a message and never acking itดึงข้อความแล้วไม่ ack

A worker that pulls without acking, or crashes before it acks, gets the same message again once the ack deadline passes: the same data and the same ID, 1. Pub/Sub delivers at least once, so make handlers safe to run twice (skip an order ID you've already handled, for example), and ack only after the work is done.ผู้รับที่ดึงข้อความแต่ไม่ ack หรือล่มก่อนจะ ack จะได้รับข้อความเดิมอีกครั้งเมื่อเลย ack deadline ทั้งข้อมูลเดิมและ ID เดิม คือ 1 Pub/Sub ส่งข้อความ อย่างน้อยหนึ่งครั้ง (at least once) จึงต้องทำให้โค้ดที่จัดการข้อความรันซ้ำได้อย่างปลอดภัย (เช่น ข้ามเลขคำสั่งซื้อที่ทำไปแล้ว) และ ack หลังจากทำงานเสร็จเท่านั้น

Terminal, with a fresh Pub/Sub emulatorเทอร์มินัล กับ emulator ของ Pub/Sub ที่เริ่มใหม่gcloud 587 · Terraform 1.16
$ gcloud pubsub topics create jobs
$ gcloud pubsub subscriptions create workers --topic=jobs --ack-deadline=10
$ gcloud pubsub topics publish jobs --message="resize photo 42" --format="value(messageIds)"
$ gcloud pubsub subscriptions pull workers --format="value(message.data, message.messageId)"
$ sleep 12
$ gcloud pubsub subscriptions pull workers --format="value(message.data, message.messageId)"
Created topic [projects/shop-dev/topics/jobs].
Created subscription [projects/shop-dev/subscriptions/workers].
1
resize photo 42	1
resize photo 42	1

Working in the wrong projectทำงานผิดโปรเจกต์

gcloud uses the default project unless you say otherwise. The jobs topic exists in shop-dev, so the list for shop-prod is empty, and a command meant for dev could just as easily have run against prod. Check with gcloud config get project, keep one configuration per environment (gcloud config configurations create prod), and pass --project in scripts.gcloud ใช้โปรเจกต์เริ่มต้นเสมอ ถ้าเราไม่ได้ระบุเป็นอย่างอื่น topic jobs อยู่ใน shop-dev รายการของ shop-prod จึงว่างเปล่า และคำสั่งที่ตั้งใจรันกับ dev ก็อาจไปรันกับ prod ได้ง่ายๆ เช่นกัน ให้ตรวจด้วย gcloud config get project แยก configuration ละหนึ่งชุดต่อสภาพแวดล้อม (gcloud config configurations create prod) และระบุ --project ในสคริปต์เสมอ

Terminalเทอร์มินัลgcloud 587 · Terraform 1.16
$ gcloud pubsub topics list --format="value(name)"
$ gcloud pubsub topics list --project=shop-prod --format="value(name)"
projects/shop-dev/topics/jobs

Downloading service account keysดาวน์โหลดกุญแจของ service account

A key file is a password that doesn't expire, and it ends up in Git, in chat, in a laptop backup. Attach the service account to the service instead; on a laptop, use your own login through ADC; in CI, use Workload Identity Federation (Lesson 7).ไฟล์กุญแจคือรหัสผ่านที่ไม่มีวันหมดอายุ และมักหลุดไปอยู่ใน Git ในแชต หรือในข้อมูลสำรองของโน้ตบุ๊ก ให้ผูก service account กับบริการแทน บนโน้ตบุ๊กให้ใช้การล็อกอินของเราผ่าน ADC และใน CI ให้ใช้ Workload Identity Federation (บทที่ 7)

✗ Wrong✗ ผิด
✓ Right✓ ถูก

Lesson 11 · Referenceบทที่ 11 · อ้างอิง

Glossaryคำศัพท์

EnglishไทยMeaningความหมาย
projectโปรเจกต์The container for resources, permissions, APIs and billingที่รวมทรัพยากร สิทธิ์ API และการคิดเงิน
billing accountบัญชีเรียกเก็บเงินPays for one or more projectsบัญชีที่จ่ายเงินให้โปรเจกต์หนึ่งหรือหลายโปรเจกต์
region / zoneregion / zoneA geographic area / one data-centre location inside itพื้นที่ทางภูมิศาสตร์ / ตำแหน่งศูนย์ข้อมูลหนึ่งแห่งในพื้นที่นั้น
bucket / objectbucket / objectA Cloud Storage container / a file stored in itที่เก็บของ Cloud Storage / ไฟล์ที่เก็บอยู่ข้างใน
storage classstorage classStandard, Nearline, Coldline or Archive: price by how often you readStandard, Nearline, Coldline หรือ Archive: ราคาตามความถี่ในการอ่าน
signed URLsigned URLA link to one object that works until it expiresลิงก์ไปยัง object หนึ่งที่ใช้ได้จนกว่าจะหมดอายุ
topic / subscriptiontopic / subscriptionWhere messages are published / a named stream of copies for one consumerที่ที่ข้อความถูกส่งเข้าไป / สายสำเนาข้อความที่มีชื่อสำหรับผู้รับหนึ่งราย
ack deadlineack deadlineHow long a subscriber has to ack before redeliveryเวลาที่ผู้รับมีให้ ack ก่อนข้อความจะถูกส่งซ้ำ
document / collectionเอกสาร / คอลเลกชันA Firestore record / the group it belongs toระเบียนใน Firestore / กลุ่มที่ระเบียนนั้นอยู่
composite indexดัชนีแบบผสมAn index over several fields, needed by some Firestore queriesดัชนีที่ครอบคลุมหลายฟิลด์ ซึ่งบางคิวรีของ Firestore ต้องใช้
revisionrevisionOne deployed version of a Cloud Run serviceเวอร์ชันหนึ่งที่ deploy แล้วของบริการบน Cloud Run
CloudEventCloudEventA standard format for describing an eventรูปแบบมาตรฐานสำหรับอธิบายอีเวนต์
service accountservice accountAn identity for software rather than a personตัวตนสำหรับซอฟต์แวร์ ไม่ใช่สำหรับคน
role / bindingrole / bindingA set of permissions / granting it to a principal on a resourceชุดของสิทธิ์ / การมอบชุดสิทธิ์นั้นให้ principal บนทรัพยากรหนึ่ง
ADCADCApplication Default Credentials: how the libraries find who they run asApplication Default Credentials: วิธีที่ไลบรารีหาว่ากำลังรันในนามของใคร
Terraform state / importstate / importTerraform's record of what it manages / adding an existing resource to itบันทึกของ Terraform ว่ามันดูแลอะไรอยู่ / การเพิ่มทรัพยากรที่มีอยู่แล้วเข้าไปในบันทึกนั้น