Last active
April 3, 2020 15:38
-
-
Save heitorlessa/5d2295655f9d76483969d215986e53b0 to your computer and use it in GitHub Desktop.
L@E - Aggregate Logs from all regions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
AWSTemplateFormatVersion: 2010-09-09 | |
Description: Quick snippet for main resources to aggregate L@E Logs | |
Parameters: | |
Stage: | |
Type: String | |
EdgeFunction: | |
Type: String | |
Description: Lambda@Edge Function Name | |
Resources: | |
EdgeAuthParameter: | |
Type: "AWS::SSM::Parameter" | |
Properties: | |
Name: !Sub /${Stage}/service/auth/edgeAuthFunctionName | |
Description: Edge Auth Lambda function name | |
Type: String | |
Value: !Ref EdgeAuthFunction | |
# Lambda@Edge Log aggregate | |
AggregateS3Bucket: | |
Type: AWS::S3::Bucket | |
EdgeLogsKinesisFirehoseStreamRole: | |
Type: AWS::IAM::Role | |
Properties: | |
AssumeRolePolicyDocument: | |
Version: 2012-10-17 | |
Statement: | |
- | |
Effect: Allow | |
Principal: | |
Service: firehose.amazonaws.com | |
Action: 'sts:AssumeRole' | |
Policies: | |
- | |
PolicyName: kinesis_delivery_lambda_edge | |
PolicyDocument: | |
Version: 2012-10-17 | |
Statement: | |
- | |
Effect: Allow | |
Action: | |
- 's3:AbortMultipartUpload' | |
- 's3:GetBucketLocation' | |
- 's3:GetObject' | |
- 's3:ListBucket' | |
- 's3:ListBucketMultipartUploads' | |
- 's3:PutObject' | |
Resource: | |
- !Sub "${AggregateS3Bucket.Arn}" | |
- !Sub "${AggregateS3Bucket.Arn}/*" | |
# NOTE: Enable it if you attach Kinesis Data Streams to Firehose | |
# - | |
# Effect: Allow | |
# Action: | |
# - 'kinesis:DescribeStream' | |
# - 'kinesis:GetShardIterator' | |
# - 'kinesis:GetRecords' | |
# - 'kinesis:ListShards' | |
# Resource: '*' | |
# NOTE: Enable it if you enable Log delivery in Kinesis Firehose | |
# NOTE2: We need both Log Group and Stream created upfront | |
# - | |
# Effect: Allow | |
# Action: | |
# - 'logs:PutLogEvents' | |
# Resource: !Sub KinesisFirehoseCloudWatchLogGroup.Arn | |
EdgeLogsKinesisFirehoseStream: | |
Type: AWS::KinesisFirehose::DeliveryStream | |
Properties: | |
ExtendedS3DestinationConfiguration: | |
BucketARN: !Sub "arn:aws:s3:::${AggregateS3Bucket}" | |
BufferingHints: | |
IntervalInSeconds: 60 | |
SizeInMBs: 3 | |
CompressionFormat: UNCOMPRESSED | |
Prefix: processed/ | |
ErrorOutputPrefix: failed/ | |
RoleARN: !GetAtt EdgeLogsKinesisFirehoseStreamRole.Arn | |
CloudwatchSubscriptionFiltersRole: | |
Type: AWS::IAM::Role | |
Properties: | |
AssumeRolePolicyDocument: | |
Version: 2012-10-17 | |
Statement: | |
- | |
Effect: Allow | |
Principal: | |
Service: | |
- logs.us-east-1.amazonaws.com | |
- logs.us-west-2.amazonaws.com | |
- logs.us-east-2.amazonaws.com | |
- logs.eu-central-1.amazonaws.com | |
- logs.eu-west-1.amazonaws.com | |
- logs.eu-west-2.amazonaws.com | |
- logs.eu-central-1.amazonaws.com | |
- logs.ap-south-1.amazonaws.com | |
- logs.ap-southeast-1.amazonaws.com | |
- logs.ap-southeast-2.amazonaws.com | |
- logs.ap-northeast-1.amazonaws.com | |
- logs.ap-northeast-2.amazonaws.com | |
- logs.sa-east-1.amazonaws.com | |
Action: 'sts:AssumeRole' | |
CloudwatchSubscriptionFilterPolicy: | |
Type: AWS::IAM::Policy | |
Properties: | |
PolicyName: firehose_subscription_filter | |
PolicyDocument: | |
Version: 2012-10-17 | |
Statement: | |
- Effect: Allow | |
Action: | |
- 'kinesis:PutRecord' | |
- 'firehose:PutRecord' | |
- 'firehose:PutRecordBatch' | |
Resource: !Sub ${EdgeLogsKinesisFirehoseStream.Arn} | |
- Effect: Allow | |
Action: 'iam:PassRole' | |
Resource: !Sub ${CloudwatchSubscriptionFiltersRole.Arn} | |
Roles: | |
- | |
Ref: CloudwatchSubscriptionFiltersRole | |
CloudWatchSubscriptionRoleParameter: | |
Type: "AWS::SSM::Parameter" | |
Properties: | |
Name: !Sub /${Stage}/service/auth/cloudwatchSubscriptionRole | |
Description: CloudWatch IAM Role used for cross-region logs ingestion to Kinesis Firehose | |
Type: String | |
Value: !Sub ${CloudwatchSubscriptionFiltersRole.Arn} | |
KinesisFirehoseDeliveryParameter: | |
Type: "AWS::SSM::Parameter" | |
Properties: | |
Name: !Sub /${Stage}/service/auth/firehoseStream | |
Description: Kinesis Firehose Delivery Stream ARN | |
Type: String | |
Value: !Sub ${EdgeLogsKinesisFirehoseStream.Arn} | |
EdgeLogsAggregateS3BucketParameter: | |
Type: "AWS::SSM::Parameter" | |
Properties: | |
Name: !Sub /${Stage}/service/auth/logsBucket | |
Description: S3 Bucket where Lambda@Edge Logs are aggregated | |
Type: String | |
Value: !Ref AggregateS3Bucket | |
CloudformationStackSetAdminRole: | |
Type: "AWS::IAM::Role" | |
Properties: | |
RoleName: AWSCloudFormationStackSetAdministrationRole | |
AssumeRolePolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: cloudformation.amazonaws.com | |
Action: sts:AssumeRole | |
Policies: | |
- PolicyName: AssumeRole-AWSCloudFormationStackSetExecutionRole | |
PolicyDocument: | |
Version: 2012-10-17 | |
Statement: | |
- Effect: Allow | |
Action: | |
- sts:AssumeRole | |
Resource: | |
- "arn:aws:iam::*:role/AWSCloudFormationStackSetExecutionRole" | |
CloudformationStackSetExecutionRole: | |
Type: "AWS::IAM::Role" | |
Properties: | |
RoleName: AWSCloudFormationStackSetExecutionRole | |
AssumeRolePolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: cloudformation.amazonaws.com | |
Action: sts:AssumeRole | |
- Effect: Allow | |
Principal: | |
AWS: !Sub "arn:aws:iam::${AWS::AccountId}:root" | |
Action: sts:AssumeRole | |
Policies: | |
- PolicyName: StackSet-Execution-Admin0Permissions | |
PolicyDocument: | |
Version: 2012-10-17 | |
Statement: | |
- Effect: Allow | |
Action: '*' | |
Resource: '*' | |
Outputs : | |
AggregateS3BucketDomainName: | |
Description: S3 bucket domain name | |
Value: !Sub ${AggregateS3Bucket} | |
EdgeLogsKinesisFirehoseStreamArn: | |
Description: Kinesis Delivery Stream | |
Value: !Sub ${EdgeLogsKinesisFirehoseStream.Arn} | |
CloudwatchSubscriptionFiltersRole: | |
Description: CloudWatch role for subscription filters | |
Value: !Sub ${CloudwatchSubscriptionFiltersRole.Arn} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## Lambda-Edge centralize log stacks | |
STACKSET_REGIONS = eu-west-2 eu-central-1 ap-south-1 ap-northeast-2 ap-northeast-1 ap-southeast-1 ap-southeast-2 sa-east-1 us-east-1 us-east-2 us-west-2 | |
centralize-auth-logs: | |
$(info [+] Deploying Log centralization for Lambda@Edge - Log aggregation across regions) | |
export STACK_SET=$(shell aws cloudformation list-stack-sets --query 'Summaries[?Status==`ACTIVE` && StackSetName==`lambda-edge-log-groups-stackset`].StackSetName' --output text) | |
echo $(STACK_SET) | |
# If CloudFormation Stackset already exists, execute new version only | |
test -n $(STACK_SET) \ | |
&& $(MAKE) _execute_stackset \ | |
|| echo "[*] No stackset found... creating one" | |
# If a CloudFormation Stackset does not exist, create and execute one | |
test -z $(STACK_SET) \ | |
&& $(MAKE) _create_stackset && $(MAKE) _execute_stackset \ | |
|| echo "[!] Failed to create stackset..." | |
_create_stackset: | |
$(info [+] Deploying Log centralization for Lambda@Edge - Creating Stackset) | |
aws cloudformation create-stack-set --template-body file://regional-stack.yaml \ | |
--stack-set-name lambda-edge-log-groups-stackset \ | |
--capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \ | |
--parameters \ | |
ParameterKey=LambdaEdgeFunctionName,ParameterValue=$(shell aws ssm get-parameter --name /prod/service/auth/edgeAuthFunctionName --query 'Parameter.Value' --output text --region ${INFRASTRUCTURE_REGION}) \ | |
ParameterKey=CloudWatchRoleArn,ParameterValue=$(shell aws ssm get-parameter --name /prod/service/auth/cloudwatchSubscriptionRole --query 'Parameter.Value' --output text --region ${INFRASTRUCTURE_REGION}) \ | |
ParameterKey=FirehoseDestinationArn,ParameterValue=$(shell aws ssm get-parameter --name /prod/service/auth/firehoseStream --query 'Parameter.Value' --output text --region ${INFRASTRUCTURE_REGION}) | |
_execute_stackset: | |
$(info [+] Deploying Log centralization for Lambda@Edge - Creating CloudFormation Stack across regions) | |
aws cloudformation create-stack-instances --stack-set-name lambda-edge-log-groups-stackset \ | |
--accounts $(shell aws sts get-caller-identity --query 'Account' --output text) \ | |
--regions $(STACKSET_REGIONS) \ | |
--operation-preferences FailureToleranceCount=1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
AWSTemplateFormatVersion: 2010-09-09 | |
Description: Quick snippet for regional resources to ship L@E Logs to a central location via CW Subscription Filters -> Kinesis FireHose | |
Parameters: | |
LambdaEdgeFunctionName: | |
Description: The name of the LambdaEdge to apply the subscription filter to. | |
Type: String | |
MinLength: 1 | |
CloudWatchRoleArn: | |
Description: The CloudWatch role ARN used to apply the subscription filter. | |
Type: String | |
MinLength: 1 | |
FirehoseDestinationArn: | |
Description: The ARN for the Firehose delivery stream where logs will be aggregated. | |
Type: String | |
MinLength: 1 | |
FilterPattern: | |
Description: A symbolic description of how CloudWatch Logs should interpret the data in each log event, along with filtering expressions that restrict what gets delivered to the destination AWS resource. Please note - If left blank, the subscription filter will match all log events. | |
Type: String | |
Default: '' | |
Conditions: | |
isLondonRegion: !Equals [ !Ref 'AWS::Region', 'eu-west-2' ] | |
isFrankfurtRegion: !Equals [ !Ref 'AWS::Region', 'eu-central-1' ] | |
isMumbaiRegion: !Equals [ !Ref 'AWS::Region', 'ap-south-1' ] | |
isSeoulRegion: !Equals [ !Ref 'AWS::Region', 'ap-northeast-2' ] | |
isTokyoRegion: !Equals [ !Ref 'AWS::Region', 'ap-northeast-1' ] | |
isSingaporeRegion: !Equals [ !Ref 'AWS::Region', 'ap-southeast-1' ] | |
isSydneyRegion: !Equals [ !Ref 'AWS::Region', 'ap-southeast-2' ] | |
isSaoPauloRegion: !Equals [ !Ref 'AWS::Region', 'sa-east-1' ] | |
isNVirginiaRegion: !Equals [ !Ref 'AWS::Region', 'us-east-1' ] | |
isOhioRegion: !Equals [ !Ref 'AWS::Region', 'us-east-2' ] | |
isOregonRegion: !Equals [ !Ref 'AWS::Region', 'us-west-2' ] | |
IsCloudFrontRegionalCachePt1: !Or # Max of 10 conditions | |
- !Condition isLondonRegion | |
- !Condition isFrankfurtRegion | |
- !Condition isMumbaiRegion | |
- !Condition isSeoulRegion | |
- !Condition isTokyoRegion | |
- !Condition isSingaporeRegion | |
- !Condition isSydneyRegion | |
- !Condition isSaoPauloRegion | |
IsCloudFrontRegionalCachePt2: !Or | |
- !Condition isNVirginiaRegion | |
- !Condition isOhioRegion | |
- !Condition isOregonRegion | |
IsCloudFrontRegionalCache: !Or | |
- !Condition IsCloudFrontRegionalCachePt1 | |
- !Condition IsCloudFrontRegionalCachePt2 | |
Resources: | |
# Key resources that should be deployed where CloudFront has Regional Cache | |
RegionalCloudWatchLogGroupCustomResource: | |
Condition: IsCloudFrontRegionalCache | |
Type: 'AWS::Lambda::Function' | |
Properties: | |
Code: | |
ZipFile: > | |
const response = require('cfn-response'); | |
const aws = require('aws-sdk'); | |
exports.handler = async (event, context, callback) => { | |
const logGroupName = event.ResourceProperties.logGroupName; | |
const cloudwatchlogs = new aws.CloudWatchLogs(); | |
const doesLogGroupExist = async (logGroupName) => { | |
console.info(`Verifying if ${logGroupName} exists`) | |
let params = { | |
logGroupNamePrefix: logGroupName | |
} | |
let logGroupStatus = false | |
try { | |
const ret = await cloudwatchlogs.describeLogGroups(params) | |
const logGroups = ret && ret.data && ret.data.logGroups | |
console.log(`Log Group found: ${logGroups}`) | |
if (!logGroups) return logGroupStatus | |
if (logGroups.length > 0) logGroupStatus = true | |
return logGroupStatus | |
} catch (error) { | |
console.error(error) | |
return logGroupStatus | |
} | |
} | |
const createLogGroup = async (logGroupName) => { | |
var params = { | |
logGroupName: logGroupName | |
} | |
console.info(`Creating Log Group ${logGroupName}`) | |
return cloudwatchlogs.createLogGroup(params).promise() | |
} | |
if (event.RequestType == 'Delete' || event.RequestType == 'Update') { | |
// don't delete | |
console.info("No action to be taken upon DELETE || UPDATE.") | |
return response.send(event, context, response.SUCCESS, {}) | |
} | |
if (event.RequestType == 'Create') { | |
const logGroupExists = await doesLogGroupExist(logGroupName) | |
if (!logGroupExists) { | |
try { | |
console.info(`Log Group ${logGroupName} doesn't exist`) | |
await createLogGroup(logGroupName) | |
console.log(`Log Group ${logGroupName} created successfully`) | |
return response.send(event, context, response.SUCCESS, {}) | |
} catch (error) { | |
console.error("Error while creating Log Group") | |
return response.send(event, context, response.FAILED, { error }) | |
} | |
} else { | |
console.info(`Log Group ${logGroupName} already exists; ignoring Create signal`) | |
return response.send(event, context, response.SUCCESS, {}) | |
} | |
} | |
} | |
Handler: index.handler | |
Runtime: nodejs10.x | |
Timeout: 300 | |
Role: !Sub ${RegionalCloudWatchLogGroupCustomResourceRole.Arn} | |
RegionalCloudWatchLogGroupCustomResourceRole: | |
Condition: IsCloudFrontRegionalCache | |
Type: AWS::IAM::Role | |
Properties: | |
AssumeRolePolicyDocument: | |
Version: 2012-10-17 | |
Statement: | |
- | |
Effect: Allow | |
Principal: | |
Service: lambda.amazonaws.com | |
Action: 'sts:AssumeRole' | |
Policies: | |
- | |
PolicyName: cloudwatch_log_groups | |
PolicyDocument: | |
Version: 2012-10-17 | |
Statement: | |
- | |
Effect: Allow | |
Action: | |
- 'logs:*' # FIXME - Describe/Create should work | |
Resource: '*' | |
RegionalCloudWatchLogGroup: | |
Condition: IsCloudFrontRegionalCache | |
Type: 'Custom::CloudWatchLogGroupEdge' | |
Properties: | |
ServiceToken: !Sub ${RegionalCloudWatchLogGroupCustomResource.Arn} | |
logGroupName: !Sub '/aws/lambda/us-east-1.${LambdaEdgeFunctionName}' | |
SubscriptionFilter: | |
Condition: IsCloudFrontRegionalCache | |
Type: AWS::Logs::SubscriptionFilter | |
Properties: | |
DestinationArn: !Ref FirehoseDestinationArn | |
FilterPattern: !Ref FilterPattern | |
LogGroupName: !Sub '/aws/lambda/us-east-1.${LambdaEdgeFunctionName}' | |
RoleArn: !Ref CloudWatchRoleArn | |
DependsOn: RegionalCloudWatchLogGroup | |
# Key resources that should be deployed where CloudFront has Regional Cache | |
Outputs: | |
RegionalCloudWatchLogGroup: | |
Condition: IsCloudFrontRegionalCache | |
Description: Custom Resource for CloudWatch Log Group | |
Value: !Sub ${RegionalCloudWatchLogGroupCustomResource.Arn} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment