---
AWSTemplateFormatVersion: 2010-09-09
Description: Initializes global resources and logging/monitoring capabilities (qs-1nb14cqc5)
Metadata:
  Stack:
    Value: 0
  VersionDate:
    Value: 20160510
  Identifier:
    Value: template-logging
  Input:
    Description: CloudTrail bucket name
  Output:
    Description: Outputs ID of all deployed resources
Parameters:
  pNotifyEmail:
    Description: Notification email for security events
    Type: String
    Default: ''
  pSupportsGlacier:
    Description: Determines hether this region supports Glacier (passed in from Main
      template)
    Type: String
    Default: true
Conditions:
  IsGovCloud:
    !Equals
    - us-gov-west-1
    - !Ref AWS::Region
  SupportsGlacier:
    !Equals
    - true
    - !Ref pSupportsGlacier
Resources:
  rSecurityAlarmTopic:
    Type: AWS::SNS::Topic
    Properties:
      Subscription:
      - Endpoint: !Ref pNotifyEmail
        Protocol: email
  rArchiveLogsBucket:
    Type: AWS::S3::Bucket
    DeletionPolicy: Retain
    Properties:
      AccessControl: LogDeliveryWrite
      LifecycleConfiguration:
        Rules:
        - Id: Transition90daysRetain7yrs
          Status: Enabled
          ExpirationInDays: 2555
          Transition:
            TransitionInDays: 90
            StorageClass:
              !If
              - SupportsGlacier
              - GLACIER
              - STANDARD_IA
      VersioningConfiguration:
        Status: Enabled
  rArchiveLogsBucketPolicy:
    Type: AWS::S3::BucketPolicy
    DependsOn: rArchiveLogsBucket
    Properties:
      Bucket: !Ref rArchiveLogsBucket
      PolicyDocument:
        Statement:
        - Sid: Enforce HTTPS Connections
          Action: s3:*
          Effect: Deny
          Principal: '*'
          Resource:
            !Sub
            - arn:${Partition}:s3:::${rArchiveLogsBucket}/*
            - Partition:
                !If
                - IsGovCloud
                - aws-us-gov
                - aws
          Condition:
            Bool:
              aws:SecureTransport: false
        - Sid: Restrict Delete* Actions
          Action: s3:Delete*
          Effect: Deny
          Principal: '*'
          Resource:
            !Sub
            - arn:${Partition}:s3:::${rArchiveLogsBucket}/*
            - Partition:
                !If
                - IsGovCloud
                - aws-us-gov
                - aws
        - Sid: DenyUnEncryptedObjectUploads
          Effect: Deny
          Principal: '*'
          Action: s3:PutObject
          Resource:
            !Sub
            - arn:${Partition}:s3:::${rArchiveLogsBucket}/*
            - Partition:
                !If
                - IsGovCloud
                - aws-us-gov
                - aws
          Condition:
            StringNotEquals:
              s3:x-amz-server-side-encryption: AES256
  rCloudTrailBucket:
    Type: AWS::S3::Bucket
    DeletionPolicy: Retain
    Properties:
      AccessControl: Private
      VersioningConfiguration:
        Status: Enabled
      LoggingConfiguration:
        DestinationBucketName: !Ref rArchiveLogsBucket
        LogFilePrefix: cloudtraillogs
  rCloudTrailS3Policy:
    Type: AWS::S3::BucketPolicy
    DependsOn: rCloudTrailBucket
    Properties:
      Bucket: !Ref rCloudTrailBucket
      PolicyDocument:
        Statement:
        - Sid: AWSCloudTrailAclCheck20150319
          Effect: Allow
          Principal:
            Service: cloudtrail.amazonaws.com
          Action: s3:GetBucketAcl
          Resource:
            !Sub
            - arn:${Partition}:s3:::${rCloudTrailBucket}
            - Partition:
                !If
                - IsGovCloud
                - aws-us-gov
                - aws
        - Sid: AWSCloudTrailWrite20150319
          Effect: Allow
          Principal:
            Service: cloudtrail.amazonaws.com
          Action: s3:PutObject
          Resource:
            !Sub
            - arn:${Partition}:s3:::${rCloudTrailBucket}/*
            - Partition:
                !If
                - IsGovCloud
                - aws-us-gov
                - aws
          Condition:
            StringEquals:
              s3:x-amz-acl: bucket-owner-full-control
        - Sid: Enforce HTTPS Connections
          Action: s3:*
          Effect: Deny
          Principal: '*'
          Resource:
            !Sub
            - arn:${Partition}:s3:::${rCloudTrailBucket}/*
            - Partition:
                !If
                - IsGovCloud
                - aws-us-gov
                - aws
          Condition:
            Bool:
              aws:SecureTransport: false
        - Sid: Restrict Delete* Actions
          Action: s3:Delete*
          Effect: Deny
          Principal: '*'
          Resource:
            !Sub
            - arn:${Partition}:s3:::${rCloudTrailBucket}/*
            - Partition:
                !If
                - IsGovCloud
                - aws-us-gov
                - aws
        - Sid: DenyUnEncryptedObjectUploads
          Effect: Deny
          Principal: '*'
          Action: s3:PutObject
          Resource:
            !Sub
            - arn:${Partition}:s3:::${rCloudTrailBucket}/*
            - Partition:
                !If
                - IsGovCloud
                - aws-us-gov
                - aws
          Condition:
            StringNotEquals:
              s3:x-amz-server-side-encryption: AES256
  rCloudTrailLoggingLocal:
    Type: AWS::CloudTrail::Trail
    DependsOn: rCloudTrailS3Policy
    Properties:
      S3BucketName: !Ref rCloudTrailBucket
      IsLogging: true
      EnableLogFileValidation: true
      IncludeGlobalServiceEvents: true
      CloudWatchLogsLogGroupArn:
        !GetAtt
        - rCloudTrailLogGroup
        - Arn
      CloudWatchLogsRoleArn:
        !GetAtt
        - rCloudWatchLogsRole
        - Arn
  rCloudWatchLogsRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - cloudtrail.amazonaws.com
          Action:
          - sts:AssumeRole
      Path: /
      Policies:
      - PolicyName: cloudwatchlogsrole
        PolicyDocument:
          Version: 2012-10-17
          Statement:
          - Sid: AWSCloudTrailCreateLogStream20141101
            Effect: Allow
            Action:
            - logs:CreateLogStream
            Resource:
              !Sub
              - arn:${Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:${rCloudTrailLogGroup}:log-stream:*
              - Partition:
                  !If
                  - IsGovCloud
                  - aws-us-gov
                  - aws
          - Sid: AWSCloudTrailPutLogEvents20141101
            Effect: Allow
            Action:
            - logs:PutLogEvents
            Resource:
              !Sub
              - arn:${Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:${rCloudTrailLogGroup}:log-stream:*
              - Partition:
                  !If
                  - IsGovCloud
                  - aws-us-gov
                  - aws
  rCloudTrailRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - ec2.amazonaws.com
          Action:
          - sts:AssumeRole
      Path: /
      Policies:
      - PolicyName: cloudtrail-limited-actions
        PolicyDocument:
          Version: 2012-10-17
          Statement:
          - Effect: Allow
            Action:
            - s3:ListBucket
            Resource:
              !Sub
              - arn:${Partition}:s3:::${rCloudTrailBucket}
              - Partition:
                  !If
                  - IsGovCloud
                  - aws-us-gov
                  - aws
          - Effect: Allow
            Action:
            - s3:PutObject
            - s3:GetObject
            Resource:
              !Sub
              - arn:${Partition}:s3:::${rCloudTrailBucket}/*
              - Partition:
                  !If
                  - IsGovCloud
                  - aws-us-gov
                  - aws
  rCloudTrailProfile:
    Type: AWS::IAM::InstanceProfile
    DependsOn: rCloudTrailRole
    Properties:
      Path: /
      Roles:
      - !Ref rCloudTrailRole
  rCloudTrailLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      RetentionInDays: 90
  rIAMPolicyChangesMetricFilter:
    Type: AWS::Logs::MetricFilter
    Properties:
      LogGroupName: !Ref rCloudTrailLogGroup
      FilterPattern: |-
        {
          ($.eventName=DeleteGroupPolicy) ||
          ($.eventName=DeleteRolePolicy) ||
          ($.eventName=DeleteUserPolicy) ||
          ($.eventName=PutGroupPolicy) ||
          ($.eventName=PutRolePolicy) ||
          ($.eventName=PutUserPolicy) ||
          ($.eventName=CreatePolicy) ||
          ($.eventName=DeletePolicy) ||
          ($.eventName=CreatePolicyVersion) ||
          ($.eventName=DeletePolicyVersion) ||
          ($.eventName=AttachRolePolicy) ||
          ($.eventName=DetachRolePolicy) ||
          ($.eventName=AttachUserPolicy) ||
          ($.eventName=DetachUserPolicy) ||
          ($.eventName=AttachGroupPolicy) ||
          ($.eventName=DetachGroupPolicy)
        }
      MetricTransformations:
      - MetricNamespace: CloudTrailMetrics
        MetricName: IAMPolicyEventCount
        MetricValue: 1
  rNetworkAclChangesMetricFilter:
    Type: AWS::Logs::MetricFilter
    Properties:
      LogGroupName: !Ref rCloudTrailLogGroup
      FilterPattern: |-
        {
          ($.eventName = CreateNetworkAcl) ||
          ($.eventName = CreateNetworkAclEntry) ||
          ($.eventName = DeleteNetworkAcl) ||
          ($.eventName = DeleteNetworkAclEntry) ||
          ($.eventName = ReplaceNetworkAclEntry) ||
          ($.eventName = ReplaceNetworkAclAssociation)
        }
      MetricTransformations:
      - MetricNamespace: CloudTrailMetrics
        MetricName: NetworkAclEventCount
        MetricValue: 1
  rNetworkAclChangesAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmDescription: Alarms when an API call is made to create, update or delete
        a Network ACL.
      AlarmActions:
      - !Ref rSecurityAlarmTopic
      MetricName: NetworkAclEventCount
      Namespace: CloudTrailMetrics
      ComparisonOperator: GreaterThanOrEqualToThreshold
      EvaluationPeriods: 1
      Period: 300
      Statistic: Sum
      Threshold: 1
  rSecurityGroupChangesMetricFilter:
    Type: AWS::Logs::MetricFilter
    Properties:
      LogGroupName: !Ref rCloudTrailLogGroup
      FilterPattern: |-
        {
          ($.eventName = AuthorizeSecurityGroupIngress) ||
          ($.eventName = AuthorizeSecurityGroupEgress) ||
          ($.eventName = RevokeSecurityGroupIngress) ||
          ($.eventName = RevokeSecurityGroupEgress) ||
          ($.eventName = CreateSecurityGroup) ||
          ($.eventName = DeleteSecurityGroup)
        }
      MetricTransformations:
      - MetricNamespace: CloudTrailMetrics
        MetricName: SecurityGroupEventCount
        MetricValue: 1
  rSecurityGroupChangesAlarm:
    Type: AWS::CloudWatch::Alarm
    DependsOn: rNetworkAclChangesAlarm
    Properties:
      AlarmDescription: Alarms when an API call is made to create, update or delete
        a Security Group.
      AlarmActions:
      - !Ref rSecurityAlarmTopic
      MetricName: SecurityGroupEventCount
      Namespace: CloudTrailMetrics
      ComparisonOperator: GreaterThanOrEqualToThreshold
      EvaluationPeriods: 1
      Period: 300
      Statistic: Sum
      Threshold: 1
  rIAMRootActivity:
    Type: AWS::Logs::MetricFilter
    Properties:
      LogGroupName: !Ref rCloudTrailLogGroup
      FilterPattern: |-
        {
          ($.userIdentity.type = "Root") &&
          ($.userIdentity.invokedBy NOT EXISTS) &&
          ($.eventType != "AwsServiceEvent")
        }
      MetricTransformations:
      - MetricNamespace: CloudTrailMetrics
        MetricName: RootUserPolicyEventCount
        MetricValue: 1
  rRootActivityAlarm:
    Type: AWS::CloudWatch::Alarm
    DependsOn: rSecurityGroupChangesAlarm
    Properties:
      AlarmDescription: Root user activity detected!
      AlarmActions:
      - !Ref rSecurityAlarmTopic
      MetricName: RootUserPolicyEventCount
      Namespace: CloudTrailMetrics
      ComparisonOperator: GreaterThanOrEqualToThreshold
      EvaluationPeriods: 1
      Period: 300
      Statistic: Sum
      Threshold: 1
  rUnauthorizedAttempts:
    Type: AWS::Logs::MetricFilter
    Properties:
      LogGroupName: !Ref rCloudTrailLogGroup
      FilterPattern: |-
        {
          ($.errorCode=AccessDenied) ||
          ($.errorCode=UnauthorizedOperation)
        }
      MetricTransformations:
      - MetricNamespace: CloudTrailMetrics
        MetricName: UnauthorizedAttemptCount
        MetricValue: 1
  rUnauthorizedAttemptAlarm:
    Type: AWS::CloudWatch::Alarm
    DependsOn: rRootActivityAlarm
    Properties:
      AlarmDescription: Multiple unauthorized actions or logins attempted!
      AlarmActions:
      - !Ref rSecurityAlarmTopic
      MetricName: UnauthorizedAttemptCount
      Namespace: CloudTrailMetrics
      ComparisonOperator: GreaterThanOrEqualToThreshold
      EvaluationPeriods: 1
      Period: 300
      Statistic: Sum
      Threshold: 5
  rIAMPolicyChangesAlarm:
    Type: AWS::CloudWatch::Alarm
    DependsOn: rUnauthorizedAttemptAlarm
    Properties:
      AlarmDescription: IAM Configuration changes detected!
      AlarmActions:
      - !Ref rSecurityAlarmTopic
      MetricName: IAMPolicyEventCount
      Namespace: CloudTrailMetrics
      ComparisonOperator: GreaterThanOrEqualToThreshold
      EvaluationPeriods: 1
      Period: 300
      Statistic: Sum
      Threshold: 1
  rIAMCreateAccessKeyAlarm:
    Type: AWS::CloudWatch::Alarm
    DependsOn: rIAMPolicyChangesAlarm
    Properties:
      AlarmDescription: 'Warning: New IAM access key was created. Please be sure this
        action was neccessary.'
      AlarmActions:
      - !Ref rSecurityAlarmTopic
      MetricName: NewAccessKeyCreated
      Namespace: CloudTrailMetrics
      ComparisonOperator: GreaterThanOrEqualToThreshold
      EvaluationPeriods: 1
      Period: 300
      Statistic: Sum
      Threshold: 1
  rIAMCreateAccessKey:
    Type: AWS::Logs::MetricFilter
    DependsOn: rIAMCreateAccessKeyAlarm
    Properties:
      LogGroupName: !Ref rCloudTrailLogGroup
      FilterPattern: |-
        {
          ($.eventName=CreateAccessKey)
        }
      MetricTransformations:
      - MetricNamespace: CloudTrailMetrics
        MetricName: NewAccessKeyCreated
        MetricValue: 1
  rCloudTrailChangeAlarm:
    Type: AWS::CloudWatch::Alarm
    DependsOn: rIAMCreateAccessKeyAlarm
    Properties:
      AlarmDescription: 'Warning: Changes to CloudTrail log configuration detected
        in this account'
      AlarmActions:
      - !Ref rSecurityAlarmTopic
      MetricName: CloudTrailChangeCount
      Namespace: CloudTrailMetrics
      ComparisonOperator: GreaterThanOrEqualToThreshold
      EvaluationPeriods: 1
      Period: 300
      Statistic: Sum
      Threshold: 1
  rCloudTrailChange:
    Type: AWS::Logs::MetricFilter
    Properties:
      LogGroupName: !Ref rCloudTrailLogGroup
      FilterPattern: |-
        {
          ($.eventSource = cloudtrail.amazonaws.com) &&
          (
            ($.eventName != Describe*) &&
            ($.eventName != Get*) &&
            ($.eventName != Lookup*) &&
            ($.eventName != List*)
          )
        }
      MetricTransformations:
      - MetricNamespace: CloudTrailMetrics
        MetricName: CloudTrailChangeCount
        MetricValue: 1
Outputs:
  rArchiveLogsBucket:
    Value: !Ref rArchiveLogsBucket
  rSecurityAlarmTopic:
    Value: !Ref rSecurityAlarmTopic
  rCloudTrailLogGroup:
    Value: !Ref rCloudTrailLogGroup
...