

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 写作 AWS CloudFormation Guard 规则
<a name="writing-rules"></a>

在中 AWS CloudFormation Guard，*规则*就是 policy-as-code规则。您使用 Guard 域专用语言 (DSL) 编写规则，您可以根据这些规则验证 JSON 或 YAML 格式的数据。规则由*条款*组成。

您可以将使用 Guard DSL 编写的规则保存到使用任何文件扩展名的纯文本文件中。

您可以创建多个规则文件并将其归类为一个*规则集*。规则集允许您同时根据多个规则文件验证 JSON 或 YAML 格式的数据。

**Topics**
+ [子句](#clauses)
+ [在子句中使用查询](#clauses-queries)
+ [在子句中使用运算符](#clauses-operators)
+ [在子句中使用自定义消息](#clauses-custom-messages)
+ [合并条款](#combining-clauses)
+ [使用带有守卫规则的方块](#blocks)
+ [使用内置函数](#built-in-functions)
+ [定义 Guard 查询和过滤](query-and-filtering.md)
+ [在 Guard 规则中分配和引用变量](variables.md)
+ [在中撰写命名规则块 AWS CloudFormation Guard](named-rule-block-composition.md)
+ [编写用于执行情境感知评估的子句](context-aware-evaluations.md)

## 子句
<a name="clauses"></a>

子句是计算结果为 true (`PASS`) 或 false (`FAIL`) 的布尔表达式。子句要么使用二元运算符来比较两个值，要么使用对单个值进行运算的一元运算符。

**一元子句的示例**

以下一元子句评估集合`TcpBlockedPorts`是否为空。

```
InputParameters.TcpBlockedPorts not empty
```

以下一元子句评估该`ExecutionRoleArn`属性是否为字符串。

```
Properties.ExecutionRoleArn is_string
```

**二进制子句的示例**

以下二进制子句评估该`BucketName`属性是否包含字符串`encrypted`，无论大小写如何。

```
Properties.BucketName != /(?i)encrypted/
```

以下二进制子句评估该`ReadCapacityUnits`属性是否小于或等于 5,000。

```
Properties.ProvisionedThroughput.ReadCapacityUnits <= 5000
```

### 编写 Guard 规则子句的语法
<a name="clauses-syntax"></a>

```
<query> <operator> [query|value literal] [custom message]
```

### 守卫规则子句的属性
<a name="clauses-properties"></a>

`query`  <a name="clauses-properties-query"></a>
写入以点 (`.`) 分隔的表达式，用于遍历分层数据。查询表达式可以包括筛选表达式来定位值的子集。可以将查询分配给变量，这样您就可以编写一次查询，然后在规则集的其他地方引用它们，这样您就可以访问查询结果。  
有关编写查询和筛选的更多信息，请参阅[定义查询和筛选](query-and-filtering.md)。  
 *是否必需*：是

`operator`  <a name="clauses-properties-operator"></a>
一元运算符或二进制运算符，可帮助检查查询的状态。二元运算符的左侧 (LHS) 必须是查询，右侧 (RHS) 必须是查询或值文字。  
 *支持的二元运算符*：`==``!=`（等于）\|`>`（不等于）\|`>=`（大于）\|（大于或等于）\|`<=`（小于）\|（小于或等于）\|`IN`（在 [x、y、z 格式列表中] `<`  
 *支持的一元运算符*：`exists`\| `empty` \| `is_string` \| \| `is_list` \| `is_struct` `not(!)`  
 *是否必需*：是

`query|value literal`  <a name="clauses-properties-value-literal"></a>
查询或支持的值文字，例如`string`或`integer(64)`。  
*支持的值文字：*  
+ 所有原始类型：`string`、、`integer(64)`、`float(64)`、`bool`、`char` `regex`
+ 所有用于表达`integer(64)``float(64)`、或`char`范围的专用范围类型，表示为：
  + `r[<lower_limit>, <upper_limit>]`，它转换为满足以下表达式`k`的任何值：`lower_limit <= k <= upper_limit`
  + `r[<lower_limit>, <upper_limit>`)，它会转换为满足以下表达式`k`的任何值：`lower_limit <= k < upper_limit`
  + `r(<lower_limit>, <upper_limit>]`，它转换为满足以下表达式`k`的任何值：`lower_limit < k <= upper_limit`
  + `r(<lower_limit>, <upper_limit>),`这表示任何满足以下表达式`k`的值：`lower_limit < k < upper_limit`
+ 嵌套键值结构数据的关联数组（映射）。例如：

  `{ "my-map": { "nested-maps": [ { "key": 10, "value": 20 } ] } }`
+ 原始类型或关联数组类型的数组
 *必需*：有条件的；使用二元运算符时为必填项。

`custom message`  <a name="clauses-properties-custom-message"></a>
提供有关子句的信息的字符串。该消息显示在`validate`和`test`命令的详细输出中，可用于理解或调试分层数据的规则评估。  
 *必需*：否

## 在子句中使用查询
<a name="clauses-queries"></a>

有关编写查询的信息，请参见[定义查询和筛选](query-and-filtering.md)和[在 Guard 规则中分配和引用变量](variables.md)。

## 在子句中使用运算符
<a name="clauses-operators"></a>

以下是示例 CloudFormation 模板，`Template-1`和`Template-2`。为了演示支持的运算符的使用，本节中的示例查询和子句参考了这些示例模板。

**模板-1**

```
Resources:
 S3Bucket:
   Type: AWS::S3::Bucket
   Properties:
     BucketName: MyServiceS3Bucket
     BucketEncryption:
       ServerSideEncryptionConfiguration:
         - ServerSideEncryptionByDefault:
             SSEAlgorithm: 'aws:kms'
             KMSMasterKeyID: 'arn:aws:kms:us-east-1:123456789:key/056ea50b-1013-3907-8617-c93e474e400'
     Tags:
       - Key: stage
         Value: prod
       - Key: service
         Value: myService
```

**模板-2**

```
Resources:
 NewVolume:
   Type: AWS::EC2::Volume
   Properties: 
     Size: 100
     VolumeType: io1
     Iops: 100
     AvailabilityZone:
       Fn::Select:
         - 0
         - Fn::GetAZs: us-east-1
     Tags:
       - Key: environment
         Value: test
   DeletionPolicy: Snapshot
```

### 使用一元运算符的子句示例
<a name="clauses-unary-operators"></a>
+ `empty`— 检查集合是否为空。您还可以使用它来检查查询是否在分层数据中包含值，因为查询会生成集合。你不能用它来检查字符串值查询是否定义了空字符串 (`""`)。有关更多信息，请参阅 [定义查询和筛选](query-and-filtering.md)。

  以下子句检查模板是否定义了一个或多个资源。它的计算结果`PASS`为，因为中定义了具有逻辑 ID `S3Bucket` 的`Template-1`资源。

  ```
  Resources !empty
  ```

  以下子句检查是否为`S3Bucket`资源定义了一个或多个标签。它的计算结果`PASS`为，因为在中`Template-1`为该`Tags`属性定义`S3Bucket`了两个标签。

  ```
  Resources.S3Bucket.Properties.Tags !empty
  ```
+ `exists`— 检查每次出现的查询是否都有值并且可以用来代替`!= null`。

  以下子句检查是否为定义了该`BucketEncryption`属性`S3Bucket`。它的计算结果`PASS`为，因为`BucketEncryption`是在`S3Bucket`中定义的。`Template-1`

  ```
  Resources.S3Bucket.Properties.BucketEncryption exists
  ```

**注意**  
遍历输入数据时，`empty`和`not exists`检查`true`的计算结果是否缺少属性键。例如，如果未在的模板中定义该`Properties`部分`S3Bucket`，则该子句的`Resources.S3Bucket.Properties.Tag empty`计算结果为。`true``exists`和`empty`检查不会在错误消息中显示文档内的 JSON 指针路径。这两个子句通常都有检索错误，无法保留这些遍历信息。
+ `is_string`— 检查每次出现的查询是否为`string`类型。

  以下子句检查是否为`S3Bucket`资源的`BucketName`属性指定了字符串值。它的计算结果`PASS`为，因为在`BucketName`中`Template-1`指定了字符串值`"MyServiceS3Bucket"`。

  ```
  Resources.S3Bucket.Properties.BucketName is_string
  ```
+ `is_list`— 检查每次出现的查询是否为`list`类型。

  以下子句检查是否为`S3Bucket`资源的`Tags`属性指定了列表。它的计算结果`PASS`为，因为在中指定了两个键值对。`Tags` `Template-1`

  ```
  Resources.S3Bucket.Properties.Tags is_list
  ```
+ `is_struct`— 检查每次出现的查询是否都是结构化数据。

  以下子句检查是否为`S3Bucket`资源的`BucketEncryption`属性指定了结构化数据。它的计算结果`PASS`为，因为`BucketEncryption`是使用{{(object)}}中的`ServerSideEncryptionConfiguration``Template-1`属性类型指定的。

  ```
  Resources.S3Bucket.Properties.BucketEncryption is_struct
  ```

**注意**  
要检查反向状态，可以将 (` not !`) 运算符与`is_string``is_list`、和运`is_struct`算符一起使用。

### 使用二元运算符的子句示例
<a name="clauses-binary-operators"></a>

以下子句检查为中的`S3Bucket`资源`BucketName`属性指定的值是否`Template-1`包含字符串`encrypt`，无论大小写如何。其计算结果`PASS`为，因为指定的存储桶名称`"MyServiceS3Bucket"`不包含字符串`encrypt`。

```
Resources.S3Bucket.Properties.BucketName != /(?i)encrypt/
```

以下子句检查为中的`NewVolume`资源`Size`属性指定的值`Template-2`是否在特定范围内：50 <= `Size` <= 200。它的计算结果`PASS`为，因为`100`是指定的。`Size`

```
Resources.NewVolume.Properties.Size IN r[50,200]
```

以下子句检查中为`NewVolume`资源`VolumeType`属性指定的值是否`Template-2`为`io1``io2`、或`gp3`。它的计算结果`PASS`为，因为`io1`是指定的。`NewVolume`

```
Resources.NewVolume.Properties.NewVolume.VolumeType IN [ 'io1','io2','gp3' ]
```

**注意**  
本节中的示例查询演示了如何使用带有逻辑 IDs `S3Bucket`和的资源来使用运算符`NewVolume`。资源名称通常是用户定义的，可以在基础设施即代码 (IaC) 模板中任意命名。要编写适用于模板中定义的所有`AWS::S3::Bucket`资源的通用规则，最常用的查询形式是`Resources.*[ Type == ‘AWS::S3::Bucket’ ]`。有关更多信息，请参阅[定义查询和筛选](query-and-filtering.md)，了解有关用法的详细信息，并浏览`cloudformation-guard` GitHub 存储库中的[示例](https://github.com/aws-cloudformation/cloudformation-guard/tree/main/guard-examples)目录。

## 在子句中使用自定义消息
<a name="clauses-custom-messages"></a>

在以下示例中，的子句`Template-2`包括自定义消息。

```
Resources.NewVolume.Properties.Size IN r(50,200) 
<<
    EC2Volume size must be between 50 and 200, 
    not including 50 and 200
>>
Resources.NewVolume.Properties.VolumeType IN [ 'io1','io2','gp3' ] <<Allowed Volume Types are io1, io2, and gp3>>
```

## 合并条款
<a name="combining-clauses"></a>

在 Guard 中，写在新行上的每个子句都使用连词（布尔`and`逻辑）与下一个子句隐式组合。请参阅以下示例。

```
# clause_A ^ clause_B ^ clause_C
clause_A
clause_B
clause_C
```

也可以使用 disjunction 将子句与下一个子句合并，方法是在第一个子句的`or|OR`末尾指定。

```
<query> <operator> [query|value literal] [custom message] [or|OR]
```

在 Guard 子句中，首先评估分离词，然后评估连词。保护规则可以定义为计算结果为 () 或 `false` () `and|AND` 的子句（一个 of `or|OR` s`PASS`）的组合。`true` `FAIL`这类似于[连词法线形式](https://en.wikipedia.org/wiki/Conjunctive_normal_form)。

以下示例演示了子句的求值顺序。

```
# (clause_E v clause_F) ^ clause_G
clause_E OR clause_F
clause_G

# (clause_H v clause_I) ^ (clause_J v clause_K)
clause_H OR
clause_I
clause_J OR
clause_K

# (clause_L v clause_M v clause_N) ^ clause_O
clause_L OR
clause_M OR
clause_N 
clause_O
```

所有基于该示例的子句均`Template-1`可使用连词进行组合。请参阅以下示例。

```
Resources.S3Bucket.Properties.BucketName is_string
Resources.S3Bucket.Properties.BucketName != /(?i)encrypt/
Resources.S3Bucket.Properties.BucketEncryption exists
Resources.S3Bucket.Properties.BucketEncryption is_struct
Resources.S3Bucket.Properties.Tags is_list
Resources.S3Bucket.Properties.Tags !empty
```

## 使用带有守卫规则的方块
<a name="blocks"></a>

块是从一组相关的子句、条件或规则中去除冗长和重复内容的组合。有三种类型的方块：
+ 查询区块
+ `when`方块
+ 命名规则块

### 查询区块
<a name="query-blocks"></a>

以下是基于该示例的子句`Template-1`。连词用于合并子句。

```
Resources.S3Bucket.Properties.BucketName is_string
Resources.S3Bucket.Properties.BucketName != /(?i)encrypt/
Resources.S3Bucket.Properties.BucketEncryption exists
Resources.S3Bucket.Properties.BucketEncryption is_struct
Resources.S3Bucket.Properties.Tags is_list
Resources.S3Bucket.Properties.Tags !empty
```

每个子句中的部分查询表达式都是重复的。您可以使用查询块来提高可组合性，并消除一组具有相同初始查询路径的相关子句的冗长性和重复性。可以编写相同的子句集，如以下示例所示。

```
Resources.S3Bucket.Properties {
    BucketName is_string
    BucketName != /(?i)encrypt/
    BucketEncryption exists
    BucketEncryption is_struct
    Tags is_list
    Tags !empty
}
```

在查询块中，块之前的查询为块内的子句设置上下文。

有关使用方块的更多信息，请参阅[撰写命名规则块](named-rule-block-composition.md)。

### `when`方块
<a name="when-blocks"></a>

你可以使用方块有条件地评估`when`方块，其形式如下。

```
  when <condition> {
       Guard_rule_1
       Guard_rule_2
       ...
   }
```

`when`关键字表示`when`区块的起点。 `condition`是守卫规则。仅当对条件的评估结果为 `true` (`PASS`) 时，才会对方块进行评估。

以下是基于的示例`when`块`Template-1`。

```
when Resources.S3Bucket.Properties.BucketName is_string {
     Resources.S3Bucket.Properties.BucketName != /(?i)encrypt/
 }
```

仅当为指定的值为字符串时，才会对`when`块中的子句进行求值。`BucketName`如果在模板的`Parameters`部分中引用了`BucketName`为指定的值，如以下示例所示，则不会评估该`when`块中的子句。

```
Parameters:
   S3BucketName:
     Type: String
 Resources:
   S3Bucket:
     Type: AWS::S3::Bucket
     Properties:
       BucketName: 
         Ref: S3BucketName
     ...
```

### 命名规则块
<a name="named-rule-blocks"></a>

您可以为一组规则（规则*集）指定名称，然后在其他规则*中引用这些模块化验证块，*称为命名规则块*。命名规则块采用以下形式。

```
  rule <rule name> [when <condition>] {
    Guard_rule_1
    Guard_rule_2
    ...
    }
```

`rule`关键字表示命名规则块的开头。

`rule name`是一个人类可读的字符串，用于唯一标识命名规则块。这是它封装的 Guard 规则集的标签。在这种用法中，“*保护规则*” 一词包括子句、查询块、`when`块和命名规则块。规则名称可用于指代其封装的规则集的评估结果，这使得命名规则块可重复使用。规则名称还提供了有关`validate`和`test`命令输出中规则失败的上下文。规则名称与区块的评估状态（`PASS``FAIL`、或`SKIP`）一起显示在规则文件的评估输出中。请参阅以下示例。

```
# Sample output of an evaluation where check1, check2, and check3 are rule names.
template.json Status = **FAIL**
**SKIP rules**
check1 **SKIP**
**PASS rules**
check2 **PASS**
**FAILED rules**
check3 **FAIL**
```

您还可以通过在规则名称后指定`when`关键字和条件来有条件地评估命名规则块。

以下是本主题前面讨论的示例`when`块。

```
rule checkBucketNameStringValue when Resources.S3Bucket.Properties.BucketName is_string {
    Resources.S3Bucket.Properties.BucketName != /(?i)encrypt/
}
```

使用命名规则块，也可以将前面的内容写成以下内容。

```
rule checkBucketNameIsString {
    Resources.S3Bucket.Properties.BucketName is_string
}
rule checkBucketNameStringValue when checkBucketNameIsString {
    Resources.S3Bucket.Properties.BucketName != /(?i)encrypt/
}
```

您可以重复使用命名规则块并将其与其他 Guard 规则分组。以下是几个例子。

```
rule rule_name_A {
    Guard_rule_1 OR
    Guard_rule_2
    ...
}

rule rule_name_B {
    Guard_rule_3
    Guard_rule_4
    ...
}

rule rule_name_C {
    rule_name_A OR rule_name_B
}

rule rule_name_D {
    rule_name_A
    rule_name_B
}

rule rule_name_E when rule_name_D {
    Guard_rule_5
    Guard_rule_6
    ...
}
```

## 使用内置函数
<a name="built-in-functions"></a>

AWS CloudFormation Guard 提供了内置函数，您可以在规则中使用这些函数来执行诸如字符串操作、JSON 解析和数据类型转换之类的操作。只有通过对变量的赋值才能支持函数。

### 关键功能
<a name="key-functions"></a>

`json_parse(json_string)`  
解析模板中的内联 JSON 字符串。解析后，您可以评估生成的对象的属性。

`count(collection)`  
返回查询解析到的项目数。

`regex_replace(base_string, regex_to_extract, regex_replacement)`  
使用正则表达式替换部分字符串。

有关可用函数的完整列表，包括字符串操作、集合操作和数据类型转换函数，请参阅 Guard GitHub 存储库中的 [Functions 文档](https://github.com/aws-cloudformation/cloudformation-guard/blob/main/docs/FUNCTIONS.md)。