在模板中,可以声明要创建和配置的 AWS 资源。将对象声明为 name-value 对,或 name 与一组括起来的子对象的配对。

唯一需要的顶级对象就是 Resources 对象,它至少须声明一种资源。

1. 单一资源的基础模板

Resources 对象包含一系列资源对象。

资源对象必须具有 Type 属性,该属性定义要创建的 AWS 资源的类型。
Type 属性的格式: AWS::ProductIdentifier::ResourceType

以下模板声明了一个名字为 HelloBucket 的 AWS::S3: 存储桶类型的单一资源。

  1. Resources:
  2. HelloBucket:
  3. Type: AWS::S3::Bucket

创建存储桶的声明比较简单,但是也可以在 Properties 属性中添加可选属性,因为上面声明的 S3 存储桶不需要任何可选功能,因此在模板中我们选择接受默认值忽略 Properties 属性。

对于其他资源,如 Auto Scaling 组或 EC2 实例,AWS CloudFormation 需要更多信息。

2. 资源属性和引用其他资源

模板中用于资源的名称是逻辑名称。当 AWS CloudFormation 创建资源时,它会生成一个物理名称,该名称基于逻辑名称、堆栈名称和唯一 ID 的组合。

如何基于另一个资源的名称或属性,在一个资源上设置它属性?
下面的模板包含一个 AWS::EC2::Instance 资源。资源的 SecurityGroups 属性调用 Ref 函数来引用 AWS::EC2::SecurityGroup 资源 InstanceSecurityGroup
参考:Ref Function

Ref 引用模板中创建的资源

  1. Resources:
  2. Ec2Instance:
  3. Type: 'AWS::EC2::Instance'
  4. Properties:
  5. SecurityGroups:
  6. - !Ref InstanceSecurityGroup
  7. KeyName: mykey
  8. ImageId: ''
  9. InstanceSecurityGroup:
  10. Type: 'AWS::EC2::SecurityGroup'
  11. Properties:
  12. GroupDescription: Enable SSH access via port 22
  13. SecurityGroupIngress:
  14. - IpProtocol: tcp
  15. FromPort: '22'
  16. ToPort: '22'
  17. CidrIp: 0.0.0.0/0

上面的 SecurityGroups 属性通过 Ref 函数引用了在同一模板中创建的 InstanceSecurityGroup 安全组资源;

Ref 引用现存的资源

下面将在 SecurityGroups 属性中附加一个现有存在的安全组资源,直接现有安全组名称字符串来指定现存资源;

  1. Resources:
  2. Ec2Instance:
  3. Type: 'AWS::EC2::Instance'
  4. Properties:
  5. SecurityGroups:
  6. - !Ref InstanceSecurityGroup
  7. - MyExistingSecurityGroup
  8. KeyName: mykey
  9. ImageId: ami-7a11e213
  10. InstanceSecurityGroup:
  11. Type: 'AWS::EC2::SecurityGroup'
  12. Properties:
  13. GroupDescription: Enable SSH access via port 22
  14. SecurityGroupIngress:
  15. - IpProtocol: tcp
  16. FromPort: '22'
  17. ToPort: '22'
  18. CidrIp: 0.0.0.0/0

Ref 引用 Parameters 部分对象

如果希望与其他人共享模板,那么对某些资源(如密钥对)可以在 Parameters 部分使用输入参数,然后通过 Ref 函数引用那些在拆功能键堆栈时指定的输入参数。

