O blog da AWS

Como monitorar conformidade criando regras dinâmicas com AWS Config Rules, AWS Cloudformation, AWS Lambda e documentos do AWS Systems Manager

Por Guilherme Greco, Arquiteto de Soluções AWS Brasil e
Kimmy Wu, Arquiteta de Soluções AWS Brasil

 

Os projetos bem-sucedidos de migração massiva para nuvem consideram a necessidade de governança e criação de padrões de conformidade para segurança, monitoração, arquitetura e custo.  A conformidade dos recursos criados com regras pertinentes para sua organização, trazem liberdade para times de operação e infraestrutura criarem recursos com segurança de que serão automaticamente avaliados e alertados em caso de desvio.

Esse blog post demonstra as automações que podem ser utilizadas em uma perspectiva de monitoração. Garantindo que determinados recursos lançados em uma conta AWS tenham o padrão de conformidade definido previamente pela organização.

 

Visão Geral:

Com templates de Cloudformation, Template de Fundação – foundation.yaml e o template de monitoramento – monitoring-compliance.yaml, para criação de recursos dinamicamente, é possível atualizar o template com determinado parâmetro e recriar automaticamente regras e monitorações de conformidade.

Utilizaremos regras de conformidade (AWS Config Rules) customizadas e avaliações de recursos feitas via AWS Lambda.  Isso é realizado através de eventos iniciados a partir do AWS Config, que são enviados para a função Lambda responsável pela avaliação de conformidade do recurso. Será através de um documento do Systems Manager que remediações serão feitas.

Esse cenário é possível graça as integrações entre AWS Config Rule e SSM Documents.  Através do Event Bridge, a automação proposta poderá remover alarmes de instâncias que não existam mais ou iniciar a execução de fluxos de automações usando SSM Documents.

Alarmes do CloudWatch farão parte da conformidade definida pela organização, permitindo regras de conformidade para monitoração nos serviços Amazon EC2, Amazon RDS, Amazon Lambda, Amazon S3, Amazon SNS, Amazon SQS, Amazon EBS, Amazon DynamoDB e Amazon Application Load Balancer

Na arquitetura proposta, temos as seguintes regras de conformidade:

  • Regra de Conformidade 1 – verifica se os alarmes criados seguem o padrão de notificação
  • Regra de Conformidade 2 – verifica Alarmes e Métricas necessários para monitorar um recurso foram criados
  • Regra de Conformidade 3 – verifica se seus alarmes sofreram modificações
  • Regra de Conformidade 4 – verifica se uma instância EC2 foi removida

Você poderá optar pela métrica e limite de cada serviço. Assim, através de Infraestrutura como código, regras de conformidade serão criadas automaticamente, seguindo os parâmetros inseridos.

Regra de Conformidade 1 – verifica se os alarmes criados seguem o padrão de notificação

A automação é responsável por garantir que todos os alarmes possuam o padrão de notificação. O template foundation.yaml criará recursos e automações que serão utilizados pela stack de Conformidade de Alarmes, como função Lambda e tópico SNS para notificação padrão. Outros recursos que serão criados

  • Tópico SNS padrão de notificação.
  • Função Lambda: Será utilizado para fazer a validação da conformidade do recurso.
  • Regra de Eventos no Event Bridge: utilizada para disparar um documento do SSM que removerá alarmes desnecessários para EC2.
  • Documentos do SSM.
  • ClearAlarms: Documento SSM que removerá alarmes CloudWatch de instancias que foram removidas.
  • CreateSNSAction: Documento SSM que criará ação obrigatória em todos os alarmes direcionando as notificações para o Tópico SNS padrão.
  • CloudWatchConfigRule: Regra de conformidade que avalia os alarmes do CloudWatch.

 

Figura 1: Conformidade de Notificação de Alarmes

 

