Search code examples
amazon-web-servicesaws-cloudformationamazon-cloudwatchaws-event-bridgeamazon-cloudtrail

Getting Cloudwatch to send CreateLogGroup messages to EventBridge


I want CloudWatch to send CreateLogGroup messages to EventBridge.

I understand this is possible, but also that Cloudwatch doesn't seem to send these messages by default. It seems you have to configure CloudTrail to get it to forward the message. But I can't find a CloudTrail configuration which works - in general deployment fails with: AWS::CloudTrail::Trail - "Invalid request provided: Incorrect S3 bucket policy is detected for bucket"

AWSTemplateFormatVersion: '2010-09-09'
Outputs:
  HelloFunction:
    Value:
      Ref: HelloFunction
  WatcherFunction:
    Value:
      Ref: WatcherFunction
  WatcherTrailBucket:
    Value:
      Ref: WatcherTrailBucket
Parameters:
  MemorySizeDefault:
    Default: '512'
    Type: String
  RuntimeVersion:
    Default: '3.8'
    Type: String
  TimeoutDefault:
    Default: '5'
    Type: String
Resources:
  HelloFunction:
    Properties:
      Code:
        ZipFile: |
          def handler(event, context):
              print (event)
      Handler: index.handler
      MemorySize:
        Ref: MemorySizeDefault
      Role:
        Fn::GetAtt:
        - HelloFunctionRole
        - Arn
      Runtime:
        Fn::Sub: python${RuntimeVersion}
      Timeout:
        Ref: TimeoutDefault
    Type: AWS::Lambda::Function
  HelloFunctionRole:
    Properties:
      AssumeRolePolicyDocument:
        Statement:
        - Action: sts:AssumeRole
          Effect: Allow
          Principal:
            Service: lambda.amazonaws.com
        Version: '2012-10-17'
      Policies:
      - PolicyDocument:
          Statement:
          - Action:
            - logs:CreateLogGroup
            - logs:CreateLogStream
            - logs:PutLogEvents
            Effect: Allow
            Resource: '*'
          Version: '2012-10-17'
        PolicyName:
          Fn::Sub: hello-function-role-policy-${AWS::StackName}
    Type: AWS::IAM::Role
  WatcherFunction:
    Properties:
      Code:
        ZipFile: |
          def handler(event, context):
              print (event)
      Handler: index.handler
      MemorySize:
        Ref: MemorySizeDefault
      Role:
        Fn::GetAtt:
        - WatcherFunctionRole
        - Arn
      Runtime:
        Fn::Sub: python${RuntimeVersion}
      Timeout:
        Ref: TimeoutDefault
    Type: AWS::Lambda::Function
  WatcherFunctionRole:
    Properties:
      AssumeRolePolicyDocument:
        Statement:
        - Action: sts:AssumeRole
          Effect: Allow
          Principal:
            Service: lambda.amazonaws.com
        Version: '2012-10-17'
      Policies:
      - PolicyDocument:
          Statement:
          - Action:
            - logs:CreateLogGroup
            - logs:CreateLogStream
            - logs:PutLogEvents
            Effect: Allow
            Resource: '*'
          Version: '2012-10-17'
        PolicyName:
          Fn::Sub: watcher-function-role-policy-${AWS::StackName}
    Type: AWS::IAM::Role
  WatcherEventRule:
    Type: AWS::Events::Rule
    Properties:
      EventPattern:
        source:
          - aws.logs
        detail-type:
          - "AWS API Call via CloudTrail"
        detail:
          eventName:
            - CreateLogGroup
      Targets:
      - Id:
          Fn::Sub: watcher-event-rule-${AWS::StackName}
        Arn:
          Fn::GetAtt:
          - WatcherFunction
          - Arn
      State: ENABLED
  WatcherEventRulePermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      Principal: events.amazonaws.com
      FunctionName:
        Ref: WatcherFunction
      SourceArn:
        Fn::GetAtt:
        - WatcherEventRule
        - Arn
  WatcherTrailBucket:
    Type: AWS::S3::Bucket
  WatcherTrailBucketPolicy:
    DependsOn:
    - WatcherTrailBucket
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket:
         Ref: WatcherTrailBucket
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: cloudtrail.amazonaws.com
            Action: s3:GetBucketAcl
            Resource: "*"
            Condition: {}
          - Effect: Allow
            Principal:
              Service: cloudtrail.amazonaws.com
            Action: s3:PutObject
            Resource: "*"
            Condition:
              StringEquals:
                s3:x-amz-acl: bucket-owner-full-control
  WatcherTrail:
    Type: AWS::CloudTrail::Trail
    Properties:
      EventSelectors:
        - ReadWriteType: All
      IsLogging: true
      S3BucketName:
        Ref: WatcherTrailBucket
      IsLogging: true
      S3KeyPrefix: logs/

Solution

  • Your WatcherTrail runs before WatcherTrailBucketPolicy, so that's why it fails (CloudFormation does not deploy resource in the order of their definition in the template). Add explicit DependsOn dependency on the bucket policy. Also your WatcherTrailBucketPolicy is incorrect, and trial needs a name. So it should be:

      WatcherTrailBucketPolicy:
        Type: AWS::S3::BucketPolicy
        Properties:
          Bucket:
             Ref: WatcherTrailBucket
          PolicyDocument: !Sub |
            {
                "Version": "2012-10-17",
                "Statement": [
                    {
                        "Sid": "AWSCloudTrailAclCheck20150319",
                        "Effect": "Allow",
                        "Principal": {"Service": "cloudtrail.amazonaws.com"},
                        "Action": "s3:GetBucketAcl",
                        "Resource": "arn:aws:s3:::${WatcherTrailBucket}",
                        "Condition": {
                            "StringEquals": {
                                "aws:SourceArn": "arn:aws:cloudtrail:${AWS::Region}:${AWS::AccountId}:trail/MyTrial"
                            }
                        }
                    },
                    {
                        "Sid": "AWSCloudTrailWrite20150319",
                        "Effect": "Allow",
                        "Principal": {"Service": "cloudtrail.amazonaws.com"},
                        "Action": "s3:PutObject",
                        "Resource": "arn:aws:s3:::${WatcherTrailBucket}/logs/AWSLogs/${AWS::AccountId}/*",
                        "Condition": {
                            "StringEquals": {
                                "s3:x-amz-acl": "bucket-owner-full-control",
                                "aws:SourceArn": "arn:aws:cloudtrail:${AWS::Region}:${AWS::AccountId}:trail/MyTrial"
                            }
                        }
                    }
                ]
            }
    
    
    
      WatcherTrail:
        Type: AWS::CloudTrail::Trail
        DependsOn: WatcherTrailBucketPolicy
        Properties:
          TrailName: MyTrial
          EventSelectors:
            - ReadWriteType: All
          IsLogging: true
          S3BucketName:
            Ref: WatcherTrailBucket
          IsLogging: true
          S3KeyPrefix: logs/