Giới thiệu
Trong bài viết này, chúng ta sẽ tìm hiểu về 3 loại Helper Script của AWS CloudFormation: cfn-init
, cfn-hup
, và cfn-signal
. Các script này hỗ trợ việc xây dựng, quản lý và đồng bộ hóa tài nguyên EC2 trong CloudFormation stack một cách hiệu quả. Bài viết sẽ trình bày chức năng, cách sử dụng, và ví dụ cụ thể cho từng script, dựa trên thông tin từ bài đăng của Saori Kato.
Mục tiêu:
- Hiểu rõ chức năng của từng helper script.
- Biết cách áp dụng chúng trong template CloudFormation.
- Minh họa bằng các ví dụ thực tế.
Lab Introduction
- AWS experience: Beginner / Intermediate
- Time to complete: 30 phút
- AWS Region: Tokyo (ap-northeast-1) hoặc bất kỳ region nào hỗ trợ các dịch vụ sử dụng
- Cost to complete: Free Tier eligible (sử dụng instance t3.micro và các tài nguyên cơ bản)
- Services used: EC2, CloudFormation, Security Group
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:

Giới thiệu Helper Scripts
AWS CloudFormation cung cấp 3 helper script để hỗ trợ quản lý tài nguyên EC2 trong stack:
cfn-init
: Dùng để cài đặt các gói phần mềm, tạo file, và khởi động dịch vụ dựa trên metadata được định nghĩa trong template.cfn-hup
: Theo dõi thay đổi trong metadata và thực thi hành động tùy chỉnh khi phát hiện thay đổi.cfn-signal
: Gửi tín hiệu đến CloudFormation để báo hiệu trạng thái hoàn thành của tài nguyên, thường dùng với CreationPolicy
hoặc WaitCondition
.
Tasks Details
- UserData và Hạn chế của nó (Bootloading)
- 1.1. Access CloudFormation
- 1.2. Deploy stack (UserData)
- 1.3. Đặt tên Specify stack details
- 1.4. Configure stack options
- 1.5. Submit stack
- 1.6. Đợi đến khi Stack sẵn sàng
- 1.7. Kiểm tra trạng thái Webapp
- 1.8. Vấn đề Bootloading
- UserData và Hạn chế của nó ( Application Update)
- 2.1. Update Stack
- 2.2. Vấn đề Application Update
- 2.3. Clean up UserData
- Deploy stack sử dụng cfn-signal
- 3.1. Deploy Stack
- 3.2. Kiểm tra Ec2, application
- 3.3. Test với vấn đề Application Update
- 3.4. Clean up
- Deploy stack sử dụng cfn-init
- 4.1. Deploy stack
- 4.2. Kiểm tra Ec2, application
- 4.3. Xử lý vấn đề (thủ công)
- 4.4. Phân tích vấn đề
- 4.5. Clean up
- Deploy stack sử dụng cfn-hup
- 5.1 Deploy stack
- 5.2. Kiểm tra Ec2, application
- 5.3 Clean up
1.UserData và Hạn chế của nó (Bootloading)
Để chứng minh được lợi ích của việc sử dụng các cfn thì mình sẽ đi từ vấn đề trước các bạn nhé.
1.2. Deploy stack (UserData)
Mình đã chuẩn bị sẵn một cloudformation template sử dụng
Upload template for UserData case
Download template : 1_userdata.yaml 
Nội dung:
Parameters:
LatestAmiId:
Description: "AMI for EC2"
Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2'
Message:
Description: "Message for HTML page"
Default: "UserData first problem!!!"
Type: "String"
Resources:
InstanceSecurityGroup:
Type: 'AWS::EC2::SecurityGroup'
Properties:
GroupDescription: Enable SSH and HTTP access via port 22 IPv4 & port 80 IPv4
DeletionPolicy: Delete
SecurityGroupIngress:
- Description: 'Allow SSH IPv4 IN'
IpProtocol: tcp
FromPort: '22'
ToPort: '22'
CidrIp: '0.0.0.0/0'
- Description: 'Allow HTTP IPv4 IN'
IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: '0.0.0.0/0'
Bucket:
Type: 'AWS::S3::Bucket'
DeletionPolicy: Delete
Instance:
Type: 'AWS::EC2::Instance'
Properties:
DeletionPolicy: Delete
InstanceType: "t2.micro"
ImageId: !Ref "LatestAmiId"
SecurityGroupIds:
- !Ref InstanceSecurityGroup
Tags:
- Key: Name
Value: SOA-Lab4-UserData Test
UserData:
Fn::Base64: !Sub |
#!/bin/bash -xe
yum -y update
yum -y upgrade
# simulate some other processes here
sleep 300
# Continue
yum install -y httpd
systemctl enable httpd
systemctl start httpd
echo "<html><head><title>Amazing test page</title></head><body><h1><center>${Message}</center></h1></body></html>" > /var/www/html/index.html
1.3. Đặt tên Specify stack details
- Đặt tên stackname : CMPUserData
- Phần message : "UserData first problem!!!" (để default)

