

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

# AWS CloudFormation Guard Règles d'écriture
<a name="writing-rules"></a>

Dans AWS CloudFormation Guard, *les règles* sont les policy-as-code règles. Vous rédigez des règles dans le langage spécifique au domaine Guard (DSL) par rapport auxquelles vous pouvez valider vos données au format JSON ou YAML. Les règles sont composées de *clauses*.

Vous pouvez enregistrer les règles écrites à l'aide du Guard DSL dans des fichiers en texte brut qui utilisent n'importe quelle extension de fichier.

Vous pouvez créer plusieurs fichiers de règles et les classer en tant qu'*ensemble de règles*. Les ensembles de règles vous permettent de valider vos données au format JSON ou YAML par rapport à plusieurs fichiers de règles en même temps.

**Topics**
+ [Clauses](#clauses)
+ [Utilisation de requêtes dans les clauses](#clauses-queries)
+ [Utilisation d'opérateurs dans les clauses](#clauses-operators)
+ [Utilisation de messages personnalisés dans les clauses](#clauses-custom-messages)
+ [Combinaison de clauses](#combining-clauses)
+ [Utiliser des blocs avec les règles de garde](#blocks)
+ [Utilisation des fonctions intégrées](#built-in-functions)
+ [Définition des requêtes Guard et filtrage](query-and-filtering.md)
+ [Affectation et référencement de variables dans les règles Guard](variables.md)
+ [Composer des blocs de règles nommées dans AWS CloudFormation Guard](named-rule-block-composition.md)
+ [Rédaction de clauses pour effectuer des évaluations contextuelles](context-aware-evaluations.md)

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

Les clauses sont des expressions booléennes dont la valeur est vraie (`PASS`) ou fausse (`FAIL`). Les clauses utilisent soit des opérateurs binaires pour comparer deux valeurs, soit des opérateurs unaires qui agissent sur une seule valeur.

**Exemples de clauses unaires**

La clause unaire suivante évalue si la collection `TcpBlockedPorts` est vide.

```
InputParameters.TcpBlockedPorts not empty
```

La clause unaire suivante détermine si la `ExecutionRoleArn` propriété est une chaîne.

```
Properties.ExecutionRoleArn is_string
```

**Exemples de clauses binaires**

La clause binaire suivante évalue si la `BucketName` propriété contient la chaîne`encrypted`, quel que soit le casier.

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

La clause binaire suivante évalue si la `ReadCapacityUnits` propriété est inférieure ou égale à 5 000.

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

### Syntaxe pour écrire les clauses des règles Guard
<a name="clauses-syntax"></a>

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

### Propriétés des clauses de la règle Guard
<a name="clauses-properties"></a>

`query`  <a name="clauses-properties-query"></a>
Expression séparée par des points (`.`) écrite pour parcourir des données hiérarchiques. Les expressions de requête peuvent inclure des expressions de filtre pour cibler un sous-ensemble de valeurs. Les requêtes peuvent être attribuées à des variables afin que vous puissiez les écrire une seule fois et les référencer ailleurs dans un ensemble de règles, ce qui vous permettra d'accéder aux résultats des requêtes.  
Pour plus d'informations sur la rédaction de requêtes et le filtrage, consultez[Définition des requêtes et filtrage](query-and-filtering.md).  
 *Obligatoire* : oui

`operator`  <a name="clauses-properties-operator"></a>
Opérateur unaire ou binaire qui permet de vérifier l'état de la requête. Le côté gauche (LHS) d'un opérateur binaire doit être une requête, et le côté droit (RHS) doit être une requête ou une valeur littérale.  
 *Opérateurs binaires pris en charge* : `==` `!=` (Égal) \$1 `>` (Non égal) \$1 `>=` (Supérieur à) \$1 (Supérieur ou égal à) \$1 `<` (Inférieur à) \$1 `<=` (Inférieur ou égal à) \$1 `IN` (Dans une liste au format [x, y, z]  
 *Opérateurs unaires pris en charge* : `exists` \$1 `empty` \$1 `is_string` \$1 \$1 `is_list` \$1 `is_struct` `not(!)`  
 *Obligatoire* : oui

`query|value literal`  <a name="clauses-properties-value-literal"></a>
Une requête ou un littéral de valeur pris en charge tel que `string` ou`integer(64)`.   
*Litéraux de valeur pris* en charge :  
+ Tous les types primitifs : `string``integer(64)`,`float(64)`,`bool`,`char`, `regex`
+ Tous les types de plages spécialisés pour exprimer `integer(64)``float(64)`, ou les `char` plages exprimées comme suit :
  + `r[<lower_limit>, <upper_limit>]`, qui se traduit par toute valeur `k` répondant à l'expression suivante : `lower_limit <= k <= upper_limit`
  + `r[<lower_limit>, <upper_limit>`), qui se traduit par toute valeur `k` répondant à l'expression suivante : `lower_limit <= k < upper_limit`
  + `r(<lower_limit>, <upper_limit>]`, qui se traduit par toute valeur `k` répondant à l'expression suivante : `lower_limit < k <= upper_limit`
  + `r(<lower_limit>, <upper_limit>),`qui se traduit par toute valeur `k` répondant à l'expression suivante : `lower_limit < k < upper_limit`
+ Tableaux associatifs (cartes) pour les données de structure clé-valeur imbriquées. Par exemple :

  `{ "my-map": { "nested-maps": [ { "key": 10, "value": 20 } ] } }`
+ Tableaux de types primitifs ou de types de tableaux associatifs
 *Obligatoire* : conditionnel ; obligatoire lorsqu'un opérateur binaire est utilisé.

`custom message`  <a name="clauses-properties-custom-message"></a>
Chaîne fournissant des informations sur la clause. Le message s'affiche dans les sorties détaillées des `test` commandes `validate` et et peut être utile pour comprendre ou débugger l'évaluation des règles sur des données hiérarchiques.  
 *Obligatoire* : non

## Utilisation de requêtes dans les clauses
<a name="clauses-queries"></a>

Pour plus d'informations sur la rédaction de requêtes, reportez-vous [Définition des requêtes et filtrage](query-and-filtering.md) aux sections et[Affectation et référencement de variables dans les règles Guard](variables.md).

## Utilisation d'opérateurs dans les clauses
<a name="clauses-operators"></a>

Voici des exemples de CloudFormation modèles, `Template-1` et`Template-2`. Pour démontrer l'utilisation des opérateurs pris en charge, les exemples de requêtes et de clauses de cette section font référence à ces exemples de modèles.

**Modèle-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
```

**Modèle-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
```

### Exemples de clauses utilisant des opérateurs unaires
<a name="clauses-unary-operators"></a>
+ `empty`— Vérifie si une collection est vide. Vous pouvez également l'utiliser pour vérifier si une requête contient des valeurs dans des données hiérarchiques, car les requêtes aboutissent à une collection. Vous ne pouvez pas l'utiliser pour vérifier si une chaîne vide (`""`) est définie dans les requêtes contenant des valeurs de chaîne. Pour de plus amples informations, veuillez consulter [Définition des requêtes et filtrage](query-and-filtering.md).

  La clause suivante vérifie si une ou plusieurs ressources sont définies dans le modèle. Il est évalué comme `PASS` étant donné qu'une ressource avec l'ID logique `S3Bucket` est définie dans`Template-1`.

  ```
  Resources !empty
  ```

  La clause suivante vérifie si une ou plusieurs balises sont définies pour la `S3Bucket` ressource. Il est évalué à `PASS` parce `S3Bucket` que deux balises sont définies pour la `Tags` propriété dans`Template-1`.

  ```
  Resources.S3Bucket.Properties.Tags !empty
  ```
+ `exists`— Vérifie si chaque occurrence de la requête possède une valeur et peut être utilisée à la place de`!= null`.

  La clause suivante vérifie si la `BucketEncryption` propriété est définie pour`S3Bucket`. Il est évalué à `PASS` parce qu'il `BucketEncryption` est défini pour `S3Bucket` dans`Template-1`.

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

**Note**  
Les `not exists` contrôles `empty` et évaluent la présence `true` de clés de propriété manquantes lors de la traversée des données d'entrée. Par exemple, si la `Properties` section n'est pas définie dans le modèle pour le`S3Bucket`, la clause est `Resources.S3Bucket.Properties.Tag empty` évaluée à`true`. Les `empty` vérifications `exists` et n'affichent pas le chemin du pointeur JSON à l'intérieur du document dans les messages d'erreur. Ces deux clauses comportent souvent des erreurs de récupération qui ne tiennent pas compte de ces informations de traversée.
+ `is_string`— Vérifie si chaque occurrence de la requête est de `string` type.

  La clause suivante vérifie si une valeur de chaîne est spécifiée pour la `BucketName` propriété de la `S3Bucket` ressource. Il est évalué à `PASS` parce que la valeur de chaîne `"MyServiceS3Bucket"` est spécifiée `BucketName` dans`Template-1`.

  ```
  Resources.S3Bucket.Properties.BucketName is_string
  ```
+ `is_list`— Vérifie si chaque occurrence de la requête est de `list` type.

  La clause suivante vérifie si une liste est spécifiée pour la `Tags` propriété de la `S3Bucket` ressource. Il est évalué à `PASS` parce que deux paires clé-valeur sont spécifiées pour in. `Tags` `Template-1`

  ```
  Resources.S3Bucket.Properties.Tags is_list
  ```
+ `is_struct`— Vérifie si chaque occurrence de la requête est une donnée structurée.

  La clause suivante vérifie si des données structurées sont spécifiées pour la `BucketEncryption` propriété de la `S3Bucket` ressource. Il est évalué à `PASS` parce qu'il `BucketEncryption` est spécifié à l'aide du type de `ServerSideEncryptionConfiguration` propriété *(object)* in`Template-1`.

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

**Note**  
Pour vérifier l'état inverse, vous pouvez utiliser l'opérateur (` not !`) avec les `is_struct` opérateurs `is_string``is_list`, et.

### Exemples de clauses utilisant des opérateurs binaires
<a name="clauses-binary-operators"></a>

La clause suivante vérifie si la valeur spécifiée pour la `BucketName` propriété de la `S3Bucket` ressource `Template-1` contient la chaîne`encrypt`, quel que soit le casier. Cela se traduit par le `PASS` fait que le nom du compartiment spécifié `"MyServiceS3Bucket"` ne contient pas la chaîne`encrypt`.

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

La clause suivante vérifie si la valeur spécifiée pour la `Size` propriété de la `NewVolume` ressource `Template-2` se situe dans une plage spécifique : 50 <= `Size` <= 200. Il est évalué à `PASS` parce que `100` c'est spécifié pour`Size`.

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

La clause suivante vérifie si la valeur spécifiée pour la `VolumeType` propriété de la `NewVolume` ressource dans `Template-2` est `io1``io2`, ou`gp3`. Il est évalué à `PASS` parce que `io1` c'est spécifié pour`NewVolume`.

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

**Note**  
Les exemples de requêtes présentés dans cette section illustrent l'utilisation d'opérateurs utilisant les ressources avec un caractère logique IDs `S3Bucket` et`NewVolume`. Les noms de ressources sont souvent définis par l'utilisateur et peuvent être nommés arbitrairement dans un modèle d'infrastructure en tant que code (IaC). Pour écrire une règle générique qui s'applique à toutes les `AWS::S3::Bucket` ressources définies dans le modèle, la forme de requête la plus couramment utilisée est`Resources.*[ Type == ‘AWS::S3::Bucket’ ]`. Pour plus d'informations, consultez [Définition des requêtes et filtrage](query-and-filtering.md) pour plus de détails sur l'utilisation et explorez le répertoire [des exemples](https://github.com/aws-cloudformation/cloudformation-guard/tree/main/guard-examples) dans le `cloudformation-guard` GitHub référentiel.

## Utilisation de messages personnalisés dans les clauses
<a name="clauses-custom-messages"></a>

Dans l'exemple suivant, les clauses pour `Template-2` inclure un message personnalisé.

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

## Combinaison de clauses
<a name="combining-clauses"></a>

Dans Guard, chaque clause écrite sur une nouvelle ligne est combinée implicitement à la clause suivante en utilisant une conjonction (logique booléenne`and`). Consultez l'exemple suivant.

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

Vous pouvez également utiliser la disjonction pour combiner une clause avec la clause suivante en spécifiant `or|OR` à la fin de la première clause.

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

Dans une clause Guard, les disjonctions sont évaluées en premier, suivies des conjonctions. Les règles de protection peuvent être définies comme une conjonction de disjonctions de clauses (une `and|AND` ou `or|OR` plusieurs) dont l'évaluation correspond à `true` (`PASS`) ou `false` (`FAIL`). Ceci est similaire à la [forme normale conjonctive](https://en.wikipedia.org/wiki/Conjunctive_normal_form). 

Les exemples suivants illustrent l'ordre des évaluations des clauses.

```
# (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
```

Toutes les clauses basées sur cet exemple `Template-1` peuvent être combinées à l'aide d'une conjonction. Consultez l'exemple suivant.

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

## Utiliser des blocs avec les règles de garde
<a name="blocks"></a>

Les blocs sont des compositions qui éliminent la verbosité et la répétition d'un ensemble de clauses, de conditions ou de règles connexes. Il existe trois types de blocs :
+ Blocs de requêtes
+ `when`blocs
+ Blocs de règles nommés

### Blocs de requêtes
<a name="query-blocks"></a>

Les clauses suivantes sont basées sur cet exemple`Template-1`. La conjonction a été utilisée pour combiner les clauses.

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

Des parties de l'expression de requête de chaque clause sont répétées. Vous pouvez améliorer la composabilité et supprimer la verbosité et la répétition d'un ensemble de clauses associées ayant le même chemin de requête initial en utilisant un bloc de requête. Le même ensemble de clauses peut être écrit comme indiqué dans l'exemple suivant.

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

Dans un bloc de requête, la requête qui précède le bloc définit le contexte des clauses contenues dans le bloc.

Pour plus d'informations sur l'utilisation des blocs, consultez[Composer des blocs de règles nommées](named-rule-block-composition.md).

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

Vous pouvez évaluer les blocs de manière conditionnelle en utilisant des `when` blocs, qui se présentent sous la forme suivante.

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

Le `when` mot clé indique le début du `when` bloc. `condition`est une règle de la Garde. Le bloc n'est évalué que si l'évaluation de la condition aboutit à `true` (`PASS`).

Voici un exemple de `when` bloc basé sur`Template-1`.

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

La clause contenue dans le `when` bloc n'est évaluée que si la valeur spécifiée pour `BucketName` est une chaîne. Si la valeur spécifiée pour `BucketName` est référencée dans la `Parameters` section du modèle, comme indiqué dans l'exemple suivant, la clause contenue dans le `when` bloc n'est pas évaluée.

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

### Blocs de règles nommés
<a name="named-rule-blocks"></a>

Vous pouvez attribuer un nom à un ensemble de règles (*ensemble de règles*), puis référencer ces blocs de validation modulaires, appelés blocs de *règles nommées*, dans d'autres règles. Les blocs de règles nommées se présentent sous la forme suivante.

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

Le `rule` mot clé indique le début du bloc de règles nommées.

`rule name`est une chaîne lisible par l'homme qui identifie de manière unique un bloc de règles nommé. Il s'agit d'une étiquette pour l'ensemble de règles Guard qu'elle encapsule. Dans cette utilisation, le terme *règle de garde* inclut les clauses, les blocs de requêtes, les `when` blocs et les blocs de règles nommées. Le nom de la règle peut être utilisé pour faire référence au résultat de l'évaluation de l'ensemble de règles qu'il encapsule, ce qui rend les blocs de règles nommés réutilisables. Le nom de la règle fournit également un contexte sur les échecs des règles dans les sorties de `test` commande `validate` et. Le nom de la règle est affiché avec le statut d'évaluation du bloc (`PASS`,`FAIL`, ou`SKIP`) dans le résultat d'évaluation du fichier de règles. Consultez l'exemple suivant.

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

Vous pouvez également évaluer les blocs de règles nommées de manière conditionnelle en spécifiant le `when` mot clé suivi d'une condition après le nom de la règle.

Voici l'exemple de `when` bloc dont il a été question précédemment dans cette rubrique.

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

En utilisant des blocs de règles nommées, ce qui précède peut également être écrit comme suit.

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

Vous pouvez réutiliser et regrouper des blocs de règles nommés avec d'autres règles Guard. Voici quelques exemples.

```
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
    ...
}
```

## Utilisation des fonctions intégrées
<a name="built-in-functions"></a>

AWS CloudFormation Guard fournit des fonctions intégrées que vous pouvez utiliser dans vos règles pour effectuer des opérations telles que la manipulation de chaînes, l'analyse JSON et la conversion de types de données. Les fonctions ne sont prises en charge que par l'affectation à une variable.

### Principales fonctions
<a name="key-functions"></a>

`json_parse(json_string)`  
Analyse les chaînes JSON en ligne à partir d'un modèle. Après l'analyse, vous pouvez évaluer les propriétés de l'objet obtenu.

`count(collection)`  
Renvoie le nombre d'éléments auxquels une requête aboutit.

`regex_replace(base_string, regex_to_extract, regex_replacement)`  
Remplace des parties d'une chaîne à l'aide d'expressions régulières.

Pour une liste complète des fonctions disponibles, notamment la manipulation de chaînes, les opérations de collecte et les fonctions de conversion de type de données, consultez la [documentation des fonctions](https://github.com/aws-cloudformation/cloudformation-guard/blob/main/docs/FUNCTIONS.md) dans le GitHub référentiel Guard.

# Définition des requêtes Guard et filtrage
<a name="query-and-filtering"></a>

Cette rubrique traite de la rédaction de requêtes et de l'utilisation du filtrage lors de la rédaction de clauses de règles Guard.

## Conditions préalables
<a name="query-filtering-prerequisites"></a>

Le filtrage est un AWS CloudFormation Guard concept avancé. Nous vous recommandons de consulter les sujets fondamentaux suivants avant de vous familiariser avec le filtrage :
+ [Qu'est-ce que c'est AWS CloudFormation Guard ?](what-is-guard.md)
+ [Règles de rédaction, clauses](writing-rules.md)

## Définition des requêtes
<a name="defining-queries"></a>

Les expressions de requête sont de simples expressions séparées par des points (`.`) écrites pour parcourir des données hiérarchiques. Les expressions de requête peuvent inclure des expressions de filtre pour cibler un sous-ensemble de valeurs. Lorsque les requêtes sont évaluées, elles aboutissent à un ensemble de valeurs, similaire à un jeu de résultats renvoyé par une requête SQL.

L'exemple de requête suivant recherche des `AWS::IAM::Role` ressources dans un CloudFormation modèle.

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

Les requêtes suivent les principes de base suivants :
+ Chaque point (`.`) de la requête traverse la hiérarchie lorsqu'un terme clé explicite est utilisé, par exemple `Resources` ou `Properties.Encrypted.` si une partie de la requête ne correspond pas à la donnée entrante, Guard génère une erreur de récupération.
+ Un point (`.`) de la requête qui utilise un caractère générique `*` traverse toutes les valeurs de la structure à ce niveau.
+ Une partie point (`.`) de la requête qui utilise un caractère générique de tableau `[*]` parcourt tous les indices de ce tableau.
+ Toutes les collections peuvent être filtrées en spécifiant des filtres entre crochets`[]`. Les collections peuvent être rencontrées des manières suivantes :
  + Les tableaux présents naturellement dans le datum sont des collections. Voici quelques exemple de commandes  :

    Ports : `[20, 21, 110, 190]`

    Balises : `[{"Key": "Stage", "Value": "PROD"}, {"Key": "App", "Value": "MyService"}]`
  + Lorsque vous parcourez toutes les valeurs d'une structure telle que `Resources.*`
  + Tout résultat de requête est lui-même une collection à partir de laquelle les valeurs peuvent être filtrées davantage. Consultez l'exemple suivant.

    ```
    # 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
    }
    ```

Voici un exemple d'extrait CloudFormation de modèle.

```
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
    ...
```

Sur la base de ce modèle, le chemin parcouru est `SampleRole` et la valeur finale sélectionnée est`Type: AWS::IAM::Role`.

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

La valeur résultante de la requête `Resources.*[ Type == 'AWS::IAM::Role' ]` au format YAML est illustrée dans l'exemple suivant.

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

Voici certaines des manières dont vous pouvez utiliser les requêtes :
+ Affectez une requête à des variables afin que les résultats de la requête soient accessibles en référençant ces variables.
+ Suivez la requête avec un bloc qui teste chacune des valeurs sélectionnées.
+ Comparez une requête directement à une clause de base.

## Affectation de requêtes à des variables
<a name="queries-and-filtering-variables"></a>

Guard prend en charge les assignations de variables ponctuelles dans un périmètre donné. Pour plus d'informations sur les variables dans les règles Guard, consultez[Affectation et référencement de variables dans les règles Guard](variables.md).

Vous pouvez attribuer des requêtes à des variables afin de pouvoir écrire des requêtes une seule fois, puis les référencer ailleurs dans vos règles Guard. Consultez les exemples d'attribution de variables suivants qui illustrent les principes de requête décrits plus loin dans cette section.

```
#
# 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'
    }
]
```

## Parcours direct des valeurs d'une variable affectée à une requête
<a name="variable-assigned-from-query"></a>

Guard prend en charge l'exécution directe par rapport aux résultats d'une requête. Dans l'exemple suivant, le `when` bloc est testé par rapport à la `AvailabilityZone` propriété `Encrypted``VolumeType`, et pour chaque `AWS::EC2::Volume` ressource trouvée dans un CloudFormation modèle.

```
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']
        }
    }
}
```

## Comparaisons directes au niveau des clauses
<a name="direct-clause-level-comparisons"></a>

Guard prend également en charge les requêtes dans le cadre de comparaisons directes. Par exemple, consultez ce qui suit.

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

Dans l'exemple précédent, les deux clauses (en commençant par le `some` mot clé) exprimées sous la forme illustrée sont considérées comme des clauses indépendantes et sont évaluées séparément.

### Formulaire de clause unique et de clause de bloc
<a name="single-versus-block-clause-form"></a>

Pris ensemble, les deux exemples de clauses présentés dans la section précédente ne sont pas équivalents au bloc suivant.

```
let resources = Resources.*

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

Ce bloc interroge chaque `Tag` valeur de la collection et compare les valeurs de ses propriétés aux valeurs de propriété attendues. La forme combinée des clauses de la section précédente évalue les deux clauses indépendamment. Tenez compte de l'entrée suivante.

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

Les clauses du premier formulaire sont évaluées à`PASS`. Lors de la validation de la première clause sous sa forme initiale, le chemin suivant traverse`Resources`, `Properties``Tags`, et `Key` correspond à la valeur `NotPRODEnd` et ne correspond pas à la valeur `PROD` attendue.

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

Il en va de même pour la deuxième clause du premier formulaire. Le chemin traverse`Resources`, `Properties``Tags`, et `Value` correspond à la valeur`AppStart`. Par conséquent, la deuxième clause est indépendante.

Le résultat global est un`PASS`.

Cependant, le formulaire de bloc est évalué comme suit. Pour chaque `Tags` valeur, il compare si `Key` et `Value` si les valeurs correspondent ; `NotAppStart` et `NotPRODEnd` les valeurs ne correspondent pas dans l'exemple suivant.

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

Parce que les évaluations vérifient les deux `Key == /PROD$/``Value == /^App/`, la correspondance n'est pas complète. Par conséquent, le résultat est`FAIL`.

**Note**  
Lorsque vous travaillez avec des collections, nous vous recommandons d'utiliser le formulaire de clause de blocage lorsque vous souhaitez comparer plusieurs valeurs pour chaque élément de la collection. Utilisez le formulaire à clause unique lorsque la collection est un ensemble de valeurs scalaires ou lorsque vous souhaitez uniquement comparer un seul attribut.

## Résultats de la requête et clauses associées
<a name="query-outcomes"></a>

Toutes les requêtes renvoient une liste de valeurs. Toute partie d'une traversée, telle qu'une clé manquante, des valeurs vides pour un tableau (`Tags: []`) lors de l'accès à tous les index, ou des valeurs manquantes pour une carte lorsque vous rencontrez une carte vide (`Resources: {}`), peut entraîner des erreurs de récupération.

Toutes les erreurs de récupération sont considérées comme des échecs lors de l'évaluation des clauses par rapport à de telles requêtes. La seule exception est lorsque des filtres explicites sont utilisés dans la requête. Lorsque des filtres sont utilisés, les clauses associées sont ignorées.

Les échecs de blocage suivants sont associés à l'exécution de requêtes.
+ Si un modèle ne contient pas de ressources, la requête est évaluée à`FAIL`, et les clauses de niveau de bloc associées sont également évaluées à`FAIL`.
+ Lorsqu'un modèle contient un bloc de ressources vide comme`{ "Resources": {} }`, la requête est évaluée à`FAIL`, et les clauses de niveau de bloc associées sont également évaluées à`FAIL`.
+ Si un modèle contient des ressources mais qu'aucune ne correspond à la requête, la requête renvoie des résultats vides et les clauses de niveau de bloc sont ignorées.

## Utilisation de filtres dans les requêtes
<a name="filtering"></a>

Les filtres dans les requêtes sont en fait des clauses Guard utilisées comme critères de sélection. Voici la structure d'une clause.

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

Gardez à l'esprit les points essentiels suivants [AWS CloudFormation Guard Règles d'écriture](writing-rules.md) lorsque vous travaillez avec des filtres :
+ Combinez des clauses à l'aide de la [forme normale conjonctive (CNF](https://en.wikipedia.org/wiki/Conjunctive_normal_form)).
+ Spécifiez chaque clause de conjonction (`and`) sur une nouvelle ligne.
+ Spécifiez les disjonctions (`or`) en utilisant le `or` mot clé entre deux clauses.

L'exemple suivant illustre les clauses conjonctives et disjonctives.

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

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

### Utilisation de clauses pour les critères de sélection
<a name="selection-criteria"></a>

Vous pouvez appliquer un filtrage à n'importe quelle collection. Le filtrage peut être appliqué directement sur les attributs de l'entrée qui ressemblent déjà à une collection`securityGroups: [....]`. Vous pouvez également appliquer un filtrage à une requête, qui est toujours une collection de valeurs. Vous pouvez utiliser toutes les fonctionnalités des clauses, y compris la forme normale conjonctive, pour le filtrage.

La requête courante suivante est souvent utilisée lors de la sélection de ressources par type à partir d'un CloudFormation modèle.

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

La requête `Resources.*` renvoie toutes les valeurs présentes dans la `Resources` section de l'entrée. Pour l'exemple de modèle saisi dans[Définition des requêtes](#defining-queries), la requête renvoie ce qui suit.

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

Maintenant, appliquez le filtre à cette collection. Le critère à respecter est`Type == AWS::IAM::Role`. Voici le résultat de la requête après l'application du filtre.

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

Ensuite, vérifiez les différentes clauses relatives aux `AWS::IAM::Role` ressources.

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

Voici un exemple de requête de filtrage qui sélectionne toutes les `AWS::IAM::ManagedPolicy` ressources `AWS::IAM::Policy` et toutes les ressources.

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

L'exemple suivant vérifie si ces ressources de politique ont une valeur `PolicyDocument` spécifiée.

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

### Définition de besoins de filtrage plus complexes
<a name="complex-filtering"></a>

Prenons l'exemple suivant d'élément de AWS Config configuration pour les informations relatives aux groupes de sécurité d'entrée et de sortie.

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

Notez ce qui suit :
+ `ipPermissions`(règles d'entrée) est un ensemble de règles à l'intérieur d'un bloc de configuration.
+ Chaque structure de règles contient des attributs tels que `ipv4Ranges` et `ipv6Ranges` pour spécifier une collection de blocs CIDR.

Écrivons une règle qui sélectionne toutes les règles d'entrée qui autorisent les connexions depuis n'importe quelle adresse IP et vérifie que les règles n'autorisent pas l'exposition des ports TCP bloqués.

Commencez par la partie de requête qui couvre IPv4, comme indiqué dans l'exemple suivant.

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

Le `some` mot clé est utile dans ce contexte. Toutes les requêtes renvoient un ensemble de valeurs correspondant à la requête. Par défaut, Guard évalue que toutes les valeurs renvoyées à la suite de la requête sont comparées aux vérifications. Toutefois, il se peut que ce comportement ne soit pas toujours celui dont vous avez besoin pour les vérifications. Tenez compte de la partie suivante de l'entrée provenant de l'élément de configuration.

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

Deux valeurs sont présentes pour`ipv4Ranges`. Toutes les `ipv4Ranges` valeurs ne correspondent pas à une adresse IP désignée par`0.0.0.0/0`. Vous voulez voir si au moins une valeur correspond`0.0.0.0/0`. Vous indiquez à Guard qu'il n'est pas nécessaire que tous les résultats renvoyés par une requête correspondent, mais qu'au moins un résultat doit correspondre. Le `some` mot clé indique à Guard de s'assurer qu'une ou plusieurs valeurs de la requête résultante correspondent à la vérification. Si aucune valeur de résultat de requête ne correspond, Guard génère une erreur.

Ajoutez ensuite IPv6, comme indiqué dans l'exemple suivant.

```
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'
]
```

Enfin, dans l'exemple suivant, confirmez que le protocole ne l'est pas`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' ] 
]
```

Voici la règle complète.

```
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
                    >>
                }
            }

        }       
     }
}
```

### Séparer les collections en fonction de leurs types contenus
<a name="splitting-collection"></a>

Lorsque vous utilisez des modèles de configuration d'infrastructure en tant que code (IaC), vous pouvez rencontrer une collection contenant des références à d'autres entités dans le modèle de configuration. Voici un exemple de CloudFormation modèle qui décrit les tâches Amazon Elastic Container Service (Amazon ECS) avec une référence locale, une référence `TaskRoleArn` à et une référence `TaskArn` de chaîne directe.

```
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'
```

Considérons la requête suivante :

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

Cette requête renvoie une collection de valeurs contenant les trois `AWS::ECS::TaskDefinition` ressources présentées dans l'exemple de modèle. Séparez `ecs_tasks` les références `TaskRoleArn` locales des autres, comme indiqué dans l'exemple suivant.

```
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
    >>
}
```

# Affectation et référencement de variables dans les règles Guard
<a name="variables"></a>

Vous pouvez attribuer des variables dans vos fichiers de AWS CloudFormation Guard règles pour stocker les informations auxquelles vous souhaitez faire référence dans vos règles Guard. Guard prend en charge l'attribution de variables en un seul coup. Les variables sont évaluées paresseusement, ce qui signifie que Guard n'évalue les variables que lorsque les règles sont exécutées.

**Topics**
+ [Affectation de variables](#assigning-variables)
+ [Référencement de variables](#referencing-variables)
+ [Champ d'application variable](#variable-scope)
+ [Exemples de variables dans les fichiers de règles Guard](#variables-examples)

## Affectation de variables
<a name="assigning-variables"></a>

Utilisez le `let` mot clé pour initialiser et attribuer une variable. Il est recommandé d'utiliser Snake Case pour les noms de variables. Les variables peuvent stocker des littéraux statiques ou des propriétés dynamiques résultant de requêtes. Dans l'exemple suivant, la variable `ecs_task_definition_task_role_arn` stocke la valeur de chaîne statique`arn:aws:iam:123456789012:role/my-role-name`.

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

Dans l'exemple suivant, la variable `ecs_tasks` stocke les résultats d'une requête qui recherche toutes les `AWS::ECS::TaskDefinition` ressources d'un CloudFormation modèle. Vous pouvez faire référence `ecs_tasks` aux informations d'accès relatives à ces ressources lorsque vous rédigez des règles.

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

## Référencement de variables
<a name="referencing-variables"></a>

Utilisez le `%` préfixe pour référencer une variable.

Sur la base de l'exemple de `ecs_task_definition_task_role_arn` variable dans[Affectation de variables](#assigning-variables), vous pouvez faire référence `ecs_task_definition_task_role_arn` dans la `query|value literal` section d'une clause de règle de garde. L'utilisation de cette référence garantit que la valeur spécifiée pour la `TaskDefinitionArn` propriété de toute `AWS::ECS::TaskDefinition` ressource dans un CloudFormation modèle est la valeur de chaîne statique`arn:aws:iam:123456789012:role/my-role-name`.

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

Sur la base de la `ecs_tasks` variable exemple in[Affectation de variables](#assigning-variables), vous pouvez faire référence `ecs_tasks` à une requête (par exemple, %ECS\$1Tasks.properties). Guard évalue d'abord la variable, `ecs_tasks` puis utilise les valeurs renvoyées pour parcourir la hiérarchie. Si la variable `ecs_tasks` prend des valeurs autres que des chaînes, Guard génère une erreur.

**Note**  
À l'heure actuelle, Guard ne prend pas en charge le référencement de variables dans les messages d'erreur personnalisés.

## Champ d'application variable
<a name="variable-scope"></a>

La portée fait référence à la visibilité des variables définies dans un fichier de règles. Un nom de variable ne peut être utilisé qu'une seule fois dans une portée. Il existe trois niveaux où une variable peut être déclarée, ou trois portées de variables possibles :
+ **Niveau fichier** — Généralement déclarées en haut du fichier de règles, vous pouvez utiliser des variables de niveau fichier dans toutes les règles du fichier de règles. Ils sont visibles dans l'ensemble du fichier.

  Dans l'exemple de fichier de règles suivant, les variables `ecs_task_definition_task_role_arn` et B `ecs_task_definition_execution_role_arn` sont initialisées au niveau du fichier.

  ```
  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
  }
  ```
+ **Niveau règle** — Déclarées dans une règle, les variables au niveau de la règle ne sont visibles que pour cette règle spécifique. Toute référence en dehors de la règle entraîne une erreur.

  Dans l'exemple de fichier de règles suivant, les variables `ecs_task_definition_task_role_arn` et B `ecs_task_definition_execution_role_arn` sont initialisées au niveau des règles. Ils ne `ecs_task_definition_task_role_arn` peuvent être référencés que dans la règle `check_ecs_task_definition_task_role_arn` nommée. Vous ne pouvez référencer la `ecs_task_definition_execution_role_arn` variable que dans la règle `check_ecs_task_definition_execution_role_arn` nommée.

  ```
  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
  }
  ```
+ **Niveau bloc** — Déclarées dans un bloc, tel qu'une `when` clause, les variables au niveau du bloc ne sont visibles que pour ce bloc spécifique. Toute référence en dehors du bloc entraîne une erreur.

  Dans l'exemple de fichier de règles suivant, les variables `ecs_task_definition_task_role_arn` et B `ecs_task_definition_execution_role_arn` sont initialisées au niveau du bloc au sein du `AWS::ECS::TaskDefinition` bloc de type. Vous ne pouvez référencer les `ecs_task_definition_execution_role_arn` variables `ecs_task_definition_task_role_arn` et dans les blocs `AWS::ECS::TaskDefinition` de type que pour leurs règles respectives.

  ```
  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
      }
  }
  ```

## Exemples de variables dans les fichiers de règles Guard
<a name="variables-examples"></a>

Les sections suivantes fournissent des exemples d'attribution statique et dynamique de variables.

### Affectation statique
<a name="assigning-static-variables"></a>

Voici un exemple de CloudFormation modèle.

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

Sur la base de ce modèle, vous pouvez écrire une règle appelée `check_ecs_task_definition_task_role_arn` qui garantit que la `TaskRoleArn` propriété de toutes les ressources du `AWS::ECS::TaskDefinition` modèle est`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
}
```

Dans le cadre de la règle, vous pouvez initialiser une variable appelée `ecs_task_definition_task_role_arn` et lui attribuer la valeur `'arn:aws:iam::123456789012:role/my-role-name'` de chaîne statique. La clause de règle vérifie si la valeur spécifiée pour la `TaskRoleArn` propriété de la `EcsTask` ressource est `arn:aws:iam::123456789012:role/my-role-name` en faisant référence à la `ecs_task_definition_task_role_arn` variable dans la `query|value literal` section.

### Affectation dynamique
<a name="example-dynamic-assignment"></a>

Voici un exemple de CloudFormation modèle.

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

Sur la base de ce modèle, vous pouvez initialiser une variable appelée `ecs_tasks` dans le cadre du fichier et lui attribuer la requête`Resources.*[ Type == 'AWS::ECS::TaskDefinition'`. Guard interroge toutes les ressources du modèle de saisie et y stocke les informations les concernant`ecs_tasks`. Vous pouvez également écrire une règle appelée `check_ecs_task_definition_task_role_arn` qui garantit que la `TaskRoleArn` propriété de toutes les ressources du `AWS::ECS::TaskDefinition` modèle est `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'
}
```

La clause de règle vérifie si la valeur spécifiée pour la `TaskRoleArn` propriété de la `EcsTask` ressource est `arn:aws:iam::123456789012:role/my-role-name` en faisant référence à la `ecs_task_definition_task_role_arn` variable dans la `query` section.

### Application de la configuration des CloudFormation modèles
<a name="example-3"></a>

Passons en revue un exemple plus complexe de cas d'utilisation en production. Dans cet exemple, nous écrivons des règles Guard pour garantir des contrôles plus stricts sur la façon dont les tâches Amazon ECS sont définies.

Voici un exemple de CloudFormation modèle.

```
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'
```

Sur la base de ce modèle, nous écrivons les règles suivantes pour garantir le respect de ces exigences :
+ Chaque `AWS::ECS::TaskDefinition` ressource du modèle est associée à la fois à un rôle de tâche et à un rôle d'exécution.
+ Les rôles de tâches et les rôles d'exécution sont des rôles Gestion des identités et des accès AWS (IAM).
+ Les rôles sont définis dans le modèle.
+ La `PermissionsBoundary` propriété est spécifiée pour chaque rôle.

```
# 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
    }
}
```

# Composer des blocs de règles nommées dans AWS CloudFormation Guard
<a name="named-rule-block-composition"></a>

Lorsque vous écrivez des blocs de règles nommées à l'aide de AWS CloudFormation Guard, vous pouvez utiliser les deux styles de composition suivants :
+ Dépendance conditionnelle
+ Dépendance corrélationnelle

L'utilisation de l'un ou l'autre de ces styles de composition des dépendances contribue à promouvoir la réutilisabilité et à réduire la verbosité et la répétition dans les blocs de règles nommés.

**Topics**
+ [Conditions préalables](#named-rules-prerequisites)
+ [Composition des dépendances conditionnelles](#named-rules-conditional-dependency)
+ [Composition des dépendances corrélationnelles](#named-rules-correlational-dependency)

## Conditions préalables
<a name="named-rules-prerequisites"></a>

Pour en savoir plus sur les blocs de règles nommés, consultez [Writing rules](writing-rules.md#named-rule-blocks).

## Composition des dépendances conditionnelles
<a name="named-rules-conditional-dependency"></a>

Dans ce style de composition, l'évaluation d'un `when` bloc ou d'un bloc de règles nommées dépend conditionnellement du résultat de l'évaluation d'un ou de plusieurs autres blocs ou clauses de règles nommées. L'exemple de fichier de règles de garde suivant contient des blocs de règles nommées qui illustrent les dépendances conditionnelles.

```
# 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
    ...
}
```

Dans l'exemple de fichier de règles précédent, `Example-1` les résultats possibles sont les suivants :
+ Si `rule_name_A` la valeur est égale à`PASS`, les règles Guard encapsulées par `rule_name_B` sont évaluées.
+ Si `rule_name_A` la valeur est égale à`FAIL`, les règles Guard encapsulées par ne `rule_name_B` sont pas évaluées. `rule_name_B`évalue à. `SKIP`
+ Si `rule_name_A` la valeur est égale à`SKIP`, les règles Guard encapsulées par ne `rule_name_B` sont pas évaluées. `rule_name_B`évalue à. `SKIP`
**Note**  
Ce cas se produit s'il dépend `rule_name_A` conditionnellement d'une règle qui évalue jusqu'à `FAIL` et aboutit à une `rule_name_A` évaluation vers. `SKIP`

Voici un exemple d'élément de configuration d'une base de données de gestion de configuration (CMDB) provenant d'un AWS Config élément contenant des informations sur les groupes de sécurité d'entrée et de sortie. Cet exemple illustre la composition des dépendances conditionnelles.

```
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
                }
            }
        }
    }
}
```

Dans l'exemple précédent, `check_parameter_validity` dépend conditionnellement de `check_resource_type_and_parameter` et `check_ip_procotol_and_port_range_validity` dépend conditionnellement de. `check_parameter_validity` Voici un élément de configuration de la base de données de gestion de la configuration (CMDB) conforme aux règles précédentes.

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

## Composition des dépendances corrélationnelles
<a name="named-rules-correlational-dependency"></a>

Dans ce style de composition, l'évaluation d'un `when` bloc ou d'un bloc à règles nommées dépend corrélationnellement du résultat de l'évaluation d'une ou de plusieurs autres règles Guard. La dépendance corrélationnelle peut être obtenue comme suit.

```
# 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
    ...
}
```

Pour vous aider à comprendre la composition des dépendances corrélationnelles, consultez l'exemple suivant de fichier de règles 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'
}
```

