Tổng quan
Trong thời đại công nghệ hiện nay, serverless computing đang trở thành xu hướng phổ biến nhờ khả năng giảm chi phí vận hành và tăng tính linh hoạt. AWS Serverless Application Model (SAM) là một công cụ mạnh mẽ giúp các nhà phát triển dễ dàng xây dựng và triển khai ứng dụng serverless trên AWS. Trong bài viết này, chúng ta sẽ tìm hiểu cách triển khai một API serverless đơn giản bằng SAM và tự động hóa quy trình triển khai bằng GitHub Actions, sử dụng xác thực OpenID Connect (OIDC) để tăng cường bảo mật.
Bài viết này sẽ hướng dẫn bạn qua các bước cụ thể, từ việc khởi tạo ứng dụng SAM, triển khai thủ công, đến thiết lập tự động hóa với GitHub Actions. Hướng dẫn được viết với giọng văn thân thiện, dễ hiểu, phù hợp cho cả người mới bắt đầu và người đã có kinh nghiệm với AWS.
Giới thiệu về AWS SAM
AWS SAM (Serverless Application Model) là một framework mã nguồn mở giúp các nhà phát triển xây dựng và triển khai ứng dụng serverless trên AWS. SAM sử dụng AWS CloudFormation để định nghĩa cơ sở hạ tầng và tài nguyên như Lambda Functions, API Gateway, và DynamoDB. Một số đặc điểm nổi bật của SAM:
- Hỗ trợ nhiều ngôn ngữ lập trình: Node.js, Java, Python, .NET, và Go.
- Cung cấp môi trường kiểm thử và gỡ lỗi cục bộ, giúp nhà phát triển kiểm tra mã trước khi triển khai.
- Tích hợp với AWS CodePipeline để hỗ trợ CI/CD.
- Cung cấp giao diện dòng lệnh (CLI) và tích hợp với AWS Toolkit for Visual Studio Code.
- Miễn phí sử dụng.
Một số lệnh SAM CLI phổ biến:
sam init: Khởi tạo một ứng dụng serverless mới.sam build: Chuẩn bị ứng dụng serverless cho kiểm thử cục bộ hoặc triển khai lên AWS.sam deploy: Triển khai ứng dụng serverless lên AWS Cloud.sam delete: Xóa tài nguyên của ứng dụng serverless bằng cách xóa CloudFormation stack.
Lab Introduction
- Trình độ AWS: Người mới bắt đầu / Trung cấp
- Thời gian hoàn thành: Khoảng 45 phút
- Vùng AWS: US East (N. Virginia) us-east-1
- Chi phí hoàn thành: Có thể sử dụng Free Tier
- Dịch vụ sử dụng: AWS SAM, AWS Lambda, API Gateway, S3, IAM, GitHub Actions
Architecture Diagram
Dưới đây là kiến trúc tổng quan của ứng dụng sau khi hoàn thành triển khai: 
Ứng dụng mẫu trong hướng dẫn này là một API "Hello World" đơn giản, bao gồm:
- Một AWS Lambda Function xử lý các yêu cầu HTTP.
- Một API Gateway để cung cấp endpoint cho API.
- Một bucket S3 để lưu trữ artifact triển khai. (sam sẽ tự động tạo)
- CloudFormation để quản lý cơ sở hạ tầng.
| Tài nguyên | Mô tả |
|---|
| Lambda Function | Xử lý logic của API |
| API Gateway | Cung cấp endpoint để truy cập API |
| S3 Bucket | Lưu trữ artifact triển khai |
| CloudFormation | Quản lý và triển khai cơ sở hạ tầng |
Prerequisites
Trước khi bắt đầu, bạn cần chuẩn bị:
- Một tài khoản AWS .
- Một tài khoản GitHub.
- Git được cài đặt trên máy tính .
Task Details
- Cài đặt Sam CLI
- Khởi tạo ứng dụng SAM
- Xây dựng ứng dụng
- Triển khai ứng dụng lên AWS
- Thiết lập OIDC trong AWS
- Cấu hình GitHub Actions
- Test workflow:
- Clean Up
1. Cài đặt SAM CLI
Truy cập trang hướng dẫn chính thức để cài đặt SAM CLI phiên bản mới nhất cho hệ điều hành của bạn:
https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html
Sau khi cài đặt, kiểm tra phiên bản:
Tại thời điểm viết bài, phiên bản mới nhất là SAM CLI 1.135.x. Nếu gặp lỗi khi deploy, hãy kiểm tra release notes để cập nhật.
2. Khởi tạo ứng dụng SAM
Để bắt đầu, chúng ta sử dụng lệnh sam init để tạo một ứng dụng mẫu.