A Figura 1 descreve a automação que garante que todos os alarmes do CloudWatch tenham uma ação de notificação para o tópico SNS padrão. O tópico encaminhará as mensagens para a subscrição feita via e-mail:

  1. Um novo CloudWatch Alarm é criado ou um existente é alterado.
  2. Um evento é enviado ao AWS Config, descrevendo as alterações ou configurações do recurso.
  3. A regra de configuração da AWS avalia o recurso criado invocando a função Lambda.
  4. A Função Lambda responde à regra de configuração com status COMPLIANT ou NON_COMPLIANT. Se NON_COMPLIANT, o AWS Config Rule acionará um documento SSM para executar a correção automática. Assim, o CloudWatch Alarme terá a ação de notificação configurada.

Regra de Conformidade 2  – verifica Alarmes e Métricas necessários para monitorar um recurso foram criados (template: monitoring-compliance.yaml)

Essa porção da automação verifica se as métricas necessárias para monitoração de um recurso foram criadas.

 

Figura 2: Fluxo de Automação para conformidade de monitoração quando um recurso é criado

 

Conforme o diagrama da figura 2, nós temos o seguinte fluxo:

  1. Um novo recurso suportado pela automação é criado.
  2. Um evento é enviado ao AWS Config após a criação.
  3. A regra de configuração da AWS avalia o recurso criado chamando um Lambda que verifica se o recurso possui todas as métricas relacionadas ao monitoramento.
  4. Uma função Lambda responde à regra de configuração com status COMPLIANT ou NON_COMPLIANT. Se NON_COMPLIANT, o AWS Config acionará um documento SSM para executar a correção automaticamente, criando alarmes com métricas padrões.

Regra de Conformidade 3  – verifica se seus alarmes sofreram modificações (template: monitoring-compliance.yaml)

A segunda porção da automação verifica se os alertas de sofreram modificação. O valor padrão do limite de cada métrica não é obrigatório. Somente as métricas e alarmes são. Isso beneficia um cenário em que um determinado servidor possa ter um limite de CPU de 99%.  Assim podendo atualizar o alarme mudando o limite para o valor desejado. A automação manterá o limite desejado e não haverá remediações.  A remediação ocorrerá somente quando um alarme mandatório e sua respectiva métrica forem removidos ou editados.

 

Figura 3: Fluxo de Automação para conformidade de monitoração quando um alarme é modificado

 

Conforme o diagrama da figura 3, nós temos o seguinte fluxo:

  1. Um Alarm CloudWatch é modificado ou removido.
  2. O evento é enviado para o Event Bridge, validando se o Alarme não foi modificado pelo Systems Manager. Isso é necessário para evitar um loop na automação.
  3. O Document SSM invoca a API do AWS Config, solicitando uma nova reavaliação do recurso e dos alarmes.
  4. A função Lambda é invocada e analisa os alarmes existentes. Caso seja necessário a remediação dos alarmes, a Lambda envia para o Config o status de NON_COMPLIANT.
  5. AWS Config Rule invoca um Document do SSM, que remedia as configurações dos alarmes.

Regra de Conformidade 4  – verifica se uma instância EC2 foi removida (template: monitoring-compliance.yaml)

 

Figura 4: Fluxo de Automação para conformidade de monitoração quando uma EC2 é removida

 

Conforme o diagrama da Figura 4, nós temos o seguinte fluxo:

  1. Uma instância EC2 é removida.
  2. Um evento é enviado para o Event Bridge. Uma Rule invoca o Document SSM.
  3. O Document SSM aguarda 5 minutos. Esse tempo é necessário para que o AWS Config seja atualizado, sincronizando a informação que a instância EC2 não existe mais. Isso evita que a automação entre em loop.
  4. O Document SSM remove os alarmes correspondentes a essa instância.

Fluxo para criação das regras de conformidade

O fluxo para criação de conformidades de monitoração para cada serviço será o seguinte:

 

Figura 5: Fluxo para criação de conformidades de monitoração

 

  1. Analisar métricas do Serviço: identifica a métrica mandatória para a conformidade do serviço.
  2. Definir quais métricas são mandatórias: O administrador do ambiente, define quais as métricas do serviço são obrigatórias para a conformidade, criando alarmes dedicados utilizados como regra para avaliação. Por padrão, a automaçãoaté 3 métricas.
  3. Lançar o template: O template monitoring-compliance.yaml será executado.

