¿Cómo soluciono el error que recibo en CloudFormation cuando intento publicar registros lentos en registros de CloudWatch?

6 minutos de lectura
0

Quiero resolver el error que recibo en AWS CloudFormation cuando intento publicar registros lentos en Registros de Amazon CloudWatch. El error es: «La política de acceso a los recursos especificada para el grupo de registro de registros de CloudWatch /aws/aes/domains/search/search-logs no otorga permisos suficientes a Amazon Elasticsearch Service para crear una secuencia de registro».

Breve descripción

Para resolver este error, utilice una política independiente a nivel de grupo de registros para permitir que Amazon Elasticsearch Service (Amazon ES) envíe registros a Registros de CloudWatch. A continuación, utilice AccessPolicies en el recurso AWS::Elasticsearch::Domain para establecer los permisos de los dominios de Amazon ES.

Los siguientes pasos muestran cómo publicar registros lentos en CloudWatch con CloudFormation mediante un recurso personalizado respaldado por AWS Lambda creado en Python 3.6. El recurso personalizado activa una función de Lambda, que activa la API PutResourcePolicy para publicar registros lentos.

Nota: Cuando CloudFormation permite la publicación de registros en CloudWatch, el recurso AWS::Logs::LogGroup no tiene una propiedad para asignar una política de acceso a los recursos. La política de acceso a los recursos especificada para el grupo de registro de Registros de CloudWatch debe conceder permisos suficientes para que Amazon ES publique la secuencia de registro. No puede crear un permiso de política de acceso directamente mediante un recurso de CloudFormation. Esto se debe a que CloudFormation no admite la llamada a la API PutResourcePolicy para el recurso AWS::Logs::LogGroup.

Resolución

Nota: Si recibe errores al ejecutar los comandos de la Interfaz de la línea de comandos de AWS (AWS CLI), asegúrese de utilizar la versión más reciente de la AWS CLI.

La siguiente plantilla de CloudFormation utiliza un recurso personalizado para obtener el nombre del grupo de registro. A continuación, la plantilla aplica la política que permite al servicio de Amazon ES realizar llamadas a la API en el grupo de registro.

1.    Cree una plantilla de CloudFormation llamada ESlogsPermission.yaml:

Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

AWSTemplateFormatVersion: 2010-09-09
Description: AWS cloudFormation template to publish slow logs to Amazon CloudWatch Logs.
Parameters:
  LogGroupName:
    Type: String
    Description: Please don't change the log group name while updating
  ESDomainName:
    Description: A name for the Amazon Elastic Search domain
    Type: String
  LambdaFunctionName:
    Description: Lambda Function Name
    Type: String
