

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

# AWS CloudFormation Guard Reglas de escritura
<a name="writing-rules"></a>

En AWS CloudFormation Guard, las *reglas son reglas*. policy-as-code Las reglas se escriben en el lenguaje específico de dominio (DSL) de Guard con las que se pueden validar los datos con formato JSON o YAML. *Las reglas se componen de cláusulas.*

Puede guardar las reglas escritas con Guard DSL en archivos de texto sin formato que utilicen cualquier extensión de archivo.

*Puede crear varios archivos de reglas y clasificarlos como un conjunto de reglas.* Los conjuntos de reglas te permiten validar tus datos con formato JSON o YAML comparándolos con varios archivos de reglas al mismo tiempo.

**Topics**
+ [Cláusulas](#clauses)
+ [Uso de consultas en las cláusulas](#clauses-queries)
+ [Uso de operadores en cláusulas](#clauses-operators)
+ [Uso de mensajes personalizados en las cláusulas](#clauses-custom-messages)
+ [Combinación de cláusulas](#combining-clauses)
+ [Uso de bloques con reglas de guardia](#blocks)
+ [Uso de funciones integradas](#built-in-functions)
+ [Definición de consultas y filtrado de Guard](query-and-filtering.md)
+ [Asignación y referencia de variables en las reglas de Guard](variables.md)
+ [Componer bloques de reglas con nombre en AWS CloudFormation Guard](named-rule-block-composition.md)
+ [Redacción de cláusulas para realizar evaluaciones sensibles al contexto](context-aware-evaluations.md)

## Cláusulas
<a name="clauses"></a>

Las cláusulas son expresiones booleanas que se evalúan como true (`PASS`) o false (`FAIL`). Las cláusulas utilizan operadores binarios para comparar dos valores u operadores unarios que funcionan con un único valor.

**Ejemplos de cláusulas unarias**

La siguiente cláusula unaria evalúa si la colección `TcpBlockedPorts` está vacía.

```
InputParameters.TcpBlockedPorts not empty
```

La siguiente cláusula unaria evalúa si la `ExecutionRoleArn` propiedad es una cadena.

```
Properties.ExecutionRoleArn is_string
```

**Ejemplos de cláusulas binarias**

La siguiente cláusula binaria evalúa si la `BucketName` propiedad contiene la cadena`encrypted`, independientemente de las mayúsculas y minúsculas.

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

La siguiente cláusula binaria evalúa si la `ReadCapacityUnits` propiedad es inferior o igual a 5000.

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

### Sintaxis para escribir las cláusulas de la regla de Guard
<a name="clauses-syntax"></a>

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

### Propiedades de las cláusulas de las reglas de Guard
<a name="clauses-properties"></a>

`query`  <a name="clauses-properties-query"></a>
Expresión separada por puntos (`.`) escrita para recorrer datos jerárquicos. Las expresiones de consulta pueden incluir expresiones de filtro para dirigirse a un subconjunto de valores. Las consultas se pueden asignar a variables para que pueda escribirlas una vez y hacer referencia a ellas en cualquier otro lugar de un conjunto de reglas, lo que le permitirá acceder a los resultados de las consultas.  
Para obtener más información sobre cómo escribir consultas y filtrar, consulte[Definición de consultas y filtrado](query-and-filtering.md).  
 *Obligatorio*: sí

`operator`  <a name="clauses-properties-operator"></a>
Operador binario o unario que ayuda a comprobar el estado de la consulta. El lado izquierdo (LHS) de un operador binario debe ser una consulta y el lado derecho (RHS) debe ser una consulta o un valor literal.  
 *Operadores binarios compatibles*: `==` (Igual) \$1 `!=` (No igual a) \$1 `>` (Mayor que) \$1 `>=` (Mayor o igual a) \$1 `<` (Menor que) \$1 `<=` (Menor que o igual a) \$1 `IN` (En forma de lista [x, y, z]  
 *Operadores unarios compatibles*: `exists` \$1 `empty` \$1 `is_string` \$1 `is_list` \$1 `is_struct` `not(!)`  
 *Obligatorio*: sí

`query|value literal`  <a name="clauses-properties-value-literal"></a>
Una consulta o un valor literal compatible, como `string` o`integer(64)`.   
*Valores literales admitidos*:  
+ Todos los tipos primitivos: `string``integer(64)`,,`float(64)`,`bool`, `char` `regex`
+ Todos los tipos de rangos especializados para expresar `integer(64)``float(64)`, o `char` rangos expresados como:
  + `r[<lower_limit>, <upper_limit>]`, que se traduce en cualquier valor `k` que satisfaga la siguiente expresión: `lower_limit <= k <= upper_limit`
  + `r[<lower_limit>, <upper_limit>`), que se traduce en cualquier valor `k` que satisfaga la siguiente expresión: `lower_limit <= k < upper_limit`
  + `r(<lower_limit>, <upper_limit>]`, que se traduce en cualquier valor `k` que satisfaga la siguiente expresión: `lower_limit < k <= upper_limit`
  + `r(<lower_limit>, <upper_limit>),`que se traduce en cualquier valor `k` que satisfaga la siguiente expresión: `lower_limit < k < upper_limit`
+ Matrices asociativas (mapas) para datos de estructura clave-valor anidados. Por ejemplo:

  `{ "my-map": { "nested-maps": [ { "key": 10, "value": 20 } ] } }`
+ Matrices de tipos primitivos o tipos de matrices asociativas
 *Obligatorio*: condicional; obligatorio cuando se utiliza un operador binario.

`custom message`  <a name="clauses-properties-custom-message"></a>
Cadena que proporciona información sobre la cláusula. El mensaje se muestra en las salidas detalladas de los `test` comandos `validate` y y puede resultar útil para comprender o depurar la evaluación de las reglas en datos jerárquicos.  
 *Obligatorio*: no

## Uso de consultas en las cláusulas
<a name="clauses-queries"></a>

Para obtener información sobre cómo escribir consultas, consulte [Definición de consultas y filtrado](query-and-filtering.md) y[Asignación y referencia de variables en las reglas de Guard](variables.md).

## Uso de operadores en cláusulas
<a name="clauses-operators"></a>

A continuación se muestran ejemplos CloudFormation de plantillas `Template-1` y`Template-2`. Para demostrar el uso de operadores compatibles, las consultas y cláusulas de ejemplo de esta sección hacen referencia a estas plantillas de ejemplo.

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

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

### Ejemplos de cláusulas que utilizan operadores unarios
<a name="clauses-unary-operators"></a>
+ `empty`— Comprueba si una colección está vacía. También se puede utilizar para comprobar si una consulta tiene valores en un dato jerárquico, ya que las consultas dan como resultado una recopilación. No se puede usar para comprobar si las consultas con valores de cadena tienen definida una cadena vacía (`""`). Para obtener más información, consulte [Definición de consultas y filtrado](query-and-filtering.md).

  La siguiente cláusula comprueba si la plantilla tiene uno o más recursos definidos. Se evalúa `PASS` porque un recurso con el identificador lógico `S3Bucket` está definido en`Template-1`.

  ```
  Resources !empty
  ```

  La siguiente cláusula comprueba si hay una o más etiquetas definidas para el `S3Bucket` recurso. Se evalúa como `PASS` porque `S3Bucket` tiene dos etiquetas definidas para la `Tags` propiedad en`Template-1`.

  ```
  Resources.S3Bucket.Properties.Tags !empty
  ```
+ `exists`— Comprueba si cada aparición de la consulta tiene un valor y se puede utilizar en lugar de`!= null`.

  La siguiente cláusula comprueba si la `BucketEncryption` propiedad está definida para`S3Bucket`. Se evalúa como `PASS` porque `BucketEncryption` se define para `S3Bucket` en`Template-1`.

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

**nota**  
Las `not exists` comprobaciones `empty` y se evalúan `true` para detectar claves de propiedad faltantes al recorrer los datos de entrada. Por ejemplo, si la `Properties` sección no está definida en la plantilla de`S3Bucket`, la cláusula se `Resources.S3Bucket.Properties.Tag empty` evalúa como. `true` Las `exists` marcas y `empty` no muestran la ruta del puntero JSON dentro del documento en los mensajes de error. Ambas cláusulas suelen tener errores de recuperación que no mantienen esta información de recorrido.
+ `is_string`— Comprueba si cada aparición de la consulta es de `string` tipo.

  La siguiente cláusula comprueba si se ha especificado un valor de cadena para la `BucketName` propiedad del `S3Bucket` recurso. Se evalúa `PASS` porque el valor de cadena `"MyServiceS3Bucket"` está especificado `BucketName` en`Template-1`.

  ```
  Resources.S3Bucket.Properties.BucketName is_string
  ```
+ `is_list`— Comprueba si cada aparición de la consulta es del `list` tipo.

  La siguiente cláusula comprueba si se ha especificado una lista para la `Tags` propiedad del `S3Bucket` recurso. El resultado es `PASS` porque se especifican dos pares clave-valor en. `Tags` `Template-1`

  ```
  Resources.S3Bucket.Properties.Tags is_list
  ```
+ `is_struct`— Comprueba si cada aparición de la consulta son datos estructurados.

  La siguiente cláusula comprueba si los datos estructurados están especificados para la `BucketEncryption` propiedad del `S3Bucket` recurso. Se evalúa como `PASS` porque `BucketEncryption` se especifica mediante el tipo de `ServerSideEncryptionConfiguration` propiedad *(object)* en`Template-1`.

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

**nota**  
Para comprobar el estado inverso, puede usar el operador (` not !`) con los operadores `is_string``is_list`, y`is_struct`.

### Ejemplos de cláusulas que utilizan operadores binarios
<a name="clauses-binary-operators"></a>

La siguiente cláusula comprueba si el valor especificado para la `BucketName` propiedad del `S3Bucket` recurso `Template-1` contiene la cadena`encrypt`, independientemente de las mayúsculas y minúsculas. Esto se `PASS` debe a que el nombre del depósito especificado `"MyServiceS3Bucket"` no contiene la cadena`encrypt`.

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

La siguiente cláusula comprueba si el valor especificado para la `Size` propiedad del `NewVolume` recurso `Template-2` se encuentra dentro de un rango específico: 50 <= `Size` <= 200. Se evalúa como `PASS` porque `100` está especificado para. `Size`

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

La siguiente cláusula comprueba si el valor especificado para la `VolumeType` propiedad del `NewVolume` recurso `Template-2` es `io1``io2`, o`gp3`. Se evalúa como `PASS` porque `io1` está especificado para`NewVolume`.

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

**nota**  
Las consultas de ejemplo de esta sección muestran el uso de operadores que utilizan los recursos con una IDs `S3Bucket` y `NewVolume` lógica. Los nombres de los recursos suelen estar definidos por el usuario y se pueden nombrar arbitrariamente en una plantilla de infraestructura como código (IaC). Para escribir una regla que sea genérica y se aplique a todos los `AWS::S3::Bucket` recursos definidos en la plantilla, la forma de consulta más común utilizada es. `Resources.*[ Type == ‘AWS::S3::Bucket’ ]` Para obtener más información, consulta [Definición de consultas y filtrado](query-and-filtering.md) los detalles sobre el uso y explora el directorio de [ejemplos](https://github.com/aws-cloudformation/cloudformation-guard/tree/main/guard-examples) del `cloudformation-guard` GitHub repositorio.

## Uso de mensajes personalizados en las cláusulas
<a name="clauses-custom-messages"></a>

En el siguiente ejemplo, las cláusulas para `Template-2` incluir un mensaje personalizado.

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

## Combinación de cláusulas
<a name="combining-clauses"></a>

En Guard, cada cláusula escrita en una nueva línea se combina implícitamente con la siguiente mediante una conjunción (lógica booleana`and`). Consulte el siguiente ejemplo.

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

También puedes usar la disyunción para combinar una cláusula con la siguiente especificándola `or|OR` al final de la primera cláusula.

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

En una cláusula de guarda, las disyunciones se evalúan primero, seguidas de las conjunciones. Las reglas de protección se pueden definir como una combinación de disyunción de cláusulas (una `and|AND` de `or|OR` s) que dan como resultado () o `true` (`PASS`). `false` `FAIL` Es similar a la forma [normal conjuntiva.](https://en.wikipedia.org/wiki/Conjunctive_normal_form) 

Los siguientes ejemplos muestran el orden de evaluación de las cláusulas.

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

Todas las cláusulas que se basan en el ejemplo se `Template-1` pueden combinar mediante la conjunción. Consulte el siguiente ejemplo.

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

## Uso de bloques con reglas de guardia
<a name="blocks"></a>

Los bloques son composiciones que eliminan la verbosidad y la repetición de un conjunto de cláusulas, condiciones o reglas relacionadas. Hay tres tipos de bloques:
+ Bloques de consultas
+ `when`bloques
+ bloques con reglas nombradas

### Bloques de consultas
<a name="query-blocks"></a>

A continuación se muestran las cláusulas que se basan en el ejemplo`Template-1`. Se utilizó la conjunción para combinar las cláusulas.

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

Se repiten partes de la expresión de consulta de cada cláusula. Puede mejorar la componibilidad y eliminar la verbosidad y la repetición de un conjunto de cláusulas relacionadas con la misma ruta de consulta inicial mediante el uso de un bloque de consulta. Se puede escribir el mismo conjunto de cláusulas, como se muestra en el siguiente ejemplo.

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

En un bloque de consulta, la consulta que precede al bloque establece el contexto de las cláusulas del bloque.

Para obtener más información sobre el uso de bloques, consulte[Composición de bloques de reglas nombradas](named-rule-block-composition.md).

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

Puede evaluar los bloques de forma condicional mediante `when` bloques, que adoptan la siguiente forma.

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

La `when` palabra clave designa el inicio del `when` bloque. `condition`es una regla de guardia. El bloque solo se evalúa si la evaluación de la condición da como resultado `true` (`PASS`).

El siguiente es un ejemplo de `when` bloque que se basa en`Template-1`.

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

La cláusula del `when` bloque solo se evalúa si el valor especificado `BucketName` es una cadena. Si `BucketName` se hace referencia al valor especificado en la `Parameters` sección de la plantilla, como se muestra en el siguiente ejemplo, no se evalúa la cláusula del `when` bloque.

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

### Bloques de reglas con nombre
<a name="named-rule-blocks"></a>

*Puede asignar un nombre a un conjunto de reglas (conjunto de reglas) y, a continuación, hacer referencia a estos bloques de validación modulares, denominados bloques de reglas *con nombre, en otras reglas*.* Los bloques de reglas con nombre tienen la siguiente forma.

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

La `rule` palabra clave designa el inicio del bloque de reglas con nombre asignado.

`rule name`es una cadena legible por humanos que identifica de forma única un bloque de reglas con nombre. Es una etiqueta para el conjunto de reglas de Guard que encapsula. En este uso, el término *regla de protección* incluye cláusulas, bloques de consulta, bloques y `when` bloques de reglas con nombre. El nombre de la regla se puede usar para hacer referencia al resultado de la evaluación del conjunto de reglas que encapsula, lo que hace que los bloques de reglas con nombre asignado sean reutilizables. El nombre de la regla también proporciona un contexto sobre los errores de las reglas en las salidas de los comandos `validate` y`test`. El nombre de la regla se muestra junto con el estado de evaluación del bloque (`PASS``FAIL`, o`SKIP`) en el resultado de la evaluación del archivo de reglas. Consulte el siguiente ejemplo.

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

También puede evaluar los bloques de reglas con nombre de forma condicional especificando la `when` palabra clave seguida de una condición después del nombre de la regla.

A continuación se muestra el `when` bloque de ejemplo que se analizó anteriormente en este tema.

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

Al usar bloques de reglas con nombre, lo anterior también se puede escribir de la siguiente manera.

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

Puedes reutilizar y agrupar bloques de reglas con nombre propio con otras reglas de Guard. A continuación se muestran algunos ejemplos.

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

## Uso de funciones integradas
<a name="built-in-functions"></a>

AWS CloudFormation Guard proporciona funciones integradas que puede usar en sus reglas para realizar operaciones como la manipulación de cadenas, el análisis de JSON y la conversión de tipos de datos. Las funciones solo se admiten mediante la asignación a una variable.

### Funciones clave
<a name="key-functions"></a>

`json_parse(json_string)`  
Analiza las cadenas JSON en línea de una plantilla. Tras el análisis, puede evaluar las propiedades del objeto resultante.

`count(collection)`  
Devuelve el número de elementos en los que se resuelve una consulta.

`regex_replace(base_string, regex_to_extract, regex_replacement)`  
Sustituye partes de una cadena mediante expresiones regulares.

Para obtener una lista completa de las funciones disponibles, incluidas la manipulación de cadenas, las operaciones de recopilación y las funciones de conversión de tipos de datos, consulte la [documentación de funciones](https://github.com/aws-cloudformation/cloudformation-guard/blob/main/docs/FUNCTIONS.md) en el GitHub repositorio de Guard.

# Definición de consultas y filtrado de Guard
<a name="query-and-filtering"></a>

En este tema se describe la redacción de consultas y el uso de filtros al escribir cláusulas de reglas de Guard.

## Requisitos previos
<a name="query-filtering-prerequisites"></a>

El filtrado es un AWS CloudFormation Guard concepto avanzado. Le recomendamos que revise los siguientes temas fundamentales antes de aprender sobre el filtrado:
+ [¿Qué es AWS CloudFormation Guard?](what-is-guard.md)
+ [Redacción de reglas y cláusulas](writing-rules.md)

## Definición de consultas
<a name="defining-queries"></a>

Las expresiones de consulta son simples expresiones separadas por puntos (`.`) que se escriben para recorrer datos jerárquicos. Las expresiones de consulta pueden incluir expresiones de filtro para dirigirse a un subconjunto de valores. Cuando se evalúan las consultas, dan como resultado una colección de valores, similar a un conjunto de resultados devuelto por una consulta SQL.

El siguiente ejemplo de consulta busca `AWS::IAM::Role` recursos en una CloudFormation plantilla.

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

Las consultas siguen estos principios básicos:
+ Cada punto (`.`) de la consulta desciende en la jerarquía cuando se utiliza un término clave explícito, por ejemplo, `Properties.Encrypted.` si `Resources` alguna parte de la consulta no coincide con el dato entrante, Guard arroja un error de recuperación.
+ Una parte de la consulta con puntos (`.`) que utiliza un comodín `*` recorre todos los valores de la estructura en ese nivel.
+ Una parte con puntos (`.`) de la consulta que utiliza un comodín de matriz `[*]` recorre todos los índices de esa matriz.
+ Todas las colecciones se pueden filtrar especificando los filtros entre corchetes. `[]` Las colecciones se pueden encontrar de las siguientes maneras:
  + Las matrices que se encuentran de forma natural en los datos son colecciones. A continuación se muestran algunos ejemplos realizados con la :

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

    Etiquetas: `[{"Key": "Stage", "Value": "PROD"}, {"Key": "App", "Value": "MyService"}]`
  + Al recorrer todos los valores de una estructura como `Resources.*`
  + El resultado de cualquier consulta es en sí mismo una colección a partir de la cual se pueden filtrar aún más los valores. Consulte el siguiente ejemplo.

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

A continuación se muestra un ejemplo de fragmento CloudFormation de plantilla.

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

Según esta plantilla, la ruta recorrida es `SampleRole` y el valor final seleccionado es. `Type: AWS::IAM::Role`

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

El valor resultante de la consulta `Resources.*[ Type == 'AWS::IAM::Role' ]` en formato YAML se muestra en el siguiente ejemplo.

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

Algunas de las formas en que puedes usar las consultas son las siguientes:
+ Asigne una consulta a las variables para poder acceder a los resultados de la consulta haciendo referencia a esas variables.
+ Siga la consulta con un bloque que compare cada uno de los valores seleccionados.
+ Compara una consulta directamente con una cláusula básica.

## Asignación de consultas a variables
<a name="queries-and-filtering-variables"></a>

Guard admite la asignación de variables de una sola vez dentro de un ámbito determinado. Para obtener más información sobre las variables de las reglas de Guard, consulte[Asignación y referencia de variables en las reglas de Guard](variables.md).

Puede asignar consultas a las variables para poder escribirlas una vez y, después, hacer referencia a ellas en cualquier otro lugar de las reglas de Guard. Consulte los siguientes ejemplos de asignaciones de variables, que muestran los principios de consulta que se describen más adelante en esta sección.

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

## Recorrer directamente los valores de una variable asignada a una consulta
<a name="variable-assigned-from-query"></a>

Guard permite comparar directamente los resultados de una consulta. En el siguiente ejemplo, el `when` bloque se compara con la `AvailabilityZone` propiedad `Encrypted``VolumeType`, y de cada `AWS::EC2::Volume` recurso que se encuentra en una CloudFormation plantilla.

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

## Comparaciones directas a nivel de cláusula
<a name="direct-clause-level-comparisons"></a>

Guard también admite consultas como parte de las comparaciones directas. Consulte los ejemplos siguientes.

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

En el ejemplo anterior, las dos cláusulas (que comienzan con la `some` palabra clave) expresadas en la forma que se muestra se consideran cláusulas independientes y se evalúan por separado.

### Formulario de cláusula única y cláusula de bloque
<a name="single-versus-block-clause-form"></a>

En conjunto, las dos cláusulas de ejemplo que se muestran en la sección anterior no equivalen al bloque siguiente.

```
let resources = Resources.*

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

Este bloque consulta cada `Tag` valor de la colección y compara los valores de sus propiedades con los valores de propiedad esperados. La forma combinada de las cláusulas de la sección anterior evalúa las dos cláusulas de forma independiente. Tenga en cuenta la siguiente entrada.

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

Las cláusulas del primer formulario se evalúan como`PASS`. Al validar la primera cláusula del primer formulario, la siguiente ruta cruza`Resources`, `Properties``Tags`, y `Key` coincide con el valor `NotPRODEnd` y no coincide con el valor `PROD` esperado.

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

Lo mismo ocurre con la segunda cláusula del primer formulario. La ruta que cruza `Resources``Properties`,`Tags`, y `Value` coincide con el valor`AppStart`. Como resultado, la segunda cláusula de forma independiente.

El resultado general es un`PASS`.

Sin embargo, el formulario de bloque se evalúa de la siguiente manera. Para cada `Tags` valor, se compara si ambos valores coinciden `Key` y`Value`, `NotAppStart` en el ejemplo siguiente, `NotPRODEnd` los valores no coinciden.

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

Porque las evaluaciones comprueban ambos y `Key == /PROD$/``Value == /^App/`, además, la coincidencia no está completa. Por lo tanto, el resultado es`FAIL`.

**nota**  
Cuando trabaje con colecciones, le recomendamos que utilice el formulario de cláusula de bloque cuando desee comparar varios valores de cada elemento de la colección. Utilice el formulario de cláusula única cuando la colección sea un conjunto de valores escalares o cuando desee comparar solo un atributo.

## Resultados de la consulta y cláusulas asociadas
<a name="query-outcomes"></a>

Todas las consultas devuelven una lista de valores. Cualquier parte de un recorrido, como la falta de una clave, los valores vacíos de una matriz (`Tags: []`) al acceder a todos los índices o la falta de valores en un mapa al encontrar un mapa vacío (`Resources: {}`), puede provocar errores de recuperación.

Todos los errores de recuperación se consideran errores al evaluar las cláusulas en función de dichas consultas. La única excepción se produce cuando se utilizan filtros explícitos en la consulta. Cuando se utilizan filtros, se omiten las cláusulas asociadas.

Los siguientes errores de bloqueo están asociados a la ejecución de consultas.
+ Si una plantilla no contiene recursos, la consulta se evalúa y las cláusulas de nivel de bloque asociadas también se evalúan como. `FAIL` `FAIL`
+ Cuando una plantilla contiene un bloque de recursos vacío`{ "Resources": {} }`, la consulta se evalúa como y las cláusulas de nivel de bloque asociadas también se evalúan como. `FAIL` `FAIL`
+ Si una plantilla contiene recursos pero ninguno coincide con la consulta, la consulta devuelve resultados vacíos y se omiten las cláusulas a nivel de bloque.

## Uso de filtros en las consultas
<a name="filtering"></a>

Los filtros en las consultas son, en efecto, cláusulas de protección que se utilizan como criterios de selección. A continuación se muestra la estructura de una cláusula.

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

Tenga en cuenta los siguientes puntos clave [AWS CloudFormation Guard Reglas de escritura](writing-rules.md) al trabajar con filtros:
+ Combine las cláusulas mediante [la forma normal conjuntiva (CNF)](https://en.wikipedia.org/wiki/Conjunctive_normal_form).
+ Especifique cada cláusula de conjunción (`and`) en una línea nueva.
+ Especifique las disyunciones (`or`) mediante la `or` palabra clave entre dos cláusulas.

El siguiente ejemplo muestra las cláusulas conjuntivas y disyuntivas.

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

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

### Uso de cláusulas como criterios de selección
<a name="selection-criteria"></a>

Puede aplicar el filtrado a cualquier colección. El filtrado se puede aplicar directamente a los atributos de la entrada que ya son similares a una colección`securityGroups: [....]`. También puede aplicar el filtrado a una consulta, que siempre es una colección de valores. Puede utilizar todas las características de las cláusulas, incluida la forma normal conjuntiva, para filtrar.

La siguiente consulta común se utiliza a menudo al seleccionar recursos por tipo de una CloudFormation plantilla.

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

La consulta `Resources.*` devuelve todos los valores presentes en la `Resources` sección de la entrada. En el caso de la plantilla de ejemplo introducida[Definir consultas](#defining-queries), la consulta devuelve lo siguiente.

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

Ahora, aplique el filtro a esta colección. El criterio que debe coincidir es`Type == AWS::IAM::Role`. A continuación se muestra el resultado de la consulta después de aplicar el filtro.

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

A continuación, consulte varias cláusulas para ver `AWS::IAM::Role` los recursos.

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

El siguiente es un ejemplo de consulta de filtrado que selecciona todos los `AWS::IAM::ManagedPolicy` recursos `AWS::IAM::Policy` y.

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

El siguiente ejemplo comprueba si estos recursos de políticas tienen un valor `PolicyDocument` específico.

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

### Definir necesidades de filtrado más complejas
<a name="complex-filtering"></a>

Considere el siguiente ejemplo de un elemento de AWS Config configuración para la información de los grupos de seguridad de entrada y salida.

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

Tenga en cuenta lo siguiente:
+ `ipPermissions`(reglas de entrada) es un conjunto de reglas dentro de un bloque de configuración.
+ Cada estructura de reglas contiene atributos tales como `ipv4Ranges` y `ipv6Ranges` para especificar un conjunto de bloques CIDR.

Escribamos una regla que seleccione cualquier regla de entrada que permita conexiones desde cualquier dirección IP y compruebe que las reglas no permiten exponer los puertos TCP bloqueados.

Comience con la parte de consulta que cubre IPv4, como se muestra en el siguiente ejemplo.

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

La `some` palabra clave es útil en este contexto. Todas las consultas devuelven un conjunto de valores que coinciden con la consulta. De forma predeterminada, Guard evalúa que todos los valores devueltos como resultado de la consulta se comparan con las comprobaciones. Sin embargo, es posible que este comportamiento no siempre sea lo que necesita para las comprobaciones. Tenga en cuenta la siguiente parte de la entrada del elemento de configuración.

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

Hay dos valores presentes para`ipv4Ranges`. No todos los `ipv4Ranges` valores son iguales a una dirección IP indicada por`0.0.0.0/0`. Desea comprobar si al menos un valor coincide`0.0.0.0/0`. Le dices a Guard que no es necesario que coincidan todos los resultados devueltos por una consulta, pero que al menos uno de ellos debe coincidir. La `some` palabra clave indica a Guard que se asegure de que uno o más valores de la consulta resultante coincidan con la comprobación. Si ningún valor del resultado de la consulta coincide, Guard arroja un error.

A continuación, añada IPv6, como se muestra en el siguiente ejemplo.

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

Por último, en el siguiente ejemplo, valide que el protocolo no lo sea`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' ] 
]
```

La siguiente es la regla completa.

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

        }       
     }
}
```

### Separar las colecciones en función de los tipos de contenido
<a name="splitting-collection"></a>

Al utilizar plantillas de configuración de infraestructura como código (IaC), es posible que encuentre una colección que contenga referencias a otras entidades dentro de la plantilla de configuración. La siguiente es una CloudFormation plantilla de ejemplo que describe las tareas de Amazon Elastic Container Service (Amazon ECS) con una referencia local `TaskRoleArn` a, una referencia `TaskArn` a y una referencia de cadena directa.

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

Analice la siguiente consulta.

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

Esta consulta devuelve un conjunto de valores que contiene los tres `AWS::ECS::TaskDefinition` recursos que se muestran en la plantilla de ejemplo. Separe `ecs_tasks` los que contienen referencias `TaskRoleArn` locales de los demás, como se muestra en el siguiente ejemplo.

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

# Asignación y referencia de variables en las reglas de Guard
<a name="variables"></a>

Puedes asignar variables en tus archivos de AWS CloudFormation Guard reglas para almacenar la información a la que quieras hacer referencia en tus reglas de Guard. Guard admite la asignación de variables con un solo disparo. Las variables se evalúan de forma perezosa, lo que significa que Guard solo evalúa las variables cuando se ejecutan reglas.

**Topics**
+ [Asignación de variables](#assigning-variables)
+ [Variables de referencia](#referencing-variables)
+ [Ámbito de la variable](#variable-scope)
+ [Ejemplos de variables en los archivos de reglas de Guard](#variables-examples)

## Asignación de variables
<a name="assigning-variables"></a>

Use la `let` palabra clave para inicializar y asignar una variable. Como práctica recomendada, utilice las mayúsculas y minúsculas para los nombres de las variables. Las variables pueden almacenar literales estáticos o propiedades dinámicas resultantes de las consultas. En el siguiente ejemplo, la variable `ecs_task_definition_task_role_arn` almacena el valor `arn:aws:iam:123456789012:role/my-role-name` de cadena estática.

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

En el siguiente ejemplo, la variable `ecs_tasks` almacena los resultados de una consulta que busca todos los `AWS::ECS::TaskDefinition` recursos de una CloudFormation plantilla. Puede hacer referencia `ecs_tasks` a la información sobre esos recursos para acceder a ellos al escribir reglas.

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

## Variables de referencia
<a name="referencing-variables"></a>

Use el `%` prefijo para hacer referencia a una variable.

Según el ejemplo de `ecs_task_definition_task_role_arn` variable que aparece en[Asignación de variables](#assigning-variables), puede hacer referencia `ecs_task_definition_task_role_arn` a la `query|value literal` sección de una cláusula de regla de guardia. El uso de esa referencia garantiza que el valor especificado para la `TaskDefinitionArn` propiedad de cualquier `AWS::ECS::TaskDefinition` recurso de una CloudFormation plantilla sea el valor de cadena estática`arn:aws:iam:123456789012:role/my-role-name`.

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

Según el ejemplo de `ecs_tasks` variable de[Asignación de variables](#assigning-variables), puede hacer referencia `ecs_tasks` en una consulta (por ejemplo, %ECS\$1Tasks.properties). En primer lugar, Guard evalúa la variable `ecs_tasks` y, a continuación, utiliza los valores devueltos para recorrer la jerarquía. Si la variable se `ecs_tasks` resuelve en valores que no son cadenas, Guard arroja un error.

**nota**  
Actualmente, Guard no admite la referencia a variables dentro de los mensajes de error personalizados.

## Ámbito de la variable
<a name="variable-scope"></a>

El alcance se refiere a la visibilidad de las variables definidas en un archivo de reglas. El nombre de una variable solo se puede usar una vez dentro de un ámbito. Hay tres niveles en los que se puede declarar una variable, o tres posibles ámbitos de variables:
+ A **nivel de archivo**: normalmente se declaran en la parte superior del archivo de reglas, pero se pueden usar variables a nivel de archivo en todas las reglas del archivo de reglas. Están visibles en todo el archivo.

  En el siguiente archivo de reglas de ejemplo, las variables `ecs_task_definition_task_role_arn` y `ecs_task_definition_execution_role_arn` se inicializan a nivel de archivo.

  ```
  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
  }
  ```
+ **Nivel de regla**: declaradas dentro de una regla, las variables de nivel de regla solo son visibles para esa regla específica. Cualquier referencia fuera de la regla produce un error.

  En el siguiente archivo de reglas de ejemplo, las variables `ecs_task_definition_task_role_arn` y `ecs_task_definition_execution_role_arn` se inicializan en el nivel de la regla. Solo se `ecs_task_definition_task_role_arn` puede hacer referencia a ellas dentro de la `check_ecs_task_definition_task_role_arn` regla nombrada. Solo puede hacer referencia a la `ecs_task_definition_execution_role_arn` variable dentro de la regla `check_ecs_task_definition_execution_role_arn` nombrada.

  ```
  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
  }
  ```
+ **Nivel de bloque**: declaradas dentro de un bloque, como una `when` cláusula, las variables de nivel de bloque solo son visibles para ese bloque específico. Cualquier referencia fuera del bloque produce un error.

  En el siguiente archivo de reglas de ejemplo, las variables `ecs_task_definition_task_role_arn` y `ecs_task_definition_execution_role_arn` se inicializan a nivel de bloque dentro del `AWS::ECS::TaskDefinition` bloque de tipos. Solo puede hacer referencia a `ecs_task_definition_execution_role_arn` las variables `ecs_task_definition_task_role_arn` y dentro de los bloques de `AWS::ECS::TaskDefinition` tipos para sus reglas respectivas.

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

## Ejemplos de variables en los archivos de reglas de Guard
<a name="variables-examples"></a>

En las siguientes secciones se proporcionan ejemplos de asignación estática y dinámica de variables.

### Asignación estática
<a name="assigning-static-variables"></a>

A continuación se muestra un ejemplo CloudFormation de plantilla.

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

Basándose en esta plantilla, puede escribir una regla denominada `check_ecs_task_definition_task_role_arn` que garantice que la `TaskRoleArn` propiedad de todos los recursos de la `AWS::ECS::TaskDefinition` plantilla es`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
}
```

Dentro del ámbito de la regla, puede inicializar una variable llamada `ecs_task_definition_task_role_arn` y asignarle el valor `'arn:aws:iam::123456789012:role/my-role-name'` de cadena estática. La cláusula de regla comprueba si el valor especificado para la `TaskRoleArn` propiedad del `EcsTask` recurso es `arn:aws:iam::123456789012:role/my-role-name` haciendo referencia a la `ecs_task_definition_task_role_arn` variable de la sección. `query|value literal`

### Asignación dinámica
<a name="example-dynamic-assignment"></a>

A continuación se muestra un ejemplo CloudFormation de plantilla.

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

Basándose en esta plantilla, puede inicializar una variable llamada `ecs_tasks` dentro del ámbito del archivo y asignarle la consulta`Resources.*[ Type == 'AWS::ECS::TaskDefinition'`. Guard consulta todos los recursos de la plantilla de entrada y almacena información sobre ellos en `ecs_tasks` ella. También puede escribir una regla llamada `check_ecs_task_definition_task_role_arn` que garantice que la `TaskRoleArn` propiedad de todos los recursos de la `AWS::ECS::TaskDefinition` plantilla sea `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 cláusula de regla comprueba si el valor especificado para la `TaskRoleArn` propiedad del `EcsTask` recurso es `arn:aws:iam::123456789012:role/my-role-name` haciendo referencia a la `ecs_task_definition_task_role_arn` variable de la `query` sección.

### Hacer cumplir la configuración de la plantilla CloudFormation
<a name="example-3"></a>

Veamos un ejemplo más complejo de un caso de uso de producción. En este ejemplo, escribimos reglas de Guard para garantizar controles más estrictos sobre la forma en que se definen las tareas de Amazon ECS.

La siguiente es una CloudFormation plantilla de ejemplo.

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

Basándonos en esta plantilla, escribimos las siguientes reglas para garantizar que se cumplan estos requisitos:
+ Cada `AWS::ECS::TaskDefinition` recurso de la plantilla tiene un rol de tarea y un rol de ejecución adjunto.
+ Los roles de tarea y los roles de ejecución son roles AWS Identity and Access Management (IAM).
+ Los roles se definen en la plantilla.
+ La `PermissionsBoundary` propiedad se especifica para cada función.

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

# Componer bloques de reglas con nombre en AWS CloudFormation Guard
<a name="named-rule-block-composition"></a>

Al escribir bloques de reglas con nombre AWS CloudFormation Guard, puede usar los dos estilos de composición siguientes:
+ Dependencia condicional
+ Dependencia correlacional

El uso de cualquiera de estos estilos de composición de dependencias ayuda a promover la reutilización y reduce la verbosidad y la repetición en los bloques de reglas nombradas.

**Topics**
+ [Requisitos previos](#named-rules-prerequisites)
+ [Composición de dependencias condicionales](#named-rules-conditional-dependency)
+ [Composición de dependencias correlacionales](#named-rules-correlational-dependency)

## Requisitos previos
<a name="named-rules-prerequisites"></a>

[Obtén más información sobre los bloques de reglas con nombre en Cómo escribir reglas.](writing-rules.md#named-rule-blocks)

## Composición de dependencias condicionales
<a name="named-rules-conditional-dependency"></a>

En este estilo de composición, la evaluación de un `when` bloque o bloque de reglas nominadas depende condicionalmente del resultado de la evaluación de uno o más bloques o cláusulas de reglas nombradas. El siguiente archivo de reglas de Guard de ejemplo contiene bloques de reglas con nombres que muestran las dependencias condicionales.

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

En el ejemplo anterior, el archivo de reglas `Example-1` tiene los siguientes resultados posibles:
+ Si `rule_name_A` se evalúa así`PASS`, se evalúan las reglas de Guard `rule_name_B` encapsuladas por.
+ Si `rule_name_A` se evalúa así`FAIL`, las reglas de Guard encapsuladas por `rule_name_B` no se evalúan. `rule_name_B`se evalúa como. `SKIP`
+ Si `rule_name_A` se evalúa como tal`SKIP`, las reglas de Guard encapsuladas por `rule_name_B` no se evalúan. `rule_name_B`se evalúa como. `SKIP`
**nota**  
Este caso ocurre si depende `rule_name_A` condicionalmente de una regla que evalúa `FAIL` y da como resultado una evaluación de`rule_name_A`. `SKIP`

A continuación se muestra un ejemplo de un elemento de configuración de una base de datos de administración de la configuración (CMDB) a partir de un AWS Config elemento para la información de grupos de seguridad de entrada y salida. En este ejemplo se muestra la composición de dependencias condicionales.

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

En el ejemplo anterior, `check_parameter_validity` depende condicionalmente de `check_resource_type_and_parameter` y `check_ip_procotol_and_port_range_validity` depende condicionalmente de. `check_parameter_validity` El siguiente es un elemento de configuración de la base de datos de administración de la configuración (CMDB) que cumple con las reglas anteriores.

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

## Composición de dependencias correlacionales
<a name="named-rules-correlational-dependency"></a>

En este estilo de composición, la evaluación de un `when` bloque o de un bloque de reglas nominadas tiene una dependencia correlacional del resultado de la evaluación de una o más reglas de Guard distintas. La dependencia correlacional se puede lograr de la siguiente manera.

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

Para ayudarle a entender la composición de la dependencia correlacional, revise el siguiente ejemplo de un archivo de reglas de 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'
}
```

En el archivo de reglas anterior, `ensure_elbs_are_internal_and_secure` tiene una dependencia correlacional de. `ensure_all_elbs_are_secure` A continuación se muestra un ejemplo de CloudFormation plantilla que se ajusta a las reglas anteriores.

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

# Redacción de cláusulas para realizar evaluaciones sensibles al contexto
<a name="context-aware-evaluations"></a>

AWS CloudFormation Guard las cláusulas se evalúan en función de los datos jerárquicos. El motor de evaluación Guard resuelve las consultas con los datos entrantes siguiendo los datos jerárquicos según se especifique, mediante una simple notación punteada. Con frecuencia, se necesitan varias cláusulas para realizar la evaluación en función de un mapa de datos o de una colección. Guard proporciona una sintaxis práctica para escribir dichas cláusulas. El motor tiene en cuenta el contexto y utiliza los datos correspondientes asociados para las evaluaciones.

El siguiente es un ejemplo de una configuración de Kubernetes Pod con contenedores, a la que puedes aplicar evaluaciones contextuales.

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

Puedes crear cláusulas Guard para evaluar estos datos. Al evaluar un archivo de reglas, el contexto es todo el documento de entrada. A continuación, se muestran ejemplos de cláusulas que validan la aplicación de los límites a los contenedores especificados en 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
            >>
        }
    }
}
```

## Comprensión `context` en las evaluaciones
<a name="context"></a>

A nivel de bloque de reglas, el contexto entrante es el documento completo. Las evaluaciones de la `when` condición se realizan en relación con este contexto raíz entrante en el que se encuentran `kind` los atributos `apiVersion` y. En el ejemplo anterior, estas condiciones se evalúan como`true`.

Ahora, recorra la jerarquía que `spec.containers[*]` se muestra en el ejemplo anterior. Para cada recorrido de la jerarquía, el valor del contexto cambia en consecuencia. Una vez finalizado el recorrido del `spec` bloque, el contexto cambia, como se muestra en el siguiente ejemplo.

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

Tras recorrer el `containers` atributo, el contexto se muestra en el siguiente ejemplo.

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

## Entender los bucles
<a name="loops"></a>

Puede utilizar la expresión `[*]` para definir un bucle para todos los valores contenidos en la matriz del `containers` atributo. El bloque se evalúa para cada uno de los elementos que contiene`containers`. En el fragmento de regla del ejemplo anterior, las cláusulas contenidas en el bloque definen las comprobaciones que deben validarse con respecto a una definición de contenedor. El bloque de cláusulas que contiene se evalúa dos veces, una para cada definición de contenedor.

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

Para cada iteración, el valor de contexto es el valor del índice correspondiente.

**nota**  
El único formato de acceso al índice admitido es `[<integer>]` o`[*]`. Actualmente, Guard no admite rangos como`[2..4]`.

## Matrices
<a name="arrays"></a>

A menudo, en los lugares donde se acepta una matriz, también se aceptan valores individuales. Por ejemplo, si solo hay un contenedor, se puede eliminar la matriz y aceptar la siguiente entrada.

```
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 atributo puede aceptar una matriz, asegúrese de que la regla utilice la forma de matriz. En el ejemplo anterior, se utiliza `containers[*]` y no`containers`. Guard evalúa correctamente al recorrer los datos cuando encuentra solo el formulario de un solo valor.

**nota**  
Utilice siempre la forma matricial al expresar el acceso a una cláusula de regla cuando un atributo acepte una matriz. Guard evalúa correctamente incluso en el caso de que se utilice un único valor.

## Utilizar el formulario `spec.containers[*]` en lugar de `spec.containers`
<a name="containers"></a>

Las consultas de protección devuelven una colección de valores resueltos. Al utilizar el formulario`spec.containers`, los valores resueltos de la consulta contienen la matriz a la que se hace referencia`containers`, no los elementos que contiene. Al utilizar el formulario`spec.containers[*]`, se hace referencia a cada elemento individual contenido. Recuerde utilizar el `[*]` formulario siempre que desee evaluar cada elemento contenido en la matriz.

## Se utiliza `this` para hacer referencia al valor del contexto actual
<a name="this"></a>

Al crear una regla de protección, puede hacer referencia al valor de contexto mediante`this`. A menudo, `this` está implícita porque está vinculada al valor del contexto. Por ejemplo, `this.apiVersion``this.kind`, y `this.spec` están enlazados a la raíz o al documento. Por el contrario, `this.resources` está enlazado a cada valor de`containers`, como `/spec/containers/0/` y`/spec/containers/1`. Del mismo modo, `this.cpu` y `this.memory` mapea los límites, específicamente `/spec/containers/0/resources/limits` y`/spec/containers/1/resources/limits`. 

En el siguiente ejemplo, la regla anterior para la configuración del Kubernetes Pod se reescribió para utilizarla de forma explícita. `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
            >>
        }
    }
}
```

No es necesario que la utilices de forma explícita. `this` Sin embargo, la `this` referencia puede resultar útil cuando se trabaja con escalares, como se muestra en el siguiente ejemplo.

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

En el ejemplo anterior, `this` se utiliza para hacer referencia a cada número de puerto.

## Posibles errores relacionados con el uso del término implícito `this`
<a name="common-errors"></a>

Al crear reglas y cláusulas, hay algunos errores comunes al hacer referencia a elementos del valor de contexto implícito`this`. Por ejemplo, considere el siguiente dato de entrada para realizar la evaluación (debe aprobarse).

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

Cuando se compara con la plantilla anterior, la siguiente regla produce un error porque supone erróneamente que se aprovecha lo implícito. `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
                >>
            }                
        }
    }
}
```

Para ver este ejemplo, guarde el archivo de reglas anterior con el nombre `any_ip_ingress_check.guard` y los datos con el nombre del archivo. `ip_ingress.yaml` A continuación, ejecute el siguiente `validate` comando con estos archivos.

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

En el siguiente resultado, el motor indica que su intento de recuperar una propiedad `InputParameters.TcpBlockedPorts[*]` del valor `/configuration/ipPermissions/0` `/configuration/ipPermissions/1` ha fallado.

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

Para entender mejor este resultado, reescribe la regla utilizando referencias `this` explícitas.

```
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`hace referencia a cada valor contenido en la variable`any_ip_permissions`. La consulta asignada a la variable selecciona `configuration.ipPermissions` los valores que coinciden. El error indica un intento de recuperación `InputParamaters` en este contexto, pero `InputParameters` estaba en el contexto raíz.

El bloque interno también hace referencia a variables que están fuera del alcance, como se muestra en el siguiente ejemplo.

```
{
    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`hace referencia a cada valor de puerto de`[21, 22, 110]`, pero también hace referencia a `fromPort` y`toPort`. Ambos pertenecen al ámbito del bloque exterior.

### Resolver errores con el uso implícito de `this`
<a name="common-errors-resolution"></a>

Utilice variables para asignar valores y hacer referencia a ellos de forma explícita. En primer lugar, `InputParameter.TcpBlockedPorts` forma parte del contexto de entrada (raíz). `InputParameter.TcpBlockedPorts`Salga del bloque interno y asígnelo de forma explícita, como se muestra en el siguiente ejemplo.

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

A continuación, consulte esta variable de forma explícita.

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

Haga lo mismo con `this` las referencias internas internas`%ports`.

Sin embargo, aún no se han corregido todos los errores porque el bucle interior `ports` todavía tiene una referencia incorrecta. En el siguiente ejemplo, se muestra la eliminación de la referencia incorrecta.

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

A continuación, vuelva a ejecutar el `validate` comando. Esta vez, pasa.

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

El siguiente es el resultado del `validate` comando.

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

Para probar este enfoque en busca de errores, en el siguiente ejemplo se utiliza un cambio de carga útil.

```
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 está dentro del rango de 89 a 109, y todas IPv6 las direcciones están permitidas. El siguiente es el resultado del `validate` comando después de volver a ejecutarlo.

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