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