CloudFormation 模板 Rules 语法 - Amazon CloudFormation
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

CloudFormation 模板 Rules 语法

Rules 部分是 CloudFormation 模板的可选部分,可启用自定义验证逻辑。如果包含,此部分包含在 CloudFormation 创建或更新任何资源之前验证参数值的规则函数。

当标准参数约束不足时,规则非常有用。例如,如果启用了 SSL,则必须同时提供证书和域名。

语法

Rules 部分使用以下语法:

JSON

模板的Rules 部分由后跟冒号的密钥名称 Rules 组成。必须使用括号将所有规则声明括起来。如果您声明多个规则,则可用逗号将它们分隔开。对于每个规则,您必须声明一个用引号引起来的逻辑名称,后跟冒号以及将规则条件和断言括起来的括号。

{ "Rules": { "LogicalRuleName1": { "RuleCondition": { "rule-specific intrinsic function": "Value" }, "Assertions": [ { "Assert": { "rule-specific intrinsic function": "Value" }, "AssertDescription": "Information about this assert" }, { "Assert": { "rule-specific intrinsic function": "Value" }, "AssertDescription": "Information about this assert" } ] }, "LogicalRuleName2": { "Assertions": [ { "Assert": { "rule-specific intrinsic function": "Value" }, "AssertDescription": "Information about this assert" } ] } } }

YAML

Rules: LogicalRuleName1: RuleCondition: rule-specific intrinsic function: Value Assertions: - Assert: rule-specific intrinsic function: Value AssertDescription: Information about this assert - Assert: rule-specific intrinsic function: Value AssertDescription: Information about this assert LogicalRuleName2: Assertions: - Assert: rule-specific intrinsic function: Value AssertDescription: Information about this assert

规则字段

Rules 部分包含以下字段。

