📦 CloudFormation: template, stack và change set

Mục tiêu bài học

Sau bài này, bạn sẽ:

  • Đọc hiểu cấu trúc một template CloudFormation và biết section nào dùng để làm gì.
  • Phân biệt rõ template, stack, change set và khi nào phải dùng change set.
  • Xử lý được các tình huống rollback, UPDATE_ROLLBACK_COMPLETE và drift.
  • Chọn đúng cơ chế truyền tham số và secret (parameter, NoEcho, SSM, Secrets Manager) theo câu hỏi thi.

Ôn nhanh bài trước

Bài trước bạn đã đi qua luồng CodePipeline, CodeDeploy và SAM. Điểm cần nhớ: SAM chính là một lớp chuyển đổi (transform) của CloudFormation, nghĩa là file template.yaml của SAM khi deploy vẫn biến thành một stack CloudFormation bên dưới. Vì vậy hiểu CloudFormation là điều kiện để hiểu luôn cả SAM.

Lý thuyết

Template, stack và change set là gì?

Ba khái niệm này hay bị hỏi lẫn lộn, hãy tách rõ:

Khái niệm Bản chất Ví dụ
Template File JSON/YAML mô tả tài nguyên mong muốn template.yaml trong repo
Stack Một tập tài nguyên được tạo và quản lý như một đơn vị từ template prod-api-stack
Change set Bản xem trước (preview) những gì sẽ thay đổi trước khi apply Xem trước khi thêm một Lambda

Nói ngắn: template là bản thiết kế, stack là ngôi nhà đã xây, change set là bản dự toán sửa nhà trước khi cho thợ đập tường.

flowchart LR
    T["📄<br/>Template YAML"] --> CS["👁️<br/>Change set"]
    CS --> S["🏗️<br/>Stack"]
    S --> R["🧱<br/>Resources"]
    R --> D["🚨<br/>Drift detection"]

Các section trong template

Một template có các section sau, thứ tự này cũng là thứ tự ưu tiên khi ôn thi:

  • AWSTemplateFormatVersion: phiên bản template, hiện chỉ có 2010-09-09.
  • Description: mô tả ngắn.
  • Parameters: giá trị truyền vào lúc deploy, có Type, Default, AllowedValues.
  • Mappings: bảng tra cứu tĩnh, ví dụ map Region sang AMI ID.
  • Conditions: điều kiện boolean, dùng với Fn::If để bật/tắt tài nguyên theo môi trường.
  • Transform: khai báo AWS::Serverless-2016-10-31 khi dùng SAM.
  • Resources: section duy nhất bắt buộc, mọi thứ khác đều tùy chọn.
  • Outputs: xuất giá trị, có thể dùng Export để stack khác Fn::ImportValue.

[!IMPORTANT] Câu hỏi kinh điển: section nào bắt buộc trong template? Chỉ có Resources. Nếu đề cho một template chỉ có Parameters và Outputs, template đó invalid.

Pseudo parameters và intrinsic functions cần nhớ

Pseudo parameters có sẵn, không cần khai báo: AWS::Region, AWS::AccountId, AWS::StackName, AWS::NoValue.

Vài hàm hay gặp:

  • Ref lấy giá trị tham số hoặc ID tài nguyên; với resource thì trả về physical ID.
  • Fn::GetAtt lấy attribute cụ thể, ví dụ !GetAtt MyBucket.Arn.
  • Fn::Sub thay biến trong chuỗi, gọn hơn nối chuỗi.
  • Fn::ImportValue lấy giá trị Output được Export từ stack khác.
  • Fn::If kết hợp với Conditions, dùng AWS::NoValue để bỏ hẳn thuộc tính.

Truyền tham số và secret

Đây là vùng dễ mất điểm nhất. Nguyên tắc:

  • Không hard-code secret trong template.
  • NoEcho: true chỉ che giá trị trong log/console, không mã hoá gì cả. Đừng nhầm nó với bảo mật thật.
  • Với secret thật, dùng SSM Parameter Store (SecureString) hoặc Secrets Manager, rồi tham chiếu động: {{resolve:ssm:/myapp/db-password}} hoặc {{resolve:secretsmanager:myapp/db:SecretString:password}}.
  • Tham chiếu resolve: được giải quyết tại thời điểm deploy, nên giá trị secret không nằm trong template.
  • Trên EC2/ECS/Lambda, nên dùng IAM role + Secrets Manager thay vì nhét secret vào environment variable dạng plaintext.
