Sửa lỗi AccessDeniedException: User s3.amazonaws.com is Not Authorized to Perform lambda:InvokeFunction

intermediate☁️ AWS2026-06-17| AWS Lambda, Amazon S3, AWS CLI, Terraform, CloudFormation

Error Message

An error occurred (AccessDeniedException) when calling the Invoke operation: User: s3.amazonaws.com is not authorized to perform: lambda:InvokeFunction on resource
#aws#lambda#s3#iam-policy#devops

Vấn đề

Bạn đã kết nối một bucket Amazon S3 để kích hoạt một hàm Lambda mỗi khi người dùng tải tệp mới vào thư mục. Trên lý thuyết, mọi thứ đều có vẻ đúng. Nhưng thực tế, hàm không bao giờ được kích hoạt. Nếu bạn thử kiểm tra trigger thủ công qua CLI, bạn sẽ gặp phải rào cản này:

An error occurred (AccessDeniedException) when calling the Invoke operation: User: s3.amazonaws.com is not authorized to perform: lambda:InvokeFunction on resource: arn:aws:lambda:us-east-1:123456789012:function:my-processor-func

Nguyên nhân gốc rễ: Vấn đề về "Danh sách khách mời"

Lỗi này xuất phát từ việc thiếu chính sách dựa trên tài nguyên (resource-based policy). Hầu hết các nhà phát triển tập trung vào IAM execution roles, cái cho phép Lambda biết nó được phép thao tác với những gì. Tuy nhiên, S3 sử dụng mô hình "push" (đẩy). Nó cần quyền rõ ràng để có thể gọi và kích hoạt hàm của bạn.

Hãy coi IAM Role như thẻ ID của Lambda—nó cho thấy hàm có thể làm gì. Còn chính sách dựa trên tài nguyên giống như danh sách khách mời tại cửa. Nếu service principal của S3 (s3.amazonaws.com) không có trong danh sách đó, yêu cầu sẽ bị từ chối ngay lập tức.

Cách khắc phục

Cách 1: Khắc phục nhanh (AWS CLI)

Bạn có thể giải quyết vấn đề này trong vài giây bằng cách chạy lệnh add-permission. Lệnh này sẽ thêm một statement vào chính sách nội bộ của Lambda mà không yêu cầu bạn phải chỉnh sửa tệp JSON theo cách thủ công.

aws lambda add-permission \
    --function-name my-processor-func \
    --statement-id AllowS3Invocation \
    --action lambda:InvokeFunction \
    --principal s3.amazonaws.com \
    --source-arn arn:aws:s3:::my-app-uploads-123 \
    --source-account 123456789012
- **--statement-id**: Tên duy nhất cho quy tắc này (ví dụ: `ProductionS3Trigger`).
- **--source-arn**: Điều này rất quan trọng cho bảo mật. Nó đảm bảo *chỉ* bucket cụ thể của bạn mới có thể kích hoạt hàm, ngăn chặn các cuộc tấn công "confused deputy" từ các tài khoản khác.

Cách 2: Giao diện trực quan (AWS Console)

Nếu bạn thích giao diện web, hãy sử dụng bảng điều khiển (dashboard) của Lambda thay vì S3. Giao diện người dùng của Lambda xử lý việc "bắt tay cấp quyền" chạy ngầm đáng tin cậy hơn nhiều so với ngăn thông báo của S3.

- Mở hàm của bạn trong **Lambda Console**.
- Chọn tab **Configuration**, sau đó nhấp vào **Permissions**.
- Tìm phần **Resource-based policy statements**.
- Nếu nó trống, nhấp vào **Add trigger** ở đầu trang.
- Chọn **S3**, chọn bucket của bạn và nhấp **Add**. AWS bây giờ sẽ tự động chèn statement chính sách bắt buộc.

Cách 3: Khắc phục vĩnh viễn (Terraform)

Việc nhấp chuột thủ công dẫn đến "trôi cấu hình" (configuration drift), nơi mã của bạn không còn khớp với hạ tầng thực tế. Sử dụng tài nguyên aws_lambda_permission để thực hiện thay đổi này một cách vĩnh viễn và có thể lặp lại.

resource "aws_lambda_permission" "allow_s3" {
  statement_id  = "AllowS3Invoke"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.processor.function_name
  principal     = "s3.amazonaws.com"
  source_arn    = "arn:aws:s3:::${var.bucket_name}"
}

Kiểm tra kết quả

Kiểm tra lại xem chính sách đã thực sự hoạt động chưa bằng cách xem JSON thô:

aws lambda get-policy --function-name my-processor-func

Tìm kiếm "Service": "s3.amazonaws.com" trong kết quả đầu ra. Khi bạn thấy nó, hãy tải một tệp thử nghiệm 1KB lên bucket của bạn. Kiểm tra CloudWatch Logs ngay lập tức—bạn sẽ thấy một luồng nhật ký (log stream) mới xuất hiện trong vòng 5 đến 10 giây.

Mẹo chuyên nghiệp để phòng tránh

- **Giới hạn ARN**: Đừng bao giờ để trống `--source-arn`. Nếu bạn làm vậy, bất kỳ bucket S3 nào trên thế giới biết ARN hàm của bạn đều có khả năng kích hoạt nó.
- **Chú ý đến Region**: Đảm bảo bucket và Lambda của bạn nằm trong cùng một region (ví dụ: `us-east-1`). Mặc dù có tồn tại trigger chéo region, chúng thường thất bại trong âm thầm nếu quyền không hoàn hảo.
- **Kiểm tra phụ thuộc vòng**: Nếu bạn sử dụng CloudFormation, việc tạo thông báo bucket và quyền Lambda cùng một lúc đôi khi có thể gây ra vòng lặp triển khai. Hãy luôn tạo quyền trước.

Related Error Notes