例如,下面模板中添加一个 KeyName 的参数对象,指定为AWS::EC2::KeyPair::KeyName类型,然后在 KeyName 属性中通过 Ref 引用这个参数对象。

  1. Parameters:
  2. KeyName:
  3. Description: The EC2 Key Pair to allow SSH access to the instance
  4. Type: 'AWS::EC2::KeyPair::KeyName'
  5. Resources:
  6. Ec2Instance:
  7. Type: 'AWS::EC2::Instance'
  8. Properties:
  9. SecurityGroups:
  10. - !Ref InstanceSecurityGroup
  11. - MyExistingSecurityGroup
  12. KeyName: !Ref KeyName
  13. ImageId: ami-7a11e213
  14. InstanceSecurityGroup:
  15. Type: 'AWS::EC2::SecurityGroup'
  16. Properties:
  17. GroupDescription: Enable SSH access via port 22
  18. SecurityGroupIngress:
  19. - IpProtocol: tcp
  20. FromPort: '22'
  21. ToPort: '22'
  22. CidrIp: 0.0.0.0/0

这将确保用户在其账户和创建堆栈的区域中指定一个有效的密钥对名称。

Fn::GetAtt 获取其他资源的属性

如果该参数或为资源返回的值正是您需要的,该 Ref 函数是很方便的,但可能还需要其他的资源属性。

以下模板创建了一个具有 S3 起始地址的 CloudFront 分发资源,通过使用 Fn::GetAtt 函数,获得 S3 存储桶的 DomainName 属性。

  1. Resources:
  2. myBucket:
  3. Type: 'AWS::S3::Bucket'
  4. myDistribution:
  5. Type: 'AWS::CloudFront::Distribution'
  6. Properties:
  7. DistributionConfig:
  8. Origins:
  9. - DomainName: !GetAtt
  10. - myBucket
  11. - DomainName
  12. Id: myS3Origin
  13. S3OriginConfig: {}
  14. Enabled: 'true'
  15. DefaultCacheBehavior:
  16. TargetOriginId: myS3Origin
  17. ForwardedValues:
  18. QueryString: 'false'
  19. ViewerProtocolPolicy: allow-all

Fn::GetAtt 函数有两个参数:资源的逻辑名、要检索的属性名。
有关这些资源的可用属性的完整列表,参考:Fn::GetAtt

3. 使用输入参数接收用户输入

这里将了解一下参数声明,以及如何限制并验证用户输入。

声明模板参数对象中的那些参数,一个参数包含一系列属性,这些属性定义了其值但也限制了其值。
唯一必需的属性就是 Type,它可以是 String、Number 或 AWS 特定的类型。

您还可以添加一个描述属性,该属性会告诉用户应输入何种指定值的信息。当用户使用控制台创建模板时,参数的名称和描述会出现在指定参数页面。

  1. Parameters:
  2. KeyName:
  3. Description: Name of an existing EC2 KeyPair to enable SSH access into the WordPress web server
  4. Type: AWS::EC2::KeyPair::KeyName
  5. WordPressUser:
  6. Default: admin
  7. NoEcho: true
  8. Description: The WordPress database admin account user name
  9. Type: String
  10. MinLength: 1
  11. MaxLength: 16
  12. AllowedPattern: "[a-zA-Z][a-zA-Z0-9]*"
  13. WebServerPort:
  14. Default: 8888
  15. Description: TCP/IP port for the WordPress web server
  16. Type: Number
  17. MinValue: 1
  18. MaxValue: 65535

对于没有默认值的参数,创建堆栈时会要求用户指定该参数的值,为了确保该值有效,需要声明一些约束来验证参数的值。

像 KeyName、VPC 这类参数对象,对需要指定一个特定的参数类型。例如:

  • KeyName 的特定参数类型是 AWS::EC2::KeyPair::KeyName ,CloudFormation 会对照用户账户及创建堆栈的区别中的现有 KeyName 值来验证输入值;
  • VPC 的参数类型是 AWS::EC2::VPC::Id ,该类型要求用户指定堆栈区域内一个有效的 VPC ID。

在 CloudFormation 控制台 Create Stack 时,该特定类型的参数会显示一个有效值的下拉菜单。

像 String 类型,您可以使用以下属性来声明约束:MinLength、MaxLength、Default、AllowedValues 和 AllowedPattern。
示例中,WordPressUser 参数有三个约束:参数值必须是 1 到 16 个字符长 (MinLength,MaxLength),必须以字母开头,后面是字母和数字的任何组合 (AllowedPattern)。