- Chọn 1 - AWS Quick Start Templates

- Chọn template "Hello World Example".

- Chọn runtime là python (ấn y).

- Không enable X-Ray tracing

- Chọn không enable CloudWatch Application Insights

- Enable Json format

- Đặt tên dự án, ví dụ:
soa-sam-app. 
Sau khi chạy lệnh, thư mục dự án sẽ có cấu trúc như sau:
└── soa-sam-app
├── README.md
├── __init__.py
├── events
│ └── event.json
├── hello_world
│ ├── __init__.py
│ ├── app.py
│ └── requirements.txt
├── samconfig.toml
├── template.yaml
└── tests
├── __init__.py
├── integration
│ ├── __init__.py
│ └── test_api_gateway.py
├── requirements.txt
└── unit
├── __init__.py
└── test_handler.py
template.yaml: Định nghĩa cơ sở hạ tầng của ứng dụng (Lambda Function, API Gateway, v.v.).hello_world/app.py: Chứa mã nguồn của Lambda Function.
Hiểu file template.yaml — "trái tim" của SAM
File template.yaml là nơi bạn khai báo toàn bộ tài nguyên AWS mà ứng dụng cần. SAM sử dụng cú pháp mở rộng của CloudFormation, giúp viết ngắn gọn hơn nhiều. Dưới đây là nội dung mặc định của template Hello World:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: SAM Template for soa-sam-app
Globals:
Function:
Timeout: 3
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello_world/
Handler: app.lambda_handler
Runtime: python3.13
Architectures:
- x86_64
Events:
HelloWorld:
Type: Api
Properties:
Path: /hello
Method: get
Outputs:
HelloWorldApi:
Description: "API Gateway endpoint URL"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
Điểm quan trọng cần hiểu:
| Khái niệm | Giải thích |
|---|
Transform: AWS::Serverless-2016-10-31 | Cho CloudFormation biết cần "dịch" SAM syntax sang CloudFormation chuẩn trước khi deploy |
AWS::Serverless::Function | Resource type riêng của SAM — tự động tạo Lambda Function + IAM Role + Log Group. Nếu viết bằng CloudFormation thuần, bạn phải khai báo riêng từng resource |
Events > Type: Api | SAM tự động tạo API Gateway + tích hợp với Lambda. Bạn không cần viết AWS::ApiGateway::RestApi riêng |
SAM vs CloudFormation: Khi bạn chạy sam deploy, SAM sẽ transform template này thành CloudFormation template đầy đủ (có thể dài gấp 3-4 lần), sau đó gửi cho CloudFormation xử lý. Bạn có thể vào CloudFormation Console > chọn stack > tab Template để xem phiên bản đã được transform.
3. Xây dựng ứng dụng
Tiếp theo, chúng ta xây dựng ứng dụng bằng lệnh sam build để chuẩn bị các artifact cần thiết cho triển khai.