Utilizando o template monitoring-compliance.yaml

Para utilizar o template, basta fazer o download e utilizar dentro do CloudFormation. A figura 6 descreve os seguintes parâmetros utilizados no template:

  • Stack Name: nome da stack. Ex: EC2-Mandatory-Alarms.
  • List of AWS Config ResourceIds that is whiltelisted for this compliance rule: lista de ID’s de instância EC2 que serão excluídas na avaliação da conformidade. Trata-se de um parâmetro opcional.
  • Topic Name that will be used to send notifications: Nome do Tópico SNS que foi criado na stack de fundação, gerada pelo template foundation.yaml.
  • Enable Automatic Remediation: parâmetro true para a remediação automática ou false para a não automatização.
  • NameSpace for CloudWatch Metric: Nome do serviço AWS que fará parte da conformidade de monitoração. Ex: EC2.

 

Figura 6: Parâmetros para criação de stack usando o template monitoring-compliance.yaml

 

O próximo passo é identificar os parâmetros que definirão quantas métricas e respectivos alarmes existirão para determinado serviço. No exemplo estamos utilizando instância EC2, com 2 alarmes mandatórios, seguindo as métricas de CPUUtilization e StatusCheckFailed_System. Não utilizaremos a terceira métrica e consequentemente o Alarme 3 não será criado.

 

Figura 7: Continuação de Parâmetros para criação de stack usando o template monitoring-compliance.yaml

 

Alarm1 Configurations:

  • Alarm1 Metric Name: Nome da Métrica mandatória. Ex: CPUUtilization.
  • Alarm1 Metric Threshold: Limite da Métrica escolhida para o Alarme 1. Esse será o valor padrão que será configurado para o alarme durante. Ex: 80.
  • Alarm1 Metric Statitstics: Lista de opções para a estatística. Ex: Maximum.

O conjunto de parâmetros acima (Alarm1 Configurations) criará o primeiro padrão de monitoração para EC2. Toda instância deverá possuir um Alarme monitorando a métrica (CPUUtilization), e enviará uma notificação para o Tópico LocalTopic quando o limite ultrapassar o máximo de 80.

Para o alarme 2 é feito as configurações com os mesmos parâmetros do alarme 1, alterando o nome para StatusCheckFailed_System e Metric Threshold para 1. A automação criará 2 ações: uma notificação para o tópico padrão e uma ação para o serviço EC2, fazendo a recuperação automática da instância EC2. Para o alarme 3 basta não inserir o nome para não ter a sua criação.

Ao executar o template, uma stack dedicada para conformidade de EC2 será criada. Caso seja necessário a criação de conformidades para outros serviços, outra stack será criada e o parâmetro NameSpace for CloudWatch Metric direcionado para o serviço.

Inclusão ou alteração de métricas mandatórias para determinado serviço

Para ver detalhes da conformidade, navegue até AWS Config e Rules. Teremos a regra EC2-with-mandatory-alarms. O padrão de nomes será sempre: <Nome do Serviço>-with-mandatory-alarms. Exemplo: caso uma stack de conformidade de RDS seja criado, teríamos RDS-with-mandatory-alarms

 

Figura 8: Config Rules e parâmetros

 

A figura 8, descreve a Config Rule e seus parâmetros, que foram inseridos durante a criação da stack. Temos 4 parâmetros, que foram inseridos no lançamento da stack:

  • Whitelistedresources: é uma lista de recursos que não fará parte da conformidade. Representa os identificadores únicos de cada recurso que será excluído das regras de validação de conformidade. Neste exemplo nós utilizamos os Ids das instancias EC2;
  • Alarm1/2/3 Config: Um objeto json criado pelo Cloudformation, seguindo os parâmetros inseridos durante a criação da stack para o alarm1, alarm2 e alarm3;