像 Number 类型,您可以声明以下约束:MinValue、MaxValue、Default 和 AllowedValues。数字可以是一个整数或一个浮点值。
示例中,WebServerPort 参数必须是一个介于 1 和 65535 (含 65535) 之间的一个数字 (MinValue, MaxValue)。

如果参数属于敏感信息,不希望展示在 CloudFormation 中,可以使用 NoEcho 属性,CloudFormation 会使用星号(*)遮蔽该参数值。
示例中,WordPressUser 参数值对于查看堆栈设置的任何人都是不可见的,其值以星号形式返回。

:::info 使用 NoEcho 属性不会遮蔽在以下各区段中存储的任何信息:

  • Metadata 模板区段。CloudFormation 不会转换、修改或编辑您在 Metadata 区段中包含的任何信息。
  • Outputs 模板区段。
  • Recources 部分的 Metadata 属性。 :::

:::info 与其在 AWS CloudFormation 模板中直接嵌入敏感信息,官方建议使用栈模板中的动态参数来引用存储和管理在 CloudFormation 外部的敏感信息,例如 AWS Systems Manager Parameter StoreAWS Secrets Manager 。 :::

4. 使用 Mappings 指定条件型的值

参数是一种很好的方式,让用户能够指定在堆栈资源属性中使用的唯一或敏感值。但是可能有一些设置是依赖于区域,或者其他条件或依赖关系,用户理解起来有些复杂。在这些情况下,您可能希望在模板本身中放置一些逻辑,以便用户可以指定更简单的值(或者根本不指定)来获得他们想要的结果。

在前面的示例中,我们为 EC2 实例的 ImageId 属性硬编码 AMI ID(ImageId: ami-7a11e213)。这在美国东部地区工作得很好,在那里它代表我们想要的 AMI。但是,如果用户试图在不同的区域构建堆栈,将得到错误的 AMI,或者根本没有 AMI。(AMI ID 对于一个区域是唯一的,因此不同区域中的相同 AMI ID 可能不代表任何 AMI 或完全不同的 AMI )

为了避免这个问题,需要一种方法来根据条件输入 (在本例中,是创建堆栈的区域) 指定正确的 AMI ID。有两个模板特性可以提供帮助,Mappings 和 AWS::Region 伪参数。

AWS::Region 参数是 CloudFormation 解析出来的创建堆栈的区域值。
Mappings 可以让用户使用一个输入值作为条件来决定另一个值。

示例模板包含一个 Mappings 对象,该对象带有一个名为 RegionMap 的映射,用于将 AMI ID 映射到适当的区域。

  1. Parameters:
  2. KeyName:
  3. Description: Name of an existing EC2 KeyPair to enable SSH access to the instance
  4. Type: String
  5. Mappings:
  6. RegionMap:
  7. us-east-1:
  8. AMI: ami-76f0061f
  9. us-west-1:
  10. AMI: ami-655a0a20
  11. eu-west-1:
  12. AMI: ami-7fd4e10b
  13. ap-southeast-1:
  14. AMI: ami-72621c20
  15. ap-northeast-1:
  16. AMI: ami-8e08a38f
  17. Resources:
  18. Ec2Instance:
  19. Type: 'AWS::EC2::Instance'
  20. Properties:
  21. KeyName: !Ref KeyName
  22. ImageId: !FindInMap
  23. - RegionMap
  24. - !Ref 'AWS::Region'
  25. - AMI
  26. UserData: !Base64 '80'

在 RegionMap 中,每个区域都映射到一个 name-value 对。在 RegionMap 中,AMI 是标签,AMI ID 是值。要使用映射返回值,可以使用 Fn::FindInMap 函数,传递映射的名称( RegionMap )、用于查找映射值的值( AWS::Region 解析出来的区域值),以及希望返回的映射值的标签( AMI 标签值)。

