

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# AWS CloudFormation Guard ルールの記述
<a name="writing-rules"></a>

では AWS CloudFormation Guard、*ルール*は policy-as-code ルールです。JSON 形式または YAML 形式のデータを検証できる Guard ドメイン固有の言語 (DSL) でルールを記述します。ルールは **句で構成されます。

Guard DSL を使用して記述されたルールは、任意のファイル拡張子を使用するプレーンテキストファイルに保存できます。

複数のルールファイルを作成し、*ルールセット*として分類できます。ルールセットを使用すると、JSON 形式または YAML 形式のデータを複数のルールファイルに対して同時に検証できます。

**Topics**
+ [句](#clauses)
+ [句でのクエリの使用](#clauses-queries)
+ [句での演算子の使用](#clauses-operators)
+ [句でのカスタムメッセージの使用](#clauses-custom-messages)
+ [句の組み合わせ](#combining-clauses)
+ [ガードルールでのブロックの使用](#blocks)
+ [組み込み関数の使用](#built-in-functions)
+ [ガードクエリの定義とフィルタリング](query-and-filtering.md)
+ [ガードルールでの変数の割り当てと参照](variables.md)
+ [で名前付きルールブロックを作成する AWS CloudFormation Guard](named-rule-block-composition.md)
+ [コンテキスト対応評価を実行するための句の記述](context-aware-evaluations.md)

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

句は、true (`PASS`) または false () のいずれかに評価されるブール式です`FAIL`。句は、バイナリ演算子を使用して 2 つの値を比較するか、単一の値で動作する単一演算子を使用します。

**単項句の例**

次の unary 句は、コレクション`TcpBlockedPorts`が空かどうかを評価します。

```
InputParameters.TcpBlockedPorts not empty
```

次の unary 句は、 `ExecutionRoleArn`プロパティが文字列であるかどうかを評価します。

```
Properties.ExecutionRoleArn is_string
```

**バイナリ句の例**

次のバイナリ句は、大文字と小文字に関係なく`encrypted`、 `BucketName`プロパティに文字列 が含まれているかどうかを評価します。

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

次のバイナリ句は、`ReadCapacityUnits`プロパティが 5,000 以下であるかどうかを評価します。

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

### ガードルール句を記述するための構文
<a name="clauses-syntax"></a>

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

### ガードルール句のプロパティ
<a name="clauses-properties"></a>

`query`  <a name="clauses-properties-query"></a>
階層データをトラバースするために書き込まれたドット (`.`) 区切り式。クエリ式には、値のサブセットをターゲットとするフィルター式を含めることができます。クエリを変数に割り当てると、変数を 1 回書き込み、ルールセットの他の場所で参照できるため、クエリ結果にアクセスできます。  
クエリの記述とフィルタリングの詳細については、「」を参照してください[クエリとフィルタリングの定義](query-and-filtering.md)。  
 *必須:* はい

`operator`  <a name="clauses-properties-operator"></a>
クエリの状態を確認するのに役立つ単項演算子またはバイナリ演算子。バイナリ演算子の左側 (LHS) はクエリで、右側 (RHS) はクエリまたは値リテラルである必要があります。  
 *サポートされているバイナリ演算子*: `==` (Equal) \$1 `!=` (Not equal) \$1 `>` (Greater than) \$1 `>=` (Greater than or equal to) \$1 `<` (Less than) \$1 `<=` (Less than than) \$1 `IN` (In a list of form [x, y, z]  
 *サポートされている単項演算子*: `exists` \$1 `empty` \$1 `is_string` \$1 `is_list` \$1 `is_struct` \$1 `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)「」および「」を参照してください[ガードルールでの変数の割り当てと参照](variables.md)。

## 句での演算子の使用
<a name="clauses-operators"></a>

CloudFormation テンプレートの例を次に示します`Template-1``Template-2`。サポートされている演算子の使用を示すために、このセクションのクエリと句の例は、これらのサンプルテンプレートを参照しています。

**Template-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
```

**Template-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)」を参照してください。

  次の句は、テンプレートに 1 つ以上のリソースが定義されているかどうかを確認します。論理 ID を持つリソース`S3Bucket`が で定義されている`PASS`ため、 に評価されます`Template-1`。

  ```
  Resources !empty
  ```

  次の句は、`S3Bucket`リソースに 1 つ以上のタグが定義されているかどうかを確認します。`S3Bucket` には の `Tags`プロパティに 2 つのタグが定義されている`PASS`ため、 に評価されます`Template-1`。

  ```
  Resources.S3Bucket.Properties.Tags !empty
  ```
+ `exists` – クエリの各出現に値があり、 の代わりに使用できるかどうかを確認します`!= null`。

  次の句は、 `BucketEncryption`プロパティが に対して定義されているかどうかを確認します`S3Bucket`。は `S3Bucket`で に`BucketEncryption`定義されている`PASS`ため、 に評価されます`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`プロパティに文字列値が指定されているかどうかをチェックします。文字列値が `BucketName`の に`"MyServiceS3Bucket"`指定されている`PASS`ため、 に評価されます`Template-1`。

  ```
  Resources.S3Bucket.Properties.BucketName is_string
  ```
+ `is_list` – クエリの各出現が `list`タイプであるかどうかを確認します。

  次の句は、`S3Bucket`リソースの `Tags`プロパティにリストが指定されているかどうかを確認します。2 つのキーと値のペアが `Tags`で に指定されている`PASS`ため、 に評価されます`Template-1`。

  ```
  Resources.S3Bucket.Properties.Tags is_list
  ```
+ `is_struct` – クエリの各出現が構造化データであるかどうかをチェックします。

  次の句は、`S3Bucket`リソースの `BucketEncryption`プロパティに構造化データが指定されているかどうかを確認します。`BucketEncryption` で `ServerSideEncryptionConfiguration`プロパティタイプ *(オブジェクト)* を使用して が指定され`PASS`るため、 に評価されます`Template-1`。

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

**注記**  
逆状態を確認するには、 (` not !`) 演算子を `is_string`、`is_list`、および `is_struct`演算子で使用できます。

### バイナリ演算子を使用する句の例
<a name="clauses-binary-operators"></a>

次の句は、 の`S3Bucket`リソースの `BucketName`プロパティに指定された値に、大文字と小文字に関係なく`encrypt`文字列 `Template-1`が含まれているかどうかを確認します。指定されたバケット名に文字列 が含まれ`"MyServiceS3Bucket"`ていない`PASS`ため、これは に評価されます`encrypt`。

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

次の`Template-2`句は、 の`NewVolume`リソースの `Size`プロパティに指定された値が、50 <= `Size` <= 200 の範囲内にあるかどうかを確認します。が に`100`指定されている`PASS`ため、 に評価されます`Size`。

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

次の`Template-2`句は、 の`NewVolume`リソースの `VolumeType`プロパティに指定された値が `io1`、`io2`、または であるかどうかをチェックします`gp3`。が に`io1`指定されている`PASS`ため、 に評価されます`NewVolume`。

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

**注記**  
このセクションのクエリ例は、論理 IDs `S3Bucket`と を持つリソースを使用した演算子の使用を示しています`NewVolume`。多くの場合、リソース名はユーザー定義であり、Infrastructure as Code (IaC) テンプレートで任意の名前を付けることができます。汎用的で、テンプレートで定義されているすべての`AWS::S3::Bucket`リソースに適用されるルールを記述する場合、使用されるクエリの最も一般的な形式は です`Resources.*[ Type == ‘AWS::S3::Bucket’ ]`。詳細については、使用状況の詳細については[クエリとフィルタリングの定義](query-and-filtering.md)「」を参照し、`cloudformation-guard`GitHub リポジトリの [examples](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
```

差分を使用して、最初の句の`or|OR`最後に を指定することで、句を次の句と組み合わせることもできます。

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

Guard 句では、差分が最初に評価され、次に結合が評価されます。ガードルールは、 (`or|OR`) または `true` () のいずれかに評価される句 ( `and|AND` の `PASS`) の分散の組み合わせとして定義できます`false``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>

ブロックは、関連する句、条件、またはルールのセットから冗長性と繰り返しを削除する構成です。ブロックには 3 つのタイプがあります。
+ クエリブロック
+ `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` キーワードは、named-rule ブロックの開始を指定します。

`rule name` は、名前付きルールブロックを一意に識別する人間が読める文字列です。これは、カプセル化する Guard ルールセットのラベルです。この使用では、*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)を参照してください。

# ガードクエリの定義とフィルタリング
<a name="query-and-filtering"></a>

このトピックでは、クエリの記述と、Guard ルール句の記述時のフィルタリングの使用について説明します。

## 前提条件
<a name="query-filtering-prerequisites"></a>

フィルタリングは高度な AWS CloudFormation Guard 概念です。フィルタリングについて学習する前に、以下の基本的なトピックを確認することをお勧めします。
+ [とは AWS CloudFormation Guard](what-is-guard.md)
+ [ルール、句の記述](writing-rules.md)

## クエリの定義
<a name="defining-queries"></a>

クエリ式は、階層データをトラバースするために書き込まれる単純なドット (`.`) で区切られた式です。クエリ式には、値のサブセットをターゲットとするフィルター式を含めることができます。クエリが評価されると、SQL クエリから返された結果セットと同様に、値のコレクションが生成されます。

次のクエリ例では、 CloudFormation テンプレートで`AWS::IAM::Role`リソースを検索します。

```
Resources.*[ Type == 'AWS::IAM::Role' ]
```

クエリは、次の基本原則に従います。
+ クエリの各ドット (`.`) 部分は、 `Resources`や `Properties.Encrypted.` などの明示的なキー用語が使用されると階層を通過します。クエリのいずれかの部分が受信データムと一致しない場合、Guard は取得エラーをスローします。
+ ワイルドカードを使用するクエリのドット (`.`) 部分は、そのレベルで構造体のすべての値を`*`横断します。
+ 配列ワイルドカードを使用するクエリのドット (`.`) 部分は、その配列のすべてのインデックス`[*]`を横断します。
+ すべてのコレクションは、角括弧 内でフィルターを指定することでフィルタリングできます`[]`。コレクションは、次の方法で検出できます。
  + データムで自然に発生する配列はコレクションです。 の例を次に示します。

    ポート: `[20, 21, 110, 190]`

    タグ: `[{"Key": "Stage", "Value": "PROD"}, {"Key": "App", "Value": "MyService"}]`
  + のような構造のすべての値をトラバースする場合 `Resources.*`
  + クエリ結果自体は、値をさらにフィルタリングできるコレクションです。次の例を参照してください。

    ```
    # Query all resources
    let all_resources = Resource.*
    
    # Filter IAM resources from query results
    let iam_resources = %resources[ Type == /IAM/ ]
    
    # Further refine to get managed policies
    let managed_policies = %iam_resources[ Type == /ManagedPolicy/ ]
    
    # Traverse each managed policy
    %managed_policies {
        # Do something with each policy
    }
    ```

CloudFormation テンプレートスニペットの例を次に示します。

```
Resources:
  SampleRole:
    Type: AWS::IAM::Role
    ...
  SampleInstance:
    Type: AWS::EC2::Instance
    ...
  SampleVPC:
     Type: AWS::EC2::VPC
    ...
  SampleSubnet1:
    Type: AWS::EC2::Subnet
    ...
  SampleSubnet2:
    Type: AWS::EC2::Subnet
    ...
```

このテンプレートに基づいて、トラバースされるパスは `SampleRole`で、選択した最終値は です`Type: AWS::IAM::Role`。

```
Resources:
  SampleRole:
    Type: AWS::IAM::Role
    ...
```

YAML `Resources.*[ Type == 'AWS::IAM::Role' ]` 形式のクエリの結果の値を次の例に示します。

```
- Type: AWS::IAM::Role
  ...
```

クエリを使用する方法には、次のようなものがあります。
+ 変数にクエリを割り当てて、それらの変数を参照してクエリ結果にアクセスできるようにします。
+ 選択した各値に対してテストするブロックを使用してクエリに従います。
+ クエリを基本句と直接比較します。

## 変数へのクエリの割り当て
<a name="queries-and-filtering-variables"></a>

Guard は、特定のスコープ内でワンショット変数の割り当てをサポートします。ガードルールの変数の詳細については、「」を参照してください[ガードルールでの変数の割り当てと参照](variables.md)。

変数にクエリを割り当てると、クエリを 1 回書き込み、Guard ルールの他の場所で参照できます。このセクションで後述するクエリ原則を示す変数割り当ての例を以下に示します。

```
#
# Simple query assignment
#
let resources = Resources.* # All resources

#
# A more complex query here (this will be explained below)
#
let iam_policies_allowing_log_creates = Resources.*[
    Type in [/IAM::Policy/, /IAM::ManagedPolicy/]
    some Properties.PolicyDocument.Statement[*] {
         some Action[*] == 'cloudwatch:CreateLogGroup'
         Effect == 'Allow'
    }
]
```

## クエリに割り当てられた変数の値を直接ループスルーする
<a name="variable-assigned-from-query"></a>

Guard は、クエリの結果に対する直接実行をサポートしています。次の例では、 `when`ブロックは CloudFormation テンプレートにある各`AWS::EC2::Volume`リソースの `Encrypted`、`VolumeType`、および `AvailabilityZone`プロパティに対してテストします。

```
let ec2_volumes = Resources.*[ Type == 'AWS::EC2::Volume' ] 

when %ec2_volumes !empty {
    %ec2_volumes {
        Properties {
            Encrypted == true
            VolumeType in ['gp2', 'gp3']
            AvailabilityZone in ['us-west-2b', 'us-west-2c']
        }
    }
}
```

## 句レベルの直接比較
<a name="direct-clause-level-comparisons"></a>

Guard は、直接比較の一部としてクエリもサポートしています。例えば、次のようになります。

```
let resources = Resources.*
    
    some %resources.Properties.Tags[*].Key == /PROD$/
    some %resources.Properties.Tags[*].Value == /^App/
```

前の例では、示されている形式で表される 2 つの句 ( `some`キーワードで始まる) は独立した句と見なされ、個別に評価されます。

### 単一句とブロック句の形式
<a name="single-versus-block-clause-form"></a>

まとめると、前のセクションで示した 2 つのサンプル句は、次のブロックと同等ではありません。

```
let resources = Resources.*

some %resources.Properties.Tags[*] {
    Key == /PROD$/
    Value == /^App/
}
```

このブロックは、コレクション内の各`Tag`値をクエリし、そのプロパティ値を予想されるプロパティ値と比較します。前のセクションの句の組み合わせ形式は、2 つの句を個別に評価します。次の入力を検討してください。

```
Resources:
  ...
  MyResource:
    ...
    Properties:
      Tags:
        - Key: EndPROD
          Value: NotAppStart
        - Key: NotPRODEnd
          Value: AppStart
```

最初の形式の句は に評価されます`PASS`。最初の形式で最初の句を検証する場合、`Resources`、、`Properties`、および の次のパス`NotPRODEnd`は 値`Key`と一致し`Tags`、想定値 と一致しません`PROD`。

```
Resources:
  ...
  MyResource:
    ...
    Properties:
      Tags:
        - Key: EndPROD
          Value: NotAppStart
        - Key: NotPRODEnd
          Value: AppStart
```

最初の形式の 2 番目の句でも同じことが起こります。`Resources`、`Properties`、、および のパスは`Tags`、値 `Value`と一致します`AppStart`。その結果、2 番目の句は独立しています。

全体的な結果は です`PASS`。

ただし、ブロックフォームは次のように評価されます。`Tags` 値ごとに、 `Key`と の両方`Value`が一致しているかどうかを比較します。次の例では、 `NotAppStart`と `NotPRODEnd`の値は一致しません。

```
Resources:
  ...
  MyResource:
    ...
    Properties:
      Tags:
        - Key: EndPROD
          Value: NotAppStart
        - Key: NotPRODEnd
          Value: AppStart
```

評価では `Key == /PROD$/`と の両方がチェックされるため`Value == /^App/`、一致は完了しません。したがって、結果は です`FAIL`。

**注記**  
コレクションを使用する場合は、コレクション内の各要素の複数の値を比較するときに、 ブロック句フォームを使用することをお勧めします。コレクションがスカラー値のセットである場合、または単一の属性のみを比較する場合は、単一句フォームを使用します。

## クエリ結果と関連句
<a name="query-outcomes"></a>

すべてのクエリは値のリストを返します。キーの欠落、すべてのインデックスへのアクセス時の配列の空の値 (`Tags: []`)、空のマップ () に遭遇したときのマップの欠損値など、トラバーサルのどの部分でも、取得エラーが発生する`Resources: {}`可能性があります。

このようなクエリに対して句を評価する場合、すべての取得エラーは失敗と見なされます。唯一の例外は、クエリで明示的なフィルターを使用する場合です。フィルターを使用すると、関連する句はスキップされます。

次のブロック障害は、実行中のクエリに関連付けられています。
+ テンプレートにリソースが含まれていない場合、クエリは に評価され`FAIL`、関連するブロックレベルの句も に評価されます`FAIL`。
+ テンプレートに のような空のリソースブロックが含まれている場合`{ "Resources": {} }`、クエリは に評価され`FAIL`、関連するブロックレベルの句も に評価されます`FAIL`。
+ テンプレートにリソースが含まれているが、クエリに一致するものがない場合、クエリは空の結果を返し、ブロックレベルの句はスキップされます。

## クエリでのフィルターの使用
<a name="filtering"></a>

クエリのフィルターは、実質的に選択基準として使用されるガード句です。以下は、 句の構造です。

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

フィルター[AWS CloudFormation Guard ルールの記述](writing-rules.md)を使用する際の以下の重要なポイントに注意してください。
+ [Conjunctive Normal Form (CNF) ](https://en.wikipedia.org/wiki/Conjunctive_normal_form)を使用して句を結合します。
+ 新しい行で各連結 (`and`) 句を指定します。
+ 2 つの句間の`or`キーワードを使用して、差分 (`or`) を指定します。

次の例は、結合句と非結合句を示しています。

```
resourceType == 'AWS::EC2::SecurityGroup'
InputParameters.TcpBlockedPorts not empty 

InputParameters.TcpBlockedPorts[*] {
    this in r(100, 400] or 
    this in r(4000, 65535]
}
```

### 選択基準に句を使用する
<a name="selection-criteria"></a>

任意のコレクションにフィルタリングを適用できます。フィルタリングは、 などのコレクションがすでに存在する入力内の属性に直接適用できます`securityGroups: [....]`。常に値のコレクションであるクエリにフィルタリングを適用することもできます。結合法線形式を含む句のすべての機能をフィルタリングに使用できます。

次の一般的なクエリは、CloudFormation テンプレートからタイプ別にリソースを選択するときによく使用されます。

```
Resources.*[ Type == 'AWS::IAM::Role' ]
```

クエリは、入力の `Resources`セクションに存在するすべての値`Resources.*`を返します。のテンプレート入力例の場合[クエリの定義](#defining-queries)、クエリは以下を返します。

```
- Type: AWS::IAM::Role
  ...
- Type: AWS::EC2::Instance
  ...
- Type: AWS::EC2::VPC
  ...
- Type: AWS::EC2::Subnet
  ...
- Type: AWS::EC2::Subnet
  ...
```

次に、このコレクションにフィルターを適用します。一致させる基準は です`Type == AWS::IAM::Role`。以下は、フィルターが適用された後のクエリの出力です。

```
- Type: AWS::IAM::Role
  ...
```

次に、`AWS::IAM::Role`リソースのさまざまな句を確認します。

```
let all_resources = Resources.*
let all_iam_roles = %all_resources[ Type == 'AWS::IAM::Role' ]
```

以下は、すべての `AWS::IAM::Policy`および `AWS::IAM::ManagedPolicy`リソースを選択するフィルタリングクエリの例です。

```
Resources.*[
    Type in [ /IAM::Policy/,
              /IAM::ManagedPolicy/ ]
]
```

次の例では、これらのポリシーリソースに `PolicyDocument`が指定されているかどうかを確認します。

```
Resources.*[ 
    Type in [ /IAM::Policy/,
              /IAM::ManagedPolicy/ ]
    Properties.PolicyDocument exists
]
```

### より複雑なフィルタリングニーズの構築
<a name="complex-filtering"></a>

イングレスおよびエグレスセキュリティグループ情報 AWS Config の設定項目の次の例を考えてみましょう。

```
---
resourceType: 'AWS::EC2::SecurityGroup'
configuration:
  ipPermissions:
    - fromPort: 172
      ipProtocol: tcp
      toPort: 172
      ipv4Ranges:
        - cidrIp: 10.0.0.0/24
        - cidrIp: 0.0.0.0/0
    - fromPort: 89
      ipProtocol: tcp
      ipv6Ranges:
        - cidrIpv6: '::/0'
      toPort: 189
      userIdGroupPairs: []
      ipv4Ranges:
        - cidrIp: 1.1.1.1/32
    - fromPort: 89
      ipProtocol: '-1'
      toPort: 189
      userIdGroupPairs: []
      ipv4Ranges:
        - cidrIp: 1.1.1.1/32
  ipPermissionsEgress:
    - ipProtocol: '-1'
      ipv6Ranges: []
      prefixListIds: []
      userIdGroupPairs: []
      ipv4Ranges:
        - cidrIp: 0.0.0.0/0
      ipRanges:
        - 0.0.0.0/0
  tags:
    - key: Name
      value: good-sg-delete-me
  vpcId: vpc-0123abcd
InputParameter:
  TcpBlockedPorts:
    - 3389
    - 20
    - 21
    - 110
    - 143
```

次の点に注意してください。
+ `ipPermissions` (進入ルール) は、設定ブロック内のルールのコレクションです。
+ 各ルール構造には、CIDR ブロックのコレクションを指定する `ipv6Ranges` `ipv4Ranges`や などの属性が含まれています。

任意の IP アドレスからの接続を許可する進入ルールを選択し、ルールで TCP ブロックされたポートを公開できないことを確認するルールを記述しましょう。

次の例に示すように、IPv4 を対象とするクエリ部分から始めます。

```
configuration.ipPermissions[
    #
    # at least one ipv4Ranges equals ANY IPv4
    #
    some ipv4Ranges[*].cidrIp == '0.0.0.0/0'
]
```

`some` キーワードはこのコンテキストで便利です。すべてのクエリは、クエリに一致する値のコレクションを返します。デフォルトでは、Guard は、クエリの結果として返されたすべての値がチェックと照合されると評価します。ただし、この動作が常にチェックに必要な動作であるとは限りません。設定項目からの入力の次の部分を考慮してください。

```
ipv4Ranges: 
  - cidrIp: 10.0.0.0/24
  - cidrIp: 0.0.0.0/0 # any IP allowed
```

には 2 つの値があります`ipv4Ranges`。すべての`ipv4Ranges`値が で表される IP アドレスと等しいわけではありません`0.0.0.0/0`。少なくとも 1 つの値が と一致するかどうかを確認します`0.0.0.0/0`。クエリから返されたすべての結果が一致する必要はありませんが、少なくとも 1 つの結果が一致する必要があることを Guard に伝えます。`some` キーワードは、結果のクエリの 1 つ以上の値がチェックと一致するように Guard に指示します。一致するクエリ結果値がない場合、Guard はエラーをスローします。

次に、次の例に示すように IPv6 を追加します。

```
configuration.ipPermissions[
    #
    # at-least-one ipv4Ranges equals ANY IPv4
    #
    some ipv4Ranges[*].cidrIp == '0.0.0.0/0' or
    #
    # at-least-one ipv6Ranges contains ANY IPv6
    #    
    some ipv6Ranges[*].cidrIpv6 == '::/0'
]
```

最後に、次の例では、プロトコルが ではないことを確認します`udp`。

```
configuration.ipPermissions[
    #
    # at-least-one ipv4Ranges equals ANY IPv4
    #
    some ipv4Ranges[*].cidrIp == '0.0.0.0/0' or
    #
    # at-least-one ipv6Ranges contains ANY IPv6
    #    
    some ipv6Ranges[*].cidrIpv6 == '::/0'
    
    #
    # and ipProtocol is not udp
    #
    ipProtocol != 'udp' ] 
]
```

完全なルールは次のとおりです。

```
rule any_ip_ingress_checks
{

    let ports = InputParameter.TcpBlockedPorts[*]

    let targets = configuration.ipPermissions[
        #
        # if either ipv4 or ipv6 that allows access from any address
        #
        some ipv4Ranges[*].cidrIp == '0.0.0.0/0' or
        some ipv6Ranges[*].cidrIpv6 == '::/0'

        #
        # the ipProtocol is not UDP
        #
        ipProtocol != 'udp' ]
        
    when %targets !empty
    {
        %targets {
            ipProtocol != '-1'
            <<
              result: NON_COMPLIANT
              check_id: HUB_ID_2334
              message: Any IP Protocol is allowed
            >>

            when fromPort exists 
                 toPort exists 
            {
                let each_target = this
                %ports {
                    this < %each_target.fromPort or
                    this > %each_target.toPort
                    <<
                        result: NON_COMPLIANT
                        check_id: HUB_ID_2340
                        message: Blocked TCP port was allowed in range
                    >>
                }
            }

        }       
     }
}
```

### 含まれるタイプに基づいてコレクションを分離する
<a name="splitting-collection"></a>

Infrastructure as Code (IaC) 設定テンプレートを使用する場合、設定テンプレート内の他のエンティティへの参照を含むコレクションが表示されることがあります。以下は、 へのローカル参照、 への参照`TaskRoleArn`、`TaskArn`および直接文字列参照を使用して Amazon Elastic Container Service (Amazon ECS) タスクを記述する CloudFormation テンプレートの例です。

```
Parameters:
  TaskArn:
    Type: String
Resources:
  ecsTask:
    Type: 'AWS::ECS::TaskDefinition'
    Metadata:
      SharedExectionRole: allowed
    Properties:
      TaskRoleArn: 'arn:aws:....'
      ExecutionRoleArn: 'arn:aws:...'
  ecsTask2:
    Type: 'AWS::ECS::TaskDefinition'
    Metadata:
      SharedExectionRole: allowed
    Properties:
      TaskRoleArn:
        'Fn::GetAtt':
          - iamRole
          - Arn
      ExecutionRoleArn: 'arn:aws:...2'
  ecsTask3:
    Type: 'AWS::ECS::TaskDefinition'
    Metadata:
      SharedExectionRole: allowed
    Properties:
      TaskRoleArn:
        Ref: TaskArn
      ExecutionRoleArn: 'arn:aws:...2'
  iamRole:
    Type: 'AWS::IAM::Role'
    Properties:
      PermissionsBoundary: 'arn:aws:...3'
```

次のクエリについて考えます。

```
let ecs_tasks = Resources.*[ Type == 'AWS::ECS::TaskDefinition' ]
```

このクエリは、サンプルテンプレートに表示される 3 つの`AWS::ECS::TaskDefinition`リソースすべてを含む値のコレクションを返します。次の例に示すように、`TaskRoleArn`ローカル参照`ecs_tasks`を含む を他の から分離します。

```
let ecs_tasks = Resources.*[ Type == 'AWS::ECS::TaskDefinition' ]

let ecs_tasks_role_direct_strings = %ecs_tasks[ 
    Properties.TaskRoleArn is_string ]

let ecs_tasks_param_reference = %ecs_tasks[
    Properties.TaskRoleArn.'Ref' exists ]

rule task_role_from_parameter_or_string {
    %ecs_tasks_role_direct_strings !empty or
    %ecs_tasks_param_reference !empty
}

rule disallow_non_local_references {
    # Known issue for rule access: Custom message must start on the same line
    not task_role_from_parameter_or_string 
    <<
        result: NON_COMPLIANT
        message: Task roles are not local to stack definition
    >>
}
```

# ガードルールでの変数の割り当てと参照
<a name="variables"></a>

 AWS CloudFormation Guard ルールファイルに変数を割り当てて、Guard ルールで参照する情報を保存できます。Guard はワンショット変数割り当てをサポートしています。変数は遅延的に評価されます。つまり、Guard はルールの実行時にのみ変数を評価します。

**Topics**
+ [変数の割り当て](#assigning-variables)
+ [変数の参照](#referencing-variables)
+ [変数のスコープ](#variable-scope)
+ [ガードルールファイルの変数の例](#variables-examples)

## 変数の割り当て
<a name="assigning-variables"></a>

`let` キーワードを使用して変数を初期化して割り当てます。ベストプラクティスとして、変数名にはスネークケースを使用します。変数は、クエリの結果として生じる静的リテラルまたは動的プロパティを保存できます。次の例では、 変数は静的文字列値 `ecs_task_definition_task_role_arn`を保存します`arn:aws:iam:123456789012:role/my-role-name`。

```
let ecs_task_definition_task_role_arn = 'arn:aws:iam::123456789012:role/my-role-name'
```

次の例では、 変数は、 テンプレート内のすべての`AWS::ECS::TaskDefinition` CloudFormation リソースを検索するクエリの結果`ecs_tasks`を保存します。ルールを記述するときに、 `ecs_tasks`を参照してこれらのリソースに関する情報にアクセスできます。

```
let ecs_tasks = Resources.*[
    Type == 'AWS::ECS::TaskDefinition'
]
```

## 変数の参照
<a name="referencing-variables"></a>

`%` プレフィックスを使用して変数を参照します。

の`ecs_task_definition_task_role_arn`変数の例に基づいて[変数の割り当て](#assigning-variables)、Guard ルール句の `query|value literal`セクション`ecs_task_definition_task_role_arn`で を参照できます。このリファレンスを使用すると、CloudFormation テンプレート内の`AWS::ECS::TaskDefinition`リソースの `TaskDefinitionArn`プロパティに指定された値が静的文字列値 になります`arn:aws:iam:123456789012:role/my-role-name`。

```
Resources.*.Properties.TaskDefinitionArn == %ecs_task_definition_role_arn
```

の`ecs_tasks`変数の例に基づいて[変数の割り当て](#assigning-variables)、クエリ`ecs_tasks`で を参照できます (%ecs\$1tasks.Properties など）。まず、Guard は変数を評価し`ecs_tasks`、返された値を使用して階層を横断します。変数が文字列以外の値に`ecs_tasks`解決された場合、Guard はエラーをスローします。

**注記**  
現在、Guard はカスタムエラーメッセージ内の変数の参照をサポートしていません。

## 変数のスコープ
<a name="variable-scope"></a>

スコープとは、ルールファイルで定義された変数の可視性を指します。変数名は、スコープ内で 1 回のみ使用できます。変数を宣言できるレベルは 3 つ、または可能な変数スコープは 3 つあります。
+ **ファイルレベル** – 通常、ルールファイルの上部で宣言され、ルールファイル内のすべてのルールでファイルレベルの変数を使用できます。ファイル全体に表示されます。

  次のルールファイルの例では、変数 `ecs_task_definition_task_role_arn`と `ecs_task_definition_execution_role_arn`はファイルレベルで初期化されます。

  ```
  let ecs_task_definition_task_role_arn = 'arn:aws:iam::123456789012:role/my-task-role-name'
  let ecs_task_definition_execution_role_arn = 'arn:aws:iam::123456789012:role/my-execution-role-name'
  
  rule check_ecs_task_definition_task_role_arn
  {
      Resources.*.Properties.TaskRoleArn == %ecs_task_definition_task_role_arn
  }
  
  rule check_ecs_task_definition_execution_role_arn
  {
      Resources.*.Properties.ExecutionRoleArn == %ecs_task_definition_execution_role_arn
  }
  ```
+ **ルールレベル** – ルール内で宣言されたルールレベルの変数は、その特定のルールにのみ表示されます。ルール外の参照はエラーになります。

  次のルールファイルの例では、変数 `ecs_task_definition_task_role_arn`と `ecs_task_definition_execution_role_arn`はルールレベルで初期化されます。は、`check_ecs_task_definition_task_role_arn`名前付きルール内でのみ参照`ecs_task_definition_task_role_arn`できます。`ecs_task_definition_execution_role_arn` 変数は、`check_ecs_task_definition_execution_role_arn`名前付きルール内でのみ参照できます。

  ```
  rule check_ecs_task_definition_task_role_arn
  {
      let ecs_task_definition_task_role_arn = 'arn:aws:iam::123456789012:role/my-task-role-name'
      Resources.*.Properties.TaskRoleArn == %ecs_task_definition_task_role_arn
  }
  
  rule check_ecs_task_definition_execution_role_arn
  {
      let ecs_task_definition_execution_role_arn = 'arn:aws:iam::123456789012:role/my-execution-role-name'
      Resources.*.Properties.ExecutionRoleArn == %ecs_task_definition_execution_role_arn
  }
  ```
+ **ブロックレベル** – `when`句などのブロック内で宣言されたブロックレベルの変数は、その特定のブロックにのみ表示されます。ブロック外の参照はエラーになります。

  次のルールファイルの例では、変数 `ecs_task_definition_task_role_arn`と `ecs_task_definition_execution_role_arn`は`AWS::ECS::TaskDefinition`タイプブロック内のブロックレベルで初期化されます。`ecs_task_definition_task_role_arn` および `ecs_task_definition_execution_role_arn`変数は、それぞれのルールの`AWS::ECS::TaskDefinition`型ブロック内でのみ参照できます。

  ```
  rule check_ecs_task_definition_task_role_arn
  {
      AWS::ECS::TaskDefinition
      {
          let ecs_task_definition_task_role_arn = 'arn:aws:iam::123456789012:role/my-task-role-name'
          Properties.TaskRoleArn == %ecs_task_definition_task_role_arn
      }
  }
  
  rule check_ecs_task_definition_execution_role_arn
  {
      AWS::ECS::TaskDefinition
      {
          let ecs_task_definition_execution_role_arn = 'arn:aws:iam::123456789012:role/my-execution-role-name'
          Properties.ExecutionRoleArn == %ecs_task_definition_execution_role_arn
      }
  }
  ```

## ガードルールファイルの変数の例
<a name="variables-examples"></a>

以下のセクションでは、変数の静的割り当てと動的割り当ての両方の例を示します。

### 静的割り当て
<a name="assigning-static-variables"></a>

CloudFormation テンプレートの例を次に示します。

```
Resources:
  EcsTask:
    Type: 'AWS::ECS::TaskDefinition'
    Properties:
      TaskRoleArn: 'arn:aws:iam::123456789012:role/my-role-name'
```

このテンプレートに基づいて、すべてのテンプレート`AWS::ECS::TaskDefinition`リソースの `TaskRoleArn`プロパティ`check_ecs_task_definition_task_role_arn`が であることを保証する というルールを記述できます`arn:aws:iam::123456789012:role/my-role-name`。

```
rule check_ecs_task_definition_task_role_arn
{
    let ecs_task_definition_task_role_arn = 'arn:aws:iam::123456789012:role/my-role-name'
    Resources.*.Properties.TaskRoleArn == %ecs_task_definition_task_role_arn
}
```

ルールの範囲内で、 という変数を初期化`ecs_task_definition_task_role_arn`し、静的文字列値 を割り当てることができます`'arn:aws:iam::123456789012:role/my-role-name'`。ルール句は、 `query|value literal`セクションの `ecs_task_definition_task_role_arn`変数を参照`arn:aws:iam::123456789012:role/my-role-name`することで、`EcsTask`リソースの `TaskRoleArn`プロパティに指定された値が であるかどうかをチェックします。

### 動的割り当て
<a name="example-dynamic-assignment"></a>

CloudFormation テンプレートの例を次に示します。

```
Resources:
  EcsTask:
    Type: 'AWS::ECS::TaskDefinition'
    Properties:
      TaskRoleArn: 'arn:aws:iam::123456789012:role/my-role-name'
```

このテンプレートに基づいて、 ファイル`ecs_tasks`の範囲内で という変数を初期化し、クエリ を割り当てることができます`Resources.*[ Type == 'AWS::ECS::TaskDefinition'`。Guard は入力テンプレート内のすべてのリソースをクエリし、それらの情報を に保存します`ecs_tasks`。すべての`AWS::ECS::TaskDefinition`テンプレートリソースの `TaskRoleArn`プロパティ`check_ecs_task_definition_task_role_arn`が `arn:aws:iam::123456789012:role/my-role-name`

```
let ecs_tasks = Resources.*[
    Type == 'AWS::ECS::TaskDefinition'
]

rule check_ecs_task_definition_task_role_arn
{
    %ecs_tasks.Properties.TaskRoleArn == 'arn:aws:iam::123456789012:role/my-role-name'
}
```

ルール句は、 `query`セクションの `ecs_task_definition_task_role_arn`変数を参照`arn:aws:iam::123456789012:role/my-role-name`することで、`EcsTask`リソースの `TaskRoleArn`プロパティに指定された値が であるかどうかをチェックします。

### CloudFormation テンプレート設定の適用
<a name="example-3"></a>

本番稼働用ユースケースのより複雑な例を見てみましょう。この例では、Amazon ECS タスクの定義方法をより厳密に制御するための Guard ルールを記述します。

CloudFormation テンプレートの例を次に示します。

```
Resources:
  EcsTask:
    Type: 'AWS::ECS::TaskDefinition'
    Properties:
      TaskRoleArn: 
        'Fn::GetAtt': [TaskIamRole, Arn]
      ExecutionRoleArn:
        'Fn::GetAtt': [ExecutionIamRole, Arn]

  TaskIamRole:
    Type: 'AWS::IAM::Role'
    Properties:
      PermissionsBoundary: 'arn:aws:iam::123456789012:policy/MyExamplePolicy'

  ExecutionIamRole:
    Type: 'AWS::IAM::Role'
    Properties:
      PermissionsBoundary: 'arn:aws:iam::123456789012:policy/MyExamplePolicy'
```

このテンプレートに基づいて、これらの要件が満たされるように次のルールを記述します。
+ テンプレート内の各`AWS::ECS::TaskDefinition`リソースには、タスクロールと実行ロールの両方がアタッチされています。
+ タスクロールと実行ロールは AWS Identity and Access Management (IAM) ロールです。
+ ロールは テンプレートで定義されます。
+ `PermissionsBoundary` プロパティはロールごとに指定されます。

```
# Select all Amazon ECS task definition resources from the template
let ecs_tasks = Resources.*[
    Type == 'AWS::ECS::TaskDefinition'
]

# Select a subset of task definitions whose specified value for the TaskRoleArn property is an Fn::Gett-retrievable attribute
let task_role_refs = some %ecs_tasks.Properties.TaskRoleArn.'Fn::GetAtt'[0]

# Select a subset of TaskDefinitions whose specified value for the ExecutionRoleArn property is an Fn::Gett-retrievable attribute
let execution_role_refs = some %ecs_tasks.Properties.ExecutionRoleArn.'Fn::GetAtt'[0]

# Verify requirement #1
rule all_ecs_tasks_must_have_task_end_execution_roles 
    when %ecs_tasks !empty 
{
    %ecs_tasks.Properties {
        TaskRoleArn exists
        ExecutionRoleArn exists
    }
}

# Verify requirements #2 and #3
rule all_roles_are_local_and_type_IAM
    when all_ecs_tasks_must_have_task_end_execution_roles
{
    let task_iam_references = Resources.%task_role_refs
    let execution_iam_reference = Resources.%execution_role_refs

    when %task_iam_references !empty {
        %task_iam_references.Type == 'AWS::IAM::Role'
    }

    when %execution_iam_reference !empty {
        %execution_iam_reference.Type == 'AWS::IAM::Role'
    }
}

# Verify requirement #4
rule check_role_have_permissions_boundary
    when all_ecs_tasks_must_have_task_end_execution_roles
{
    let task_iam_references = Resources.%task_role_refs
    let execution_iam_reference = Resources.%execution_role_refs

    when %task_iam_references !empty {
        %task_iam_references.Properties.PermissionsBoundary exists
    }

    when %execution_iam_reference !empty {
        %execution_iam_reference.Properties.PermissionsBoundary exists
    }
}
```

# で名前付きルールブロックを作成する AWS CloudFormation Guard
<a name="named-rule-block-composition"></a>

を使用して名前付きルールブロックを記述する場合 AWS CloudFormation Guard、次の 2 つの構成スタイルを使用できます。
+ 条件依存関係
+ 相関依存関係

これらの依存関係構成のいずれかのスタイルを使用すると、再利用性が向上し、名前付きルールブロックの冗長性と繰り返しが軽減されます。

**Topics**
+ [前提条件](#named-rules-prerequisites)
+ [条件依存関係の構成](#named-rules-conditional-dependency)
+ [相関依存関係の構成](#named-rules-correlational-dependency)

## 前提条件
<a name="named-rules-prerequisites"></a>

ルール[の記述で名前付きルール](writing-rules.md#named-rule-blocks)ブロックについて説明します。

## 条件依存関係の構成
<a name="named-rules-conditional-dependency"></a>

この構成スタイルでは、`when`ブロックまたは名前付きルールブロックの評価は、1 つ以上の他の名前付きルールブロックまたは句の評価結果に条件付き依存します。次の Guard ルールファイルの例には、条件依存関係を示す名前付きルールブロックが含まれています。

```
# Named-rule block, rule_name_A
rule rule_name_A {
    Guard_rule_1
    Guard_rule_2
    ...
}

# Example-1, Named-rule block, rule_name_B, takes a conditional dependency on rule_name_A
rule rule_name_B when rule_name_A {
    Guard_rule_3
    Guard_rule_4
    ...
}

# Example-2, when block takes a conditional dependency on rule_name_A
when rule_name_A {
    Guard_rule_3
    Guard_rule_4
    ...
}

# Example-3, Named-rule block, rule_name_C, takes a conditional dependency on rule_name_A ^ rule_name_B
rule rule_name_C when rule_name_A
                      rule_name_B {
    Guard_rule_3
    Guard_rule_4
    ...
}

# Example-4, Named-rule block, rule_name_D, takes a conditional dependency on (rule_name_A v clause_A) ^ clause_B ^ rule_name_B
rule rule_name_D when rule_name_A OR
                      clause_A
                      clause_B
                      rule_name_B {
    Guard_rule_3
    Guard_rule_4
    ...
}
```

前述のルールファイルの例では、 `Example-1`には次のような結果があります。
+ が に`rule_name_A`評価されると`PASS`、 によってカプセル化された Guard ルール`rule_name_B`が評価されます。
+ が に`rule_name_A`評価される場合`FAIL`、 によってカプセル化された Guard ルール`rule_name_B`は評価されません。 は に`rule_name_B`評価されます`SKIP`。
+ が に`rule_name_A`評価される場合`SKIP`、 によってカプセル化された Guard ルール`rule_name_B`は評価されません。 は に`rule_name_B`評価されます`SKIP`。
**注記**  
このケースは、 が と評価`FAIL`され、 が と`rule_name_A`評価されるルールに`rule_name_A`条件付きで依存している場合に発生します`SKIP`。

以下は、イングレスおよびエグレスセキュリティグループ情報の項目からの設定管理データベース (CMDB) 設定 AWS Config 項目の例です。この例では、条件依存関係の構成を示します。

```
rule check_resource_type_and_parameter {
    resourceType == /AWS::EC2::SecurityGroup/
    InputParameters.TcpBlockedPorts NOT EMPTY 
}

rule check_parameter_validity when check_resource_type_and_parameter {
    InputParameters.TcpBlockedPorts[*] {
        this in r[0,65535] 
    }
}

rule check_ip_procotol_and_port_range_validity when check_parameter_validity {
    let ports = InputParameters.TcpBlockedPorts[*]

    # 
    # select all ipPermission instances that can be reached by ANY IP address
    # IPv4 or IPv6 and not UDP
    #
    let configuration = configuration.ipPermissions[ 
        some ipv4Ranges[*].cidrIp == "0.0.0.0/0" or
        some ipv6Ranges[*].cidrIpv6 == "::/0"
        ipProtocol != 'udp' ] 
    when %configuration !empty {
        %configuration {
            ipProtocol != '-1'

            when fromPort exists 
                toPort exists {
                let ip_perm_block = this
                %ports {
                    this < %ip_perm_block.fromPort or
                    this > %ip_perm_block.toPort
                }
            }
        }
    }
}
```

前述の例では、 `check_parameter_validity`は条件付きで に依存し`check_resource_type_and_parameter`、 `check_ip_procotol_and_port_range_validity`は条件付きで に依存します`check_parameter_validity`。以下は、前述のルールに準拠した設定管理データベース (CMDB) 設定項目です。

```
---
version: '1.3'
resourceType: 'AWS::EC2::SecurityGroup'
resourceId: sg-12345678abcdefghi
configuration:
  description: Delete-me-after-testing
  groupName: good-sg-test-delete-me
  ipPermissions:
    - fromPort: 172
      ipProtocol: tcp
      ipv6Ranges: []
      prefixListIds: []
      toPort: 172
      userIdGroupPairs: []
      ipv4Ranges:
        - cidrIp: 0.0.0.0/0
      ipRanges:
        - 0.0.0.0/0
    - fromPort: 89
      ipProtocol: tcp
      ipv6Ranges:
        - cidrIpv6: '::/0'
      prefixListIds: []
      toPort: 89
      userIdGroupPairs: []
      ipv4Ranges:
        - cidrIp: 0.0.0.0/0
      ipRanges:
        - 0.0.0.0/0
  ipPermissionsEgress:
    - ipProtocol: '-1'
      ipv6Ranges: []
      prefixListIds: []
      userIdGroupPairs: []
      ipv4Ranges:
        - cidrIp: 0.0.0.0/0
      ipRanges:
        - 0.0.0.0/0
  tags:
    - key: Name
      value: good-sg-delete-me
  vpcId: vpc-0123abcd
InputParameters:
  TcpBlockedPorts:
    - 3389
    - 20
    - 110
    - 142
    - 1434
    - 5500
supplementaryConfiguration: {}
resourceTransitionStatus: None
```

## 相関依存関係の構成
<a name="named-rules-correlational-dependency"></a>

この構成スタイルでは、`when`ブロックまたは名前付きルールブロックの評価は、1 つ以上の他の Guard ルールの評価結果に相関依存します。相関依存関係は次のように達成できます。

```
# Named-rule block, rule_name_A, takes a correlational dependency on all of the Guard rules encapsulated by the named-rule block
rule rule_name_A {
    Guard_rule_1
    Guard_rule_2
    ...
}

# when block takes a correlational dependency on all of the Guard rules encapsulated by the when block
when condition {
    Guard_rule_1
    Guard_rule_2
    ...
}
```

相関依存関係の構成を理解するには、次の Guard ルールファイルの例を確認してください。

```
#
# Allowed valid protocols for AWS::ElasticLoadBalancingV2::Listener resources
#
let allowed_protocols = [ "HTTPS", "TLS" ]

let elbs = Resources.*[ Type == 'AWS::ElasticLoadBalancingV2::Listener' ]

#
# If there are AWS::ElasticLoadBalancingV2::Listener resources present, ensure that they have protocols specified from the 
# list of allowed protocols and that the Certificates property is not empty
#
rule ensure_all_elbs_are_secure when %elbs !empty {
    %elbs.Properties {
        Protocol in %allowed_protocols
        Certificates !empty
    }
}

# 
# In addition to secure settings, ensure that AWS::ElasticLoadBalancingV2::Listener resources are private
#
rule ensure_elbs_are_internal_and_secure when %elbs !empty {
    ensure_all_elbs_are_secure
    %elbs.Properties.Scheme == 'internal'
}
```

前述のルールファイルでは、 `ensure_elbs_are_internal_and_secure`には との相関依存関係があります`ensure_all_elbs_are_secure`。以下は、前述のルールに準拠する CloudFormation テンプレートの例です。

```
Resources:
  ServiceLBPublicListener46709EAA:
    Type: 'AWS::ElasticLoadBalancingV2::Listener'
    Properties:
      Scheme: internal
      Protocol: HTTPS
      Certificates:
        - CertificateArn: 'arn:aws:acm...'
  ServiceLBPublicListener4670GGG:
    Type: 'AWS::ElasticLoadBalancingV2::Listener'
    Properties:
      Scheme: internal
      Protocol: HTTPS
      Certificates:
        - CertificateArn: 'arn:aws:acm...'
```

# コンテキスト対応評価を実行するための句の記述
<a name="context-aware-evaluations"></a>

AWS CloudFormation Guard 句は階層データに対して評価されます。ガード評価エンジンは、シンプルな点線表記を使用して、指定された階層データに従って受信データに対するクエリを解決します。多くの場合、データのマップまたはコレクションに対して評価するには、複数の句が必要です。Guard は、このような句を記述するのに便利な構文を提供します。エンジンはコンテキストを認識し、評価に関連する対応するデータを使用します。

以下は、コンテキスト対応評価を適用できるコンテナを使用した Kubernetes Pod 設定の例です。

```
apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
    - name: app
      image: 'images.my-company.example/app:v4'
      resources:
        requests:
          memory: 64Mi
          cpu: 0.25
        limits:
          memory: 128Mi
          cpu: 0.5
    - name: log-aggregator
      image: 'images.my-company.example/log-aggregator:v6'
      resources:
        requests:
          memory: 64Mi
          cpu: 0.25
        limits:
          memory: 128Mi
          cpu: 0.75
```

Guard 句を作成して、このデータを評価できます。ルールファイルを評価する場合、コンテキストは入力ドキュメント全体です。以下は、Pod で指定されたコンテナの制限の適用を検証する句の例です。

```
#
# At this level, the root document is available for evaluation
#

#
# Our rule only evaluates for apiVersion == v1 and K8s kind is Pod
#
rule ensure_container_limits_are_enforced
    when apiVersion == 'v1'
        kind == 'Pod' 
{
    spec.containers[*] {
        resources.limits {
            #
            # Ensure that cpu attribute is set
            #
            cpu exists
            <<
                Id: K8S_REC_18
                Description: CPU limit must be set for the container
            >> 

            #
            # Ensure that memory attribute is set
            #
            memory exists
            <<
                Id: K8S_REC_22
                Description: Memory limit must be set for the container
            >>
        }
    }
}
```

## 評価`context`における理解
<a name="context"></a>

ルールブロックレベルでは、受信コンテキストは完全なドキュメントです。`when` 条件の評価は、 属性`apiVersion`と `kind` 属性があるこの受信ルートコンテキストに対して行われます。前の例では、これらの条件は に評価されます`true`。

ここで、前の例`spec.containers[*]`に示す の階層をトラバースします。階層のトラバースごとに、コンテキスト値はそれに応じて変わります。`spec` ブロックのトラバーサルが完了すると、次の例に示すようにコンテキストが変更されます。

```
containers:
  - name: app
    image: 'images.my-company.example/app:v4'
    resources:
      requests:
        memory: 64Mi
        cpu: 0.25
      limits:
        memory: 128Mi
        cpu: 0.5
  - name: log-aggregator
    image: 'images.my-company.example/log-aggregator:v6'
    resources:
      requests:
        memory: 64Mi
        cpu: 0.25
      limits:
        memory: 128Mi
        cpu: 0.75
```

`containers` 属性をトラバースした後、コンテキストを次の例に示します。

```
- name: app
  image: 'images.my-company.example/app:v4'
  resources:
    requests:
      memory: 64Mi
      cpu: 0.25
    limits:
      memory: 128Mi
      cpu: 0.5
- name: log-aggregator
  image: 'images.my-company.example/log-aggregator:v6'
  resources:
    requests:
      memory: 64Mi
      cpu: 0.25
    limits:
      memory: 128Mi
      cpu: 0.75
```

## ループについて
<a name="loops"></a>

式を使用して`[*]`、 `containers` 属性の配列に含まれるすべての値のループを定義できます。ブロックは、 内の各要素について評価されます`containers`。前述のルールスニペットの例では、 ブロックに含まれる句は、コンテナ定義に対して検証されるチェックを定義します。内部に含まれる句のブロックは、コンテナ定義ごとに 1 回、2 回評価されます。

```
{
    spec.containers[*] {
       ...
    }
}
```

反復ごとに、コンテキスト値は対応するインデックスの値です。

**注記**  
サポートされているインデックスアクセス形式は、 `[<integer>]`または のみです`[*]`。現在、Guard は のような範囲をサポートしていません`[2..4]`。

## 配列
<a name="arrays"></a>

多くの場合、配列が受け入れられる場所では、単一の値も受け入れられます。たとえば、コンテナが 1 つしかない場合、配列を削除して次の入力を受け入れることができます。

```
apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
    name: app
    image: images.my-company.example/app:v4
    resources:
      requests:
        memory: "64Mi"
        cpu: 0.25
      limits:
        memory: "128Mi"
        cpu: 0.5
```

属性が配列を受け入れることができる場合は、ルールが配列形式を使用していることを確認します。前の例では、 `containers[*]`ではなく を使用します`containers`。Guard は、単一値フォームのみを検出したときにデータをトラバースするときに正しく評価します。

**注記**  
属性が配列を受け入れるときにルール句へのアクセスを表現するときは、常に配列形式を使用してください。単一の値を使用する場合でも、ガードは正しく評価されます。

## `spec.containers[*]` の代わりに フォームを使用する `spec.containers`
<a name="containers"></a>

ガードクエリは解決された値のコレクションを返します。フォーム を使用する場合`spec.containers`、クエリの解決された値には、クエリ内の要素ではなく`containers`、 によって参照される配列が含まれます。フォームを使用する場合は`spec.containers[*]`、含まれる個々の要素を参照します。配列に含まれる各要素を評価する場合は、必ず `[*]`フォームを使用してください。

## `this` を使用して現在のコンテキスト値を参照する
<a name="this"></a>

ガードルールを作成するときは、 を使用してコンテキスト値を参照できます`this`。多くの場合、 `this`はコンテキストの値にバインドされているため、暗黙的です。たとえば、、`this.apiVersion``this.kind`、 `this.spec`はルートまたはドキュメントにバインドされます。対照的に、 `this.resources`は `/spec/containers/0/`や など`containers`、 の各値にバインドされます`/spec/containers/1`。同様に、 `this.cpu`と は制限、特に `/spec/containers/0/resources/limits`と に`this.memory`マッピングされます`/spec/containers/1/resources/limits`。

次の例では、Kubernetes Pod 設定の前述のルールが`this`明示的に を使用するように書き換えられています。

```
rule ensure_container_limits_are_enforced
    when this.apiVersion == 'v1'
         this.kind == 'Pod' 
{
    this.spec.containers[*] {
        this.resources.limits {
            #
            # Ensure that cpu attribute is set
            #
            this.cpu exists
            <<
                Id: K8S_REC_18
                Description: CPU limit must be set for the container
            >> 

            #
            # Ensure that memory attribute is set
            #
            this.memory exists
            <<
                Id: K8S_REC_22
                Description: Memory limit must be set for the container
            >>
        }
    }
}
```

を`this`明示的に使用する必要はありません。ただし、次の例に示すように、この`this`リファレンスはスカラーを使用する場合に便利です。

```
InputParameters.TcpBlockedPorts[*] {
    this in r[0, 65535) 
    <<
        result: NON_COMPLIANT
        message: TcpBlockedPort not in range (0, 65535)
    >>
}
```

前の例では、 `this` は各ポート番号を参照するために使用されます。

## 暗黙的な の使用による潜在的なエラー `this`
<a name="common-errors"></a>

ルールと句を作成する場合、暗黙的な`this`コンテキスト値から要素を参照するときによくある間違いがあります。たとえば、評価する次の入力データムを考えてみます (合格する必要があります）。

```
resourceType: 'AWS::EC2::SecurityGroup'
InputParameters:
  TcpBlockedPorts: [21, 22, 110]
configuration:
  ipPermissions:
  - fromPort: 172
    ipProtocol: tcp
    ipv6Ranges: []
    prefixListIds: []
    toPort: 172
    userIdGroupPairs: []
    ipv4Ranges:
      - cidrIp: "0.0.0.0/0"   
  - fromPort: 89
    ipProtocol: tcp
    ipv6Ranges:
      - cidrIpv6: "::/0"
    prefixListIds: []
    toPort: 109
    userIdGroupPairs: []
    ipv4Ranges:
      - cidrIp: 10.2.0.0/24
```

上記のテンプレートに対してテストすると、次のルールは暗黙的な を活用することを誤って想定するため、エラーが発生します`this`。

```
rule check_ip_procotol_and_port_range_validity
{
    # 
    # select all ipPermission instances that can be reached by ANY IP address
    # IPv4 or IPv6 and not UDP
    #
    let any_ip_permissions = configuration.ipPermissions[ 
        some ipv4Ranges[*].cidrIp == "0.0.0.0/0" or
        some ipv6Ranges[*].cidrIpv6 == "::/0"

        ipProtocol != 'udp' ]
    
    when %any_ip_permissions !empty
    {
        %any_ip_permissions {
            ipProtocol != '-1' # this here refers to each ipPermission instance
            InputParameters.TcpBlockedPorts[*] {
                fromPort > this or 
                toPort   < this 
                <<
                    result: NON_COMPLIANT
                    message: Blocked TCP port was allowed in range
                >>
            }                
        }
    }
}
```

この例を実行するには、前述のルールファイルを という名前で保存`any_ip_ingress_check.guard`し、データを というファイル名で保存します`ip_ingress.yaml`。次に、これらのファイルを使用して次の`validate`コマンドを実行します。

```
cfn-guard validate -r any_ip_ingress_check.guard -d ip_ingress.yaml --show-clause-failures
```

次の出力では、エンジンは値 `InputParameters.TcpBlockedPorts[*]`のプロパティを取得しようとする試みが`/configuration/ipPermissions/0``/configuration/ipPermissions/1`失敗したことを示します。

```
Clause #2     FAIL(Block[Location[file:any_ip_ingress_check.guard, line:17, column:13]])

              Attempting to retrieve array index or key from map at Path = /configuration/ipPermissions/0, Type was not an array/object map, Remaining Query = InputParameters.TcpBlockedPorts[*]

Clause #3     FAIL(Block[Location[file:any_ip_ingress_check.guard, line:17, column:13]])

              Attempting to retrieve array index or key from map at Path = /configuration/ipPermissions/1, Type was not an array/object map, Remaining Query = InputParameters.TcpBlockedPorts[*]
```

この結果を理解するには、`this`明示的に参照された を使用してルールを書き換えます。

```
rule check_ip_procotol_and_port_range_validity
{
    # 
    # select all ipPermission instances that can be reached by ANY IP address
    # IPv4 or IPv6 and not UDP
    #
    let any_ip_permissions = this.configuration.ipPermissions[ 
        some ipv4Ranges[*].cidrIp == "0.0.0.0/0" or
        some ipv6Ranges[*].cidrIpv6 == "::/0"

        ipProtocol != 'udp' ]
    
    when %any_ip_permissions !empty
    {
        %any_ip_permissions {
            this.ipProtocol != '-1' # this here refers to each ipPermission instance
            this.InputParameters.TcpBlockedPorts[*] {
                this.fromPort > this or 
                this.toPort   < this 
                <<
                    result: NON_COMPLIANT
                    message: Blocked TCP port was allowed in range
                >>
            }                
        }
    }
}
```

`this.InputParameters` は、変数 に含まれる各値を参照します`any_ip_permissions`。変数に割り当てられたクエリは、一致する`configuration.ipPermissions`値を選択します。エラーは、このコンテキスト`InputParamaters`で を取得しようと`InputParameters`したが、ルートコンテキストにあったことを示します。

内部ブロックは、次の例に示すように、範囲外の変数も参照します。

```
{
    this.ipProtocol != '-1' # this here refers to each ipPermission instance
    this.InputParameter.TcpBlockedPorts[*] { # ERROR referencing InputParameter off /configuration/ipPermissions[*]
        this.fromPort > this or # ERROR: implicit this refers to values inside /InputParameter/TcpBlockedPorts[*]
        this.toPort   < this 
        <<
            result: NON_COMPLIANT
            message: Blocked TCP port was allowed in range
        >>
    }
}
```

`this` は の各ポート値を参照しますが`[21, 22, 110]`、 `fromPort`および も参照します`toPort`。どちらも外部ブロックスコープに属します。

### の暗黙的な使用によるエラーの解決 `this`
<a name="common-errors-resolution"></a>

変数を使用して、値を明示的に割り当て、参照します。まず、 `InputParameter.TcpBlockedPorts`は入力 (ルート) コンテキストの一部です。次の例に示すように、内部ブロック`InputParameter.TcpBlockedPorts`の外に移動し、明示的に割り当てます。

```
rule check_ip_procotol_and_port_range_validity
{
     let ports = InputParameters.TcpBlockedPorts[*]
    # ... cut off for illustrating change
}
```

次に、この変数を明示的に参照します。

```
rule check_ip_procotol_and_port_range_validity
{
    #
    # Important: Assigning InputParameters.TcpBlockedPorts results in an ERROR. 
    # We need to extract each port inside the array. The difference is the query
    # InputParameters.TcpBlockedPorts returns [[21, 20, 110]] whereas the query 
    # InputParameters.TcpBlockedPorts[*] returns [21, 20, 110]. 
    #
    let ports = InputParameters.TcpBlockedPorts[*]

    # 
    # select all ipPermission instances that can be reached by ANY IP address
    # IPv4 or IPv6 and not UDP
    #
    let any_ip_permissions = configuration.ipPermissions[ 
        some ipv4Ranges[*].cidrIp == "0.0.0.0/0" or
        some ipv6Ranges[*].cidrIpv6 == "::/0"

        ipProtocol != 'udp' ]
    
    when %any_ip_permissions !empty
    {
        %any_ip_permissions {
            this.ipProtocol != '-1' # this here refers to each ipPermission instance
            %ports {
                this.fromPort > this or 
                this.toPort   < this 
                <<
                    result: NON_COMPLIANT
                    message: Blocked TCP port was allowed in range
                >>
            }
        }
    }        
}
```

内の内部`this`参照に対しても同じ操作を行います`%ports`。

ただし、内部のループの参照`ports`が正しくないため、すべてのエラーはまだ修正されていません。次の例は、誤った参照の削除を示しています。

```
rule check_ip_procotol_and_port_range_validity
{
    #
    # Important: Assigning InputParameters.TcpBlockedPorts results in an ERROR. 
    # We need to extract each port inside the array. The difference is the query
    # InputParameters.TcpBlockedPorts returns [[21, 20, 110]] whereas the query 
    # InputParameters.TcpBlockedPorts[*] returns [21, 20, 110].
    #
    let ports = InputParameters.TcpBlockedPorts[*]

    # 
    # select all ipPermission instances that can be reached by ANY IP address
    # IPv4 or IPv6 and not UDP
    #
    let any_ip_permissions = configuration.ipPermissions[
        #
        # if either ipv4 or ipv6 that allows access from any address
        #
        some ipv4Ranges[*].cidrIp == '0.0.0.0/0' or
        some ipv6Ranges[*].cidrIpv6 == '::/0'

        #
        # the ipProtocol is not UDP
        #
        ipProtocol != 'udp' ]
        
    when %any_ip_permissions !empty
    {
        %any_ip_permissions {
            ipProtocol != '-1'
            <<
              result: NON_COMPLIANT
              check_id: HUB_ID_2334
              message: Any IP Protocol is allowed
            >>

            when fromPort exists 
                 toPort exists 
            {
                let each_any_ip_perm = this
                %ports {
                    this < %each_any_ip_perm.fromPort or
                    this > %each_any_ip_perm.toPort
                    <<
                        result: NON_COMPLIANT
                        check_id: HUB_ID_2340
                        message: Blocked TCP port was allowed in range
                    >>
                }
            }
        }       
    }   
}
```

次に、 `validate` コマンドを再度実行します。今回は合格です。

```
cfn-guard validate -r any_ip_ingress_check.guard -d ip_ingress.yaml --show-clause-failures
```

以下は、 `validate` コマンドの出力です。

```
ip_ingress.yaml Status = PASS
PASS rules
check_ip_procotol_and_port_range_validity    PASS
```

このアプローチに障害がないかテストするために、次の例ではペイロードの変更を使用します。

```
resourceType: 'AWS::EC2::SecurityGroup'
InputParameters:
  TcpBlockedPorts: [21, 22, 90, 110]
configuration:
  ipPermissions:
    - fromPort: 172
      ipProtocol: tcp
      ipv6Ranges: []
      prefixListIds: []
      toPort: 172
      userIdGroupPairs: []
      ipv4Ranges:
        - cidrIp: "0.0.0.0/0"   
    - fromPort: 89
      ipProtocol: tcp
      ipv6Ranges:
        - cidrIpv6: "::/0"
      prefixListIds: []
      toPort: 109
      userIdGroupPairs: []
      ipv4Ranges:
        - cidrIp: 10.2.0.0/24
```

90 は、任意の IPv6 アドレスが許可されている 89～109 の範囲内です。コマンドを再度実行`validate`した後の出力を次に示します。

```
Clause #3           FAIL(Clause(Location[file:any_ip_ingress_check.guard, line:43, column:21], Check: _  LESS THAN %each_any_ip_perm.fromPort))
                    Comparing Int((Path("/InputParameters/TcpBlockedPorts/2"), 90)) with Int((Path("/configuration/ipPermissions/1/fromPort"), 89)) failed
                    (DEFAULT: NO_MESSAGE)
Clause #4           FAIL(Clause(Location[file:any_ip_ingress_check.guard, line:44, column:21], Check: _  GREATER THAN %each_any_ip_perm.toPort))
                    Comparing Int((Path("/InputParameters/TcpBlockedPorts/2"), 90)) with Int((Path("/configuration/ipPermissions/1/toPort"), 109)) failed

                                            result: NON_COMPLIANT
                                            check_id: HUB_ID_2340
                                            message: Blocked TCP port was allowed in range
```