Lệnh này sẽ:
- Tạo thư mục
.aws-sam chứa các artifact đã được xây dựng. 
(Bonus) Test local trước khi deploy
Một trong những điểm mạnh lớn nhất của SAM là khả năng chạy Lambda function ngay trên máy tính mà không cần deploy lên AWS. Điều này giúp bạn debug nhanh hơn và tiết kiệm thời gian.
Yêu cầu: Cần cài đặt Docker vì SAM sử dụng Docker container để mô phỏng môi trường Lambda.
Cách 1: Gọi function một lần (sam local invoke)
sam local invoke HelloWorldFunction --event events/event.json
Lệnh này sẽ khởi động Docker container, chạy Lambda function với event mẫu, rồi trả về kết quả.
Cách 2: Chạy API Gateway local (sam local start-api)
Sau đó mở trình duyệt và truy cập http://127.0.0.1:3000/hello — bạn sẽ thấy kết quả tương tự như khi deploy lên AWS.
Trong lab này, bước test local là tùy chọn. Bạn có thể bỏ qua và deploy trực tiếp lên AWS ở bước tiếp theo.
4. Triển khai ứng dụng lên AWS
SAM deploy làm gì "behind the scenes"?
Trước khi chạy lệnh, hãy hiểu flow xử lý:
sam deploy
│
├── 1. Đọc template.yaml (SAM syntax)
├── 2. Transform → CloudFormation template chuẩn
├── 3. Upload code artifact lên S3 bucket
├── 4. Tạo/Update CloudFormation Stack
│ ├── Tạo Lambda Function
│ ├── Tạo API Gateway
│ ├── Tạo IAM Role cho Lambda
│ └── Tạo CloudWatch Log Group
└── 5. Output: API Gateway URL
Điểm mấu chốt: SAM không deploy trực tiếp. Nó chỉ là "translator" — dịch SAM template sang CloudFormation, rồi để CloudFormation làm việc nặng. Đây là lý do bạn thấy CloudFormation stack được tạo trong Console.
Chạy lệnh deploy

Lệnh --guided sẽ hỏi bạn từng tham số. Dưới đây là giải thích chi tiết:
| Prompt | Giá trị | Giải thích |
|---|
| Stack Name | SOA-Stack-Serverless-Application | Tên CloudFormation stack — dùng để quản lý toàn bộ resources như một nhóm. Khi xóa stack, tất cả resources bên trong sẽ bị xóa theo |
| AWS Region | us-east-1 | Region nơi resources được tạo |
| Confirm changes before deploy | Y | Hiển thị changeset (danh sách thay đổi) trước khi apply — giống terraform plan |
| Allow SAM CLI IAM role creation | Y | Cho phép SAM tự tạo IAM Role cho Lambda function |
| Disable rollback | y | Khi deploy thất bại, giữ nguyên resources đã tạo (tiện debug). Trong production nên chọn N để tự động rollback |
| HelloWorldFunction has no authentication | Y | API Gateway endpoint sẽ public, không cần API key. Phù hợp cho demo, không nên dùng cho production |
| Save arguments to samconfig.toml | Y | Lưu config để lần sau chỉ cần chạy sam deploy (không cần --guided) |

Cuối cùng, nhập y để deploy changeset.
Sau khi deploy xong, vào CloudFormation Console và bạn sẽ thấy SAM đã tạo một stack chứa tất cả resources:

Nhớ: SAM = CloudFormation + syntactic sugar. Mọi thứ SAM tạo ra đều quản lý qua CloudFormation stack.
Sau khi triển khai, bạn có thể kiểm tra API bằng cách gửi yêu cầu HTTP đến endpoint được cung cấp.
- Kiểm tra lambda funtion mới được tạo

- API Gateway

- Vào Stage > Prd > Copy Invoke URL

- Vào trình duyệt và test:
https://{Invoke URL}/hello