Resources:
  AwsLogGroup:
    Type: 'AWS::Logs::LogGroup'
    Properties:
      LogGroupName: !Ref LogGroupName
  LambdaLogGroup:
    Type: 'AWS::Logs::LogGroup'
    Properties:
      LogGroupName: !Sub '/aws/lambda/${LambdaFunctionName}'
  LambdaExecutionRole:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      Path: /
      Policies:
        - PolicyName: root1
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - 'logs:CreateLogStream'
                  - 'logs:PutLogEvents'
                Resource: !Sub >-
                  arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${LambdaFunctionName}:log-stream:*
        - PolicyName: root2
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - 'logs:CreateLogGroup'
                Resource:
                  - !Sub >-
                    arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${LambdaFunctionName}
                  - !Sub >-
                    arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/${LogGroupName}
              - Effect: Allow
                Action:
                  - 'logs:PutResourcePolicy'
                  - 'logs:DeleteResourcePolicy'
                Resource: !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*'
  logGroupPolicyFunction:
    DependsOn: LambdaLogGroup
    Type: 'AWS::Lambda::Function'
    Properties:
      FunctionName: !Ref LambdaFunctionName
      Code:
        ZipFile: >
          import urllib3

          import json

          import boto3

          http = urllib3.PoolManager()

          SUCCESS = "SUCCESS"

          FAILED = "FAILED"

          def send(event, context, responseStatus, responseData,
          physicalResourceId=None, noEcho=False):
              responseUrl = event['ResponseURL']
              print(responseUrl)
              responseBody = {}
              responseBody['Status'] = responseStatus
              responseBody['Reason'] = 'See the details in CloudWatch Log Stream: ' + context.log_stream_name
              responseBody['PhysicalResourceId'] = physicalResourceId or context.log_stream_name
              responseBody['StackId'] = event['StackId']
              responseBody['RequestId'] = event['RequestId']
              responseBody['LogicalResourceId'] = event['LogicalResourceId']
              responseBody['NoEcho'] = noEcho
              responseBody['Data'] = responseData
              json_responseBody = json.dumps(responseBody)
              print("Response body:\n" + json_responseBody)
              headers = {
                  'content-type' : '',
                  'content-length' : str(len(json_responseBody))
              }
              try:
                  response = http.request('PUT',responseUrl,body=json_responseBody.encode('utf-8'),headers=headers)
                  print("Status code: " + response.reason)
              except Exception as e:
                  print("send(..) failed executing requests.put(..): " + str(e))
          def handler(event, context):
              logsgroup_policy_name=event['ResourceProperties']['CWLOGS_NAME']
              cw_log_group_arn=event['ResourceProperties']['CWLOG_ARN']
              cwlogs = boto3.client('logs')
              loggroup_policy={
                  "Version": "2012-10-17",
                  "Statement": [{
                  "Sid": "",
                  "Effect": "Allow",
                  "Principal": { "Service": "es.amazonaws.com"},
                  "Action":[
                  "logs:PutLogEvents",
                  " logs:PutLogEventsBatch",
                  "logs:CreateLogStream"
                      ],
                  'Resource': f'{cw_log_group_arn}'
                  }]
                  }
              loggroup_policy = json.dumps(loggroup_policy)
              if(event['RequestType'] == 'Delete'):
                  print("Request Type:",event['RequestType'])
                  cwlogs.delete_resource_policy(
                  policyName=logsgroup_policy_name
                 )
                  responseData={}
                  send(event, context, SUCCESS, responseData)
              elif(event['RequestType'] == 'Create'):
                  try:
                      cwlogs.put_resource_policy(
                      policyName = logsgroup_policy_name,
                      policyDocument = loggroup_policy
                       )
                      responseData={}
                      print("Sending response to custom resource")
                      send(event, context, SUCCESS, responseData)
                  except Exception as  e:
                      print('Failed to process:', e)
                      send(event, context, FAILED, responseData)
              elif(event['RequestType'] == 'Update'):
                  try:
                      responseData={}
                      print("Update is not supported on this resource")
                      send(event, context, SUCCESS, responseData)
                  except Exception as  e:
                      print('Failed to process:', e)
                      send(event, context, FAILED, responseData)
      Handler: index.handler
      Role: !GetAtt
        - LambdaExecutionRole
        - Arn
      Runtime: python3.6
  logGroupPolicycustomresource:
    Type: 'Custom::LogGroupPolicy'
    Properties:
      ServiceToken: !GetAtt
        - logGroupPolicyFunction
        - Arn
      CWLOGS_NAME: !Ref LogGroupName
      CWLOG_ARN: !GetAtt
        - AwsLogGroup
        - Arn
  ElasticsearchDomain:
    Type: 'AWS::Elasticsearch::Domain'
    DependsOn: logGroupPolicycustomresource
    Properties:
      DomainName: !Ref ESDomainName
      ElasticsearchVersion: '6.2'
      EBSOptions:
        EBSEnabled: true
        VolumeSize: 10
        VolumeType: gp2
      LogPublishingOptions:
        SEARCH_SLOW_LOGS:
          CloudWatchLogsLogGroupArn: !GetAtt
            - AwsLogGroup
            - Arn
          Enabled: true

2.    Para lanzar una pila de CloudFormation con el archivoESlogsPermission.yaml, utilice la consola de CloudFormation o el siguiente comando de la AWS CLI:

aws cloudformation create-stack --stack-name yourStackName --template-body file://yourTemplateName --parameters ParameterKey=LogGroupName,ParameterValue=Your-LogGroup-Name, ParameterKey=ESDomainName,ParameterValue=Your-ES-Name --capabilities CAPABILITY_NAMED_IAM --region yourRegion

Nota: Sustituya yourStackName, yourTemplateName, Your-LogGroup-Name, Your-ES-Name, y yourRegion por sus valores.

La plantilla de CloudFormation hace lo siguiente por usted:

1.    Crea un grupo de registro.

2.    Crea una función de Lambda. La función de Lambda obtiene el nombre del grupo de registro de la sección Parámetros de la plantilla de CloudFormation mediante un recurso personalizado. La función de Lambda llama a la API PutResourcePolicy para obtener el nombre del grupo de registro. El grupo de registro debe tener una política que permita que el dominio de Amazon ES coloque los registros.

3.    Crea un recurso personalizado respaldado por Lambda para invocar la función de Lambda creada en el paso 2. El recurso personalizado ayuda a aplicar PutResourcePolicy en el grupo de registro «nombre de recurso de Amazon (ARN)» para que Amazon ES pueda transmitir los registros. En la plantilla, CloudFormation utiliza un recurso personalizado para crear un dominio de Amazon ES con LogPublishingOption.


OFICIAL DE AWS
OFICIAL DE AWSActualizada hace 3 años