Dans le fichier de règles précédent, `ensure_elbs_are_internal_and_secure` possède une dépendance corrélationnelle sur. `ensure_all_elbs_are_secure` Voici un exemple de CloudFormation modèle conforme aux règles précédentes.

```
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...'
```

# Rédaction de clauses pour effectuer des évaluations contextuelles
<a name="context-aware-evaluations"></a>

AWS CloudFormation Guard les clauses sont évaluées par rapport à des données hiérarchiques. Le moteur d'évaluation Guard résout les requêtes relatives aux données entrantes en suivant les données hiérarchiques telles que spécifiées, à l'aide d'une simple notation en pointillés. Plusieurs clauses sont souvent nécessaires pour effectuer une évaluation par rapport à une carte de données ou à une collection. Guard fournit une syntaxe pratique pour écrire de telles clauses. Le moteur est conscient du contexte et utilise les données correspondantes associées pour les évaluations.

Voici un exemple de configuration de Kubernetes Pod avec des conteneurs, à laquelle vous pouvez appliquer des évaluations contextuelles.

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

Vous pouvez créer des clauses Guard pour évaluer ces données. Lors de l'évaluation d'un fichier de règles, le contexte est l'intégralité du document d'entrée. Vous trouverez ci-dessous des exemples de clauses qui valident l'application des limites pour les conteneurs spécifiés dans un 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
            >>
        }
    }
}
```

## Compréhension `context` lors des évaluations
<a name="context"></a>

Au niveau du bloc de règles, le contexte entrant est le document complet. Les évaluations de la `when` condition sont effectuées par rapport à ce contexte racine entrant dans lequel se trouvent les `kind` attributs `apiVersion` et. Dans l'exemple précédent, ces conditions sont évaluées à`true`.

Maintenant, parcourez la hiérarchie `spec.containers[*]` comme indiqué dans l'exemple précédent. Pour chaque traversée de la hiérarchie, la valeur de contexte change en conséquence. Une fois la traversée du `spec` bloc terminée, le contexte change, comme indiqué dans l'exemple suivant.

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

Après avoir parcouru l'`containers`attribut, le contexte est illustré dans l'exemple suivant.

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

## Comprendre les boucles
<a name="loops"></a>

Vous pouvez utiliser l'expression `[*]` pour définir une boucle pour toutes les valeurs contenues dans le tableau de l'`containers`attribut. Le bloc est évalué pour chaque élément qu'il contient`containers`. Dans l'exemple d'extrait de règle précédent, les clauses contenues dans le bloc définissent les contrôles à valider par rapport à une définition de conteneur. Le bloc de clauses qu'il contient est évalué deux fois, une fois pour chaque définition de conteneur.

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

Pour chaque itération, la valeur de contexte est la valeur correspondant à l'index correspondant.

**Note**  
Le seul format d'accès à l'index pris en charge est `[<integer>]` ou`[*]`. Actuellement, Guard ne prend pas en charge les plages de ce type`[2..4]`.

## Arrays (tableaux)
<a name="arrays"></a>

Souvent, dans les endroits où un tableau est accepté, les valeurs uniques sont également acceptées. Par exemple, s'il n'y a qu'un seul conteneur, le tableau peut être supprimé et l'entrée suivante est acceptée.

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

Si un attribut peut accepter un tableau, assurez-vous que votre règle utilise la forme matricielle. Dans l'exemple précédent, vous utilisez `containers[*]` et non`containers`. Guard évalue correctement lorsqu'il parcourt les données lorsqu'elles ne rencontrent que le formulaire à valeur unique.

**Note**  
Utilisez toujours la forme de tableau lorsque vous exprimez l'accès à une clause de règle lorsqu'un attribut accepte un tableau. Guard évalue correctement même dans le cas où une seule valeur est utilisée.

## En utilisant le formulaire `spec.containers[*]` au lieu de `spec.containers`
<a name="containers"></a>

Les requêtes Guard renvoient un ensemble de valeurs résolues. Lorsque vous utilisez le formulaire`spec.containers`, les valeurs résolues pour la requête contiennent le tableau référencé par`containers`, et non les éléments qu'il contient. Lorsque vous utilisez le formulaire`spec.containers[*]`, vous faites référence à chaque élément individuel qu'il contient. N'oubliez pas d'utiliser le `[*]` formulaire chaque fois que vous avez l'intention d'évaluer chaque élément contenu dans le tableau.

## Utilisation `this` pour référencer la valeur de contexte actuelle
<a name="this"></a>

Lorsque vous créez une règle de garde, vous pouvez référencer la valeur de contexte en utilisant`this`. Souvent, elle `this` est implicite car elle est liée à la valeur du contexte. Par exemple, `this.apiVersion``this.kind`, et `this.spec` sont liés à la racine ou au document. En revanche, `this.resources` est lié à chaque valeur pour`containers`, telle que `/spec/containers/0/` et`/spec/containers/1`. De même, `this.cpu` et `this.memory` cartographiez les limites, en particulier `/spec/containers/0/resources/limits` et`/spec/containers/1/resources/limits`. 

Dans l'exemple suivant, la règle précédente pour la configuration de Kubernetes Pod est réécrite pour être utilisée explicitement. `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
            >>
        }
    }
}
```

Vous n'avez pas besoin d'utiliser `this` explicitement. Cependant, la `this` référence peut être utile lorsque vous travaillez avec des scalaires, comme le montre l'exemple suivant.

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

Dans l'exemple précédent, `this` est utilisé pour faire référence à chaque numéro de port.

## Erreurs potentielles liées à l'utilisation de l'implicite `this`
<a name="common-errors"></a>

Lors de la création de règles et de clauses, des erreurs fréquentes se produisent lors du référencement d'éléments à partir de la valeur de `this` contexte implicite. Par exemple, considérez la donnée d'entrée suivante à évaluer (elle doit être acceptée).

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

Lorsqu'elle est testée par rapport au modèle précédent, la règle suivante génère une erreur car elle suppose à tort qu'elle tire parti de l'implicite`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
                >>
            }                
        }
    }
}
```

Pour suivre cet exemple, enregistrez le fichier de règles précédent avec le nom `any_ip_ingress_check.guard` et les données avec le nom du fichier`ip_ingress.yaml`. Exécutez ensuite la `validate` commande suivante avec ces fichiers.

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

Dans le résultat suivant, le moteur indique que sa tentative de récupération d'une propriété `InputParameters.TcpBlockedPorts[*]` sur la valeur `/configuration/ipPermissions/0` a `/configuration/ipPermissions/1` échoué.

```
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[*]
```

Pour mieux comprendre ce résultat, réécrivez la règle en utilisant des références `this` explicites.

```
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`fait référence à chaque valeur contenue dans la variable`any_ip_permissions`. La requête affectée à la variable sélectionne `configuration.ipPermissions` les valeurs correspondantes. L'erreur indique une tentative de récupération `InputParamaters` dans ce contexte, mais elle `InputParameters` s'est produite dans le contexte racine.

Le bloc interne fait également référence à des variables hors de portée, comme indiqué dans l'exemple suivant.

```
{
    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`fait référence à chaque valeur de port dans`[21, 22, 110]`, mais il fait également référence à `fromPort` et`toPort`. Ils appartiennent tous deux à la portée du bloc extérieur.

### Résoudre les erreurs à l'aide de l'utilisation implicite de `this`
<a name="common-errors-resolution"></a>

Utilisez des variables pour attribuer et référencer des valeurs de manière explicite. Tout d'abord, cela `InputParameter.TcpBlockedPorts` fait partie du contexte d'entrée (racine). `InputParameter.TcpBlockedPorts`Sortez du bloc interne et attribuez-le explicitement, comme indiqué dans l'exemple suivant.

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

Ensuite, faites référence à cette variable de manière explicite.

```
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
                >>
            }
        }
    }        
}
```

Procédez de même pour les `this` références internes à l'intérieur`%ports`.

Cependant, toutes les erreurs ne sont pas encore corrigées car la boucle à l'intérieur contient `ports` toujours une référence incorrecte. L'exemple suivant montre la suppression de la référence incorrecte.

```
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
                    >>
                }
            }
        }       
    }   
}
```

Ensuite, exécutez à nouveau la `validate` commande. Cette fois, ça passe.

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

Le résultat de la `validate` commande est le suivant.

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

Pour tester cette approche en cas d'échec, l'exemple suivant utilise un changement de charge utile.

```
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 se situe dans la plage comprise entre 89 et 109 pour lesquelles n'importe quelle IPv6 adresse est autorisée. Voici le résultat de la `validate` commande après l'avoir exécutée à nouveau.

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