- Sau khi chạy xong các bước trên các bạn sẽ có file
samconfig.toml
5. Thiết lập OIDC trong AWS
Tại sao dùng OIDC thay vì Access Key?
Cách truyền thống để GitHub Actions truy cập AWS là lưu AWS_ACCESS_KEY_ID và AWS_SECRET_ACCESS_KEY vào GitHub Secrets. Nhưng cách này có rủi ro:
- Access Key là long-lived credentials — nếu bị lộ, attacker có thể dùng mãi cho đến khi bạn xóa.
- Phải rotate key định kỳ → phiền phức.
OIDC (OpenID Connect) giải quyết vấn đề này bằng cách:
GitHub Actions ──(1) Request OIDC Token──→ GitHub OIDC Provider
│
AWS STS ←──(2) Present Token + AssumeRole──────┘
│
└──(3) Return temporary credentials (hết hạn sau 1 giờ)
- Không cần lưu secret nào trong GitHub — mỗi lần workflow chạy, GitHub tự tạo temporary token.
- AWS STS xác minh token và trả về temporary credentials (hết hạn sau 1 giờ).
- Nếu token bị lộ, nó cũng vô dụng sau vài phút.
Tham khảo bài giải thích chi tiết OIDC của a PhongNX: Integrating GitHub Actions with AWS Using OIDC authentication
Tạo Identity Provider
Truy cập AWS Management Console, vào IAM > Identity providers > Add provider.
Chọn OpenID Connect.
Nhập Provider URL: https://token.actions.githubusercontent.com.
Nhập Audience: sts.amazonaws.com.
Nhấn Add provider để hoàn tất.
Tạo IAM Role cần thiết cho Github Action
Trong IAM Console, vào Roles > Create role.
Chọn Web identity và chọn Identity Provider vừa tạo (token.actions.githubusercontent.com).
Gắn các policy cần thiết. Thay vì dùng FullAccess (vi phạm nguyên tắc Least Privilege — một best practice quan trọng trong AWS), chúng ta tạo custom inline policy chỉ cấp đúng quyền cần thiết:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "CloudFormationStack",
"Effect": "Allow",
"Action": [
"cloudformation:CreateStack",
"cloudformation:UpdateStack",
"cloudformation:DeleteStack",
"cloudformation:DescribeStacks",
"cloudformation:DescribeStackEvents",
"cloudformation:DescribeChangeSet",
"cloudformation:CreateChangeSet",
"cloudformation:ExecuteChangeSet",
"cloudformation:DeleteChangeSet",
"cloudformation:GetTemplateSummary",
"cloudformation:ListStackResources"
],
"Resource": "arn:aws:cloudformation:us-east-1:YOUR_ACCOUNT_ID:stack/SOA-Stack-*"
},
{
"Sid": "S3DeploymentBucket",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject",
"s3:ListBucket",
"s3:GetBucketLocation",
"s3:CreateBucket"
],
"Resource": [
"arn:aws:s3:::aws-sam-cli-managed-default-*",
"arn:aws:s3:::aws-sam-cli-managed-default-*/*"
]
},
{
"Sid": "LambdaManagement",
"Effect": "Allow",
"Action": [
"lambda:CreateFunction",
"lambda:UpdateFunctionCode",
"lambda:UpdateFunctionConfiguration",
"lambda:DeleteFunction",
"lambda:GetFunction",
"lambda:ListTags",
"lambda:TagResource",
"lambda:UntagResource",
"lambda:AddPermission",
"lambda:RemovePermission",
"lambda:GetFunctionConfiguration"
],
"Resource": "arn:aws:lambda:us-east-1:YOUR_ACCOUNT_ID:function:SOA-Stack-*"
},
{
"Sid": "APIGateway",
"Effect": "Allow",
"Action": [
"apigateway:GET",
"apigateway:POST",
"apigateway:PUT",
"apigateway:DELETE",
"apigateway:PATCH"
],
"Resource": "arn:aws:apigateway:us-east-1::*"
},
{
"Sid": "IAMRoleForLambda",
"Effect": "Allow",
"Action": [
"iam:CreateRole",
"iam:DeleteRole",
"iam:GetRole",
"iam:PassRole",
"iam:AttachRolePolicy",
"iam:DetachRolePolicy",
"iam:PutRolePolicy",
"iam:DeleteRolePolicy",
"iam:GetRolePolicy",
"iam:TagRole",
"iam:UntagRole"
],
"Resource": "arn:aws:iam::YOUR_ACCOUNT_ID:role/SOA-Stack-*"
}
]
}
Tại sao không dùng FullAccess? Trong thực tế và trong kỳ thi SOA, AWS luôn khuyến khích nguyên tắc Least Privilege — chỉ cấp đúng quyền cần thiết. Dùng AWSLambdaFullAccess hay AmazonS3FullAccess có thể cho phép xóa toàn bộ Lambda functions hoặc S3 buckets trong account, gây rủi ro bảo mật lớn.
- Đặt tên role, ví dụ:
SOA-GitHubActions-SAMDeploy. - Chỉnh sửa trust relationship của role:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::YOUR_ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:YOUR_GITHUB_USERNAME/YOUR_REPO_NAME:*"
}
}
}
]
}
Giải thích Trust Policy:
| Field | Ý nghĩa |
|---|
Federated | Cho phép OIDC provider của GitHub (không phải IAM user) assume role này |
sts:AssumeRoleWithWebIdentity | Action dùng cho xác thực qua web identity (OIDC) |
aud: sts.amazonaws.com | Chỉ chấp nhận token được tạo cho AWS STS |
sub: repo:YOUR_GITHUB_USERNAME/YOUR_REPO_NAME:* | Chỉ cho phép repo cụ thể của bạn assume role. Dấu * cho phép tất cả branches |
StringEquals vs StringLike: Dùng StringLike với wildcard * cho trường sub giúp workflow hoạt động trên mọi branch (main, master, feature/...). Nếu muốn giới hạn chỉ branch main, dùng StringEquals với giá trị repo:YOUR_GITHUB_USERNAME/YOUR_REPO_NAME:ref:refs/heads/main.

