

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

# Definindo consultas e filtragem do Guard
<a name="query-and-filtering"></a>

Este tópico aborda como escrever consultas e usar a filtragem ao escrever cláusulas de regras do Guard.

## Pré-requisitos
<a name="query-filtering-prerequisites"></a>

A filtragem é um AWS CloudFormation Guard conceito avançado. Recomendamos que você analise os seguintes tópicos fundamentais antes de aprender sobre filtragem:
+ [O que é AWS CloudFormation Guard?](what-is-guard.md)
+ [Regras de redação, cláusulas](writing-rules.md)

## Definindo consultas
<a name="defining-queries"></a>

As expressões de consulta são expressões simples separadas por ponto (`.`) escritas para atravessar dados hierárquicos. As expressões de consulta podem incluir expressões de filtro para direcionar um subconjunto de valores. Quando as consultas são avaliadas, elas resultam em uma coleção de valores, semelhante a um conjunto de resultados retornado de uma consulta SQL.

O exemplo de consulta a seguir pesquisa `AWS::IAM::Role` recursos em um CloudFormation modelo.

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

As consultas seguem estes princípios básicos:
+ Cada parte dot (`.`) da consulta percorre a hierarquia quando um termo-chave explícito é usado, como `Resources` ou `Properties.Encrypted.` Se alguma parte da consulta não corresponder ao datum de entrada, o Guard gerará um erro de recuperação.
+ Uma parte dot (`.`) da consulta que usa um curinga `*` percorre todos os valores da estrutura nesse nível.
+ Uma parte dot (`.`) da consulta que usa um curinga de matriz `[*]` percorre todos os índices dessa matriz.
+ Todas as coleções podem ser filtradas especificando filtros dentro de colchetes. `[]` As coleções podem ser encontradas das seguintes maneiras:
  + As matrizes que ocorrem naturalmente no datum são coleções. A seguir estão exemplos da :

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

    Etiquetas: `[{"Key": "Stage", "Value": "PROD"}, {"Key": "App", "Value": "MyService"}]`
  + Ao percorrer todos os valores de uma estrutura como `Resources.*`
  + Qualquer resultado de consulta é, em si, uma coleção da qual os valores podem ser filtrados posteriormente. Veja o exemplo a seguir.

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

Veja a seguir um exemplo de trecho CloudFormation de modelo.

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

Com base nesse modelo, o caminho percorrido é `SampleRole` e o valor final selecionado é. `Type: AWS::IAM::Role`

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

O valor resultante da consulta `Resources.*[ Type == 'AWS::IAM::Role' ]` no formato YAML é mostrado no exemplo a seguir.

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

Algumas das maneiras pelas quais você pode usar consultas são as seguintes:
+ Atribua uma consulta às variáveis para que os resultados da consulta possam ser acessados referenciando essas variáveis.
+ Siga a consulta com um bloco que testa cada um dos valores selecionados.
+ Compare uma consulta diretamente com uma cláusula básica.

## Atribuição de consultas a variáveis
<a name="queries-and-filtering-variables"></a>

O Guard suporta atribuições de variáveis únicas dentro de um determinado escopo. Para obter mais informações sobre variáveis nas regras do Guard, consulte[Atribuição e referência de variáveis nas regras do Guard](variables.md).

Você pode atribuir consultas a variáveis para poder escrever consultas uma vez e depois referenciá-las em outro lugar nas regras do Guard. Veja o exemplo a seguir de atribuições de variáveis que demonstram os princípios de consulta discutidos posteriormente nesta seção.

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

## Percorrendo diretamente os valores de uma variável atribuída a uma consulta
<a name="variable-assigned-from-query"></a>

O Guard suporta a execução direta dos resultados de uma consulta. No exemplo a seguir, o `when` bloco testa a `AvailabilityZone` propriedade `Encrypted``VolumeType`, e para cada `AWS::EC2::Volume` recurso encontrado em um CloudFormation modelo.

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

## Comparações diretas em nível de cláusula
<a name="direct-clause-level-comparisons"></a>