- Cứ để default và Next

1.5. Submit stack
1.6. Đợi đến khi sẵn sàng
1.7. Kiểm tra trạng thái Webapp
Vào resource click Instance 
Confirm state là Running và copy public ip 
Sử dụng ip để access thì kết quả không load được như bên dưới 
Đợi 1 vài phút sau thì load được trang 
1.8. Vấn đề Bootloading
- Ở ví dụ trên chúng ta thấy server của chúng ta chưa sẵn sàng nhưng trạng thái stack đã báo là hoàng thành.
- Điều này có nghĩa trạng thái thực tế của application đang không được đồng bộ với trạng thái của cloudformation
- Điều này dẫn đến rất nhiều hậu quả:
- Trải nghiêm người dùng sẽ tệ vì người dùng có thể sẽ không access được service cho đến khi app thật sự được load
- Khi triển khai kiến trúc phức tạp nhiều thành phần sẽ gặp khó khăn vì không biết được tình trang ready của application
2. UserData và Hạn chế của nó ( Application Update)
2.1. Update Stack
Tiến hành update stack ở trạng thái hiện tại 
Chọn exiting template 
Cập nhật message hiển thị của application
UserData second problem!!!

Ấn Next 
Ấn Submit 
Đợi update hoàn thành 
Kiểm tra trạng thái Webapp
Vào resource click Instance
Sử dụng ip để access
2.2 Vấn đề Application Update
Chúng ta có thể dễ dàng nhận thấy là dù instance có thay đổi ip nhưng application data cũng không được cập nhật 
Để giải quyết vấn đề này chúng ta sẽ dùng cfn-signal
Nhưng trước hết chúng ta cần clean up resource đã
2.3 Clean up UserData
- Chọn Delete stack