- Thay
YOUR_ACCOUNT_ID bằng ID tài khoản AWS của bạn. - Thay
YOUR_GITHUB_USERNAME và YOUR_REPO_NAME bằng tên người dùng và kho lưu trữ GitHub của bạn.
6. Cấu hình GitHub Actions
Để tự động hóa triển khai, chúng ta tạo một workflow GitHub Actions.
- Trong kho lưu trữ GitHub, tạo thư mục
.github/workflows và file deploy.yml:
name: SAM Deploy
on:
push:
paths:
- 'hello_world/**'
permissions:
id-token: write
contents: read
jobs:
deploy-sam:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
- name: Setup SAM CLI
uses: aws-actions/setup-sam@v2
- name: Configure AWS Credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ secrets.AWS_REGION }}
- name: SAM Build
run: sam build
- name: SAM Deploy
run: |
sam deploy \
--no-confirm-changeset \
--no-fail-on-empty-changeset \
--stack-name SOA-Stack-Serverless-Application \
--capabilities CAPABILITY_IAM \
--region ${{ secrets.AWS_REGION }}
Lưu ý về workflow:
id-token: write là bắt buộc để GitHub Actions có thể request OIDC token gửi cho AWS STS.actions/checkout@v4 chỉ cần gọi một lần (không cần fetch-depth: 0 trừ khi bạn cần git history).actions/setup-python@v5 là phiên bản mới nhất (tránh dùng v3 đã cũ).- Region nên lấy từ
secrets.AWS_REGION thay vì hardcode, để dễ thay đổi sau này.
- Đẩy mã nguồn lên GitHub:
git add .
git commit -m "Initial commit - AWS SAM API setup"
git push -u origin main

- Setting Secret :
- Vào link :
https://github.com/{GIT_USERNAME}/{PROJECTNAME}/settings/secrets/actions Thêm 2 Secret như bên dưới