Como não definimos o terceiro parâmetro, essa configuração ficou com o valor de MetricName “null”.  Isso quer dizer que não haverá um terceiro alarme mandatório.

Um Documento do SSM foi criado para remediação dos alarmes, seguindo os padrões estipulados. Para visualizar navegue até AWS Systems Manager à Documents à Owned by Me. Teremos um Document com o seguinte nome: <Serviço>-Mandatory-Alarms-OpsAutomatin-<hash da stack>, possuindo os 5 passos de automação, conforme figura 9.

 

Figura 9: Steps de Automação no SSM Document

 

No passo “VerifyCloudWatchConfig”, temos uma ação “aws:executscript” que invoca um código Python descrito dentro do documento SSM. O código é responsável por coletar as informações dos alarmes e enviar para o próximo passo do fluxo. Os próximos passos possuem ações do tipo “aws:executeAwsApi”, que invocam APIs de serviço AWS nativamente.

O AWS Config Rule validará as monitorações das instâncias existentes. O resultado da avaliação (COMPLIANT ou NON_COMPLIANT) estará disponível na regra, conforme figura 10.

 

Figura 10: Detalhes da Config Rule

 

Através do AWS Config Rule conseguimos visualizar o ID da instância EC2 que foi avaliada, o tipo de serivço (EC2), o resultado da avaliação (contendo as métricas que não possuem alarmes para essa instância) e o status da remediação (integração ente AWS Config Rule e SSM Document).

Navegue para Systems Manager à Automation. Em “Automations Executions”, teremos a execução do Documento:

 

Figure 11 Execução do SSM Document

 

Após execução das automações, teremos no CloudWatch, 2 alarmes correspondentes a EC2, seguindo as métricas que estipulamos durante a criação da conformidade. A figura 12 mostra uma EC2 que foi lançada na conta, passou pela avaliação da regra e sofreu uma remediação, criando os alarmes obrigatórios.

 

Figure 12 Alarmes do CloudWatch

 

Agora vamos alterar os parâmetros do template e adicionar uma nova regra para ver como uma infraestrutura como código simplifica o processo. O objetivo é atualizar a regra do AWS Config adicionando uma métrica nova e por consequência atualizar o Documento de remediação, adicionando um passo para validação e remediação do Alarm3.

Em Cloudformation à Stacks, selecionar EC2-Mandatory-Alarms à Update à Use Current Template. Adicionar os seguintes valores nos campos Alarm3 Configurations:

  • Alarm3 Metric Name: StatusCheckFailed_Instance
  • Alarm3 Metric Threshold:1
  • Alarm3 Metric Statitstics:Maximum

Após a atualização da stack, temos novos parâmetros (Alarm3) como parte da conformidade

 

Figure 13 Config Rule e parâmetros

 

O Documento que é executado para remediação possuirá um passo para verificar e remediar o Alarme3.

 

Figura 14: Steps de Automação no SSM Document

 

Uma vez que o Config Rule está atualizado e com os 3 alarmes descritos como mandatório, podemos reavaliar a regra.  Clique em AWS Config à Rules à EC2-with-mandatory-alarms à Re-evaluate. Nesse momento o AWS Config invocará a Lambda, que receberá os parâmetros dos 3 alarmes mandatórios. Através desses parâmetros, a avaliação será feita, resultando em uma regra COMPLIANT ou NON_COMPLAINT. Visto que criamos um novo alarme e a instância EC2 não possui o Alarm3 será gerado uma não conformidade:

 

Figura 15: Resultado de Avaliação do Config Rule

 

Todo o fluxo de remediação ocorrerá novamente, criando um novo alarme para a EC2 específica. Navegue para Systems Manager à Automation. Em “Automations Executions”, teremos a execução do Documento:

 

Figura 16: Execução do SSM Document

 

Em CloudWatch Alarms, podemos ver o novo alarme criado automaticamente, fazendo parte da conformidade. Assim, temos 3 alarmes criados.

 

