- 實踐 Infrastructure as Code,不需要手動建立資源,而且可以對 Source Code 做 Version Control。
- CloudFormation 本身是免費的,只需要付建立的資源費用即可。
- 可以透過 CloudFormation Designer 產生基本的架構圖
- Template 必須要上傳到 S3 ,Template 格式可以是 Yaml or Json
- Stack 是由一個 Template 產生的包含多個資源,Stack 名字當作識別。
CloudFormation Template Section
- Parameters : 當要建立 Stack ,可以由外面設定動態參數
- 可設定資訊
- Type: String, Number, CommaDelimitedList, List, AWS Parameter
- Description
- Constraints
- ConstraintDescription
- Min/MaxLength
- Defaults
- AllowedValues (array)
- AllowedPattern (regular expression)
- NoEcho(Boolean)
- 可以從 SSM 取得資料
1VpcId: !Ref MyVPC 2 3Parameters: 4 InstanceType: 5 Type: 'AWS::SSM::Paramter::Value<String>' 6 Default: /EC2/InstanceType 7#there is public SSM parameters by AWS
- 可設定資訊
- Conditions : 建立資源的條件
- intrinsic function : Fn::And, Fn::Equals, Fn::If, Fn::Not, Fn::Or
1Conditions:
2 CreateProdResources: !Equals [!Ref EnvType, prod]
3
4Resource:
5 MountPoint:
6 Type: "AWS::EC2::VolumeAttachment"
7 Condition: CreateProdResources
- Resource: 要建立的 AWS 資源,此為必要欄位
- 格式 : AWS::aws-product-name::data-type-name
- Mapping: 建立 Key Value Mapping
1Mapping:
2 RegionMap:
3 user-east-1:
4 "32" : "ami-aaa"
5 "64" : "ami-bbb"
6 user-east-2:
7 "32" : "ami-ccc"
8 "64" : "ami-ddd"
9
10ImagesId: !FindInMap [RegionMap, !Ref "AWS:Region", 32]
- Transforms: 主要用於 Serve-less Application Model (SAM) ,宣告這是 SAM 的格式
- Output: 可以將 Stack 建立的 Resource Output 出來,所以其他 Stack 可以建立 reference
- 如果被其他 Stack reference ,那就不能 delete
1Outputs:
2 StackSSHSecurityGroup:
3 Description: The SSH Security Group for our Company
4 Vaule: !Ref MyCompanyWideSSHSecurityGroup
5 Export:
6 Name: SSHSecurityGroup
7
8Resource:
9 MySecureInstance:
10 Type: AWS::EC2::Instance
11 Properties:
12 AvailabilityZone: us-east-1a
13 ImageId: ami-a5c7edb2
14 InstanceType: t2.micro
15 SecurityGroup:
16 - !ImportValue SSHSecurityGroup
- Metadata: 提供額外有關 Template 資訊
Intrinsic Functions
- Fn::Ref
- Parameters ⇒ 返回 parameter 對應的值
- Resources ⇒ 返回資源的 physical ID
- Fn:GetAtt
1Resource:
2 EC2Instance:
3 Type: "AWS::EC2::Instance"
4 Properties:
5 ImageId: ami-1234567
6 InstanceType: t2.micro
7
8NewVolume:
9 Type: "AWS::EC2::Volume"
10 Confition: CreateProdResources
11 Properties:
12 Size: 100
13 AvailabilityZone:
14 !GetAtt EC2Instance.AvailabilityZone
- Fn::Join
1"Fn::Join" : [ ",", [ "a", "b", "c" ] ] => "a,b,c"
- Fn::Sub
1Fn::Sub:
2 - String
3 - Var1Name: Var1Value
4 Var2Name: Var2Value
5
6Name: !Sub
7 - www.${Domain}
8 - { Domain: !Ref RootDomainName }
- Fn::Split
1!Split [ "|" , "a|b|c" ] => ["a", "b", "c"]
- Fn::Select
1!Select [ "1", [ "apples", "grapes", "oranges", "mangoes" ] ]
- Fn::Base64
- 可以用在 EC2 pass 整個 script 在 user data
- user data script log ⇒ /var/log/cloud-init-output.log
1Resource:
2 EC2Instance:
3 Type: "AWS::EC2::Instance"
4 Properties:
5 ImageId: ami-1234567
6 InstanceType: t2.micro
7 UserData:
8 Fn::Base64: |
9 yum update -y
10 yum install -y httpd
11 systemctl start httpd
12 systemctl enable httpd
13 echo "Hello World from user data" > /var/www/html/index.html
cfn-init & cfn-signal & cfn-hup
- AWS::CloudFormation::Init 目的是讓 EC2 的設定可讀性更高, 需要放在 metadata section
- init 的 log 會寫在 /var/log/cfn-init.log
- cfn-init & cfn-signal 流程
- CloudFormation 先建立 EC2 Instance ,如果有設定 WaitCondition 這時 CloudFormation 會等待 EC2 這邊的 signal
- EC2 上面執行 User Data 時執行 cfn-init,這時 cfn-init 會去 CloudFormation 拉 metadata 的設定回來並執行
- cfn-signal 用於執行完 cfn-init 告訴 CloudFormation 成功與否
- 沒有收到 Signal 怎麼辦
- 確認 Script 是否有安裝
- 可以disable rollback,進機器看 /var/log/cfn-init.log 和 /var/log/cloud-init-output.log
- 由於 Signal 必須透過 Internet 通知 CloudFormation ,確認 EC2 可以 Access Internet.
- cfn-hup : 是一個 daemon 去檢查是否有 metadata 要更新, Default Interval 是 15 分鐘
1Resource:
2 EC2Instance:
3 Type: "AWS::EC2::Instance"
4 Properties:
5 ImageId: ami-1234567
6 InstanceType: t2.micro
7 UserData:
8 Fn::Base64: |
9 yum update -y aws-cfn-bootstrap
10 # Start cfn-init
11 /opt/aws/bin/cfn-init -s ${AWS::StackId} -r MyInstance --region ${AWS::Region} || error_exit 'Failed to run cfn-init'
12
13 # start the cfn-hup daemon
14 /opt/aws/bin/cfn-hup || error_exit "Failed to start cfn_hup"
15 # Start cfn-signal to the wait condition
16 /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackId} --resource SampleWaitCondition --region ${AWS::Region}
17
18Metadata:
19 Comment: Install a simple Apache HTTP page
20 AWS:CloudFormation::Init:
21 config:
22 packages:
23 yum:
24 httpd: []
25 files:
26 "/var/www/html/index.html";
27 content: |
28 <h1>Hello World from Ec2 Instance</h1>
29 mode: '000644'
30 "/etc/cfn/cfn-hup.conf":
31 content: !Sub |
32 [main]
33 stack=${AWS::StackId}
34 region=${AWS::Region}
35 interval=2
36 mode: "000400"
37 owner: "root"
38 group: "root"
39
40 "/etc/cfn/hooks.d/cfn-auto-reloader.conf":
41 content: !Sub |
42 [cfn-auto-reloader-hook]
43 triggers=post.update
44 path=Resources.WebServerHost.Metadata.AWS::CloudFormation::Init
45 action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource WebServerHost --region ${AWS::Region}
46 mode: "000400"
47 owner: "root"
48 group: "root"
49 commands:
50 hello:
51 command: "echo 'hello world'"
52 services:
53 sysvinit:
54 httpd:
55 enabled: 'true'
56 ensureRunning: 'true'
57
58SampleWaitCondition:
59 CreationPolicy:
60 ResourceSignal:
61 Timeout: PT1M #等一分鐘
62 Count:2 #要兩個成功的 Signal
63 Type: AWS::CoudFormation::WaitCondition
CloudFormation Rollback
- Stack 建立失敗時,Stack 正常狀態會是 ROLLBACK_COMPLETE ,並且只能刪除不能更新
- 預設 : 所有的資源會 rollback ,但 Stack 還在,可以看原因
- OnFailure=Rollback
- 可以 Disable rollback,所以可以做 troubleshooting
- OnFailure=DO_NOTHING
- 可以刪除整個 Stack
- OnFailure=Delete
- 預設 : 所有的資源會 rollback ,但 Stack 還在,可以看原因
- Stack 更新失敗
- Stack 如果 rollback 成功 Stack 狀態會是 UPDATE_ROLLBACK_COMPLETE ,可以嘗試更新或刪除
- Stack 如果 rollback 失敗 Stack 狀態會是 UPDATE_ROLLBACK_FAIL,可以嘗試修復問題再繼續 rollback
Nested Stacks
- 可以重複使用其他 Stack 當作整個 Stack 一部分
1Resources:
2 myStack:
3 Type: AWS::CloudFormation::Stack
4 Properties:
5 TemplateURL:
6 xxx.com/...
7 Parameters:
8 key: value
ChangeSet
- 可以在 CloudFormation 更新新的Template 之前先評估有什麼資源會變動,並確認是否要繼續執行,但執行了並不代表一定會成功。
Drift
- CloudFormation 建立的資源是沒有預防人為手動更新的
- Drift 是用來檢查哪些資源跟當初 CloudFormation 建立的不一樣,並且告訴你哪些不同"不是所有的資源 Drift 都有支援。"
Deletion Policy
- 用來控制當 Stack 被刪除時特定資源要做的事情
- 三種 Policy
- DeletionPolicy=Retain: 預防資源被刪除
- DeletionPolicy=Snapshot: 先將資料備份再刪除
- Support: EBS Volume, ElasticCache, Cluster, ElasticCache, ReplicationGroup, RDS DBInstance, RDS DBCluster, Redshift Cluster
- DeletionPolicy=Delete 刪除資源,但並非都會刪除成功,例如 S3 Bucket 裡面有 Object 就會刪除失敗
- RDS 預設 Snapshot , 其他預設 Delete
Termination Protection
- 用於保護 Stack 不會被不小心刪除
Creation Policy & Update Policy
- 像 Auto Scaling Group 可以需要啟多個 Ec2 Instance,這時 Creation Policy 就可以設定在 Auto Scaling Group,要收時間內收多少個 Signal 才算成功
- UpdatePolicy
- 如果直接更新 template 中的 user data,Auto Scaling Group 裡面的 EC2 還會是舊的 user data。
- UpdatePolicy Attribute 可以使用在三種 AWS Resource
auto scaling group 有三種 Policy
- AutoScalingReplacingUpdate: WillReplace可以用來決定建立全新的 Auto Scaling Group 取代舊,還是單純建立新的 Instance 來取代現有的 Instance 但 Auto Scaling Group 還是舊的。
- AutoScalingRollingUpdate: 在 Auto Scaling Group,將現有的 EC2 Instance 停掉,並建立新的取代
- AutoScalingScheduledAction
1Resources: 2 AutoScalingGroup: 3 Type: AWS::AutoScaling::AutoScalingGroup 4 Properties: 5 AvailabilityZones: 6 Fn::GetsAZs: 7 Ref: "AWS::Region" 8 LaunchConfigurationName: 9 Ref: LaunchConfig 10 Desiredcapacity: '3'' 11 MinSize: '1' 12 MaxSize: '4' 13 CreationPolicy: 14 ResourceSignal: 15 Count: '3' 16 Timeout: PT15M 17 UpdatePolicy: 18 ## Example1 start 19 AutoScalingRollingUpdate: 20 MinInstancesInService: '1' 21 MaxBatchSize: '2' 22 #how much time to wait for the signal 23 PauseTime: PT1M 24 WaitOnResourceSignals: 'true' 25 ## 預防CloudFormation 有排程去 update min,max or desired size 26 AutoScalingScheduledAction: 27 IgnoreUnmodifiedGroupSizeProperties: 'true 28 ## Example1 end 29 ## Example1 and Example2 不會同時存在 30 31 ## Example 2 start 32 ## 會建立全新的 Auto Scaling Group 取代舊得 33 AutoScalingReplacingUpdate: 34 WillReplace: 'true' 35 ## Example 2 end 36 37LaunchConfig: 38 Type: AWS::AutoScaling::LaunchConfiguration 39 Properties: 40 ImageId: ami-aaa 41 InstanceType: t2.micro 42 UserData: 43 Fn::Base64: | 44 yum update -y 45 yum install -y httpd 46 systemctl start httpd 47 systemctl enable httpd 48 echo "Hello World from user data" > /var/www/html/index.html
lambda alias
- CodeDeployLambdaAliasUpdate
1UpdatePolicy: 2 CodeDeployLambdaAliasUpdate: 3 AfterAllowTrafficHook: String 4 ApplicationName: String 5 BeforeAllowTrafficHook: String 6 DeploymentGroupName: String
elastic cache replication group
- UseOnlineResharding
1UpdatePolicy: 2EnableVersionUpgrade: Boolean
Depends ON
- 控制資源建立的順序,以下面為例,EC2 Instance 需要在 RDS 建立好以後才建立
1Resources:
2 Ec2Instance:
3 Type: AWS::EC2::Instance
4 Properties:
5 ImageId:
6 Fn::FindInMap:
7 - RegionMap
8 - Ref: AWS::Region
9 - AMI
10 DependsOn: myDB
11 myDB:
12 Type: AWS::RDS::DBInstance
Stack Policy
- 像是 IM Policy ,可以用來控制哪些 Resource 可以做什麼,哪些不能做什麼
1{
2 "Statement" : [
3 {
4 "Effect" : "Allow",
5 "Action" : "Update:*",
6 "Principal": "*",
7 "Resource" : "*"
8 },
9 {
10 "Effect" : "Deny",
11 "Action" : "Update:*",
12 "Principal": "*",
13 "Resource" : "LogicalResourceId/ProductionDatabase"
14 }
15 ]
16}
StackSets
- 可以一次更動多個 Region 或多個 Account 的 Stack
- 只有 Administrator Account 可以建立 StackSets
- Trust Account 可以更新 StackSets
- 每次更動 StackSets 設定,全部的 Stack 都會被更新
- 可以刪除整個 StackSets 或單獨刪除 StackSets 裡面的一個 Stack
Custom Resource
- 由於不是所有的 AWS Resource, CloudFormation 都有支援,所以有時需要透過 Lambda Function 去 call api
- 當 CloudFormation 在 Create, Update or delete 時都會觸發 Lambda Function
Delete S3 Custom Resource
- Lambda Function
1Resources:
2 LambdaExecutionRole:
3 Type: AWS::IAM::Role
4 Properties:
5 AssumeRolePolicyDocument:
6 Version: '2012-10-17'
7 Statement:
8 - Effect: Allow
9 Principal:
10 Service:
11 - lambda.amazonaws.com
12 Action:
13 - sts:AssumeRole
14 Path: "/"
15 Policies:
16 - PolicyName: root
17 PolicyDocument:
18 Version: '2012-10-17'
19 Statement:
20 - Effect: Allow
21 Action:
22 - "s3:*"
23 Resource: "*"
24 - Effect: Allow
25 Action:
26 - "logs:CreateLogGroup"
27 - "logs:CreateLogStream"
28 - "logs:PutLogEvents"
29 Resource: "*"
30
31 EmptyS3BucketLambda:
32 Type: "AWS::Lambda::Function"
33 Properties:
34 Handler: "index.handler"
35 Role:
36 Fn::GetAtt:
37 - "LambdaExecutionRole"
38 - "Arn"
39 Runtime: "python3.7"
40 # we give the function a large timeout
41 # so we can wait for the bucket to be empty
42 Timeout: 600
43 Code:
44 ZipFile: |
45 #!/usr/bin/env python
46 # -*- coding: utf-8 -*-
47 import json
48 import boto3
49 from botocore.vendored import requests
50
51 def handler(event, context):
52 try:
53 bucket = event['ResourceProperties']['BucketName']
54
55 if event['RequestType'] == 'Delete':
56 s3 = boto3.resource('s3')
57 bucket = s3.Bucket(bucket)
58 for obj in bucket.objects.filter():
59 s3.Object(bucket.name, obj.key).delete()
60
61 sendResponseCfn(event, context, "SUCCESS")
62 except Exception as e:
63 print(e)
64 sendResponseCfn(event, context, "FAILED")
65
66
67 def sendResponseCfn(event, context, responseStatus):
68 response_body = {'Status': responseStatus,
69 'Reason': 'Log stream name: ' + context.log_stream_name,
70 'PhysicalResourceId': context.log_stream_name,
71 'StackId': event['StackId'],
72 'RequestId': event['RequestId'],
73 'LogicalResourceId': event['LogicalResourceId'],
74 'Data': json.loads("{}")}
75
76 requests.put(event['ResponseURL'], data=json.dumps(response_body))
77
78Outputs:
79 StackSSHSecurityGroup:
80 Description: The ARN of the Lambda function that empties an S3 bucket
81 Value: !GetAtt EmptyS3BucketLambda.Arn
82 Export:
83 Name: EmptyS3BucketLambda
- Custom Resource with lambda function
1---
2AWSTemplateFormatVersion: '2010-09-09'
3
4Resources:
5 myBucketResource:
6 Type: AWS::S3::Bucket
7
8 LambdaUsedToCleanUp:
9 Type: Custom::cleanupbucket
10 Properties:
11 ServiceToken: !ImportValue EmptyS3BucketLambda
12 BucketName: !Ref myBucketResource
CloudFormation Request Example
1{
2 "RequestType" : "Create",
3 "ResponseURL" : "http://pre-signed-S3-url-for-response",
4 "StackId" : "arn:aws:cloudformation:us-west-2:123456789012:stack/stack-name/guid",
5 "RequestId" : "unique id for this create request",
6 "ResourceType" : "Custom::TestResource",
7 "LogicalResourceId" : "MyTestResource",
8 "ResourceProperties" : {
9 "Name" : "Value",
10 "List" : [ "1", "2", "3" ]
11 }
12}
評論