逻辑 ID(也称为逻辑名称

每条规则的唯一标识符。

RuleCondition(可选)

确定规则生效时间的属性。如果您未定义规则条件,则规则的断言始终生效。对于每个规则,您只能定义一个规则条件。

Assertions(必需)

指定特定参数可接受值的一条或多条语句。

Assert

必须计算为 true 的条件。

AssertDescription

断言失败时显示的消息。

特定于规则的内部函数

要定义规则,您必须使用特定于规则的函数,只能在模板的 Rules 部分中使用这些函数。虽然可以嵌套这些函数,但规则条件或断言的最终结果必须为 truefalse

以下规则函数可用:

这些函数在规则的条件或断言中使用。条件属性确定 CloudFormation 是否应用断言。如果条件的计算结果为 true,则 CloudFormation 将评估断言以验证在创建或更新预配置产品时参数值是否有效。如果参数值无效,则 CloudFormation 不会创建或更新堆栈。如果条件的计算结果为 false,则 CloudFormation 不会检查参数值并继续堆栈操作。

示例

按条件验证参数值

在以下示例中,两个规则检查 InstanceType 参数的值。根据环境参数(testprod)的值,用户必须为 InstanceType 参数指定 a1.mediuma1.large。必须在同一模板的 InstanceType 部分中声明 EnvironmentParameters 参数。

JSON

{ "Rules": { "testInstanceType": { "RuleCondition": { "Fn::Equals": [ {"Ref": "Environment"}, "test" ] }, "Assertions": [ { "Assert": { "Fn::Contains": [ ["a1.medium"], {"Ref": "InstanceType"} ] }, "AssertDescription": "For a test environment, the instance type must be a1.medium" } ] }, "prodInstanceType": { "RuleCondition": { "Fn::Equals": [ {"Ref": "Environment"}, "prod" ] }, "Assertions": [ { "Assert": { "Fn::Contains": [ ["a1.large"], {"Ref": "InstanceType"} ] }, "AssertDescription": "For a production environment, the instance type must be a1.large" } ] } } }

YAML

Rules: testInstanceType: RuleCondition: !Equals - !Ref Environment - test Assertions: - Assert: 'Fn::Contains': - - a1.medium - !Ref InstanceType AssertDescription: 'For a test environment, the instance type must be a1.medium' prodInstanceType: RuleCondition: !Equals - !Ref Environment - prod Assertions: - Assert: 'Fn::Contains': - - a1.large - !Ref InstanceType AssertDescription: 'For a production environment, the instance type must be a1.large'

交叉参数验证

以下示例模板演示如何使用规则进行跨参数验证。其创建了一个在负载均衡器背后自动扩缩组上运行的示例网站。根据输入参数,该网站可在端口 80 或 443 上使用。可以将自动扩缩组中的实例配置为在任何端口上侦听(默认为 8888)。

此模板中的规则会在创建堆栈之前验证输入参数。其验证所有子网是否属于指定的 VPC,并确保将 UseSSL 参数设置为 Yes 时,同时提供 SSL 证书 ARN 和托管区域名称。

注意

如果您通过本模板创建堆栈,则需为使用的 Amazon 资源支付相应费用。

JSON

{ "AWSTemplateFormatVersion": "2010-09-09", "Parameters": { "VpcId": { "Type": "AWS::EC2::VPC::Id", "Description": "VpcId of your existing Virtual Private Cloud (VPC)", "ConstraintDescription": "must be the VPC Id of an existing Virtual Private Cloud." }, "Subnets": { "Type": "List<AWS::EC2::Subnet::Id>", "Description": "The list of SubnetIds in your Virtual Private Cloud (VPC)", "ConstraintDescription": "must be a list of at least two existing subnets associated with at least two different availability zones." }, "InstanceType": { "Description": "WebServer EC2 instance type", "Type": "String", "Default": "t2.micro", "AllowedValues": ["t2.micro", "t3.micro"], "ConstraintDescription": "must be a valid EC2 instance type." }, "KeyName": { "Description": "Name of an existing EC2 KeyPair to enable SSH access to the instances", "Type": "AWS::EC2::KeyPair::KeyName", "ConstraintDescription": "must be the name of an existing EC2 KeyPair." }, "SSHLocation": { "Description": "The IP address range that can be used to SSH to the EC2 instances", "Type": "String", "MinLength": "9", "MaxLength": "18", "Default": "0.0.0.0/0", "AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})", "ConstraintDescription": "must be a valid IP CIDR range of the form x.x.x.x/x." }, "UseSSL": { "AllowedValues": ["Yes", "No"], "Default": "No", "Description": "Select \"Yes\" to implement SSL, \"No\" to skip (default).", "Type": "String" }, "ALBSSLCertificateARN": { "Default": "", "Description": "[Optional] The ARN of the SSL certificate to be used for the Application Load Balancer", "Type": "String" }, "HostedZoneName": { "AllowedPattern": "^$|(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$", "Default": "", "Description": "[Optional] The domain name of a valid Hosted Zone on AWS.", "Type": "String" } }, "Conditions": { "UseALBSSL": {"Fn::Equals": [{"Ref": "UseSSL"}, "Yes"]} }, "Rules": { "SubnetsInVPC": { "Assertions": [ { "Assert": {"Fn::EachMemberEquals": [{"Fn::ValueOf": ["Subnets", "VpcId"]}, {"Ref": "VpcId"}]}, "AssertDescription": "All subnets must be in the VPC" } ] }, "ValidateHostedZone": { "RuleCondition": {"Fn::Equals": [{"Ref": "UseSSL"}, "Yes"]}, "Assertions": [ { "Assert": {"Fn::Not": [{"Fn::Equals": [{"Ref": "ALBSSLCertificateARN"}, ""]}]}, "AssertDescription": "ACM Certificate value cannot be empty if SSL is required" }, { "Assert": {"Fn::Not": [{"Fn::Equals": [{"Ref": "HostedZoneName"}, ""]}]}, "AssertDescription": "Route53 Hosted Zone Name is mandatory when SSL is required" } ] } }, "Mappings": { "AWSAMIRegionMap": { "us-east-1": {"AMZNLINUXHVM": "ami-0ff8a91507f77f867"}, "us-west-1": {"AMZNLINUXHVM": "ami-0bdb828fd58c52235"}, "eu-west-1": {"AMZNLINUXHVM": "ami-047bb4163c506cd98"}, "ap-southeast-1": {"AMZNLINUXHVM": "ami-08569b978cc4dfa10"} } }, "Resources": { "WebServerGroup": { "Type": "AWS::AutoScaling::AutoScalingGroup", "Properties": { "VPCZoneIdentifier": {"Ref": "Subnets"}, "LaunchConfigurationName": {"Ref": "LaunchConfig"}, "MinSize": "2", "MaxSize": "2", "TargetGroupARNs": [{"Ref": "ALBTargetGroup"}] }, "CreationPolicy": { "ResourceSignal": {"Timeout": "PT15M"} }, "UpdatePolicy": { "AutoScalingRollingUpdate": { "MinInstancesInService": "1", "MaxBatchSize": "1", "PauseTime": "PT15M", "WaitOnResourceSignals": true } } }, "LaunchConfig": { "Type": "AWS::AutoScaling::LaunchConfiguration", "Metadata": { "Comment": "Install a simple application", "AWS::CloudFormation::Init": { "config": { "packages": {"yum": {"httpd": []}}, "files": { "/var/www/html/index.html": { "content": {"Fn::Join": ["\n", ["<h1>Congratulations, you have successfully launched the AWS CloudFormation sample.</h1>"]]}, "mode": "000644", "owner": "root", "group": "root" }, "/etc/cfn/cfn-hup.conf": { "content": {"Fn::Join": ["", [ "[main]\n", "stack=", {"Ref": "AWS::StackId"}, "\n", "region=", {"Ref": "AWS::Region"}, "\n" ]]}, "mode": "000400", "owner": "root", "group": "root" }, "/etc/cfn/hooks.d/cfn-auto-reloader.conf": { "content": {"Fn::Join": ["", [ "[cfn-auto-reloader-hook]\n", "triggers=post.update\n", "path=Resources.LaunchConfig.Metadata.AWS::CloudFormation::Init\n", "action=/opt/aws/bin/cfn-init -v ", " --stack ", {"Ref": "AWS::StackName"}, " --resource LaunchConfig ", " --region ", {"Ref": "AWS::Region"}, "\n", "runas=root\n" ]]}, "mode": "000400", "owner": "root", "group": "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": {"Fn::FindInMap": ["AWSAMIRegionMap", {"Ref": "AWS::Region"}, "AMZNLINUXHVM"]}, "SecurityGroups": [{"Ref": "InstanceSecurityGroup"}], "InstanceType": {"Ref": "InstanceType"}, "KeyName": {"Ref": "KeyName"}, "UserData": { "Fn::Base64": {"Fn::Join": ["", [ "#!/bin/bash -xe\n", "yum update -y aws-cfn-bootstrap\n", "/opt/aws/bin/cfn-init -v ", " --stack ", {"Ref": "AWS::StackName"}, " --resource LaunchConfig ", " --region ", {"Ref": "AWS::Region"}, "\n", "/opt/aws/bin/cfn-signal -e $? ", " --stack ", {"Ref": "AWS::StackName"}, " --resource WebServerGroup ", " --region ", {"Ref": "AWS::Region"}, "\n" ]]} } } }, "ELBSecurityGroup": { "Type": "AWS::EC2::SecurityGroup", "Properties": { "GroupDescription": "Allow access to the ELB", "VpcId": {"Ref": "VpcId"}, "SecurityGroupIngress": [{ "Fn::If": [ "UseALBSSL", { "IpProtocol": "tcp", "FromPort": 443, "ToPort": 443, "CidrIp": "0.0.0.0/0" }, { "IpProtocol": "tcp", "FromPort": 80, "ToPort": 80, "CidrIp": "0.0.0.0/0" } ] }] } }, "ApplicationLoadBalancer": { "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer", "Properties": { "Subnets": {"Ref": "Subnets"}, "SecurityGroups": [{"Ref": "ELBSecurityGroup"}] } }, "ALBListener": { "Type": "AWS::ElasticLoadBalancingV2::Listener", "Properties": { "DefaultActions": [{ "Type": "forward", "TargetGroupArn": {"Ref": "ALBTargetGroup"} }], "LoadBalancerArn": {"Ref": "ApplicationLoadBalancer"}, "Port": {"Fn::If": ["UseALBSSL", 443, 80]}, "Protocol": {"Fn::If": ["UseALBSSL", "HTTPS", "HTTP"]}, "Certificates": [{ "Fn::If": [ "UseALBSSL", {"CertificateArn": {"Ref": "ALBSSLCertificateARN"}}, {"Ref": "AWS::NoValue"} ] }] } }, "ALBTargetGroup": { "Type": "AWS::ElasticLoadBalancingV2::TargetGroup", "Properties": { "HealthCheckIntervalSeconds": 30, "HealthCheckTimeoutSeconds": 5, "HealthyThresholdCount": 3, "Port": 80, "Protocol": "HTTP", "UnhealthyThresholdCount": 5, "VpcId": {"Ref": "VpcId"} } }, "InstanceSecurityGroup": { "Type": "AWS::EC2::SecurityGroup", "Properties": { "GroupDescription": "Enable SSH access and HTTP access on the inbound port", "SecurityGroupIngress": [ { "IpProtocol": "tcp", "FromPort": 80, "ToPort": 80, "SourceSecurityGroupId": {"Fn::Select": [0, {"Fn::GetAtt": ["ApplicationLoadBalancer", "SecurityGroups"]}]} }, { "IpProtocol": "tcp", "FromPort": 22, "ToPort": 22, "CidrIp": {"Ref": "SSHLocation"} } ], "VpcId": {"Ref": "VpcId"} } }, "RecordSet": { "Type": "AWS::Route53::RecordSetGroup", "Condition": "UseALBSSL", "Properties": { "HostedZoneName": {"Fn::Join": ["", [{"Ref": "HostedZoneName"}, "."]]}, "RecordSets": [{ "Name": {"Fn::Join": ["", [ {"Fn::Select": ["0", {"Fn::Split": [".", {"Fn::GetAtt": ["ApplicationLoadBalancer", "DNSName"]}]}]}, ".", {"Ref": "HostedZoneName"}, "." ]]}, "Type": "A", "AliasTarget": { "DNSName": {"Fn::GetAtt": ["ApplicationLoadBalancer", "DNSName"]}, "EvaluateTargetHealth": true, "HostedZoneId": {"Fn::GetAtt": ["ApplicationLoadBalancer", "CanonicalHostedZoneID"]} } }] } } }, "Outputs": { "URL": { "Description": "URL of the website", "Value": {"Fn::Join": ["", [ {"Fn::If": [ "UseALBSSL", {"Fn::Join": ["", [ "https://", {"Fn::Join": ["", [ {"Fn::Select": ["0", {"Fn::Split": [".", {"Fn::GetAtt": ["ApplicationLoadBalancer", "DNSName"]}]}]}, ".", {"Ref": "HostedZoneName"}, "." ]]} ]]}, {"Fn::Join": ["", [ "http://", {"Fn::GetAtt": ["ApplicationLoadBalancer", "DNSName"]} ]]} ]} ]]} } } }

YAML

AWSTemplateFormatVersion: 2010-09-09 Parameters: VpcId: Type: AWS::EC2::VPC::Id Description: VpcId of your existing Virtual Private Cloud (VPC) ConstraintDescription: must be the VPC Id of an existing Virtual Private Cloud. Subnets: Type: List<AWS::EC2::Subnet::Id> Description: The list of SubnetIds in your Virtual Private Cloud (VPC) ConstraintDescription: >- must be a list of at least two existing subnets associated with at least two different availability zones. They should be residing in the selected Virtual Private Cloud. InstanceType: Description: WebServer EC2 instance type Type: String Default: t2.micro AllowedValues: - t2.micro - t3.micro ConstraintDescription: must be a valid EC2 instance type. KeyName: Description: Name of an existing EC2 KeyPair to enable SSH access to the instances Type: AWS::EC2::KeyPair::KeyName ConstraintDescription: must be the name of an existing EC2 KeyPair. SSHLocation: Description: The IP address range that can be used to SSH to the EC2 instances Type: String MinLength: '9' MaxLength: '18' Default: 0.0.0.0/0 AllowedPattern: '(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})' ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x. UseSSL: AllowedValues: - 'Yes' - 'No' ConstraintDescription: Select Yes to create a HTTPS Listener Default: 'No' Description: 'Select "Yes" to implement SSL, "No" to skip (default).' Type: String ALBSSLCertificateARN: Default: '' Description: >- [Optional] The ARN of the SSL certificate to be used for the Application Load Balancer Type: String HostedZoneName: AllowedPattern: >- ^$|(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$ Default: '' Description: '[Optional] The domain name of a valid Hosted Zone on AWS.' Type: String Conditions: UseALBSSL: !Equals - !Ref UseSSL - 'Yes' Rules: SubnetsInVPC: Assertions: - Assert: 'Fn::EachMemberEquals': - 'Fn::ValueOf': - Subnets - VpcId - Ref: VpcId AssertDescription: All subnets must be in the VPC ValidateHostedZone: RuleCondition: !Equals - !Ref UseSSL - 'Yes' Assertions: - Assert: !Not - !Equals - !Ref ALBSSLCertificateARN - '' AssertDescription: ACM Certificate value cannot be empty if SSL is required - Assert: !Not - !Equals - !Ref HostedZoneName - '' AssertDescription: Route53 Hosted Zone Name is mandatory when SSL is required Mappings: AWSAMIRegionMap: us-east-1: AMZNLINUXHVM: ami-0ff8a91507f77f867 us-west-1: AMZNLINUXHVM: ami-0bdb828fd58c52235 eu-west-1: AMZNLINUXHVM: ami-047bb4163c506cd98 ap-southeast-1: AMZNLINUXHVM: ami-08569b978cc4dfa10 Resources: WebServerGroup: Type: AWS::AutoScaling::AutoScalingGroup Properties: VPCZoneIdentifier: !Ref Subnets LaunchConfigurationName: !Ref LaunchConfig MinSize: '2' MaxSize: '2' TargetGroupARNs: - !Ref ALBTargetGroup CreationPolicy: ResourceSignal: Timeout: PT15M UpdatePolicy: AutoScalingRollingUpdate: MinInstancesInService: '1' MaxBatchSize: '1' PauseTime: PT15M WaitOnResourceSignals: 'true' LaunchConfig: Type: AWS::AutoScaling::LaunchConfiguration Metadata: Comment: Install a simple application 'AWS::CloudFormation::Init': config: packages: yum: httpd: [] files: /var/www/html/index.html: content: !Join - |+ - - >- <h1>Congratulations, you have successfully launched the AWS CloudFormation sample.</h1> mode: '000644' owner: root group: root /etc/cfn/cfn-hup.conf: content: !Sub | [main] stack=${AWS::StackId} region=${AWS::Region} 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.LaunchConfig.Metadata.AWS::CloudFormation::Init action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchConfig --region ${AWS::Region} runas=root mode: '000400' owner: root group: 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: !FindInMap - AWSAMIRegionMap - !Ref 'AWS::Region' - AMZNLINUXHVM SecurityGroups: - !Ref InstanceSecurityGroup InstanceType: !Ref InstanceType KeyName: !Ref KeyName UserData: !Base64 Fn::Sub: |- #!/bin/bash -xe yum update -y aws-cfn-bootstrap /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchConfig --region ${AWS::Region} /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource WebServerGroup --region ${AWS::Region} ELBSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Allow access to the ELB VpcId: !Ref VpcId SecurityGroupIngress: - !If - UseALBSSL - IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: 0.0.0.0/0 - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 ApplicationLoadBalancer: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: Subnets: !Ref Subnets SecurityGroups: - !Ref ELBSecurityGroup ALBListener: Type: AWS::ElasticLoadBalancingV2::Listener Properties: DefaultActions: - Type: forward TargetGroupArn: !Ref ALBTargetGroup LoadBalancerArn: !Ref ApplicationLoadBalancer Port: !If - UseALBSSL - 443 - 80 Protocol: !If - UseALBSSL - HTTPS - HTTP Certificates: - !If - UseALBSSL - CertificateArn: !Ref ALBSSLCertificateARN - !Ref 'AWS::NoValue' ALBTargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: HealthCheckIntervalSeconds: 30 HealthCheckTimeoutSeconds: 5 HealthyThresholdCount: 3 Port: 80 Protocol: HTTP UnhealthyThresholdCount: 5 VpcId: !Ref VpcId InstanceSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Enable SSH access and HTTP access on the inbound port SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 SourceSecurityGroupId: !Select - 0 - !GetAtt - ApplicationLoadBalancer - SecurityGroups - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: !Ref SSHLocation VpcId: !Ref VpcId RecordSet: Type: AWS::Route53::RecordSetGroup Condition: UseALBSSL Properties: HostedZoneName: !Join - '' - - !Ref HostedZoneName - . RecordSets: - Name: !Join - '' - - !Select - '0' - !Split - . - !GetAtt - ApplicationLoadBalancer - DNSName - . - !Ref HostedZoneName - . Type: A AliasTarget: DNSName: !GetAtt - ApplicationLoadBalancer - DNSName EvaluateTargetHealth: true HostedZoneId: !GetAtt - ApplicationLoadBalancer - CanonicalHostedZoneID Outputs: URL: Description: URL of the website Value: !Join - '' - - !If - UseALBSSL - !Join - '' - - 'https://' - !Join - '' - - !Select - '0' - !Split - . - !GetAtt - ApplicationLoadBalancer - DNSName - . - !Ref HostedZoneName - . - !Join - '' - - 'http://' - !GetAtt - ApplicationLoadBalancer - DNSName