Figura 17: CloudWatch Alarmes

 

Detalhes sobre o código utilizado na Automação:

Existem detalhes importantes que podem ser encontrados nos templates de Cloudformation:

  • Transformação de parâmetros no Cloudformation: Os parâmetros inseridos no template, em campos string, são transformados em json dentro do CloudFormation, em tempo de execução da stack, atuando como parâmetro para o AWS Config Rule. O trecho abaixo, demostra a criação dinâmica do objeto json com funções intrínsecas (!Sub e !FindInMap).

 

OpsConfigRule:
    Type: AWS::Config::ConfigRule
    Properties:
      ConfigRuleName: !Sub "${ServiceTobeMonitored}-with-mandatory-alarms"
      InputParameters: 
        Alarm1Config: !Sub
          - |
            {"MetricName": "${Alarm1MetricName}", "MetricThreshold": ${Alarm1Threshold}, "MetricStatistics": "${Alarm1Statistics}", "MetricNamespace": "${namespace}", "MetricDimensions": "${dimension}"}
          - namespace: !FindInMap [NamespaceMapping, !Ref ServiceTobeMonitored, Namespace] 
            dimension: !FindInMap [NamespaceMapping, !Ref ServiceTobeMonitored, Dimension] 
        Alarm2Config: !Sub
          - | 
            {"MetricName": "${Alarm2MetricName}", "MetricThreshold": ${Alarm2Threshold}, "MetricStatistics": "${Alarm2Statistics}", "MetricNamespace": "${namespace}", "MetricDimensions": "${dimension}"}
          - namespace: !FindInMap [NamespaceMapping, !Ref ServiceTobeMonitored, Namespace] 
            dimension: !FindInMap [NamespaceMapping, !Ref ServiceTobeMonitored, Dimension]
            Alarm2MetricName: !If [ CreateAlarm2, !Ref Alarm2MetricName , 'null' ]
        Alarm3Config: !Sub 
          - | 
            {"MetricName": "${Alarm3MetricName}", "MetricThreshold": ${Alarm3Threshold}, "MetricStatistics": "${Alarm3Statistics}", "MetricNamespace": "${namespace}", "MetricDimensions": "${dimension}"}
          - namespace: !FindInMap [NamespaceMapping, !Ref ServiceTobeMonitored, Namespace] 
            dimension: !FindInMap [NamespaceMapping, !Ref ServiceTobeMonitored, Dimension]
            Alarm3MetricName: !If [ CreateAlarm3, !Ref Alarm3MetricName , 'null' ]
        Whitelistedresources: !Ref Whitelistedresources

 

  • Código Python embutido no SSM Document: O caso de uso para o cenário é quando precisamos fazer uma iteração ou algoritmo mais complexo no resultado de uma chamada de API. Como benefício de um código contido no próprio Documento SSM, facilitando a centralização da automação.

 

