AWSTemplateFormatVersion: "2010-09-09" Description: "(SO0037) - Real Time Insights on AWS Account Activity (Version %%VERSION%%): This solution provides an account activity dashboard by analyzing AWS CloudTrail log analytics with Amazon Kinesis Data Analytics" Parameters: UserName: Type: String Description: Name of a new web UI user (to be created in Amazon Cognito). AllowedPattern: "^(?=\\s*\\S).*$" ConstraintDescription: " cannot be empty" UserEmail: Type: String Description: Email address for new web UI user. After successfully launching this solution, you will receive an email to this email address with logon instructions. AllowedPattern: "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$" WebsiteBucketName: Description: The new S3 bucket for the Web Dashboard UI to be deployed. Type: String AllowedPattern: "^(?=\\s*\\S).*$" ConstraintDescription: " cannot be empty" Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: "Web Dashboard Configuration" Parameters: - UserName - UserEmail - WebsiteBucketName ParameterLabels: UserName: default: "User Name" UserEmail: default: "User Email Address" WebsiteBucketName: default: "Dashboard Bucket Name" Mappings: SourceCode: General: S3Bucket: "%%BUCKET_NAME%%" KeyPrefix: "%%SOLUTION_NAME%%/%%VERSION%%" Send: AnonymousUsage: Data: "Yes" Resources: CloudTrailS3Bucket: DeletionPolicy: Retain Type: "AWS::S3::Bucket" Properties: PublicAccessBlockConfiguration: BlockPublicAcls: True BlockPublicPolicy: True IgnorePublicAcls: True RestrictPublicBuckets: True BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: AES256 LifecycleConfiguration: Rules: - Id: GlacierRule Prefix: glacier Status: Enabled ExpirationInDays: 365 Transitions: - TransitionInDays: 30 StorageClass: Glacier LoggingConfiguration: DestinationBucketName: !Ref LogsBucket LogFilePrefix: cloud-trail-bucket/ CloudTrailBucketPolicy: DeletionPolicy: Retain Type: "AWS::S3::BucketPolicy" Properties: Bucket: Ref: CloudTrailS3Bucket PolicyDocument: Version: "2012-10-17" Statement: - Sid: "AWSCloudTrailAclCheck" Effect: "Allow" Principal: Service: "cloudtrail.amazonaws.com" Action: "s3:GetBucketAcl" Resource: !Sub |- arn:aws:s3:::${CloudTrailS3Bucket} - Sid: "AWSCloudTrailWrite" Effect: "Allow" Principal: Service: "cloudtrail.amazonaws.com" Action: "s3:PutObject" Resource: !Sub |- arn:aws:s3:::${CloudTrailS3Bucket}/AWSLogs/${AWS::AccountId}/* Condition: StringEquals: s3:x-amz-acl: "bucket-owner-full-control" GlobalCloudTrail: DependsOn: - CloudTrailBucketPolicy Type: "AWS::CloudTrail::Trail" Properties: S3BucketName: Ref: CloudTrailS3Bucket IncludeGlobalServiceEvents: True IsLogging: True IsMultiRegionTrail: True FirehoseRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: Service: - "firehose.amazonaws.com" Action: - "sts:AssumeRole" Path: / Policies: - PolicyName: "FirehosePolicy" PolicyDocument: Version: "2012-10-17" Statement: - Action: - "s3:AbortMultipartUpload" - "s3:GetBucketLocation" - "s3:GetObject" - "s3:ListBucket" - "s3:ListBucketMultipartUploads" Effect: "Allow" Resource: - !Join ['' , ["arn:aws:s3:::", !GetAtt FirehoseS3Bucket.Arn ]] - !Join ['' , ["arn:aws:s3:::", !GetAtt FirehoseS3Bucket.Arn, "/*" ]] - Action: - "logs:PutLogEvents" Effect: "Allow" Resource: - !Join ['' , ["arn:aws:logs:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":log-group:/aws/kinesisfirehose/*", ":log-stream:*" ]] FirehoseLogGroup: Type: "AWS::Logs::LogGroup" Properties: LogGroupName: "CloudTrailFirehoseDestinationLog" RetentionInDays: 7 Metadata: cfn_nag: rules_to_suppress: - id: "W84" reason: "using service dafault encryption https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/data-protection.html" FirehoseLogStream: Type: "AWS::Logs::LogStream" DependsOn: FirehoseLogGroup Properties: LogGroupName: "CloudTrailFirehoseDestinationLog" LogStreamName: "S3Delivery" FireHoseDestination: Type: "AWS::KinesisFirehose::DeliveryStream" DependsOn: FirehoseLogGroup Properties: DeliveryStreamName: "RealTimeInsightsAccountActivityCloudTrailInput" DeliveryStreamEncryptionConfigurationInput: KeyType: 'AWS_OWNED_CMK' S3DestinationConfiguration: BucketARN: !GetAtt FirehoseS3Bucket.Arn BufferingHints: IntervalInSeconds: 60 SizeInMBs: 10 CloudWatchLoggingOptions: Enabled: true LogGroupName: "CloudTrailFirehoseDestinationLog" LogStreamName: "S3Delivery" CompressionFormat: "UNCOMPRESSED" EncryptionConfiguration: NoEncryptionConfig: "NoEncryption" Prefix: "cloudtrail-logs/" RoleARN: !GetAtt FirehoseRole.Arn FirehoseS3Bucket: DeletionPolicy: Retain Type: "AWS::S3::Bucket" Properties: PublicAccessBlockConfiguration: BlockPublicAcls: True BlockPublicPolicy: True IgnorePublicAcls: True RestrictPublicBuckets: True BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: AES256 LifecycleConfiguration: Rules: - Id: ExpirationRule Status: Enabled ExpirationInDays: 1 LoggingConfiguration: DestinationBucketName: !Ref LogsBucket LogFilePrefix: firehouse-bucket/ FirehoseS3Permissions: Type: 'AWS::S3::BucketPolicy' Properties: PolicyDocument: Id: MyPolicy Version: 2012-10-17 Statement: - Sid: ReadWrite Effect: Allow Principal: AWS: - !Join ['' , ["arn:aws:iam::", !Ref "AWS::AccountId" , ":root" ]] - !GetAtt FirehoseRole.Arn Action: ["s3:PutObject","s3:GetObject","s3:DeleteObject"] Resource: !Join - '' - - 'arn:aws:s3:::' - !Ref FirehoseS3Bucket - /* Bucket: !Ref FirehoseS3Bucket CloudWatchEventRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: Service: - "events.amazonaws.com" Action: - "sts:AssumeRole" Path: / Policies: - PolicyName: "CloudWatchEventPolicy" PolicyDocument: Version: "2012-10-17" Statement: Action: - "firehose:DescribeDeliveryStream" - "firehose:ListDeliveryStreams" - "firehose:PutRecord" - "firehose:PutRecordBatch" Effect: "Allow" Resource: !Join ['' , ["arn:aws:firehose:", !Ref "AWS::Region", ":" , !Ref "AWS::AccountId" , ":deliverystream/" , !Ref FireHoseDestination]] CloudTrailRule: Type: "AWS::Events::Rule" Properties: Description: "Rule to publish all CloudTrail events to Firehose" EventPattern: detail-type: - "AWS API Call via CloudTrail" State: "ENABLED" Targets: - Arn: !Join ['' , ["arn:aws:firehose:", !Ref "AWS::Region", ":" , !Ref "AWS::AccountId" , ":deliverystream/" , !Ref FireHoseDestination]] Id: "CloudTrailRuleV1" RoleArn: !GetAtt CloudWatchEventRole.Arn OutputKinesisStream: Type: "AWS::Kinesis::Stream" Properties: StreamEncryption: EncryptionType: KMS KeyId: !Sub arn:${AWS::Partition}:kms:${AWS::Region}:${AWS::AccountId}:alias/aws/kinesis ShardCount: 5 OutputFirehoseRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: Service: - "firehose.amazonaws.com" Action: - "sts:AssumeRole" Path: / Policies: - PolicyName: "FirehosePolicy" PolicyDocument: Version: "2012-10-17" Statement: - Action: - "s3:AbortMultipartUpload" - "s3:GetBucketLocation" - "s3:GetObject" - "s3:ListBucket" - "s3:ListBucketMultipartUploads" Effect: "Allow" Resource: - !Join ['' , ["arn:aws:s3:::", !GetAtt FirehoseS3Bucket.Arn ]] - !Join ['' , ["arn:aws:s3:::", !GetAtt FirehoseS3Bucket.Arn, "/*" ]] - Action: - "lambda:InvokeFunction" - "lambda:GetFunctionConfiguration" Effect: "Allow" Resource: !GetAtt UpdateDDBLambdaFunc.Arn - Action: - "logs:PutLogEvents" Effect: "Allow" Resource: - !Join ["", ["arn:aws:logs:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":log-group:/aws/lambda/*"]] - PolicyName: "OutputFirehosePolicy" PolicyDocument: Version: "2012-10-17" Statement: Action: - "kinesis:DescribeStream" - "kinesis:GetShardIterator" - "kinesis:GetRecords" Effect: "Allow" Resource: !GetAtt OutputKinesisStream.Arn OutputFireHoseDestination: Type: "AWS::KinesisFirehose::DeliveryStream" DependsOn: FirehoseLogGroup Properties: DeliveryStreamName: "RealTimeInsightsAccountActivityAnalyticsOutput" DeliveryStreamType: "KinesisStreamAsSource" KinesisStreamSourceConfiguration: KinesisStreamARN: !GetAtt OutputKinesisStream.Arn RoleARN: !GetAtt OutputFirehoseRole.Arn S3DestinationConfiguration: BucketARN: !GetAtt OutputFirehoseS3Bucket.Arn BufferingHints: IntervalInSeconds: 60 SizeInMBs: 10 CloudWatchLoggingOptions: Enabled: true LogGroupName: "CloudTrailFirehoseDestinationLog" LogStreamName: "S3Delivery" CompressionFormat: "UNCOMPRESSED" EncryptionConfiguration: NoEncryptionConfig: "NoEncryption" Prefix: "cloudtrail-logs/" RoleARN: !GetAtt FirehoseRole.Arn OutputFirehoseS3Bucket: DeletionPolicy: Retain Type: "AWS::S3::Bucket" Properties: PublicAccessBlockConfiguration: BlockPublicAcls: True BlockPublicPolicy: True IgnorePublicAcls: True RestrictPublicBuckets: True BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: AES256 LifecycleConfiguration: Rules: - Id: ExpirationRule Status: Enabled ExpirationInDays: 1 LoggingConfiguration: DestinationBucketName: !Ref LogsBucket LogFilePrefix: firehouse-output-bucket/ OutputFirehoseS3Permissions: Type: 'AWS::S3::BucketPolicy' Properties: PolicyDocument: Id: MyPolicy Version: 2012-10-17 Statement: - Sid: ReadWrite Effect: Allow Principal: AWS: - !Join ['' , ["arn:aws:iam::", !Ref "AWS::AccountId" , ":root" ]] - !GetAtt FirehoseRole.Arn Action: ["s3:PutObject","s3:GetObject","s3:DeleteObject"] Resource: !Join - '' - - 'arn:aws:s3:::' - !Ref OutputFirehoseS3Bucket - /* Bucket: !Ref OutputFirehoseS3Bucket KinesisAnalyticsRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: kinesisanalytics.amazonaws.com Action: "sts:AssumeRole" Path: "/" Policies: - PolicyName: kaaccess PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - "firehose:DescribeDeliveryStream" - "firehose:Get*" Resource: !Join ['' , ["arn:aws:firehose:", !Ref "AWS::Region", ":" , !Ref "AWS::AccountId" , ":deliverystream/" , !Ref FireHoseDestination]] - Effect: Allow Action: - "kinesis:PutRecord" - "kinesis:PutRecords" - "kinesis:DescribeStream" Resource: - !GetAtt OutputKinesisStream.Arn KinesisAnalyticsApp: Type: "AWS::KinesisAnalytics::Application" Properties: ApplicationName: "RealTimeInsightsAccountActivityApp" ApplicationDescription: "%%SOLUTION_NAME%%: Application for parsing CloudTrail logs" ApplicationCode: "CREATE STREAM \"DESTINATION_SQL_STREAM\"\n ( eventTimeStamp TIMESTAMP, computationType VARCHAR(256), category VARCHAR(1024), subCategory VARCHAR(1024),\n unit VARCHAR(256), unitValue BIGINT);\nCREATE STREAM \"DESTINATION_ANOMALY_STREAM\"\n ( eventTimeStamp TIMESTAMP, computationType VARCHAR(256), category VARCHAR(1024), subCategory VARCHAR(1024),\n unit VARCHAR(256), anomalyScore DOUBLE);\nCREATE STREAM \"TOTAL_CALL_COUNT_STREAM\" \n (eventTimeStamp TIMESTAMP, callCount BIGINT);\n\nCREATE OR REPLACE PUMP \"PUMP_FOR_TOTAL_CALL_COUNT\" AS\n INSERT INTO \"TOTAL_CALL_COUNT_STREAM\" \n SELECT STREAM STEP(cloudtraillogs.\"eventTimestamp\" BY INTERVAL '10' SECOND) eventTimeStamp, COUNT(*) totalCalls \n FROM \"SOURCE_SQL_STREAM_001\" cloudtraillogs\n GROUP BY STEP(cloudtraillogs.ROWTIME BY INTERVAL '10' SECOND), STEP(cloudtraillogs.\"eventTimestamp\" BY INTERVAL '10' SECOND);\nCREATE OR REPLACE PUMP \"PUMP_FOR_ANOMALY_CALL_COUNT\" AS\n INSERT INTO \"DESTINATION_ANOMALY_STREAM\" \n SELECT eventTimeStamp, 'AnomalyScore', CAST(callCount as VARCHAR(10)), 'None', 'Sum', ANOMALY_SCORE FROM \n TABLE(RANDOM_CUT_FOREST(\n CURSOR(SELECT STREAM * FROM \"TOTAL_CALL_COUNT_STREAM\"), 100, 256, 100000, 20)); \n \nCREATE OR REPLACE PUMP \"PUMP_FOR_CALLS_PER_IP\" AS\n INSERT INTO \"DESTINATION_SQL_STREAM\"\n SELECT eventTimeStamp, 'CallsPerUniqueIp', sip, 'None', 'Sum', callsPerIp FROM (\n SELECT STREAM STEP(cloudtraillogs.\"eventTimestamp\" BY INTERVAL '1' MINUTE) eventTimeStamp, COUNT(*) callsPerIp, \"sourceIPAddress\" sip\n FROM \"SOURCE_SQL_STREAM_001\" cloudtraillogs\n GROUP BY \"sourceIPAddress\", STEP(cloudtraillogs.ROWTIME BY INTERVAL '1' MINUTE), STEP(cloudtraillogs.\"eventTimestamp\" BY INTERVAL '1' MINUTE));\n \n \nCREATE OR REPLACE PUMP \"PUMP_FOR_CALLS_PER_API\" AS\n INSERT INTO \"DESTINATION_SQL_STREAM\"\n SELECT eventTimeStamp, 'CallsPerAPI', \"eventSource\", \"eventName\" , 'Sum', callsPerAPI FROM (\n SELECT STREAM STEP(cloudtraillogs.\"eventTimestamp\" BY INTERVAL '10' SECOND) as eventTimeStamp, COUNT(*) callsPerAPI, \"eventSource\", \"eventName\"\n FROM \"SOURCE_SQL_STREAM_001\" cloudtraillogs\n GROUP BY \"eventSource\", \"eventName\", STEP(cloudtraillogs.ROWTIME BY INTERVAL '10' SECOND), STEP(cloudtraillogs.\"eventTimestamp\" BY INTERVAL '10' SECOND));\n\nCREATE OR REPLACE PUMP \"PUMP_FOR_CALLS_PER_SOURCE\" AS\n INSERT INTO \"DESTINATION_SQL_STREAM\"\n SELECT eventTimeStamp, 'CallsPerServiceType', \"eventSource\", 'None', 'Sum', callsPerSource FROM (\n SELECT STREAM STEP(cloudtraillogs.\"eventTimestamp\" BY INTERVAL '10' SECOND) eventTimeStamp, COUNT(*) callsPerSource, \"eventSource\"\n FROM \"SOURCE_SQL_STREAM_001\" cloudtraillogs\n GROUP BY \"eventSource\", STEP(cloudtraillogs.ROWTIME BY INTERVAL '10' SECOND), STEP(cloudtraillogs.\"eventTimestamp\" BY INTERVAL '10' SECOND)\n HAVING \"eventSource\" <> 'ec2.amazonaws.com');\n\nCREATE OR REPLACE PUMP \"PUMP_FOR_EC2_CALLS\" AS\n INSERT INTO \"DESTINATION_SQL_STREAM\"\n SELECT eventTimeStamp, 'EC2Calls', \"eventSource\", \"errorCode\", 'Sum', callsPerSource FROM (\n SELECT STREAM STEP(cloudtraillogs.\"eventTimestamp\" BY INTERVAL '10' SECOND) eventTimeStamp, COUNT(*) callsPerSource, \"errorCode\", \"eventSource\"\n FROM \"SOURCE_SQL_STREAM_001\" cloudtraillogs\n GROUP BY \"eventSource\", \"errorCode\", STEP(cloudtraillogs.ROWTIME BY INTERVAL '10' SECOND), STEP(cloudtraillogs.\"eventTimestamp\" BY INTERVAL '10' SECOND)\n HAVING \"eventSource\" = 'ec2.amazonaws.com');\n \nCREATE OR REPLACE PUMP \"PUMP_FOR_CALLS_PER_USER\" AS\n INSERT INTO \"DESTINATION_SQL_STREAM\"\n SELECT eventTimeStamp, 'CallsPerUser', \"userName\" , 'None', 'Sum', callsPerUser FROM (\n SELECT STREAM STEP(cloudtraillogs.\"eventTimestamp\" BY INTERVAL '10' SECOND) as eventTimeStamp, COUNT(*) callsPerUser, \"userName\"\n FROM \"SOURCE_SQL_STREAM_001\" cloudtraillogs\n GROUP BY \"userName\", STEP(cloudtraillogs.ROWTIME BY INTERVAL '10' SECOND), STEP(cloudtraillogs.\"eventTimestamp\" BY INTERVAL '10' SECOND));\n \nCREATE OR REPLACE PUMP \"PUMP_FOR_SUCCESSFUL_CALLS\" AS\n INSERT INTO \"DESTINATION_SQL_STREAM\"\n SELECT eventTimeStamp, 'NumberOfSuccessfulCalls', 'All' , 'None', 'Sum', numberOfSuccessfulCalls FROM (\n SELECT STREAM STEP(cloudtraillogs.\"eventTimestamp\" BY INTERVAL '10' SECOND) AS eventTimeStamp, COUNT(*) numberOfSuccessfulCalls\n FROM \"SOURCE_SQL_STREAM_001\" cloudtraillogs\n GROUP BY STEP(cloudtraillogs.ROWTIME BY INTERVAL '10' SECOND), STEP(cloudtraillogs.\"eventTimestamp\" BY INTERVAL '10' SECOND));" Inputs: - NamePrefix: "SOURCE_SQL_STREAM" InputSchema: RecordColumns: - Name: "source" SqlType: "VARCHAR(256)" Mapping: "$.source" - Name: "sourceIPAddress" SqlType: "VARCHAR(64)" Mapping: "$.detail.sourceIPAddress" - Name: "eventSource" SqlType: "VARCHAR(256)" Mapping: "$.detail.eventSource" - Name: "eventName" SqlType: "VARCHAR(1024)" Mapping: "$.detail.eventName" - Name: "userName" SqlType: "VARCHAR(1024)" Mapping: "$.detail.userIdentity.sessionContext.sessionIssuer.userName" - Name: "eventTimestamp" SqlType: "TIMESTAMP" Mapping: "$.detail.eventTime" - Name: "errorCode" Mapping: "$.detail.errorCode" SqlType: "VARCHAR(256)" RecordFormat: RecordFormatType: "JSON" MappingParameters: JSONMappingParameters: RecordRowPath: "$" RecordEncoding: "UTF-8" KinesisFirehoseInput: ResourceARN: !Join ['' , ["arn:aws:firehose:", !Ref "AWS::Region", ":" , !Ref "AWS::AccountId" , ":deliverystream/" , !Ref FireHoseDestination]] RoleARN: !GetAtt KinesisAnalyticsRole.Arn KinesisAnalyticsAppOutputs: Type: "AWS::KinesisAnalytics::ApplicationOutput" Properties: ApplicationName: !Ref KinesisAnalyticsApp Output: DestinationSchema: RecordFormatType: "CSV" KinesisStreamsOutput: ResourceARN: !GetAtt OutputKinesisStream.Arn RoleARN: !GetAtt KinesisAnalyticsRole.Arn Name : "DESTINATION_SQL_STREAM" KinesisAnalyticsAppAnomalyOutput: Type: "AWS::KinesisAnalytics::ApplicationOutput" DependsOn: - KinesisAnalyticsAppOutputs Properties: ApplicationName: !Ref KinesisAnalyticsApp Output: DestinationSchema: RecordFormatType: "CSV" KinesisStreamsOutput: ResourceARN: !GetAtt OutputKinesisStream.Arn RoleARN: !GetAtt KinesisAnalyticsRole.Arn Name : "DESTINATION_ANOMALY_STREAM" CloudTrailAnalyticsTable: Type: AWS::DynamoDB::Table Properties: BillingMode: PROVISIONED PointInTimeRecoverySpecification: PointInTimeRecoveryEnabled: true AttributeDefinitions: - AttributeName: MetricType AttributeType: S - AttributeName: EventTime AttributeType: S KeySchema: - KeyType: HASH AttributeName: MetricType - KeyType: RANGE AttributeName: EventTime ProvisionedThroughput: ReadCapacityUnits: 50 WriteCapacityUnits: 50 SSESpecification: SSEEnabled: true TimeToLiveSpecification: AttributeName: TimeToLive Enabled: true CloudTrailAnalyticsIPTable: Type: AWS::DynamoDB::Table Properties: BillingMode: PROVISIONED PointInTimeRecoverySpecification: PointInTimeRecoveryEnabled: true AttributeDefinitions: - AttributeName: Hour AttributeType: S - AttributeName: Minute AttributeType: S KeySchema: - KeyType: HASH AttributeName: Hour - KeyType: RANGE AttributeName: Minute ProvisionedThroughput: ReadCapacityUnits: 20 WriteCapacityUnits: 20 SSESpecification: SSEEnabled: true TimeToLiveSpecification: AttributeName: TimeToLive Enabled: true UpdateDDBLambdaFunc: Type: AWS::Lambda::Function Properties: Code: S3Bucket: !Join ["-", [!FindInMap ["SourceCode", "General", "S3Bucket"], Ref: "AWS::Region"]] S3Key: !Join ["/", [!FindInMap ["SourceCode", "General", "KeyPrefix"], "update_ddb_from_stream.zip"]] Description: "%%SOLUTION_NAME%%: Uses Kinesis stream as event source. Updates DynamoDB table with data from stream." FunctionName: real-time-insights-account-activity-update-ddb Handler: update_ddb_from_stream.lambda_handler Role: Fn::GetAtt: - UpdateDDBLambdaExecutionRole - Arn Runtime: python3.8 Timeout: 120 Environment: Variables: SEND_ANONYMOUS_DATA: !FindInMap [ "Send", "AnonymousUsage", "Data"] UUID: !GetAtt SolutionUuid.UUID LOG_LEVEL: INFO TABLE: !Ref CloudTrailAnalyticsTable IP_TABLE: !Ref CloudTrailAnalyticsIPTable Metadata: cfn_nag: rules_to_suppress: - id: "W89" reason: "not a valid use case for VPC deployment" - id: "W92" reason: "not a valid reserved concurrency" UpdateDDBLambdaExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: "/" Policies: - PolicyName: root PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: - !Join ["", ["arn:aws:logs:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":log-group:/aws/lambda/real-time-insights-account-activity-update-ddb:*"]] - Effect: Allow Action: - kinesis:GetRecords - kinesis:GetShardIterator - kinesis:DescribeStream - kinesis:ListStreams Resource: - Fn::GetAtt: - OutputKinesisStream - Arn - Effect: Allow Action: - dynamodb:GetItem - dynamodb:PutItem Resource: - !Sub ${CloudTrailAnalyticsTable.Arn} - !Sub ${CloudTrailAnalyticsIPTable.Arn} LambdaEventSourceMapping: Type: AWS::Lambda::EventSourceMapping Properties: EventSourceArn: Fn::GetAtt: - OutputKinesisStream - Arn FunctionName: Fn::GetAtt: - UpdateDDBLambdaFunc - Arn StartingPosition: LATEST Enabled: true WebsiteBucket: Type: AWS::S3::Bucket DeletionPolicy: Retain Properties: BucketName: Ref: WebsiteBucketName WebsiteConfiguration: IndexDocument: "dash.html" ErrorDocument: "dash.html" LoggingConfiguration: DestinationBucketName: !Ref LogsBucket LogFilePrefix: website-bucket/ PublicAccessBlockConfiguration: BlockPublicAcls: True BlockPublicPolicy: True IgnorePublicAcls: True RestrictPublicBuckets: True BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: AES256 WebsiteBucketPolicy: Type: "AWS::S3::BucketPolicy" Properties: Bucket: Ref: "WebsiteBucket" PolicyDocument: Statement: - Action: - "s3:GetObject" Effect: "Allow" Resource: Fn::Join: - "" - - "arn:aws:s3:::" - Ref: "WebsiteBucket" - "/*" Principal: CanonicalUser: !GetAtt WebsiteOriginAccessIdentity.S3CanonicalUserId WebsiteOriginAccessIdentity: Type: AWS::CloudFront::CloudFrontOriginAccessIdentity Properties: CloudFrontOriginAccessIdentityConfig: Comment: !Sub "access-identity-${WebsiteBucket}" ##Logging bucket for cloudFront and other solution buckets LogsBucket: DeletionPolicy: Retain Type: AWS::S3::Bucket Properties: AccessControl: LogDeliveryWrite PublicAccessBlockConfiguration: BlockPublicAcls: True BlockPublicPolicy: True IgnorePublicAcls: True RestrictPublicBuckets: True BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: AES256 Metadata: cfn_nag: rules_to_suppress: - id: W35 reason: "This is the logs bucket for all the other S3 Buckets and CloudFront" - id: W51 reason: "Policy not required for this bucket." WebsiteDistribution: Type: AWS::CloudFront::Distribution Properties: DistributionConfig: Comment: "Website distribution for solution" Origins: - Id: S3-solution-website DomainName: !Sub "${WebsiteBucket}.s3.${AWS::Region}.amazonaws.com" S3OriginConfig: OriginAccessIdentity: !Sub "origin-access-identity/cloudfront/${WebsiteOriginAccessIdentity}" DefaultCacheBehavior: TargetOriginId: S3-solution-website AllowedMethods: - GET - HEAD - OPTIONS - PUT - POST - PATCH - DELETE CachedMethods: - GET - HEAD - OPTIONS ForwardedValues: QueryString: False ViewerProtocolPolicy: redirect-to-https IPV6Enabled: True ViewerCertificate: CloudFrontDefaultCertificate: True Enabled: True HttpVersion: 'http2' Logging: IncludeCookies: False Bucket: !GetAtt LogsBucket.DomainName Prefix: cloudfront-logs/ Metadata: cfn_nag: rules_to_suppress: - id: W70 reason: "distribution does not use Aliases https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-viewercertificate.html#cfn-cloudfront-distribution-viewercertificate-cloudfrontdefaultcertificate" CognitoUserPool: Type: "AWS::Cognito::UserPool" Properties: UserPoolName: "real-time-account-activity-user-pool" AliasAttributes: - "email" AutoVerifiedAttributes: - "email" AdminCreateUserConfig: AllowAdminCreateUserOnly: True InviteMessageTemplate: EmailMessage: !Sub |
You are invited to join the CloudTrail analytics dashboard. Your dashboard credentials are as follows:
Username: {username}
Password: {####}
Please sign in to the dashboard with the user name and your temporary password provided above at:
https://${WebsiteDistribution.DomainName}/dash.html
You are invited to join the CloudTrail analytics dashboard. Your dashboard credentials are as follows:
Username: {username}
Password: {####}
Please sign in to the dashboard with the user name and temporary password provided above at:
https://${WebsiteDistribution.DomainName}/dash.html