Section duy nhất bắt buộc trong một CloudFormation template là {{c1::Resources}}.
Để che giá trị tham số trong console và log, ta đặt thuộc tính {{c2::NoEcho}} bằng true, nhưng nó không mã hoá giá trị.
Cơ chế trả về giá trị secret tại thời điểm deploy là tham chiếu động {{c3::resolve}} (ví dụ `resolve:ssm` hoặc `resolve:secretsmanager`).
Khi CloudFormation phát hiện tài nguyên bị thay đổi ngoài stack, ta gọi đó là {{c4::drift}}.

Rollback, failure và drift

Khi tạo mới thất bại, CloudFormation rollback và tài nguyên bị xoá (trừ khi bạn bật DisableRollback hoặc OnFailure=DO_NOTHING).

Khi update thất bại:

  • Stack quay về trạng thái cũ với UPDATE_ROLLBACK_COMPLETE.
  • Nếu chính bước rollback cũng fail, stack kẹt ở UPDATE_ROLLBACK_FAILED. Khi đó dùng ContinueUpdateRollback với ResourcesToSkip để gỡ kẹt.

Drift là khi tài nguyên thực tế khác với template (ai đó sửa tay trên console). Drift detection chỉ báo cáo, không tự sửa. Bạn phải cập nhật lại template hoặc revert thủ công.

DeletionPolicy và UpdateReplacePolicy quyết định chuyện gì xảy ra khi tài nguyên bị xoá/thay thế: Retain giữ lại, Snapshot chụp bản sao (dùng cho RDS, EBS), Delete là mặc định.

Ví dụ minh hoạ

Ví dụ 1: Tạo stack từ template đơn giản.

AWSTemplateFormatVersion: "2010-09-09"
Description: Demo stack
Parameters:
  Env:
    Type: String
    AllowedValues: [dev, prod]
Conditions:
  IsProd: !Equals [!Ref Env, prod]
Resources:
  DataBucket:
    Type: AWS::S3::Bucket
    DeletionPolicy: Retain
    Properties:
      BucketName: !Sub "demo-${Env}-data-${AWS::AccountId}"
Outputs:
  BucketName:
    Value: !Ref DataBucket
    Export:
      Name: DataBucketName

Đọc từ trên xuống: Parameters cho phép chọn môi trường, Conditions tạo IsProd (ở đây chưa dùng nhưng để thấy pattern), Resources tạo bucket với tên dựng bằng Fn::Sub, DeletionPolicy: Retain nghĩa là xoá stack vẫn giữ bucket. Outputs export tên bucket để stack khác dùng Fn::ImportValue.

Ví dụ 2: Deploy có change set.

1. Tạo change set: `aws cloudformation create-change-set --stack-name prod-api --template-body file://template.yaml --change-set-name v2 --capabilities CAPABILITY_IAM`
2. Xem thay đổi: `aws cloudformation describe-change-set --change-set-name v2 --stack-name prod-api`
3. Nếu thấy resource bị Replace mà không mong muốn, sửa template rồi tạo lại change set
4. Apply: `aws cloudformation execute-change-set --change-set-name v2 --stack-name prod-api`

Lưu ý bước 1: nếu template có tạo IAM resource, bắt buộc --capabilities CAPABILITY_IAM hoặc CAPABILITY_NAMED_IAM, nếu không sẽ báo lỗi InsufficientCapabilities.

Ví dụ 3: Tình huống thi.

Đề: một stack đang ở UPDATE_ROLLBACK_FAILED do một resource bị xoá tay. Cách xử lý?

Đáp án: dùng ContinueUpdateRollback và chỉ định ResourcesToSkip cho tài nguyên lỗi, sau đó sửa lại template cho khớp thực tế.

Thảo luận và suy ngẫm

  • Trong team bạn, ai được phép sửa tay trên console? Nếu có người sửa, bạn sẽ phát hiện drift bằng cách nào?
  • Với secret trong pipeline, bạn chọn SSM Parameter Store hay Secrets Manager? Tiêu chí nào quyết định (chi phí, rotation, cross-account)?
  • Nếu một thay đổi template khiến RDS bị Replace, bạn sẽ chặn nó ở bước nào trong quy trình?

Góc chuyên gia