OpsAutomationDocument:
    Type: 'AWS::SSM::Document'
    Properties: 
      DocumentType: Automation
      Content:
        schemaVersion: "0.3"
        assumeRole: "{{ AutomationAssumeRole }}"
        description: SSM Document
        mainSteps:
          - name: VerifyCloudWatchConfig
            action: 'aws:executeScript'
            inputs:
              Runtime: python3.6
              Handler: script_handler
              Script: |-
                import json, boto3, string
                client_cw = boto3.client('cloudwatch')
                client_config = boto3.client('config')
                def get_resource_history(events):
                  response = client_config.get_resource_config_history(
                      resourceType=events['ConfigServiceName'],
                      resourceId=events['ResourceId'])
                  return response
                def script_handler(events, context):
                  print(events)
                  temp_dict = {}
                  results = {}
                  response = get_resource_history(events)
                  if events['ConfigServiceName'] == 'AWS::RDS::DBInstance' or events['ConfigServiceName'] == 'AWS::SNS::Topic':
                    resourceId_cw = response['configurationItems'][0]['resourceName']
                  elif events['ConfigServiceName'] == 'AWS::ElasticLoadBalancingV2::LoadBalancer':
                    print('this is the response %s' % response)
                    alb_resource_name = response['configurationItems'][0]['resourceId']
                    resourceId_cw = alb_resource_name.split('/')[1]+'/'+alb_resource_name.split('/')[2]+'/'+alb_resource_name.split('/')[3]
                  else:
                    resourceId_cw = events['ResourceId']
                  del events['ResourceId']
                  del events['ConfigServiceName']
                  for key in events:
                    temp_dict = json.loads(events[key])
                    if temp_dict['MetricName'] == 'null':
                      del key
                  for key in events:
                    temp_dict = json.loads(events[key])
                    metric = temp_dict['MetricName']
                    namespace = temp_dict['MetricNamespace']
                    dimension = temp_dict['MetricDimensions']
                    alarm_name = f'{resourceId_cw}-{metric}'
                    try:
                        response = client_cw.describe_alarms_for_metric(
                            MetricName=metric,
                            Namespace=namespace,
                            Dimensions=[{'Name': dimension,'Value': resourceId_cw}]
                        )
                        if len(response['MetricAlarms']) == 0:
                          results[key] = json.loads(events[key])
                          for key in results: 
                            results[key]['MetricThreshold'] = int(results[key]['MetricThreshold'])
                        else:
                          for i in response['MetricAlarms']: 
                            if i['AlarmName'] == alarm_name:
                              results[key] = {'MetricName': i['MetricName'],
                                  'MetricThreshold': int(i['Threshold']),
                                  'MetricStatistics': i['Statistic'],
                                  'MetricNamespace': i['Namespace'],
                                  'MetricDimensions': i['Dimensions'][0]['Name']}
                            else:
                              results[key] = json.loads(events[key])
                    except Exception as e:
                        print('Error %s' % (e)) 
                  results['ResourceId_CW'] = resourceId_cw
                  return results
              InputPayload:
                Alarm1Config: '{{ Alarm1Config }}'
                Alarm2Config: '{{ Alarm2Config }}'
                Alarm3Config: '{{ Alarm3Config }}'
                ResourceId: '{{ ResourceId }}'
                ConfigServiceName: '{{ ConfigServiceName }}'

 

No exemplo acima, criamos uma Automação do tipo aws:executeScript. A automação invoca o código Python através de Parâmetros de Entrada e retornará para o próximo passo os Parâmetros de Saída:

  • Parâmetros de Entrada: InputPayload (Alarm1Config, Alarm2Config, Alarm3Config e ConfigServiceName).
  • Parâmetros de Saída: Em Outputs retorna um objeto json, que usando filtros no Output, é possível encaminhar os valores para próximos passos do fluxo de automação

 

Conclusão

Conformidade como código é uma prática importante para automatizar padrões e arquiteturas de referência. Uma governança automatizada e baseada em código beneficia organizações que necessitam de monitorações para garantir seus padrões.

Os serviços utilizados criam este objetivo, integrando de forma nativa e possibilitando customizações em linguagem de notação ou linguagem de programação. AWS Config Rules podem ter regras de configuração customizadas com avaliações e integração com Lambda, Event Bridge, monitora e dispara ações através de eventos específicos, SSM Documents possuem várias opções de customização através de chamadas de API ou código embutido.

Os códigos da implementação acima estão disponíveis no GitHub . Podendo ser alterado conforme necessidade.

 

 


Sobre os autores

Guilherme é Arquiteto de Soluções na AWS, focado no segmento financeiro.. Com 18 anos de experiência em infraestrutura e arquitetura, Guilherme auxilia os clientes da AWS desde 2018 a terem sucesso em suas jornadas para nuvem, desde a etapa de criação de uma fundação sólida até migrações de soluções corporativas.

 

 

 

 

Kimmy Wu é arquiteta de soluções e responsável por ajudar clientes a criarem workloads que permitem o avanço da sua jornada de transformação digital na nuvem da AWS.