AWS for beginnersAWS สำหรับผู้เริ่มต้น
AWS Beginner Guide
The services you'll use first, on your own laptopบริการที่จะได้ใช้ก่อน บนโน้ตบุ๊กของเราเอง
Amazon Web Services is the largest cloud, with hundreds of services. This guide covers the handful almost every project uses: S3 for files, IAM for permissions, DynamoDB for data, SQS and SNS for messages, Lambda for functions, CloudFormation to create it all from a file, the SDK to use it from code, and VPC networks. You use the real AWS command-line tool, but against a local emulator, so there's no account, no credit card and no surprise bill. Amazon Web Services คือคลาวด์ที่ใหญ่ที่สุด มีบริการหลายร้อยตัว คู่มือนี้ครอบคลุมบริการไม่กี่ตัวที่เกือบทุกโปรเจกต์ใช้ ได้แก่ S3 สำหรับไฟล์ IAM สำหรับสิทธิ์ DynamoDB สำหรับข้อมูล SQS และ SNS สำหรับข้อความ Lambda สำหรับฟังก์ชัน CloudFormation เพื่อสร้างทุกอย่างจากไฟล์ SDK เพื่อใช้งานจากโค้ด และเครือข่าย VPC เราใช้เครื่องมือ command line ของ AWS ตัวจริง แต่ใช้กับ emulator ในเครื่อง จึงไม่ต้องมีบัญชี ไม่ต้องใช้บัตรเครดิต และไม่มีบิลที่ไม่คาดคิด
- Run withรันด้วย AWS CLI 2.37
- EmulatorEmulator Moto 5.2
- RegionRegion ap-southeast-7
Storeจัดเก็บ
Files in S3, who may touch them, and data in DynamoDB.ไฟล์ใน S3 ใครแตะต้องได้บ้าง และข้อมูลใน DynamoDB
Lessons 2–4 →บทที่ 2–4 →Reactตอบสนอง
Queues, topics and functions that run on events.คิว topic และฟังก์ชันที่ทำงานเมื่อมีเหตุการณ์
Lessons 5–6 →บทที่ 5–6 →Automateทำให้อัตโนมัติ
Infrastructure from a template, the SDK, and networks.โครงสร้างพื้นฐานจากเทมเพลต SDK และเครือข่าย
Lessons 7–9 →บทที่ 7–9 →Lesson 1 · Setupบทที่ 1 · เตรียมพร้อม
AWS and the labAWS และแล็บ
Everything in AWS is an API call to a service in a region, made with credentials for an account. The web console, the command line (the AWS CLI) and the SDKs all make the same calls. We'll use the CLI against Moto, which answers the same API locally. Run it with Docker, install the AWS CLI v2, and point the CLI at it with environment variables. The region ap-southeast-7 is Asia Pacific (Thailand), in Bangkok.
ทุกอย่างใน AWS คือการเรียก API ไปยังบริการใน region หนึ่ง ด้วยข้อมูลยืนยันตัวตนของ บัญชี (account) หนึ่ง หน้าเว็บ console, command line (AWS CLI) และ SDK ล้วนเรียก API แบบเดียวกัน เราจะใช้ CLI กับ Moto ซึ่งตอบ API แบบเดียวกันในเครื่อง ให้รัน Moto ด้วย Docker ติดตั้ง AWS CLI v2 แล้วชี้ CLI ไปที่ Moto ด้วยตัวแปรสภาพแวดล้อม region ap-southeast-7 คือ Asia Pacific (Thailand) ที่กรุงเทพฯ
$ docker run -d --name moto -p 4566:4566 -e MOTO_PORT=4566 motoserver/moto d49ddd555b87c54089343601ec707ee8c5c9777a61463415fc7298abcb75e7d4 $ docker ps --filter name=^moto$ --format "{{.Names}}: {{.Status}}, {{.Ports}}" moto: Up 1 second, 0.0.0.0:4566->4566/tcp, [::]:4566->4566/tcp $ aws --version aws-cli/2.37.9 Python/3.14.6 Linux/6.12.76-linuxkit docker/aarch64.amzn.2023 $ aws sts get-caller-identity { "UserId": "AKIAIOSFODNN7EXAMPLE", "Account": "123456789012", "Arn": "arn:aws:sts::123456789012:user/moto" } $ aws ec2 describe-availability-zones --query 'AvailabilityZones[].ZoneName' --output text ap-southeast-7a ap-southeast-7b ap-southeast-7c
sts get-caller-identity answers "who am I?": run it whenever you're unsure which account or user your commands use. 123456789012 is the emulator's pretend account. --query picks fields out of the JSON answer and --output text prints them plainly.sts get-caller-identity ตอบคำถาม "ฉันคือใคร" ให้รันทุกครั้งที่ไม่แน่ใจว่าคำสั่งของเราใช้บัญชีหรือผู้ใช้คนไหน 123456789012 คือบัญชีสมมุติของ emulator ส่วน --query เลือกฟิลด์จากคำตอบที่เป็น JSON และ --output text พิมพ์ออกมาแบบข้อความธรรมดา
With a real account, don't create access keys for yourself. Sign in with aws configure sso (IAM Identity Center) or aws login, which gives short-lived credentials, and remove the AWS_ENDPOINT_URL line. Everything else on this page works the same.ถ้าใช้บัญชีจริง อย่าสร้าง access key ให้ตัวเอง ให้ล็อกอินด้วย aws configure sso (IAM Identity Center) หรือ aws login ซึ่งให้ข้อมูลยืนยันตัวตนอายุสั้น และลบบรรทัด AWS_ENDPOINT_URL ออก ส่วนอื่นในหน้านี้ใช้ได้เหมือนเดิม
Lesson 2 · Storeบทที่ 2 · จัดเก็บ
S3: filesS3: ไฟล์
S3 stores files ("objects") in buckets. A bucket name is global across all of AWS, so it must be unique. There are no real folders: 2026/cat.jpg is just a key that contains a slash.
S3 เก็บไฟล์ ("object") ไว้ใน bucket ชื่อ bucket ใช้ร่วมกันทั้ง AWS จึงต้องไม่ซ้ำกับใคร ใน S3 ไม่มีโฟลเดอร์จริง 2026/cat.jpg เป็นแค่ key ที่มีเครื่องหมายทับอยู่
$ aws s3 mb s3://shop-photos make_bucket: shop-photos $ aws s3 cp cat.jpg s3://shop-photos/2026/cat.jpg upload: ./cat.jpg to s3://shop-photos/2026/cat.jpg $ aws s3 ls s3://shop-photos --recursive 2026-10-05 19:13:18 33 2026/cat.jpg $ aws s3 cp s3://shop-photos/2026/cat.jpg - pretend this is a photo of a cat $ aws s3api head-object --bucket shop-photos --key 2026/cat.jpg { "AcceptRanges": "bytes", "LastModified": "2026-10-05T19:13:18+00:00", "ContentLength": 33, "ETag": "\"36d384c434c35f525140b9a18183aa10\"", "ContentType": "image/jpeg", "Metadata": {} }
aws s3 has friendly commands like cp, ls and sync; aws s3api exposes every raw API call. - as a destination means "print it". S3 guessed the content type from the .jpg name.aws s3 มีคำสั่งที่ใช้ง่ายอย่าง cp, ls และ sync ส่วน aws s3api เปิดให้เรียก API ดิบได้ทุกตัว - ในตำแหน่งปลายทางแปลว่า "พิมพ์ออกมา" S3 เดาชนิดของเนื้อหาจากชื่อ .jpg
Buckets are private. To let someone download one file without giving them an account, make a presigned URL: a link signed with your credentials that works for a limited time. bucket เป็นแบบส่วนตัว ถ้าต้องการให้ใครดาวน์โหลดไฟล์หนึ่งโดยไม่ต้องให้บัญชี ให้สร้าง presigned URL คือลิงก์ที่ลงนามด้วยข้อมูลยืนยันตัวตนของเรา และใช้ได้ในเวลาจำกัด
$ aws s3 presign s3://shop-photos/2026/cat.jpg --expires-in 900 http://localhost:4566/shop-photos/2026/cat.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=test%2F20261005%2Fap-southeast-7%2Fs3%2Faws4_request&X-Amz-Date=20261005T191319Z&X-Amz-Expires=900&X-Amz-SignedHeaders=host&X-Amz-Signature=db383b088415781f453f1d0f58f61c6a00e5a7bd60a5d6dc6215ff789023b3f5
With versioning on, overwriting or deleting an object keeps the old versions, which protects against mistakes: เมื่อเปิด versioning การเขียนทับหรือลบ object จะยังเก็บเวอร์ชันเก่าไว้ ช่วยป้องกันความผิดพลาด
$ aws s3api put-bucket-versioning --bucket shop-photos --versioning-configuration Status=Enabled $ echo "a newer cat photo" > cat.jpg && aws s3 cp cat.jpg s3://shop-photos/2026/cat.jpg upload: ./cat.jpg to s3://shop-photos/2026/cat.jpg $ aws s3api list-object-versions --bucket shop-photos --prefix 2026/cat.jpg --query 'Versions[].[VersionId,IsLatest,Size]' --output table --------------------------------------------------------- | ListObjectVersions | +---------------------------------------+--------+------+ | 5fdee57c-7c6a-4401-9e37-c0435180893b | True | 18 | | null | False | 33 | +---------------------------------------+--------+------+
Two versions of the same key: the new 18-byte file is the latest, and the original 33-byte one is still there with version null, because it was uploaded before versioning was switched on.key เดียวกันมีสองเวอร์ชัน ไฟล์ใหม่ขนาด 18 ไบต์เป็นเวอร์ชันล่าสุด และไฟล์เดิมขนาด 33 ไบต์ยังอยู่ด้วยเวอร์ชัน null เพราะถูกอัปโหลดก่อนเปิด versioning
Lesson 3 · Storeบทที่ 3 · จัดเก็บ
IAM: who may do whatIAM: ใครทำอะไรได้
IAM (Identity and Access Management) holds the users, roles and policies of an account. A policy is a JSON document of statements: an Effect, the Actions, and the Resources they apply to, named by ARN (Amazon Resource Name). Moto normally lets every call through, so I restarted it with IAM checks on. The first 3 calls are allowed without credentials, enough to create an admin.
IAM (Identity and Access Management) เก็บผู้ใช้ role และ policy ของบัญชี policy คือเอกสาร JSON ที่ประกอบด้วย statement ซึ่งมี Effect, Action และ Resource ที่ใช้กับ โดยระบุชื่อทรัพยากรด้วย ARN (Amazon Resource Name) ปกติ Moto ปล่อยให้ทุกการเรียกผ่าน ผมจึงเริ่ม Moto ใหม่โดยเปิดการตรวจ IAM การเรียก 3 ครั้งแรกได้รับอนุญาตโดยไม่ต้องมีข้อมูลยืนยันตัวตน ซึ่งพอสำหรับสร้างผู้ดูแล
$ docker rm -f moto && docker run -d --name moto -p 4566:4566 -e MOTO_PORT=4566 -e INITIAL_NO_AUTH_ACTION_COUNT=3 motoserver/moto moto 98d03359944ba44b7230c7463913e1e6c37c462a8ea82427549c98f899025189 $ aws iam create-user --user-name admin --query User.Arn $ aws iam put-user-policy --user-name admin --policy-name everything --policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]}' $ aws iam create-access-key --user-name admin --query 'AccessKey.[AccessKeyId,SecretAccessKey]' --output text "arn:aws:iam::123456789012:user/admin" AKIARZPUZDIKEAS4FIEX 9MwcEbNg5jhbWiv7pCkZxO07Db0Z5zl7wLFSW7xt
As the admin, create a bucket with one file, and a user called reader with a policy that only allows listing and reading that bucket:
ในฐานะผู้ดูแล สร้าง bucket ที่มีไฟล์หนึ่งไฟล์ และผู้ใช้ชื่อ reader ที่มี policy อนุญาตเพียงการดูรายการและอ่าน bucket นั้น
$ export AWS_ACCESS_KEY_ID=AKIARZPUZDIKEAS4FIEX AWS_SECRET_ACCESS_KEY=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 $ aws s3 mb s3://photos && echo "a cat" > cat.txt && aws s3 cp cat.txt s3://photos/cat.txt make_bucket: photos upload: ./cat.txt to s3://photos/cat.txt $ aws iam create-user --user-name reader --query User.Arn $ aws iam put-user-policy --user-name reader --policy-name read-photos --policy-document file://read-photos.json $ aws iam create-access-key --user-name reader --query 'AccessKey.[AccessKeyId,SecretAccessKey]' --output text "arn:aws:iam::123456789012:user/reader" AKIARZPUZDIKLZDR7ASJ 2H80udbpnez1nfSzigKcdnB457mGUUH2Y8W/0WMz
$ export AWS_ACCESS_KEY_ID=AKIARZPUZDIKLZDR7ASJ AWS_SECRET_ACCESS_KEY=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 $ aws sts get-caller-identity --query Arn "arn:aws:iam::123456789012:user/reader" $ aws s3 cp s3://photos/cat.txt - a cat $ aws s3 cp cat.txt s3://photos/dog.txt upload failed: ./cat.txt to s3://photos/dog.txt An error occurred (AccessDenied) when calling the PutObject operation: Access Denied $ aws s3 mb s3://my-own-bucket make_bucket failed: s3://my-own-bucket An error occurred (AccessDenied) when calling the CreateBucket operation: Access Denied
Reading was allowed; uploading and creating a bucket were AccessDenied, because no statement allows them. That's least privilege: the reader can do exactly one job. (These keys belong to the emulator and work nowhere else; never paste real keys anywhere.)การอ่านได้รับอนุญาต ส่วนการอัปโหลดและการสร้าง bucket ได้ AccessDenied เพราะไม่มี statement ใดอนุญาต นี่คือสิทธิ์น้อยที่สุด reader ทำได้เพียงงานเดียวเท่านั้น (กุญแจเหล่านี้เป็นของ emulator และใช้ที่อื่นไม่ได้ อย่าวางกุญแจจริงไว้ที่ใดเด็ดขาด)
$ AWS_ACCESS_KEY_ID=AKIAEXAMPLEWRONGKEY0 AWS_SECRET_ACCESS_KEY=nope aws s3 cp s3://photos/cat.txt - download failed: s3://photos/cat.txt to - An error occurred (403) when calling the HeadObject operation: Forbidden
Users with keys are for this demo. In real systems, programs use roles: an EC2 machine, a Lambda function or a container is given a role, and AWS hands it short-lived credentials automatically (Lesson 6 creates one). People sign in through IAM Identity Center. After this lesson I restarted Moto without IAM checks and went back to the test keys.ผู้ใช้ที่มีกุญแจใช้สำหรับตัวอย่างนี้เท่านั้น ในระบบจริงโปรแกรมใช้ role เครื่อง EC2 ฟังก์ชัน Lambda หรือ container จะได้รับ role และ AWS ส่งข้อมูลยืนยันตัวตนอายุสั้นให้อัตโนมัติ (บทที่ 6 จะสร้าง role หนึ่งตัว) ส่วนคนจะล็อกอินผ่าน IAM Identity Center หลังจบบทนี้ ผมเริ่ม Moto ใหม่โดยไม่ตรวจ IAM และกลับไปใช้กุญแจ test
Lesson 4 · Storeบทที่ 4 · จัดเก็บ
DynamoDB: dataDynamoDB: ข้อมูล
DynamoDB is a key-value database that stays fast at any size. You design the key around how you will read the data. Here each order has a partition key customer and a sort key orderId, so "all orders of one customer" is a single fast query. PAY_PER_REQUEST means no capacity to plan: you pay per read and write.
DynamoDB คือฐานข้อมูลแบบ key-value ที่ยังเร็วได้ไม่ว่าจะมีข้อมูลมากแค่ไหน เราออกแบบ key ตามวิธีที่จะอ่านข้อมูล ในที่นี้คำสั่งซื้อแต่ละรายการมี partition key คือ customer และ sort key คือ orderId การหา "คำสั่งซื้อทั้งหมดของลูกค้าหนึ่งคน" จึงเป็นคิวรีเดียวที่เร็ว PAY_PER_REQUEST แปลว่าไม่ต้องวางแผนความจุ จ่ายตามการอ่านและเขียน
$ aws dynamodb create-table --table-name orders \ --attribute-definitions AttributeName=customer,AttributeType=S AttributeName=orderId,AttributeType=S \ --key-schema AttributeName=customer,KeyType=HASH AttributeName=orderId,KeyType=RANGE \ --billing-mode PAY_PER_REQUEST --query 'TableDescription.[TableName,TableStatus]' [ "orders", "ACTIVE" ] $ aws dynamodb put-item --table-name orders --item '{"customer":{"S":"mya"},"orderId":{"S":"1001"},"total":{"N":"120"}}' $ aws dynamodb put-item --table-name orders --item '{"customer":{"S":"mya"},"orderId":{"S":"1002"},"total":{"N":"45"}}' $ aws dynamodb put-item --table-name orders --item '{"customer":{"S":"somchai"},"orderId":{"S":"1001"},"total":{"N":"300"}}' $ aws dynamodb get-item --table-name orders --key '{"customer":{"S":"mya"},"orderId":{"S":"1002"}}' { "Item": { "customer": { "S": "mya" }, "orderId": { "S": "1002" }, "total": { "N": "45" } } } $ aws dynamodb query --table-name orders --key-condition-expression "customer = :c" \ --expression-attribute-values '{":c":{"S":"mya"}}' --query 'Items[].[orderId.S,total.N]' --output text 1001 120 1002 45 $ aws dynamodb scan --table-name orders --query 'Count' 3
In the raw API every value carries its type: S string, N number (sent as text). get-item needs the full key. query reads one partition, in sort-key order. scan reads the whole table: fine for 3 items, slow and expensive for 3 million.ใน API ดิบทุกค่ามีชนิดติดมาด้วย S คือ string และ N คือ number (ส่งเป็นข้อความ) get-item ต้องใช้ key ครบ query อ่าน partition เดียวโดยเรียงตาม sort key ส่วน scan อ่าน ทั้งตาราง ซึ่งไม่เป็นไรกับ 3 รายการ แต่ช้าและแพงกับ 3 ล้านรายการ
Lesson 5 · Reactบทที่ 5 · ตอบสนอง
SQS and SNS: messagesSQS และ SNS: ข้อความ
Queues let one part of a system hand work to another without waiting for it. SQS is a queue: producers send messages, a consumer receives one, does the work, and deletes it. Until it's deleted, the message is only hidden for a while (the visibility timeout) and then comes back, so work isn't lost if the consumer crashes. คิวทำให้ส่วนหนึ่งของระบบส่งงานต่อให้อีกส่วนได้โดยไม่ต้องรอ SQS คือคิว ผู้ผลิตส่งข้อความเข้าไป ผู้รับดึงข้อความหนึ่งออกมา ทำงาน แล้ว ลบ ทิ้ง ก่อนถูกลบข้อความจะถูกซ่อนไว้ชั่วคราว (visibility timeout) แล้วกลับมาอีก งานจึงไม่หายถ้าผู้รับพังกลางคัน
$ aws sqs create-queue --queue-name orders { "QueueUrl": "http://localhost:4566/123456789012/orders" } $ aws sqs send-message --queue-url http://localhost:4566/123456789012/orders --message-body '{"orderId":"1001"}' --query MessageId "fe4f464c-aa25-4df5-9f3b-205381e6a574" $ read BODY HANDLE < <(aws sqs receive-message --queue-url http://localhost:4566/123456789012/orders --query 'Messages[0].[Body,ReceiptHandle]' --output text) $ echo "$BODY" $ aws sqs delete-message --queue-url http://localhost:4566/123456789012/orders --receipt-handle "$HANDLE" && echo deleted $ aws sqs receive-message --queue-url http://localhost:4566/123456789012/orders {"orderId":"1001"} deleted
Deleting needs the receipt handle from that particular receive, not the message ID. After the delete, the queue was empty: the last receive-message printed nothing.การลบต้องใช้ receipt handle จากการรับครั้งนั้น ไม่ใช่ ID ของข้อความ หลังลบแล้วคิวก็ว่าง receive-message ครั้งสุดท้ายไม่พิมพ์อะไรออกมา
SNS is publish/subscribe: you publish to a topic and every subscriber gets a copy, such as a queue, a Lambda function, an email address or a webhook. A common pattern is one topic fanning out to several queues, one per kind of work: SNS คือ publish/subscribe เราเผยแพร่ข้อความไปที่ topic และผู้สมัครรับทุกรายจะได้สำเนาหนึ่งชุด เช่น คิว ฟังก์ชัน Lambda อีเมล หรือ webhook รูปแบบที่พบบ่อยคือ topic เดียวกระจายไปยังหลายคิว คิวละหนึ่งประเภทงาน
$ aws sns create-topic --name order-events { "TopicArn": "arn:aws:sns:ap-southeast-7:123456789012:order-events" } $ aws sqs create-queue --queue-name emails --query QueueUrl $ aws sns subscribe --topic-arn arn:aws:sns:ap-southeast-7:123456789012:order-events --protocol sqs \ --notification-endpoint arn:aws:sqs:ap-southeast-7:123456789012:emails --query SubscriptionArn "http://localhost:4566/123456789012/emails" "arn:aws:sns:ap-southeast-7:123456789012:order-events:0ea3fb40-955f-427a-a8ed-6c075664863c" $ aws sns publish --topic-arn arn:aws:sns:ap-southeast-7:123456789012:order-events --message "order 1001 paid" --query MessageId "84b4b8a4-fd05-4de0-bdb4-c2bfcb900a5e" $ aws sqs receive-message --queue-url http://localhost:4566/123456789012/emails --query 'Messages[0].Body' --output text \ | python3 -c 'import json, sys; b = json.load(sys.stdin); print(b["Type"], b["TopicArn"], b["Message"], sep="\n")' Notification arn:aws:sns:ap-southeast-7:123456789012:order-events order 1001 paid
The message published to the topic arrived in the emails queue, wrapped in an SNS envelope (a JSON document with the type, the topic and the original message).ข้อความที่เผยแพร่ไปยัง topic มาถึงคิว emails โดยห่อไว้ในซองของ SNS (เอกสาร JSON ที่มีชนิด topic และข้อความต้นฉบับ)
Lesson 6 · Reactบทที่ 6 · ตอบสนอง
Lambda: functionsLambda: ฟังก์ชัน
A Lambda function is code that AWS runs when an event happens: an HTTP request, a new file in S3, a message in a queue, a schedule. You pay per call and per millisecond. The function is just a handler that receives the event and returns a result:
Lambda คือโค้ดที่ AWS รันเมื่อเกิดเหตุการณ์ เช่น คำขอ HTTP ไฟล์ใหม่ใน S3 ข้อความในคิว หรือตามตารางเวลา เราจ่ายตามจำนวนครั้งที่เรียกและตามมิลลิวินาทีที่ใช้ ฟังก์ชันเป็นเพียง handler ที่รับเหตุการณ์แล้วคืนผลลัพธ์
AWS publishes the exact Lambda runtimes as container images, with a small Runtime Interface Emulator inside, so you can run a function locally the same way Lambda does: AWS เผยแพร่ runtime ของ Lambda ตัวเดียวกันเป็น container image ที่มี Runtime Interface Emulator ขนาดเล็กอยู่ข้างใน เราจึงรันฟังก์ชันในเครื่องได้แบบเดียวกับที่ Lambda รัน
$ docker run -d --name hello-fn -p 9001:8080 -v "$PWD/fn:/var/task:ro" public.ecr.aws/lambda/nodejs:22 index.handler aa165c8697755600040ffaa5679ecff5edb5cef5400236790c76e2baf25b9f29 $ curl -s -X POST localhost:9001/2015-03-31/functions/function/invocations -d '{"name": "Mya"}'; echo {"statusCode":200,"body":"{\"message\":\"Hello, Mya!\",\"at\":\"2026-10-05T19:13:43.627Z\"}"} $ docker logs hello-fn 05 Oct 2026 19:13:41,026 [INFO] (rapid) exec '/var/runtime/bootstrap' (cwd=/var/task, handler=) START RequestId: d020a2e1-8bba-4633-85c8-ca919f172ab0 Version: $LATEST 05 Oct 2026 19:13:43,539 [INFO] (rapid) INIT START(type: on-demand, phase: init) 05 Oct 2026 19:13:43,539 [INFO] (rapid) The extension's directory "/opt/extensions" does not exist, assuming no extensions to be loaded. 05 Oct 2026 19:13:43,539 [INFO] (rapid) Starting runtime without AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN , Expected?: false 05 Oct 2026 19:13:43,624 [INFO] (rapid) INIT RTDONE(status: success) 05 Oct 2026 19:13:43,624 [INFO] (rapid) INIT REPORT(durationMs: 85.499000) 05 Oct 2026 19:13:43,624 [INFO] (rapid) INVOKE START(requestId: d020a2e1-8bba-4633-85c8-ca919f172ab0) 2026-10-05T19:13:43.626Z d020a2e1-8bba-4633-85c8-ca919f172ab0 INFO event: {"name":"Mya"} END RequestId: d020a2e1-8bba-4633-85c8-ca919f172ab0 REPORT RequestId: d020a2e1-8bba-4633-85c8-ca919f172ab0 Init Duration: 0.29 ms Duration: 89.71 ms Billed Duration: 90 ms Memory Size: 3008 MB Max Memory Used: 3008 MB 05 Oct 2026 19:13:43,628 [INFO] (rapid) INVOKE RTDONE(status: success, produced bytes: 0, duration: 3.986000ms)
The log has the same START, END and REPORT lines Lambda writes to CloudWatch Logs, with the duration and the billed duration. Lines marked (rapid) come from the local emulator. console.log output appears between them with the request ID.log มีบรรทัด START, END และ REPORT แบบเดียวกับที่ Lambda เขียนลง CloudWatch Logs พร้อมระยะเวลาและเวลาที่คิดเงิน บรรทัดที่มี (rapid) มาจาก emulator ในเครื่อง ส่วนผลลัพธ์ของ console.log อยู่ระหว่างนั้นพร้อม ID ของคำขอ
To deploy, zip the code and create the function with an execution role: the role the function runs as. Its trust policy says the Lambda service may use it. การ deploy ทำได้โดย zip โค้ดแล้วสร้างฟังก์ชันพร้อม execution role ซึ่งเป็น role ที่ฟังก์ชันใช้ทำงาน trust policy ของ role บอกว่าบริการ Lambda ใช้ role นี้ได้
$ cd fn && zip ../function.zip index.mjs adding: index.mjs (deflated 31%) $ aws iam create-role --role-name hello-fn-role --assume-role-policy-document file://trust.json --query Role.Arn "arn:aws:iam::123456789012:role/hello-fn-role" $ aws lambda create-function --function-name hello --runtime nodejs22.x --handler index.handler \ --role arn:aws:iam::123456789012:role/hello-fn-role --zip-file fileb://function.zip \ --query '[FunctionName,Runtime,Handler,CodeSize]' [ "hello", "nodejs22.x", "index.handler", 403 ] $ aws lambda list-functions --query 'Functions[].[FunctionName,Runtime,MemorySize,Timeout]' --output table ------------------------------------- | ListFunctions | +--------+--------------+------+----+ | hello | nodejs22.x | 128 | 3 | +--------+--------------+------+----+
The defaults are 128 MB of memory and a 3-second timeout; raise them per function. Moto stores the function, but in this lab the code itself ran in the base image above; on AWS, aws lambda invoke runs it.ค่าเริ่มต้นคือหน่วยความจำ 128 MB และเวลาสูงสุด 3 วินาที ปรับเพิ่มได้ในแต่ละฟังก์ชัน Moto เก็บฟังก์ชันไว้ แต่ในแล็บนี้ตัวโค้ดรันใน base image ข้างบน ส่วนบน AWS ใช้ aws lambda invoke เพื่อรัน
Lesson 7 · Automateบทที่ 7 · ทำให้อัตโนมัติ
CloudFormation: infrastructure as codeCloudFormation: infrastructure as code
Typing commands doesn't scale and can't be reviewed. CloudFormation creates resources from a template, a YAML file you keep in Git. You create a stack from it; AWS works out the order, creates everything, and later updates or deletes the stack as one unit. !Sub fills in a parameter; !Ref refers to another resource.
การพิมพ์คำสั่งทีละคำสั่งขยายไม่ได้และตรวจทานไม่ได้ CloudFormation สร้างทรัพยากรจากเทมเพลต ซึ่งเป็นไฟล์ YAML ที่เก็บไว้ใน Git เราสร้าง stack จากเทมเพลตนั้น AWS จะจัดลำดับ สร้างทุกอย่าง และภายหลังอัปเดตหรือลบ stack ได้เป็นหน่วยเดียว !Sub แทนค่าพารามิเตอร์ และ !Ref อ้างถึงทรัพยากรอื่น
$ aws cloudformation create-stack --stack-name shop-dev --template-body file://template.yaml { "StackId": "arn:aws:cloudformation:ap-southeast-7:123456789012:stack/shop-dev/7ec46e2a-9766-4573-893d-0e81ffa39a7d" } $ aws cloudformation wait stack-create-complete --stack-name shop-dev && echo "stack ready" stack ready $ aws cloudformation describe-stacks --stack-name shop-dev --query 'Stacks[0].Outputs' --output table ------------------------------------------------------------------------------------ | DescribeStacks | +------------+---------------------------------------------------------------------+ | OutputKey | OutputValue | +------------+---------------------------------------------------------------------+ | BucketName| shop-uploads-dev | | TableName | orders-dev | | QueueUrl | https://sqs.ap-southeast-7.amazonaws.com/123456789012/orders-dev | +------------+---------------------------------------------------------------------+ $ aws cloudformation list-stack-resources --stack-name shop-dev --query 'StackResourceSummaries[].[LogicalResourceId,ResourceType,ResourceStatus]' --output table -------------------------------------------------------------- | ListStackResources | +---------------+------------------------+-------------------+ | UploadsBucket| AWS::S3::Bucket | CREATE_COMPLETE | | OrdersTable | AWS::DynamoDB::Table | CREATE_COMPLETE | | OrdersQueue | AWS::SQS::Queue | CREATE_COMPLETE | +---------------+------------------------+-------------------+ $ aws s3 ls; aws dynamodb list-tables --query TableNames --output text 2026-10-05 19:15:38 shop-uploads-dev orders-dev
One command created a bucket, a table and a queue. --parameter-overrides Env=prod (with aws cloudformation deploy) would make a second, separate copy for production from the same file. Terraform and the AWS CDK do the same job with different languages.คำสั่งเดียวสร้าง bucket ตาราง และคิว การใช้ --parameter-overrides Env=prod (กับ aws cloudformation deploy) จะสร้างอีกชุดที่แยกกันสำหรับ production จากไฟล์เดียวกัน Terraform และ AWS CDK ทำงานเดียวกันด้วยภาษาที่ต่างกัน
Lesson 8 · Automateบทที่ 8 · ทำให้อัตโนมัติ
The SDK in codeSDK ในโค้ด
Applications use an SDK, a library for your language that makes the same API calls. This Node.js script uses the AWS SDK for JavaScript v3 with the bucket and table the stack just created. Like the CLI, it reads the endpoint, region and credentials from the environment, so the code itself has no keys and no emulator settings (except path-style URLs for S3, which localhost needs).
แอปพลิเคชันใช้ SDK ซึ่งเป็นไลบรารีสำหรับภาษาของเราที่เรียก API แบบเดียวกัน สคริปต์ Node.js นี้ใช้ AWS SDK for JavaScript v3 กับ bucket และตารางที่ stack เพิ่งสร้าง SDK อ่าน endpoint, region และข้อมูลยืนยันตัวตนจากตัวแปรสภาพแวดล้อมเหมือน CLI ตัวโค้ดจึงไม่มีกุญแจและไม่มีการตั้งค่าของ emulator (ยกเว้น URL แบบ path-style สำหรับ S3 ที่ localhost ต้องใช้)
$ cd sdk && npm install @aws-sdk/client-s3 @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb up to date, audited 35 packages in 713ms found 0 vulnerabilities $ cd sdk && node app.mjs from S3: Order 1003: 2 x tea from DynamoDB: [ { customer: 'mya', orderId: '1003', total: 90 } ]
DynamoDBDocumentClient converts plain JavaScript objects to the typed {"S": ...} format and back, so the item comes out as normal values.DynamoDBDocumentClient แปลงออบเจกต์ JavaScript ธรรมดาเป็นรูปแบบที่มีชนิด {"S": ...} และแปลงกลับ รายการที่ได้จึงออกมาเป็นค่าปกติ
Deleting the stack deletes everything in it. A bucket must be empty first: การลบ stack จะลบทุกอย่างในนั้น แต่ bucket ต้องว่างก่อน
$ aws s3 rm s3://shop-uploads-dev --recursive $ aws cloudformation delete-stack --stack-name shop-dev $ aws cloudformation wait stack-delete-complete --stack-name shop-dev && echo "stack deleted" $ aws s3 ls; aws dynamodb list-tables --query TableNames --output text delete: s3://shop-uploads-dev/receipts/1003.txt stack deleted
Lesson 9 · Automateบทที่ 9 · ทำให้อัตโนมัติ
VPC: networksVPC: เครือข่าย
Servers, containers and databases live in a VPC, divided into subnets per availability zone, with security groups as firewalls (see Cloud Fundamentals). These are the API calls that build one. The emulator records them like AWS does, but of course no real network is created. เซิร์ฟเวอร์ container และฐานข้อมูลอยู่ใน VPC ซึ่งแบ่งเป็นซับเน็ตตาม availability zone และมี security group ทำหน้าที่ไฟร์วอลล์ (ดู Cloud Fundamentals) นี่คือการเรียก API ที่ใช้สร้าง VPC หนึ่งชุด emulator บันทึกไว้แบบเดียวกับ AWS แต่แน่นอนว่าไม่ได้สร้างเครือข่ายจริง
$ aws ec2 create-vpc --cidr-block 10.0.0.0/16 --query Vpc.VpcId --output text vpc-e6b10c38b552db77d $ aws ec2 create-subnet --vpc-id vpc-e6b10c38b552db77d --cidr-block 10.0.0.0/24 --availability-zone ap-southeast-7a --query Subnet.SubnetId --output text $ aws ec2 create-subnet --vpc-id vpc-e6b10c38b552db77d --cidr-block 10.0.16.0/20 --availability-zone ap-southeast-7a --query Subnet.SubnetId --output text subnet-b82f4941f94483507 subnet-cee2ec56a3dd74a66 $ aws ec2 create-security-group --group-name web --description "HTTPS from anywhere" --vpc-id vpc-e6b10c38b552db77d --query GroupId --output text sg-715170e6edf201859 $ aws ec2 authorize-security-group-ingress --group-id sg-715170e6edf201859 --protocol tcp --port 443 --cidr 0.0.0.0/0 --query 'SecurityGroupRules[0].[IpProtocol,FromPort,CidrIpv4]' --output text tcp 443 0.0.0.0/0 $ aws ec2 describe-subnets --filters Name=vpc-id,Values=vpc-e6b10c38b552db77d --query 'Subnets[].[SubnetId,CidrBlock,AvailabilityZone,AvailableIpAddressCount]' --output table ------------------------------------------------------------------------- | DescribeSubnets | +---------------------------+---------------+-------------------+-------+ | subnet-b82f4941f94483507 | 10.0.0.0/24 | ap-southeast-7a | 251 | | subnet-cee2ec56a3dd74a66 | 10.0.16.0/20 | ap-southeast-7a | 4091 | +---------------------------+---------------+-------------------+-------+
A /24 shows 251 free addresses and a /20 4091: AWS keeps 5 in every subnet, as in the subnet lab of Cloud Fundamentals. A real setup adds an internet gateway, route tables and a NAT gateway, and creates the same subnets in a second zone; that's a lot of calls, which is why VPCs are usually built from a template./24 แสดง address ว่าง 251 และ /20 แสดง 4091 เพราะ AWS สงวน 5 address ในทุกซับเน็ต ตรงกับแล็บซับเน็ตใน Cloud Fundamentals ระบบจริงจะเพิ่ม internet gateway, route table และ NAT gateway และสร้างซับเน็ตชุดเดียวกันในอีก zone ซึ่งเป็นการเรียกจำนวนมาก VPC จึงมักสร้างจากเทมเพลต
Lesson 10 · Your turnบทที่ 10 · ถึงตาคุณแล้ว
Practiceแบบฝึกหัด
Three tasks against the emulator. Each solution was run; the output is under it. งานสามข้อกับ emulator ทุกเฉลยรันจริงแล้ว ผลลัพธ์อยู่ด้านล่าง
1. Clean up temporary files automatically1. ลบไฟล์ชั่วคราวอัตโนมัติ
Add a lifecycle rule to shop-photos that deletes objects under tmp/ after 7 days.เพิ่ม lifecycle rule ให้ shop-photos ที่ลบ object ใต้ tmp/ หลังผ่านไป 7 วัน
Show solutionดูเฉลย
$ aws s3 mb s3://shop-photos && aws s3api put-bucket-lifecycle-configuration --bucket shop-photos --lifecycle-configuration file://lifecycle.json && aws s3api get-bucket-lifecycle-configuration --bucket shop-photos make_bucket: shop-photos { "TransitionDefaultMinimumObjectSize": "all_storage_classes_128K", "Rules": [ { "Expiration": { "Days": 7 }, "ID": "expire-tmp", "Filter": { "Prefix": "tmp/" }, "Status": "Enabled" } ] }
2. Newer orders only2. เฉพาะคำสั่งซื้อที่ใหม่กว่า
Give somchai three orders (1001–1003), then query only the orders after 1001, using the sort key.ให้ somchai มีคำสั่งซื้อสามรายการ (1001–1003) แล้วคิวรีเฉพาะรายการหลัง 1001 โดยใช้ sort key
Show solutionดูเฉลย
$ aws dynamodb create-table --table-name orders --attribute-definitions AttributeName=customer,AttributeType=S AttributeName=orderId,AttributeType=S --key-schema AttributeName=customer,KeyType=HASH AttributeName=orderId,KeyType=RANGE --billing-mode PAY_PER_REQUEST --query TableDescription.TableStatus $ for id in 1001 1002 1003; do aws dynamodb put-item --table-name orders --item "{\"customer\":{\"S\":\"somchai\"},\"orderId\":{\"S\":\"$id\"}}"; done $ aws dynamodb query --table-name orders --key-condition-expression "customer = :c AND orderId > :o" --expression-attribute-values '{":c":{"S":"somchai"},":o":{"S":"1001"}}' --query 'Items[].orderId.S' --output text "ACTIVE" 1002 1003
3. A dead-letter queue3. dead-letter queue
A message that fails again and again would block a queue forever. Create orders-dlq and make orders move a message there after 3 failed receives.ข้อความที่ล้มเหลวซ้ำแล้วซ้ำเล่าจะทำให้คิวติดไปตลอด ให้สร้าง orders-dlq แล้วตั้งให้ orders ย้ายข้อความไปที่นั่นหลังรับแล้วล้มเหลว 3 ครั้ง
Show solutionดูเฉลย
$ aws sqs create-queue --queue-name orders-dlq --query QueueUrl --output text $ aws sqs create-queue --queue-name orders --attributes '{"RedrivePolicy":"{\"deadLetterTargetArn\":\"arn:aws:sqs:ap-southeast-7:123456789012:orders-dlq\",\"maxReceiveCount\":\"3\"}"}' --query QueueUrl --output text $ aws sqs get-queue-attributes --queue-url http://localhost:4566/123456789012/orders --attribute-names RedrivePolicy --query Attributes.RedrivePolicy --output text http://localhost:4566/123456789012/orders-dlq http://localhost:4566/123456789012/orders {"deadLetterTargetArn": "arn:aws:sqs:ap-southeast-7:123456789012:orders-dlq", "maxReceiveCount": 3}
Lesson 11 · Avoiding troubleบทที่ 11 · หลีกเลี่ยงปัญหา
Common mistakesข้อผิดพลาดที่พบบ่อย
"My table disappeared": the wrong region"ตารางหายไป": ใช้ region ผิด
Most resources belong to one region. Look in another region and they simply aren't there, with no error. Check the region first (the console's top-right corner, AWS_DEFAULT_REGION, or --region).ทรัพยากรส่วนใหญ่อยู่ใน region เดียว ถ้าไปดูใน region อื่นก็จะไม่พบเลย โดยไม่มีข้อผิดพลาดใดๆ ให้ตรวจ region ก่อนเสมอ (มุมขวาบนของ console, AWS_DEFAULT_REGION หรือ --region)
$ aws dynamodb create-table --table-name sessions --attribute-definitions AttributeName=id,AttributeType=S --key-schema AttributeName=id,KeyType=HASH --billing-mode PAY_PER_REQUEST --query TableDescription.TableArn --output text $ aws dynamodb list-tables --region ap-southeast-1 --query TableNames $ aws dynamodb list-tables --region ap-southeast-7 --query TableNames arn:aws:dynamodb:ap-southeast-7:123456789012:table/sessions [] [ "sessions" ]
Forgetting to delete a processed messageลืมลบข้อความที่ทำเสร็จแล้ว
Receiving a message doesn't remove it. If the consumer doesn't delete it before the visibility timeout ends (2 seconds here), it is delivered again and the work runs twice. Delete after success, and make the work safe to repeat.การรับข้อความไม่ได้ลบมันออก ถ้าผู้รับไม่ลบก่อน visibility timeout หมด (ในที่นี้ 2 วินาที) ข้อความจะถูกส่งมาอีก และงานจะถูกทำสองครั้ง ให้ลบเมื่อทำสำเร็จ และทำให้งานปลอดภัยแม้ถูกทำซ้ำ
$ aws sqs create-queue --queue-name jobs --attributes VisibilityTimeout=2 --query QueueUrl --output text $ aws sqs send-message --queue-url http://localhost:4566/123456789012/jobs --message-body "resize photo 42" --query MessageId --output text $ aws sqs receive-message --queue-url http://localhost:4566/123456789012/jobs --attribute-names ApproximateReceiveCount --query 'Messages[0].[Body,Attributes.ApproximateReceiveCount]' --output text $ sleep 3 $ aws sqs receive-message --queue-url http://localhost:4566/123456789012/jobs --attribute-names ApproximateReceiveCount --query 'Messages[0].[Body,Attributes.ApproximateReceiveCount]' --output text http://localhost:4566/123456789012/jobs 22c96de7-4042-4141-aff5-bbc49dfe7422 resize photo 42 1 resize photo 42 2
The same message came back with a receive count of 2.ข้อความเดิมกลับมาอีกโดยมีจำนวนการรับเป็น 2
"Action": "*" to make an error go awayใช้ "Action": "*" เพื่อให้ข้อผิดพลาดหายไป
An AccessDenied tells you exactly which action is missing (PutObject in Lesson 3). Add that action on that resource, not everything on everything.AccessDenied บอกอย่างชัดเจนว่าขาด action ใด (PutObject ในบทที่ 3) ให้เพิ่มเฉพาะ action นั้นบนทรัพยากรนั้น ไม่ใช่ทุกอย่างบนทุกอย่าง
Scanning to find one customer's ordersใช้ scan เพื่อหาคำสั่งซื้อของลูกค้าคนเดียว
scan with a filter still reads, and bills, every item in the table; the filter is applied afterwards. Design the key so the question is a query.scan ที่มีตัวกรองยังคงอ่านและคิดเงินทุกรายการในตาราง ตัวกรองถูกใช้ภายหลัง ให้ออกแบบ key เพื่อให้คำถามนั้นเป็น query
Lesson 12 · Referenceบทที่ 12 · อ้างอิง
Glossaryคำศัพท์
| English | ไทย | Meaningความหมาย |
|---|---|---|
| account / region | บัญชี / รีเจียน | Who owns and pays / where resources liveเจ้าของและผู้จ่ายเงิน / ที่ที่ทรัพยากรอยู่ |
| ARN | ARN | Amazon Resource Name: the full unique name of a resourceAmazon Resource Name: ชื่อเต็มที่ไม่ซ้ำของทรัพยากร |
| bucket / object / key | บัคเก็ต / ออบเจกต์ / คีย์ | S3's container / a stored file / the file's nameภาชนะของ S3 / ไฟล์ที่เก็บ / ชื่อของไฟล์ |
| presigned URL | presigned URL | A time-limited link signed with your credentialsลิงก์ที่มีเวลาจำกัดซึ่งลงนามด้วยข้อมูลยืนยันตัวตนของเรา |
| IAM user / role / policy | ผู้ใช้ / role / นโยบาย | A person or key / an identity to assume / permission rulesคนหรือกุญแจ / ตัวตนที่สวมบทบาทได้ / กฎสิทธิ์ |
| partition key / sort key | partition key / sort key | DynamoDB's main key / the order inside a partitionkey หลักของ DynamoDB / ลำดับภายใน partition |
| queue / topic | คิว / topic | SQS: work waiting for one consumer / SNS: a message for every subscriberSQS: งานที่รอผู้รับหนึ่งราย / SNS: ข้อความสำหรับผู้สมัครรับทุกราย |
| visibility timeout | visibility timeout | How long a received message stays hidden before it comes backระยะเวลาที่ข้อความที่รับแล้วถูกซ่อนก่อนกลับมาอีก |
| dead-letter queue | dead-letter queue | Where messages go after failing too many timesที่ที่ข้อความไปเมื่อล้มเหลวหลายครั้งเกินไป |
| handler / execution role | handler / execution role | A Lambda function's entry point / the role it runs asจุดเริ่มต้นของฟังก์ชัน Lambda / role ที่ฟังก์ชันใช้ทำงาน |
| stack / template | stack / เทมเพลต | Resources created together / the file that describes themทรัพยากรที่สร้างด้วยกัน / ไฟล์ที่อธิบายทรัพยากรเหล่านั้น |
| SDK | SDK | A library that calls the AWS APIs from your codeไลบรารีที่เรียก API ของ AWS จากโค้ดของเรา |