赞
踩
参考
cloudformation是aws的iac工具,以下简称cfn
创建堆栈
aws cloudformation create-stack --stack-name testtemp \
--template-body file://testtemp.yaml
# --parameters ParameterKey=KeyPairName,ParameterValue=TestKey
删除堆栈
aws cloudformation delete-stack \
--stack-name testtemp
查看堆栈event
aws cloudformation describe-stack-events \
--stack-name \
--max-items 2
切换格式,json和yaml的转换
pip install cfn-flip
cfn-flip --version
cfn-flip example_parameter.json example_parameter.yaml
方便起见可以编写脚本将常用测试命令整合在一起,只需要修改参数即可快速重复测试模板
#!/bin/bash WEBURL=$2 FILE_NAME=resources.yaml BUCKET_NAME=zhaojiew-test FILEPATH=cfn BUCKET_FILEPATH=s3://$BUCKET_NAME/$FILEPATH/ STACK_NAME=aws CHANGESET_NAME=awsset ROLE_ARN=arn:aws-cn:iam::xxxxxxxx:role/MyCloudfotmationRole echo STACK_NAME "====>" $STACK_NAME echo FILE_NAME "====>" $FILE_NAME echo ROLE_ARN "====>" $ROLE_ARN echo BUCKET_NAME "====>" $BUCKET_NAME echo FILEPATH "====>" $FILEPATH case $1 in "create") echo " =================== create stack ==================="; aws cloudformation create-stack --stack-name $STACK_NAME\ --template-body file://$FILE_NAME \ --role-arn $ROLE_ARN \ --capabilities CAPABILITY_IAM # --tags Key=who,Value=justtest \ # --parameters ParameterKey=InstanceType,ParameterValue=t2.small \ # ParameterKey=SubnetIDs,ParameterValue=SubnetID1\\,SubnetID2 ;; "urlcreate") echo " =================== create stack from url ==================="; aws s3 cp $FILE_NAME $BUCKET_FILEPATH TEMPURL=https://$BUCKET_NAME.s3.cn-north-1.amazonaws.com.cn/$FILEPATH/$FILE_NAME echo $TEMPURL aws cloudformation create-stack --stack-name $STACK_NAME\ --template-url $TEMPURL \ --role-arn $ROLE_ARN # --tags Key=who,Value=justtest \ # --parameters ParameterKey=InstanceType,ParameterValue=t2.small \ # ParameterKey=SubnetIDs,ParameterValue=SubnetID1\\,SubnetID2 ;; "delete") echo " =================== delete stack ===================" aws cloudformation delete-stack --stack-name $STACK_NAME ;; "update") echo " =================== update stack ===================" aws cloudformation update-stack --stack-name $STACK_NAME\ --template-body file://$FILE_NAME \ --role-arn $ROLE_ARN \ --capabilities CAPABILITY_NAMED_IAM ;; "updateset") echo " =================== create stackset ===================" aws cloudformation create-change-set \ --stack-name $STACK_NAME \ --change-set-name $CHANGESET_NAME \ --template-body file://$FILE_NAME\ --capabilities CAPABILITY_IAM ;; "event") echo " =================== describe stack event ===================" aws cloudformation describe-stack-events --stack-name aws --max-items 5 \ --query 'StackEvents[].{CStatus: ResourceStatus,Rreason: ResourceStatusReason}' --output table ;; "download") echo " =================== download external url ===================" wget -nc $WEBURL -O $FILE_NAME echo "file name is =====>>>>> " ${WEBURL##*/} ;; "upload") echo " =================== upload file to bucket ===================" aws s3 cp $FILE_NAME $BUCKET_FILEPATH echo https://$BUCKET_NAME.s3.cn-north-1.amazonaws.com.cn/$FILEPATH/$FILE_NAME ;; "valid") echo " =================== validate templaet ===================" aws cloudformation validate-template --template-body file://$FILE_NAME ;; *) echo "Input Args Error..." ;; esac
cfn顶级字段,只有resource是必须的,关于cloutformation的模板剖析
AWSTemplateFormatVersion: 'version date' (optional) # version of the CloudFormation template. Only accepted value is '2010-09-09'
Description: 'String' (optional) # a text description of the Cloudformation template
Metadata: 'template metadata' (optional) # objects that provide additional information about the template
Parameters: 'set of parameters' (optional) # a set of inputs used to customize the template
Rules: 'set of rules' (optional) # a set of rules to validate the parameters provided at deployment/update
Mappings: 'set of mappings' (optional) # a mapping of keys and associated values
Conditions: 'set of conditions' (optional) # conditions that control whether certain resources are created
Transform: 'set of transforms' (optional) # for serverless applications
Resources: 'set of resources' (required) # a components of your infrastructure
Hooks: 'set of hooks' (optional) # Used for ECS Blue/Green Deployments
Outputs: 'set of outputs' (optional) # values that are returned whenever you view your stack's properties
堆栈是cfn模板的部署,单个cfn模板可以创建多个stack
如果堆栈创建失败会回滚,删除所有已创建的资源。如果无法删除则会保留资源直到能够成功删除堆栈
通过iam权限控制cfn访问,控制台使用cfn需要比cli和api更多的额外权限,例如上传文件到s3(每个区域都有默认的cf模板的存储桶),列出下拉参数所需的desctibe*权限。可见如果通过cli创建堆栈,实际上并不会将文件上传到s3桶中。
cfn创建资源的api调用来自cfn的ip地址,因此不要使用aws:SourceIp
条件键进行限制
通过--role-arn
可以指定cfn可以使用的角色,cfn的所有堆栈操作都通过该角色完成。如果不指定,则使用之前关联过的角色。如果没有可用角色,则会从用户凭证生成临时会话
aws cloudformation create-stack --stack-name testtemp \
--role-arn arn:aws-cn:iam::xxxxxxx:role/MyCloudfotmationRole
--template-body file://testtemp.yaml
Amazon CloudFormation will use this role for all stack operations. Other users that have permissions to operate on this stack will be able to use this role, even if they don’t have permission to pass it. Ensure that this role grants least privilege.
这意味着如果其他用户有权访问堆栈,即使cfn的角色没有传递,其他用户也能通过cfn间接使用该角色
简单s3 bucket 模板
#testtemp.yaml
AWSTemplateFormatVersion: "2010-09-09"
Description: AWS CloudFormation workshop - Template and stack
Resources:
S3Bucket:
Type: AWS::S3::Bucket
Properties:
VersioningConfiguration:
Status: Enabled
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
aws cloudformation create-stack --stack-name temptest \
--template-body file://temptest.yaml
由于该行为会创建iam 角色,因此在cli命令和console中要进行确认 --capabilities CAPABILITY_IAM
在模板中使用了内置函数ref
引用其他资源,可以在output中验证
policy策略有两种写法,内联和托管策略,以下为用户创建的托管策略
AWSTemplateFormatVersion: '2010-09-09' Resources: myEC2Instance: Type: AWS::EC2::Instance Version: '2009-05-15' Properties: ImageId: ami-0ab68f4313c5aff87 InstanceType: t2.micro Monitoring: 'true' DisableApiTermination: 'false' IamInstanceProfile: !Ref RootInstanceProfile RootRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com.cn Action: - sts:AssumeRole Path: "/" RolePolicies: Type: AWS::IAM::Policy Properties: PolicyName: s3readonly PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - "s3:Get*" - "s3:List*" Resource: "*" Roles: - !Ref RootRole RootInstanceProfile: Type: AWS::IAM::InstanceProfile Properties: Path: "/" Roles: - !Ref RootRole Outputs: InstanceId: Description: Instance ID of the instance you create Value: !Ref myEC2Instance RoleID: Description: role ID of the instance you create Value: !Ref RootRole
使用伪参数获取堆栈id,区域和账号等信息,例如以下使用AWS::Partition
获取中国区分区为aws-cn
意味着可以动态适配不同账号和区域的环境进行堆栈部署。
创建lambada函数使用zip对inline function进行打包,大小不超过4MB
使用内置函数sub
对伪参数进行替换,sub可以分为有mapping和无mapping两种,对于伪参数的用法为无map。
在sub
函数中使用${paramater}
能间接获取ref
和Fn::GetAttr
等函数的调用结果
$(!parameter)
将原样解析不进行替换
AWSTemplateFormatVersion: "2010-09-09" Description: AWS CloudFormation workshop - Pseudo parameters (uksb-1q9p31idr). Parameters: DatabaseUsername: Description: Value to be used with the dbUsername SSM parameter. The default value is set to 'alice', which users can override when creating a CloudFormation stack. Type: String Default: alice AllowedPattern: ^[a-z0-9]{5,12}$ S3BucketNamePrefix: Description: The prefix to use for your S3 bucket Type: String Default: my-demo-bucket AllowedPattern: ^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$ ConstraintDescription: Bucket name prefix can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-). MinLength: 3 Resources: BasicParameter: Type: AWS::SSM::Parameter Properties: Name: dbUsername Type: String Value: !Ref DatabaseUsername Description: SSM Parameter for database username. DemoRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: / Policies: - PolicyName: ssm-least-privilege PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: ssm:GetParameter Resource: !Sub 'arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${BasicParameter}' DemoLambdaFunction: Type: AWS::Lambda::Function Properties: Handler: index.lambda_handler Role: !GetAtt DemoRole.Arn Runtime: python3.8 Code: ZipFile: | import boto3 client = boto3.client('ssm') def lambda_handler(event, context): response = client.get_parameter(Name='dbUsername') print(f'SSM dbUsername parameter value: {response["Parameter"]["Value"]}') DemoBucket: Type: AWS::S3::Bucket Properties: BucketName: !Sub '${S3BucketNamePrefix}-${AWS::Region}-${AWS::AccountId}'
map是个键值对,通过Fn::FindInMap
(简写为!FindInMap
)获取对应键的值
以下获取ec2实例的id实际是对应键EnvironmentType.Test.InstanceType
的值而已
AWSTemplateFormatVersion: "2010-09-09" Description: AWS CloudFormation workshop - Mappings (uksb-1q9p31idr). Parameters: EnvironmentType: Description: 'Specify the Environment type of the stack.' Type: String Default: Test AllowedValues: - Test - Prod ConstraintDescription: 'Specify either Test or Prod.' Mappings: EnvironmentToInstanceType: Test: InstanceType: t2.micro Prod: InstanceType: t2.small Resources: WebServerInstance: Type: AWS::EC2::Instance Properties: ImageId: ami-0ab68f4313c5aff87 InstanceType: !FindInMap [EnvironmentToInstanceType, !Ref EnvironmentType, InstanceType]
可以从cloudformation资源和属性参考中查找对应资源的返回值信息,可以查看ref
和getattr
分别能够获取的值
getattr
、ref
和sub
的区别在于
${}
简介使用以上两者AWSTemplateFormatVersion: "2010-09-09" Description: AWS CloudFormation workshop - Outputs (uksb-1q9p31idr). Resources: WebServerInstance: Type: AWS::EC2::Instance Properties: ImageId: ami-xxxxxx InstanceType: t2.micro WebServerEIP: Type: AWS::EC2::EIP Properties: Domain: vpc InstanceId: !Ref WebServerInstance Outputs: WebServerPublicDNS: Description: Public DNS of EC2 instance Value: !GetAtt WebServerInstance.PublicDnsName WebServerElasticIP: Description: Elastic IP assigned to EC2 Value: !Ref WebServerEIP
创建ec2实例时,可以指定从ssm参数中获取最新的ami
每个ami都拥有公开的ssm参数空间,如下
# 获取公共ami列表
aws ssm get-parameters-by-path --path "/aws/service/ami-amazon-linux-latest"
# 使用公共ssm参数查询ami
aws ssm get-parameters --names /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
模板如下
AWSTemplateFormatVersion: "2010-09-09" Description: AWS CloudFormation workshop - Resource Return Values Lab (uksb-1q9p31idr). Parameters: LatestAmiId: Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id> Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 Resources: S3Bucket: Type: AWS::S3::Bucket Properties: Tags: - Key: Purpose Value: AWS CloudFormation Workshop S3BucketPolicy: Type: AWS::S3::BucketPolicy Properties: Bucket: !Ref S3Bucket PolicyDocument: Version: "2012-10-17" Statement: - Action: - s3:* Effect: Deny Resource: - !GetAtt S3Bucket.Arn - !Sub '${S3Bucket.Arn}/*' Principal: '*' Condition: Bool: aws:SecureTransport: false Ec2Instance: Type: AWS::EC2::Instance Properties: ImageId: !Ref LatestAmiId InstanceType: t2.micro SecurityGroups: - !Ref InstanceSecurityGroup InstanceSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Allow http to client host SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 Outputs: S3BucketDomainName: Description: IPv4 DNS name of the bucket. Value: !GetAtt S3Bucket.DomainName InstanceID: Description: The ID of the launched instance Value: !Ref Ec2Instance PublicIP: Description: Public IP of the launched instance Value: !GetAtt Ec2Instance.PublicIp SecurityGroupId: Description: ID of the security group created Value: !GetAtt InstanceSecurityGroup.GroupId
开启ssm控制台登录功能的ec2实例
AWSTemplateFormatVersion: "2010-09-09" Description: AWS CloudFormation workshop - Session manager (uksb-1q9p31idr). Resources: SSMIAMRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - sts:AssumeRole ManagedPolicyArns: - arn:aws-cn:iam::aws:policy/AmazonSSMManagedInstanceCore WebServerInstanceProfile: Type: AWS::IAM::InstanceProfile Properties: Path: / Roles: - !Ref SSMIAMRole WebServerInstance: Type: AWS::EC2::Instance Properties: IamInstanceProfile: !Ref WebServerInstanceProfile ImageId: ami-0ab68f4313c5aff87 InstanceType: t2.micro Outputs: WebServerPublicDNS: Description: Public DNS of EC2 instance Value: !GetAtt WebServerInstance.PublicDnsName
使用userdata,userdata需要通过内置函数base64
进行编码
AWSTemplateFormatVersion: "2010-09-09" Description: AWS CloudFormation workshop - User data (uksb-1q9p31idr). Resources: WebServerInstance: Type: AWS::EC2::Instance Properties: IamInstanceProfile: !Ref WebServerInstanceProfile ImageId: ami-xxxxxx InstanceType: t2.micro UserData: !Base64 | #!/bin/bash yum update -y yum install -y httpd php systemctl start httpd systemctl enable httpd usermod -a -G apache ec2-user chown -R ec2-user:apache /var/www chmod 2775 /var/www find /var/www -type d -exec chmod 2775 {} \; find /var/www -type f -exec chmod 0664 {} \; cat << 'EOF' > /var/www/html/index.php <!DOCTYPE html> <html> <body> <center> <?php # Get the instance ID from meta-data and store it in the $instance_id variable $url = "http://169.254.169.254/latest/meta-data/instance-id"; $instance_id = file_get_contents($url); # Get the instance's availability zone from metadata and store it in the $zone variable $url = "http://169.254.169.254/latest/meta-data/placement/availability-zone"; $zone = file_get_contents($url); ?> <h2>EC2 Instance ID: <?php echo $instance_id ?></h2> <h2>Availability Zone: <?php echo $zone ?></h2> </center> </body> </html> EOF
helper脚本能够在原来的模板上对应用程序进行微调(不需要重新部署和创建堆栈资源),默认helper可以不需要凭证,请求会被限制在堆栈中的实例中
帮助脚本比较难理解一共有4个
sudo /opt/aws/bin/cfn-get-metadata --region cn-north-1 --stack aws -r WebServerInstance { "AWS::CloudFormation::Init": { "config": { "files": { "/etc/cfn/cfn-hup.conf": { "mode": "256", "owner": "root", "content": "[main]\nstack=arn:aws-cn:cloudformation:cn-north-1:xxxxxxx:stack/aws/aa931910-6ee0-11ed-9669-0e2f05fc8512\nregion=cn-north-1\ninterval=1\n", "group": "root" }, "/var/www/html/index.php": { "mode": "420", "owner": "apache", "content": "<!DOCTYPE html>\n<html>\n<body>\n <center>\n <h2>EC2 Instance ID: <?php echo hello world ?></h2>\n </center>\n</body>\n</html>\n", "group": "apache" }, "/etc/cfn/hooks.d/cfn-auto-reloader.conf": { "content": "[cfn-auto-reloader-hook]\ntriggers=post.update\npath=Resources.WebServerInstance.Metadata.AWS::CloudFormation::Init\naction=/opt/aws/bin/cfn-init --stack aws --resource WebServerInstance --region cn-north-1\nrunas=root\n" } }, "services": { "sysvinit": { "cfn-hup": { "files": [ "/etc/cfn/cfn-hup.conf", "/etc/cfn/hooks.d/cfn-auto-reloader.conf" ], "ensureRunning": "true", "enabled": "true" }, "httpd": { "ensureRunning": "true", "enabled": "true" } } }, "packages": { "yum": { "php": [], "httpd": [] } } } } }
cfn-hup.conf
,钩子文件在hooks.d
以下将模板精简之后可以看到在WebServerInstance
资源中主要分为Metadata
和Properties
两部分
Metadata
是对cfn资源元数据的设置,通过init对实例进行配置,分为packages, groups, users, sources, files, commands, services
,当调用/opt/aws/bin/cfn-init
时会执行全部配置
在实例内部安装aws-cfn-bootstrap
,在al2类型实例上已经预装
cfn-hup
后台启动并读取配置文件,指定检查元数据间隔interval
为1分钟,定期调用钩子。可以检查cfn资源,cfn资源物理id和cfn元数据。action
指定检测到更改后执行的行为,实际上就是个shell脚本,下面的配置表明重新执行了cfn-init文件。此时如果在更改集中修改了ec2实例元数据中的php脚本则会触发更改刷新网页内容
[cfn-auto-reloader-hook]
triggers=post.update
path=Resources.WebServerInstance.Metadata.AWS::CloudFormation::Init
action=/opt/aws/bin/cfn-init --stack ${AWS::StackName} --resource WebServerInstance --region
runas=root
cfn-signal
使用cfn-init
的返回结果$?
进行调用,此时如果实例配置失败则堆栈会回滚
创建WebServerInstance
资源使用了CreationPolicy
策略,此时只有ec2实例使用cfn-signal
发送信号之后,才会完成ec2实例资源的创建。指定需要收到信号数量为1,超时时间为10分钟
资源策略
WebServerInstance:
CreationPolicy:
ResourceSignal:
Count: 1
Timeout: PT10M
cfn-signal
可以和资源策略相结合,但是只有部分资源支持CreationPolicy。同样指定UpdatePolicy配置WaitOnResourceSignals
可以在更新asg时触发信号等待,确保asg中的每个实例都已经配置完成
AWSTemplateFormatVersion: "2010-09-09" Description: AWS CloudFormation workshop - Helper scripts (uksb-1q9p31idr). Resources: WebServerInstance: CreationPolicy: ResourceSignal: Count: 1 Timeout: PT10M Type: AWS::EC2::Instance Metadata: AWS::CloudFormation::Init: config: packages: yum: httpd: [] php: [] files: /var/www/html/index.php: content: | <!DOCTYPE html> <html> <body> <center> <h2>EC2 Instance ID: <?php echo helloworld ?></h2> </center> </body> </html> mode: 000644 owner: apache group: apache /etc/cfn/cfn-hup.conf: content: !Sub | [main] stack=${AWS::StackId} region=${AWS::Region} interval=1 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.WebServerInstance.Metadata.AWS::CloudFormation::Init action=/opt/aws/bin/cfn-init --stack ${AWS::StackName} --resource WebServerInstance --region ${AWS::Region} runas=root services: sysvinit: httpd: enabled: true ensureRunning: true cfn-hup: enabled: true ensureRunning: true files: - /etc/cfn/cfn-hup.conf - /etc/cfn/hooks.d/cfn-auto-reloader.conf Properties: ImageId: ami-02c8191b43515df27 KeyName: "temp-key" NetworkInterfaces: - AssociatePublicIpAddress: true DeviceIndex: "0" InstanceType: t2.micro UserData: !Base64 Fn::Sub: | #!/bin/bash -xe # Update aws-cfn-bootstrap to the latest yum install -y aws-cfn-bootstrap # Call cfn-init script to install files and packages /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource WebServerInstance --region ${AWS::Region} # Call cfn-signal script to send a signal with exit code /opt/aws/bin/cfn-signal --exit-code $? --stack ${AWS::StackName} --resource WebServerInstance --region ${AWS::Region}
根据参数不同指定资源的创建与否,还可以根据条件指定属性和输出值
AWSTemplateFormatVersion: "2010-09-09" Description: AWS CloudFormation workshop - Conditions at resource level (uksb-1q9p31idr). Parameters: EnvType: Description: Specify the Environment type of the stack. Type: String AllowedValues: - test - prod Default: test ConstraintDescription: Specify either test or prod. Conditions: IsProduction: !Equals - !Ref EnvType - prod Resources: EC2Instance: Type: AWS::EC2::Instance Properties: ImageId: !Ref LatestAmiId InstanceType: !If [IsProduction, t2.small, t2.micro] MountPoint: Type: AWS::EC2::VolumeAttachment Properties: InstanceId: !Ref EC2Instance VolumeId: !Ref Volume Device: /dev/sdh Condition: IsProduction Volume: Type: AWS::EC2::Volume Properties: Size: 2 AvailabilityZone: !GetAtt EC2Instance.AvailabilityZone Encrypted: true Condition: IsProduction Outputs: VolumeId: Value: !Ref Volume Condition: IsProduction
在cfn中资源的依赖可以分为
dependon
参数ref
和getattr
参数在没有依赖的情况下,cfn创建资源的方式时并行的。下面的sg隐式依赖ingressrule,sns显式依赖s3桶
AWSTemplateFormatVersion: "2010-09-09" Description: AWS CloudFormation workshop - Resource Dependencies Lab with Ref and Fn::GetAtt Intrinsic Functions (uksb-1q9p31idr). Resources: SecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Example Security Group SecurityGroupIngress: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !GetAtt SecurityGroup.GroupId IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 S3Bucket: Type: AWS::S3::Bucket Properties: SNSTopic: Type: AWS::SNS::Topic DependsOn: S3Bucket Properties:
提前在ssm中创建参数
aws ssm put-parameter \
--name "/golden-images/amazon-linux-2" \
--value YOUR_AMI_ID \
--type "String" \
--region YOUR_REGION
在cfn中动态引用参数,固定写法
Resources: Instance: Type: AWS::EC2::Instance Properties: AvailabilityZone: !Select ["0", !GetAZs ""] InstanceType: t2.micro ImageId: '{{resolve:ssm:/golden-images/amazon-linux-2}}' DatabaseConnParams: Type: AWS::SecretsManager::Secret Properties: Description: Database Connection Parameters. Name: DatabaseConnParams SecretString: !Sub | { "RDS_HOSTNAME": "${Database.Endpoint.Address}", "RDS_PORT": "${Database.Endpoint.Port}", "RDS_USERNAME": "${DBUsername}", "RDS_PASSWORD": "${DBPassword}" } FunctionExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Action: - sts:AssumeRole Effect: Allow Principal: Service: - lambda.amazonaws.com Version: "2012-10-17" ManagedPolicyArns: - !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole' Path: / HelloWorldFunction: Type: AWS::Lambda::Function Properties: Role: !GetAtt FunctionExecutionRole.Arn Handler: index.handler Environment: Variables: RDS_HOSTNAME: '{{resolve:secretsmanager:DatabaseConnParams:SecretString:RDS_HOSTNAME}}' RDS_PORT: '{{resolve:secretsmanager:DatabaseConnParams:SecretString:RDS_PORT}}' Runtime: python3.7 Code: ZipFile: | import os def handler(event, context): RDS_HOSTNAME=os.getenv('RDS_HOSTNAME') RDS_PORT=os.getenv('RDS_PORT') return "Database: {}:{}".format(RDS_HOSTNAME,RDS_PORT)
所谓嵌套堆栈就是将原本cfn模板中的资源单独分离出来,便于复用和灵活组合。使用时只需要在root template中进行引用即可。
对于需要引用的堆栈资源需要使用AWS::CloudFormation::Stack
类型
以下模板创建stack资源,并指定了模板所在的s3 url和初始参数。stack资源之间相互引用output,通过Fn: : GetAtt
将值从子堆栈传递到根堆栈
AWSTemplateFormatVersion: "2010-09-09" Description: AWS CloudFormation workshop - Nested stacks - Root template (uksb-1q9p31idr). Parameters: S3BucketName: Type: String AvailabilityZones: Type: List<AWS::EC2::AvailabilityZone::Name> VPCName: Type: String Default: cfn-workshop-vpc VPCCidr: Type: String Default: 10.0.0.0/16 PublicSubnet1Cidr: Type: String Default: 10.0.0.0/24 PublicSubnet2Cidr: Type: String Default: 10.0.1.0/24 Resources: VpcStack: Type: AWS::CloudFormation::Stack Properties: TemplateURL: !Sub https://${S3BucketName}.s3.amazonaws.com.cn/vpc.yaml TimeoutInMinutes: 20 Parameters: AvailabilityZones: !Join - ',' - !Ref AvailabilityZones VPCCidr: !Ref VPCCidr VPCName: !Ref VPCName PublicSubnet1Cidr: !Ref PublicSubnet1Cidr PublicSubnet2Cidr: !Ref PublicSubnet2Cidr IamStack: Type: AWS::CloudFormation::Stack Properties: TemplateURL: !Sub https://${S3BucketName}.s3.amazonaws.com.cn/iam.yaml TimeoutInMinutes: 10 EC2Stack: Type: AWS::CloudFormation::Stack Properties: TemplateURL: !Sub https://${S3BucketName}.s3.amazonaws.com.cn/ec2.yaml TimeoutInMinutes: 20 Parameters: EnvironmentType: Dev VpcId: !GetAtt VpcStack.Outputs.VpcId SubnetId: !GetAtt VpcStack.Outputs.PublicSubnet1 WebServerInstanceProfile: !GetAtt IamStack.Outputs.WebServerInstanceProfile Outputs: WebsiteURL: Value: !GetAtt EC2Stack.Outputs.WebsiteURL
vpc/ec2和iam的堆栈模板如同往常一样,由于在参数中使用ref进行了引用,实际上形成了隐式依赖关系
创建嵌套堆栈需要开启权限CAPABILITY_AUTO_EXPAND
If you want to create a stack from a stack template that contains macros and nested stacks, you must create the stack directly from the template using this capability.
开启nested视图后可以看到标记
删除嵌套堆需要删除root堆栈,否则会导致不一致
Deleting this stack will delete all stack resources. Resources will be deleted according to their DeletionPolicy.
It is recommended to delete through the root stack
Deleting a nested stack may result in an unstable state where the nested stack is out-of-sync with its root stack.
嵌套堆栈的问题在于无法创建一对多关系,因为所有资源都在root中进行维护,本质上其实还是一个堆栈。分层堆栈就是创建多个堆栈,并实现跨堆栈的资源引用。
创建iam堆栈,并export instance profile
AWSTemplateFormatVersion: "2010-09-09" Resources: SSMIAMRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore WebServerInstanceProfile: Type: AWS::IAM::InstanceProfile Properties: Path: / Roles: - !Ref SSMIAMRole Outputs: WebServerInstanceProfile: Value: !Ref WebServerInstanceProfile Export: Name: cfn-workshop-WebServerInstanceProfile
导出的值可以在控制台看到
创建ec2并引用iam堆栈的instance profile
AWSTemplateFormatVersion: "2010-09-09"
Resources:
WebServerInstance:
Type: AWS::EC2::Instance
Properties:
SubnetId: sub-xxxx
IamInstanceProfile: !ImportValue cfn-workshop-WebServerInstanceProfile
ImageId: ami-xxxx
InstanceType: t2.micro
我们在之前部署过lambda资源以及嵌套堆栈。lambda使用zip的方式将code打包,嵌套堆栈则是提前将模板上传到s3中进行引用,如果这样的组件很多工作量会非常大。
使用cfn打包功能可以简化步骤
package-and-deploy
├── infrastructure.template
└── lambda/
├── lambda_function.py
└── requirements.txt
查看程序
cat lambda_function.py from datetime import datetime from pytz import timezone, utc def handler(event, context): payload = event["time_zone"] message = "Current date/time in TimeZone *{}* is: {}".format( payload, _timezone(payload) ) return {"message": message} def _timezone(time_zone): utc_now = utc.localize(datetime.utcnow()) compare_to_utc = utc_now.astimezone(timezone(time_zone)) return compare_to_utc.strftime("%Y-%m-%d %H:%M") cat requirements.txt pytz==2021.3
模板如下
AWSTemplateFormatVersion: "2010-09-09" Description: AWS CloudFormation workshop - Package and deploy (uksb-1q9p31idr). Resources: LambdaBasicExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: sts:AssumeRole Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole PythonFunction: Type: AWS::Lambda::Function Properties: FunctionName: cfn-workshop-python-function Description: Python Function to return specific TimeZone time Runtime: python3.8 Role: !GetAtt LambdaBasicExecutionRole.Arn Handler: lambda_function.handler Code: lambda/
对模板打包
aws cloudformation package \
- -template-file ./infrastructure.template \
--s3-bucket "${BUCKET_NAME}" \
--s3-prefix "${FILEPATH}" \
--output-template-file ./infrastructure-packaged.template
打包后的模板,bucketname中的prefix/9497159249ecec70572a11a73f15e601文件实际上就是lambda/
目录的压缩包
AWSTemplateFormatVersion: '2010-09-09' Description: AWS CloudFormation workshop - Package and deploy (uksb-1q9p31idr). Resources: LambdaBasicExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: sts:AssumeRole Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole PythonFunction: Type: AWS::Lambda::Function Properties: FunctionName: cfn-workshop-python-function Description: Python Function to return specific TimeZone time Runtime: python3.8 Role: Fn::GetAtt: - LambdaBasicExecutionRole - Arn Handler: lambda_function.handler Code: S3Bucket: buckname S3Key: prefix/9497159249ecec70572a11a73f15e601 Outputs: LambdaFunction: Description: AWS Lambda Python Function Value: Ref: PythonFunction
堆栈的更新行为有以下几种
AWS资源类型参考中介绍了每个属性的更新行为
使用更改集更新堆栈能够预览堆栈的变动并确认修改
cloudformation只根据在模板中显示声明的属性检测偏离。模板中没有的属性是不检查的
检测偏差的结果如下,可以看到前后模板的差异,以及修改和删除资源的细节
当偏移发生后,有两种方式解决
使用模块可以将cfn的一些常见配置和资源打包成组件,方便传播和复用
模块的结构如下
安装工具
pip install cloudformation-cli
初始化模块
mkdir module
cd module
cfn init
Initializing new project
Do you want to develop a new resource(r) or a module(m) or a hook(h)?.
>> m
What's the name of your module type?
(<Organization>::<Service>::<Name>::MODULE)
新建文件以下三部分
fragments/: cfn模板片段,sample片段默认创建s3桶。我们可以将自己的cfn模板写在这里
.rpdk-config: 模块的配置文件
rpdk.log: 记录日志
提交模块
cfn submit
在控制台找到刚创建的module
使用模块,实际上托管资源就是官方提供的module
AWSTemplateFormatVersion: 2010-09-09
Resources:
myS3Bucket:
Type: CFNWORKSHOP::EC2::VPC::MODULE
删除module
aws cloudformation deregister-type --type MODULE --type-name CFNWORKSHOP::EC2::VPC::MODULE
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。