3. Deploy stack sử dụng cfn-signal
3.1 Deploy Stack
Mình đã chuẩn bị template sử dụng cfn-signal như bên dưới .
Link download : 2_userdata_with_signal.yaml
Các bước deploy giống hệt bên trên nên mình không đi lại.
Mình sẽ note một số điểm khác biệt và cải thiện
Parameters:
LatestAmiId:
Description: "AMI for EC2"
Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2'
Message:
Description: "Message for HTML page"
Default: "UserData first problem is resolved by signal"
Type: "String"
Resources:
InstanceSecurityGroup:
Type: 'AWS::EC2::SecurityGroup'
Properties:
GroupDescription: Enable SSH and HTTP access via port 22 IPv4 & port 80 IPv4
SecurityGroupIngress:
- Description: 'Allow SSH IPv4 IN'
IpProtocol: tcp
FromPort: '22'
ToPort: '22'
CidrIp: '0.0.0.0/0'
- Description: 'Allow HTTP IPv4 IN'
IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: '0.0.0.0/0'
Bucket:
Type: 'AWS::S3::Bucket'
Instance:
Type: 'AWS::EC2::Instance'
CreationPolicy:
ResourceSignal:
Timeout: PT15M
Properties:
InstanceType: "t2.micro"
ImageId: !Ref "LatestAmiId"
SecurityGroupIds:
- !Ref InstanceSecurityGroup
Tags:
- Key: Name
Value: CMP-UserData Test
UserData:
Fn::Base64: !Sub |
#!/bin/bash -xe
yum -y update
yum -y upgrade
# simulate some other processes here
sleep 300
# Continue
yum install -y httpd
systemctl enable httpd
systemctl start httpd
echo "<html><head><title>Amazing test page</title></head><body><h1><center>${Message}</center></h1></body></html>" > /var/www/html/index.html
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackId} --resource Instance --region ${AWS::Region}
Điểm thay đổi
Mình đã thêm block
CreationPolicy:
ResourceSignal:
Timeout: PT15M
Dòng cuối cùng trong userdata mình thêm dòng này. Command này sẽ sử dụng cfn-signal để báo cho cfn biết là application đã ready.
/opt/aws/bin/cfn-signal -e $? --stack AWS-StackId --resource Instance --region AWS-Region
Thử luôn nhé !
3.2. Kiểm tra Ec2, application
Kiểm tra tình trạng ec2 instance khi mới deploy
Khác với chỉ dùng mỗi UserData stack sẽ không ready ngay mà sẽ phải đợi lâu hơn (khoảng 5-6 phút) 
Application Được khởi động thành công đồng bộ với stack báo finish 
3.3 Test với vấn đề Application Update
3.4 Clean up
4. Deploy stack sử dụng cfn-init
4.1 Deploy stack
Ngoài sử dụng UserData chúng ta có thể dùng cfn-init để cài đặt các ứng dụng bên trong Ec2 instance.
Mình đã chuẩn bị template như bên dưới .
Các bước deploy giống hệt bên trên nên mình không đi lại.
Mình sẽ note một số điểm khác biệt và cải thiện
Parameters:
LatestAmiId:
Description: "AMI for EC2"
Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2'
Message:
Description: "Message for HTML page"
Default: "UserData first problem!!!"
Type: "String"
Resources:
InstanceSecurityGroup:
Type: 'AWS::EC2::SecurityGroup'
Properties:
GroupDescription: Enable SSH and HTTP access via port 22 IPv4 & port 80 IPv4
SecurityGroupIngress:
- Description: 'Allow SSH IPv4 IN'
IpProtocol: tcp
FromPort: '22'
ToPort: '22'
CidrIp: '0.0.0.0/0'
- Description: 'Allow HTTP IPv4 IN'
IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: '0.0.0.0/0'
Bucket:
Type: 'AWS::S3::Bucket'
Instance:
Type: 'AWS::EC2::Instance'
Metadata:
'AWS::CloudFormation::Init':
config:
packages:
yum:
httpd: []
files:
/var/www/html/index.html:
content: !Sub |
<html><head><title>Amazing test page</title></head><body><h1><center>${Message}</center></h1></body></html>
commands:
simulatebootstrap:
command: "sleep 300"
services:
sysvinit:
httpd:
enabled: "true"
ensureRunning: "true"
files:
- "/var/www/html/index.html"
CreationPolicy:
ResourceSignal:
Timeout: PT15M
Properties:
InstanceType: "t2.micro"
ImageId: !Ref "LatestAmiId"
SecurityGroupIds:
- !Ref InstanceSecurityGroup
Tags:
- Key: Name
Value: CMP-UserData Test
UserData:
Fn::Base64: !Sub |
#!/bin/bash -xe
/opt/aws/bin/cfn-init -v --stack ${AWS::StackId} --resource Instance --region ${AWS::Region}
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackId} --resource Instance --region ${AWS::Region}
Điểm thay đổi:
- Thay vì install app bằng block user data mình sử dụng cfn-init
Metadata:
'AWS::CloudFormation::Init':
config:
packages:
yum:
httpd: []
files:
/var/www/html/index.html:
content: !Sub |
<html><head><title>Amazing test page</title></head><body><h1><center>${Message}</center></h1></body></html>
commands:
simulatebootstrap:
command: "sleep 300"
services:
sysvinit:
httpd:
enabled: "true"
ensureRunning: "true"
files:
- "/var/www/html/index.html"
- Dòng cuối cùng trong userdata mình thêm dòng này. Command này sẽ sử dụng cfn-init để báo cho cfn biết là application cần cập nhật.
/opt/aws/bin/cfn-init -v --stack ${AWS::StackId} --resource Instance --region ${AWS::Region}
4.2. Kiểm tra Ec2, application
Kiểm tra tình trạng ec2 instance khi mới deploy
Khác với chỉ dùng mỗi UserData stack sẽ không ready ngay mà sẽ phải đợi lâu hơn (khoảng 5-6 phút) 
Application Được khởi động thành công đồng bộ với stack báo finish 
Thử update stack với UserData second problem!!!
chúng ta có thể thấy vấn đề vẫn còn nguyên 
Vậy cfn-init đâu có khác gì với user data ?
Chưa hẳn chúng ta có thê cập nhật lại app bằng cách chạy cfn-init trong instance
4.3 Xử lý vấn đề (thủ công)
4.4 Phân tích vấn đề:
- Vậy vấn đề ở đây là gì:
- UserData chỉ chạy đúng một lần khi instance được khởi tạo , và không chạy lại khi stack được up date
- cfn-init sẽ chạy mỗi khi được kích hoạt ở trong instance . Tuy nhiên việc này không được kích hoạt tự động.
- Để tự động cập nhật application mỗi khi update stack thì chúng ta có thể sử dụng cfn-hup
4.5 Clean up
5. Deploy stack sử dụng cfn-hup
5.1. Deploy stack
Mình đã chuẩn bị template như bên dưới .
Download : 4_cfninit_with_signal_and_cfnhup.yaml
Các bước deploy giống hệt bên trên nên mình không đi lại.
Mình sẽ note một số điểm khác biệt và cải thiện
Parameters:
LatestAmiId:
Description: "AMI for EC2"
Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2'
Message:
Description: "Message for HTML page"
Default: "UserData first problem!!!"
Type: "String"
Resources:
InstanceSecurityGroup:
Type: 'AWS::EC2::SecurityGroup'
Properties:
GroupDescription: Enable SSH and HTTP access via port 22 IPv4 & port 80 IPv4
SecurityGroupIngress:
- Description: 'Allow SSH IPv4 IN'
IpProtocol: tcp
FromPort: '22'
ToPort: '22'
CidrIp: '0.0.0.0/0'
- Description: 'Allow HTTP IPv4 IN'
IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: '0.0.0.0/0'
Bucket:
Type: 'AWS::S3::Bucket'
Instance:
Type: 'AWS::EC2::Instance'
Metadata:
'AWS::CloudFormation::Init':
config:
packages:
yum:
httpd: []
files:
/etc/cfn/cfn-hup.conf:
content: !Sub |
[main]
stack=${AWS::StackName}
region=${AWS::Region}
interval=1
verbose=true
mode: '000400'
owner: 'root'
group: 'root'
/etc/cfn/hooks.d/cfn-auto-reloader.conf:
content: !Sub |
[cfn-auto-reloader-hook]
triggers=post.update
path=Resources.Instance.Metadata.AWS::CloudFormation::Init
action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackId} --resource Instance --region ${AWS::Region}
runas=root
mode: '000400'
owner: 'root'
group: 'root'
/var/www/html/index.html:
content: !Sub |
<html><head><title>Amazing test page</title></head><body><h1><center>${Message}</center></h1></body></html>
commands:
simulatebootstrap:
command: "sleep 300"
services:
sysvinit:
cfn-hup:
enabled: "true"
ensureRunning: "true"
files:
- /etc/cfn/cfn-hup.conf
- /etc/cfn/hooks.d/cfn-auto-reloader.conf
httpd:
enabled: "true"
ensureRunning: "true"
files:
- "/var/www/html/index.html"
CreationPolicy:
ResourceSignal:
Timeout: PT15M
Properties:
InstanceType: "t2.micro"
ImageId: !Ref "LatestAmiId"
SecurityGroupIds:
- !Ref InstanceSecurityGroup
Tags:
- Key: Name
Value: CMP-UserData Test
UserData:
Fn::Base64: !Sub |
#!/bin/bash -xe
/opt/aws/bin/cfn-init -v --stack ${AWS::StackId} --resource Instance --region ${AWS::Region}
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackId} --resource Instance --region ${AWS::Region}
Điểm thay đổi
Mình thêm service cfn-hup vào trong blockcode cfn-init
'AWS::CloudFormation::Init':
config:
packages:
yum:
httpd: []
files:
/etc/cfn/cfn-hup.conf:
content: !Sub |
[main]
stack=${AWS::StackName}
region=${AWS::Region}
interval=1
verbose=true
mode: '000400'
owner: 'root'
group: 'root'
/etc/cfn/hooks.d/cfn-auto-reloader.conf:
content: !Sub |
[cfn-auto-reloader-hook]
triggers=post.update
path=Resources.Instance.Metadata.AWS::CloudFormation::Init
action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackId} --resource Instance --region ${AWS::Region}
runas=root
mode: '000400'
owner: 'root'
group: 'root'
/var/www/html/index.html:
content: !Sub |
<html><head><title>Amazing test page</title></head><body><h1><center>${Message}</center></h1></body></html>
commands:
simulatebootstrap:
command: "sleep 300"
services:
sysvinit:
cfn-hup:
enabled: "true"
ensureRunning: "true"
files:
- /etc/cfn/cfn-hup.conf
- /etc/cfn/hooks.d/cfn-auto-reloader.conf
httpd:
enabled: "true"
ensureRunning: "true"
files:
- "/var/www/html/index.html"
Thử luôn nhé !
5.2. Kiểm tra Ec2, application
Kiểm tra tình trạng ec2 instance khi mới deploy
Khác với chỉ dùng mỗi UserData stack sẽ không ready ngay mà sẽ phải đợi lâu hơn (khoảng 5-6 phút) 
Application Được khởi động thành công đồng bộ với stack báo finish 
Thử update stack với UserData second problem!!!
chúng ta có thể thấy đã được cập nhật ngay tức thì 
5.3 Clean up