:::info AWS::Region 可以解析出当前区域的值。
像一些资源 AWS::EC2::InstanceAWS::AutoScaling::AutoScalingGroupAWS::ElasticLoadBalancing::LoadBalancer ,有一个属性需要指定可用区 AZ 的值,可以通过Fn::GetAZs function 获得该区域下的可用区 AZ 列表。 :::

5. 构造属性值和输出值

创建堆栈时,参数和映射是一种传递或确定特定值的极好的方式,但可能存在来自参数或其他资源属性的值只是所需值的一部分的情况。

Fu::Join 拼接属性值

例如,在下面来自 WordPress 模板的片段中, Fn::Join 函数通过连接 WebServerPort 参数和其他字符串来构成所需的值,从而为 ElasticLoadBalancer 资源构造 HealthCheck 属性的目标子属性。

  1. Resources:
  2. ElasticLoadBalancer:
  3. Type: 'AWS::ElasticLoadBalancing::LoadBalancer'
  4. Properties:
  5. AvailabilityZones: !GetAZs ''
  6. Instances:
  7. - !Ref Ec2Instance1
  8. - !Ref Ec2Instance2
  9. Listeners:
  10. - LoadBalancerPort: '80'
  11. InstancePort: !Ref WebServerPort
  12. Protocol: HTTP
  13. HealthCheck:
  14. Target: !Join
  15. - ''
  16. - - 'HTTP:'
  17. - !Ref WebServerPort
  18. - /
  19. HealthyThreshold: '3'
  20. UnhealthyThreshold: '5'
  21. Interval: '30'
  22. Timeout: '5'

Fn::Join 函数接受两个参数,一个是分割符(将后面的值用分割符拼接在一起),另一个是需要拼接的值数组。
示例中,如果 WebServerPort 有一个值为 8888,该目标属性就会被设置为以下值:HTTP:8888/

Fu::Join 拼接输出值

Fn::Join 函数对于声明该堆栈的输出值来说也是很有用的。

  1. Outputs:
  2. InstallURL:
  3. Value: !Join
  4. - ''
  5. - - 'http://'
  6. - !GetAtt
  7. - ElasticLoadBalancer
  8. - DNSName
  9. - /wp-admin/install.php
  10. Description: Installation URL of the WordPress website
  11. WebsiteURL:
  12. Value: !Join
  13. - ''
  14. - - 'http://'
  15. - !GetAtt
  16. - ElasticLoadBalancer
  17. - DNSName

每一个输出值都有一个名称,一个 value 属性,该属性包含作为输出值返回的值的声明,以及可选的值描述。
示例中,InstallURL 是一个调用 Fn::Join 函数返回的字符串,该函数连接资源 ElasticLoadBalancer 的 DNS 名称http:///wp-admin/install.php

  1. http://mywptests-elasticl-1gb51l6sl8y5v-206169572.us-east-2.elb.amazonaws.com/wp-admin/install.php

在前一篇 CloudFormation 入门使用示例 01 中,使用此链接可以很方便的跳转到已创建的 WordPress 的安装页面。

6. 其他

还有一些其他模板特性没有介绍到:

  • 两个顶级对象:
    • AWSTemplateFormatVersion:只是模板格式版本;如果未指定,AWS CloudFormation 将使用最新版本。
    • Description:是任意有效的 JSON 或 YAML 字符串。
    • 参考:Format versionDescription
  • 可与任何资源共同使用的可选属性:
    • DependsOn 属性能使您指定必须在一种资源创建之后才能接着创建另一个资源。
    • DeletionPolicy 属性,可使您指定 AWS CloudFormation 如何处理资源删除的方式。
    • Metadata 属性,可让您指定一个资源的结构化数据。
  • AWS::CloudFormation::Stack 使您能够在模板中将另一个堆栈作为资源嵌套。