Vài bẫy mình thấy nhiều người thi trượt vì nó:

  • NoEcho không phải mã hoá. Đề rất thích câu "làm sao bảo mật password trong template" và đáp án đúng thường là Secrets Manager hoặc SSM SecureString, không phải NoEcho.
  • Change set không bắt buộc. Bạn vẫn update trực tiếp được. Nhưng câu hỏi kiểu "cần xem trước thay đổi trước khi apply" thì đáp án là change set.
  • Drift detection không tự fix. Nó chỉ báo. Đọc kỹ đề để không chọn nhầm "CloudFormation tự động đồng bộ".
  • Resources là bắt buộc, mọi section khác optional.
  • Export không xoá được khi còn stack khác import. Nếu cố xoá, bạn nhận lỗi và phải gỡ ImportValue trước.
  • Nested stack dùng AWS::CloudFormation::Stack với TemplateURL phải nằm trên S3, không phải đường dẫn local.
  • CAPABILITY_IAM là lỗi hay gặp khi deploy qua CLI/pipeline mà quên.
  • Mẹo thi: khi đề nhắc "hạ tầng như code", "lặp lại được", "rollback tự động" thì nghĩ ngay CloudFormation; khi nhắc "xem trước thay đổi" thì nghĩ change set.
[
  { "front": "Section nào là bắt buộc trong CloudFormation template?", "back": "Resources. Mọi section khác đều tùy chọn." },
  { "front": "Template khác stack ở điểm nào?", "back": "Template là file mô tả tài nguyên; stack là tập tài nguyên đã được tạo và quản lý từ template đó." },
  { "front": "Change set dùng để làm gì?", "back": "Xem trước những thay đổi sẽ áp dụng lên stack trước khi execute." },
  { "front": "NoEcho: true có mã hoá giá trị tham số không?", "back": "Không. Nó chỉ che giá trị trong log/console, không mã hoá." },
  { "front": "Cách an toàn để đưa secret vào template là gì?", "back": "Tham chiếu động resolve:ssm hoặc resolve:secretsmanager, được giải quyết tại thời điểm deploy." },
  { "front": "Stack ở trạng thái UPDATE_ROLLBACK_FAILED thì xử lý thế nào?", "back": "Dùng ContinueUpdateRollback, có thể chỉ định ResourcesToSkip cho tài nguyên lỗi." },
  { "front": "Drift là gì?", "back": "Tài nguyên thực tế khác với định nghĩa trong template, thường do sửa tay ngoài stack." },
  { "front": "Drift detection có tự động sửa tài nguyên không?", "back": "Không. Nó chỉ phát hiện và báo cáo." },
  { "front": "DeletionPolicy: Retain làm gì?", "back": "Giữ lại tài nguyên khi stack bị xoá hoặc tài nguyên bị thay thế." },
  { "front": "Fn::ImportValue dùng để lấy gì?", "back": "Giá trị Output đã được Export từ một stack khác." },
  { "front": "Lỗi InsufficientCapabilities khi deploy thường do thiếu gì?", "back": "Thiếu CAPABILITY_IAM hoặc CAPABILITY_NAMED_IAM khi template tạo tài nguyên IAM." },
  { "front": "Nested stack khai báo bằng resource type nào và TemplateURL lấy từ đâu?", "back": "AWS::CloudFormation::Stack, TemplateURL phải trỏ tới một object trên S3." },
  { "front": "Transform AWS::Serverless-2016-10-31 dùng cho cái gì?", "back": "Chuyển template SAM thành CloudFormation chuẩn khi deploy." }
]

Tóm tắt và bước tiếp theo

Bốn ý mang vào phòng thi:

  1. Resources là section bắt buộc; template là bản thiết kế, stack là thực thể, change set là bản xem trước.
  2. Secret phải đi qua SSM SecureString hoặc Secrets Manager bằng tham chiếu resolve:, không hard-code, không tin NoEcho.
  3. Update fail thì rollback về UPDATE_ROLLBACK_COMPLETE; kẹt ở UPDATE_ROLLBACK_FAILED thì dùng ContinueUpdateRollback.
  4. Drift chỉ báo cáo, không tự sửa.

Bài tiếp theo chuyển sang nhóm dịch vụ tích hợp: SQS, SNS, EventBridge và Step Functions. Bạn sẽ học cách chọn đúng dịch vụ cho từng kiểu giao tiếp (queue, pub/sub, event bus, orchestration) và các bẫy hay gặp khi đề hỏi về decoupling.