- Thay
AWS_ROLE_ARN bằng ARN của role được tạo trong step Bước 4: Thiết lập OIDC trong AWS .
- Thay
AWS_REGION bằng us-east-1.

7. Test workflow
- Sửa api bằng cách sử nội dung message:

- Sau đó commit và push
- Confirm Workflow được trigger Action tab . Và đợi đến khi build thành công :

- Refresh browser và xác nhận change đã được phản ánh

8. Clean Up
Để tránh phát sinh chi phí, hãy xóa tất cả tài nguyên sau khi hoàn tất:
Bước 1: Xóa CloudFormation stack (xóa Lambda, API Gateway, IAM Role của Lambda)
sam delete --stack-name SOA-Stack-Serverless-Application
Hoặc vào AWS Console > CloudFormation > Chọn stack SOA-Stack-Serverless-Application > Delete.
Bước 2: Xóa S3 bucket (bucket do SAM CLI tạo để lưu artifact)
- Vào S3 Console > Tìm bucket có tên bắt đầu bằng
aws-sam-cli-managed-default-* - Empty bucket trước (bắt buộc) > rồi Delete bucket
Bước 3: Xóa IAM Role cho GitHub Actions
- IAM Console > Roles > Tìm
SOA-GitHubActions-SAMDeploy > Delete
Bước 4: Xóa OIDC Identity Provider
- IAM Console > Identity providers > Chọn
token.actions.githubusercontent.com > Delete
Bước 5: Xóa CloudWatch Log Groups (dễ bị bỏ quên!)
- CloudWatch Console > Log groups > Tìm
/aws/lambda/SOA-Stack-* > Delete
Tip: Nếu không chắc đã xóa hết, vào CloudFormation Console kiểm tra. Nếu stack đã bị xóa thành công, Lambda + API Gateway + IAM Role của Lambda đã được dọn sạch. Chỉ cần xóa thủ công S3 bucket, OIDC provider, GitHub Actions role, và CloudWatch Log Groups.
Nơi đặt resouce:
Troubleshooting — Các lỗi thường gặp
| Lỗi | Nguyên nhân | Cách khắc phục |
|---|
Error: PythonPipBuilder:ResolveDependencies | Python version trên máy không khớp với runtime trong template.yaml | Kiểm tra python --version và đảm bảo khớp với Runtime trong template.yaml (ví dụ: python3.13) |
Error: Failed to create changeset | IAM permissions không đủ hoặc stack name đã tồn tại | Kiểm tra IAM role có đủ quyền CloudFormation. Nếu stack đã tồn tại, dùng tên khác hoặc xóa stack cũ |
GitHub Actions: Not authorized to perform: sts:AssumeRoleWithWebIdentity | Trust policy sai hoặc repo name không khớp | Kiểm tra sub condition trong trust policy — repo name phải chính xác (phân biệt hoa/thường) |
GitHub Actions: Error: No changes to deploy | Code không thay đổi so với lần deploy trước | Đây không phải lỗi — --no-fail-on-empty-changeset sẽ skip và báo success |
S3 bucket does not exist | Chưa chạy sam deploy --guided lần đầu | Chạy sam deploy --guided trước để SAM tạo S3 bucket. Các lần sau có thể dùng sam deploy |
Tài liệu tham khảo
Kết luận
Bằng cách sử dụng AWS SAM và GitHub Actions với xác thực OIDC, bạn có thể triển khai và tự động hóa các ứng dụng serverless một cách an toàn và hiệu quả. Phương pháp này không chỉ giúp tiết kiệm thời gian mà còn tăng cường bảo mật bằng cách loại bỏ nhu cầu lưu trữ thông tin xác thực lâu dài. Hãy thử áp dụng và khám phá thêm các tính năng của SAM và GitHub Actions để xây dựng các ứng dụng serverless mạnh mẽ hơn!