O Guard também oferece suporte a consultas como parte das comparações diretas. Por exemplo, veja o seguinte:

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

No exemplo anterior, as duas cláusulas (começando com a `some` palavra-chave) expressas na forma mostrada são consideradas cláusulas independentes e são avaliadas separadamente.

### Formulário de cláusula única e cláusula de bloco
<a name="single-versus-block-clause-form"></a>

Juntas, as duas cláusulas de exemplo mostradas na seção anterior não são equivalentes ao bloco a seguir.

```
let resources = Resources.*

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

Esse bloco consulta cada `Tag` valor na coleção e compara seus valores de propriedade com os valores de propriedade esperados. A forma combinada das cláusulas na seção anterior avalia as duas cláusulas de forma independente. Considere a seguinte entrada.

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

As cláusulas na primeira forma são avaliadas como. `PASS` Ao validar a primeira cláusula na primeira forma, o caminho a seguir através de `Resources``Properties`,,`Tags`, e `Key` corresponde ao valor `NotPRODEnd` e não corresponde ao valor esperado. `PROD`

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

O mesmo acontece com a segunda cláusula do primeiro formulário. O caminho através de `Resources``Properties`,`Tags`,, e `Value` corresponde ao valor`AppStart`. Como resultado, a segunda cláusula de forma independente.

O resultado geral é um`PASS`.

No entanto, o formulário de bloqueio é avaliado da seguinte forma. Para cada `Tags` valor, ele compara se o `Key` e `Value` corresponde; `NotAppStart` e `NotPRODEnd` os valores não são correspondidos no exemplo a seguir.

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

Porque as avaliações verificam ambos e `Key == /PROD$/``Value == /^App/`, a partida não está completa. Portanto, o resultado é`FAIL`.

**nota**  
Ao trabalhar com coleções, recomendamos que você use o formulário de cláusula de bloco quando quiser comparar vários valores para cada elemento na coleção. Use o formulário de cláusula única quando a coleção for um conjunto de valores escalares ou quando você pretende comparar apenas um único atributo.

## Resultados da consulta e cláusulas associadas
<a name="query-outcomes"></a>

Todas as consultas retornam uma lista de valores. Qualquer parte de uma travessia, como uma chave ausente, valores vazios para um array (`Tags: []`) ao acessar todos os índices ou valores ausentes para um mapa ao encontrar um map (`Resources: {}`) vazio, pode levar a erros de recuperação.

Todos os erros de recuperação são considerados falhas ao avaliar as cláusulas em relação a essas consultas. A única exceção é quando filtros explícitos são usados na consulta. Quando os filtros são usados, as cláusulas associadas são ignoradas.

As seguintes falhas de bloco estão associadas à execução de consultas.
+ Se um modelo não contiver recursos, a consulta será avaliada como`FAIL`, e as cláusulas de nível de bloco associadas também serão avaliadas como. `FAIL`
+ Quando um modelo contém um bloco de recursos vazio`{ "Resources": {} }`, como, a consulta é avaliada como`FAIL`, e as cláusulas de nível de bloco associadas também são avaliadas como. `FAIL`
+ Se um modelo contiver recursos, mas nenhum corresponder à consulta, a consulta retornará resultados vazios e as cláusulas de nível de bloco serão ignoradas.

## Usando filtros em consultas
<a name="filtering"></a>

Os filtros nas consultas são efetivamente cláusulas do Guard que são usadas como critérios de seleção. A seguir está a estrutura de uma cláusula.

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

Lembre-se dos seguintes pontos-chave [AWS CloudFormation Guard Regras de redação](writing-rules.md) ao trabalhar com filtros:
+ Combine cláusulas usando a [Forma Normal Conjuntiva (CNF](https://en.wikipedia.org/wiki/Conjunctive_normal_form)).
+ Especifique cada cláusula de conjunção (`and`) em uma nova linha.
+ Especifique disjunções (`or`) usando a `or` palavra-chave entre duas cláusulas.

O exemplo a seguir demonstra as cláusulas conjuntivas e disjuntivas.

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

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

### Usando cláusulas para critérios de seleção
<a name="selection-criteria"></a>

Você pode aplicar a filtragem a qualquer coleção. A filtragem pode ser aplicada diretamente em atributos na entrada que já são como `securityGroups: [....]` uma coleção. Você também pode aplicar a filtragem em uma consulta, que é sempre uma coleção de valores. Você pode usar todos os recursos das cláusulas, incluindo a forma normal conjuntiva, para filtragem.

A consulta comum a seguir é frequentemente usada ao selecionar recursos por tipo em um CloudFormation modelo.

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

A consulta `Resources.*` retorna todos os valores presentes na `Resources` seção da entrada. Para o exemplo de entrada do modelo em[Definindo consultas](#defining-queries), a consulta retorna o seguinte.

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

Agora, aplique o filtro nessa coleção. O critério de correspondência é`Type == AWS::IAM::Role`. A seguir está a saída da consulta após a aplicação do filtro.

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

Em seguida, verifique várias cláusulas para obter `AWS::IAM::Role` recursos.

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

Veja a seguir um exemplo de consulta de filtragem que seleciona todos `AWS::IAM::Policy` os `AWS::IAM::ManagedPolicy` recursos.

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

O exemplo a seguir verifica se esses recursos de política têm um `PolicyDocument` especificado.

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

### Criando necessidades de filtragem mais complexas
<a name="complex-filtering"></a>

Considere o exemplo a seguir de um item de AWS Config configuração para informações de grupos de segurança de entrada e saída.

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

Observe o seguinte:
+ `ipPermissions`(regras de entrada) é uma coleção de regras dentro de um bloco de configuração.
+ Cada estrutura de regra contém atributos como `ipv4Ranges` e `ipv6Ranges` para especificar uma coleção de blocos CIDR.

Vamos escrever uma regra que seleciona todas as regras de entrada que permitem conexões de qualquer endereço IP e verifica se as regras não permitem que portas bloqueadas por TCP sejam expostas.

Comece com a parte da consulta que abrange IPv4, conforme mostrado no exemplo a seguir.

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

A `some` palavra-chave é útil nesse contexto. Todas as consultas retornam uma coleção de valores que correspondem à consulta. Por padrão, o Guard avalia se todos os valores retornados como resultado da consulta são comparados com as verificações. No entanto, esse comportamento nem sempre é o que você precisa para verificações. Considere a seguinte parte da entrada do item de configuração.

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

Há dois valores presentes para`ipv4Ranges`. Nem todos os `ipv4Ranges` valores são iguais a um endereço IP indicado por. `0.0.0.0/0` Você quer ver se pelo menos um valor corresponde`0.0.0.0/0`. Você diz ao Guard que nem todos os resultados retornados de uma consulta precisam corresponder, mas pelo menos um resultado deve corresponder. A `some` palavra-chave diz ao Guard que garanta que um ou mais valores da consulta resultante correspondam à verificação. Se nenhum valor do resultado da consulta corresponder, o Guard gerará um erro.

Em seguida, adicione IPv6, conforme mostrado no exemplo a seguir.

```
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 fim, no exemplo a seguir, confirme se o protocolo não `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' ] 
]
```

A seguir está a regra 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
                    >>
                }
            }

        }       
     }
}
```

### Separando coleções com base em seus tipos contidos
<a name="splitting-collection"></a>

Ao usar modelos de configuração de infraestrutura como código (IaC), você pode encontrar uma coleção que contém referências a outras entidades dentro do modelo de configuração. Veja a seguir um exemplo de CloudFormation modelo que descreve as tarefas do Amazon Elastic Container Service (Amazon ECS) com uma referência local, uma referência `TaskRoleArn` a e uma referência `TaskArn` direta de string.

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

Considere a seguinte consulta.

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

Essa consulta retorna uma coleção de valores que contém todos os três `AWS::ECS::TaskDefinition` recursos mostrados no modelo de exemplo. Separe `ecs_tasks` os que contêm referências `TaskRoleArn` locais dos outros, conforme mostrado no exemplo a seguir.

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