forked from stevemac007/aws-to-slack
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added support for alerting on Archiving GuardDuty alerts.
- Loading branch information
1 parent
d3230d1
commit 8f02391
Showing
4 changed files
with
261 additions
and
117 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,110 +1,3 @@ | ||
AWS_CLI ?= /usr/local/bin/aws | ||
TEMP_PATH = .temp | ||
RELEASE_ZIP = release.zip | ||
BUCKET_PREFIX ?= aws-to-slack | ||
|
||
# Load from .env file | ||
ifdef TARGET | ||
include $(TARGET) | ||
export | ||
endif | ||
|
||
# Dependency definitions | ||
ifdef AWS_REGION | ||
regionArg= --region $(AWS_REGION) | ||
endif | ||
ifdef AWS_PROFILE | ||
awsProfile= --profile $(AWS_PROFILE) | ||
endif | ||
ifndef LAMBDA_NAME | ||
ifndef STACK_ID | ||
usesLambdaName := create-stack load-lambda-name | ||
else | ||
usesLambdaName := load-lambda-name | ||
endif | ||
endif | ||
ifeq (,$(wildcard $(RELEASE_ZIP))) | ||
usesReleaseZip := package | ||
endif | ||
|
||
info: | ||
@echo "Deploying to $(BUCKET_PREFIX)" | ||
|
||
# Create release.zip file | ||
.PHONY: package | ||
package: | ||
# Prepare | ||
-@rm -r "$(TEMP_PATH)" 2>/dev/null || true | ||
-@rm "$(RELEASE_ZIP)" 2>/dev/null || true | ||
@mkdir -p "$(TEMP_PATH)" | ||
|
||
# Copy sources to temporary folder | ||
@cp -R src package-lock.json package.json "$(TEMP_PATH)/" | ||
|
||
# Install dependencies | ||
@cd "$(TEMP_PATH)" && npm install --production | ||
|
||
# Package artifact | ||
@cd "$(TEMP_PATH)" && zip -rq "../$(RELEASE_ZIP)" . | ||
|
||
# Cleanup | ||
@rm -r "$(TEMP_PATH)" | ||
|
||
|
||
# Perform create-stack operation | ||
.PHONY: create-stack-raw | ||
create-stack-raw: | ||
# Create CloudFormation Stack | ||
aws $(awsProfile) cloudformation create-stack --stack-name "$(STACK_NAME)" --template-body file://cloudformation.yaml \ | ||
$(regionArg) --capabilities CAPABILITY_IAM --parameters $(STACK_PARAMS) | ||
aws $(awsProfile) cloudformation wait stack-create-complete --stack-name "$(STACK_NAME)" $(regionArg) | ||
|
||
|
||
# Create the stack, print output, and save to TARGET file | ||
# (must be separate from create-stack-raw because uses $(shell ...) | ||
.PHONY: create-stack | ||
create-stack: create-stack-raw | ||
$(eval STACK_ID := $(shell aws $(awsProfile) cloudformation describe-stacks --stack-name "$(STACK_NAME)" \ | ||
$(regionArg) --output text --query 'Stacks[0].StackId' )) | ||
@echo "Add to your .env file: STACK_ID=$(STACK_ID)" | ||
@ [ -z "$(TARGET)" ] || { echo "# Makefile on `date`" >> "$(TARGET)"; echo "STACK_ID=$(STACK_ID)" >> "$(TARGET)"; } | ||
|
||
|
||
# Update CloudFormation stack | ||
.PHONY: update-stack | ||
update-stack: | ||
aws $(awsProfile) cloudformation update-stack --stack-name "$(STACK_NAME)" --template-body file://cloudformation.yaml \ | ||
$(regionArg) --capabilities CAPABILITY_IAM --parameters $(STACK_PARAMS) | ||
|
||
|
||
# Perform describe-stack to retrieve name of Lambda function | ||
.PHONY: load-lambda-name | ||
load-lambda-name: | ||
# Load Lambda name from CloudFormation | ||
@if [ -z "$(STACK_NAME)" ]; then echo "Var STACK_NAME must be defined"; exit 1; fi; | ||
$(eval LAMBDA_NAME := $(shell aws $(awsProfile) cloudformation describe-stacks --stack-name "$(STACK_NAME)" \ | ||
$(regionArg) --output text --query 'Stacks[0].Outputs[?OutputKey==`LambdaFunction`].OutputValue')) | ||
@echo "Add to your .env file: LAMBDA_NAME=$(LAMBDA_NAME)" | ||
@ [ -z "$(TARGET)" ] || { echo "# Makefile on `date`" >> "$(TARGET)"; echo "LAMBDA_NAME=$(LAMBDA_NAME)" >> "$(TARGET)"; } | ||
|
||
|
||
# Update existing Lambda function | ||
.PHONY: deploy | ||
deploy: $(usesReleaseZip) $(usesLambdaName) | ||
# Update Lambda function code | ||
aws $(awsProfile) lambda update-function-code --function-name "$(LAMBDA_NAME)" \ | ||
$(regionArg) --zip-file "fileb://$(RELEASE_ZIP)" --publish | ||
|
||
|
||
# Copy local files to global S3 deployment buckets | ||
REGIONS ?= \ | ||
us-east-1 us-east-2 us-west-1 us-west-2 \ | ||
eu-central-1 eu-west-1 eu-west-2 eu-west-3 \ | ||
ap-northeast-1 ap-northeast-2 ap-south-1 ap-southeast-1 ap-southeast-2 \ | ||
ca-central-1 sa-east-1 | ||
# disabled: cn-north-1 cn-northwest-1 | ||
.PHONY: publish | ||
publish: $(usesReleaseZip) $(REGIONS) | ||
$(REGIONS): | ||
aws $(awsProfile) s3 cp "./cloudformation.yaml" "s3://$(BUCKET_PREFIX)-$@" --acl public-read | ||
aws $(awsProfile) s3 cp "$(RELEASE_ZIP)" "s3://$(BUCKET_PREFIX)-$@" --acl public-read | ||
deploy: | ||
sam deploy |
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
--- | ||
AWSTemplateFormatVersion: '2010-09-09' | ||
Description: 'SNS notification sent to Slack channel' | ||
|
||
Transform: | ||
- AWS::Serverless-2016-10-31 | ||
|
||
Metadata: | ||
'AWS::CloudFormation::Interface': | ||
ParameterGroups: | ||
- Label: | ||
default: 'Slack Setup' | ||
Parameters: | ||
- HookUrl | ||
- Channel | ||
- KmsDecryptKeyArn | ||
- Label: | ||
default: 'Default Subscriptions' | ||
Parameters: | ||
- ErrorAlertEmail | ||
Parameters: | ||
ErrorAlertEmail: | ||
Description: 'Optional: email address to receive alert that function is failing. The email will only contain a count of failures -- you will need to then review CloudWatch logs to determine cause of the issue. HIGHLY RECOMMEND handling failures like this within a DeadLetterQueue via ParentAlertStack instead.' | ||
Type: String | ||
Default: '' | ||
HookUrl: | ||
Type: String | ||
Description: 'Slack webhook URL; see https://example.slack.com/apps/' | ||
Channel: | ||
Type: String | ||
Description: 'Optional: Channel name to post within' | ||
Default: '' | ||
KmsDecryptKeyArn: | ||
Type: String | ||
Description: 'Optional: Key used to encrypt Hook or Channel values. If provided will create IAM policy to grant access to decrypt the values.' | ||
Default: '' | ||
|
||
Conditions: | ||
HasChannel: !Not [!Equals [!Ref Channel, '']] | ||
HasKmsKey: !Not [!Equals [!Ref KmsDecryptKeyArn, '']] | ||
HasAlertEmail: !Not [!Equals [!Ref ErrorAlertEmail, '']] | ||
|
||
Resources: | ||
|
||
SlackAlertTopic: | ||
Type: AWS::SNS::Topic | ||
Properties: | ||
TopicName: | ||
Fn::Sub: ${AWS::StackName} | ||
Tags: | ||
- Key: Name | ||
Value: | ||
Fn::Sub: ${AWS::StackName} | ||
|
||
SlackFunction: | ||
Type: AWS::Serverless::Function | ||
Properties: | ||
Handler: src/index.handler | ||
Runtime: nodejs16.x | ||
Role: !GetAtt FunctionRole.Arn | ||
MemorySize: 256 | ||
Timeout: 15 # Cross-region metrics lookup requires at least 10s | ||
Tracing: Active | ||
Environment: | ||
Variables: | ||
SLACK_CHANNEL: !If | ||
- HasChannel | ||
- !Ref Channel | ||
- !Ref 'AWS::NoValue' | ||
SLACK_HOOK_URL: !Ref HookUrl | ||
Events: | ||
SNSEvent: | ||
Type: SNS | ||
Properties: | ||
Topic: | ||
Ref: SlackAlertTopic | ||
|
||
FunctionRole: | ||
Type: 'AWS::IAM::Role' | ||
Properties: | ||
ManagedPolicyArns: | ||
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole | ||
- arn:aws:iam::aws:policy/CloudWatchReadOnlyAccess | ||
- arn:aws:iam::aws:policy/AWSCodeCommitReadOnly | ||
AssumeRolePolicyDocument: | ||
Version: '2012-10-17' | ||
Statement: | ||
- Action: [ 'sts:AssumeRole' ] | ||
Effect: Allow | ||
Principal: | ||
Service: [ 'lambda.amazonaws.com' ] | ||
|
||
KmsDecryptPolicy: | ||
Condition: HasKmsKey | ||
Type: 'AWS::IAM::Policy' | ||
Properties: | ||
Roles: [ !Ref FunctionRole ] | ||
PolicyName: AllowDecryptKms | ||
PolicyDocument: | ||
Version: '2012-10-17' | ||
Statement: | ||
- Effect: Allow | ||
Action: [ 'kms:Decrypt' ] | ||
Resource: !Ref KmsDecryptKeyArn | ||
|
||
# | ||
# CloudWatch Alarm | ||
# | ||
FunctionFailing: | ||
Type: 'AWS::CloudWatch::Alarm' | ||
Properties: | ||
AlarmDescription: 'AWS-to-Slack Lambda function is failing' | ||
Namespace: 'AWS/Lambda' | ||
MetricName: Errors | ||
Statistic: Sum | ||
Period: 300 | ||
EvaluationPeriods: 1 | ||
ComparisonOperator: GreaterThanThreshold | ||
Threshold: 0 | ||
TreatMissingData: notBreaching | ||
Dimensions: | ||
- Name: FunctionName | ||
Value: !Ref SlackFunction | ||
AlarmActions: | ||
- !Ref FunctionFailingTopic | ||
|
||
FunctionFailingTopic: | ||
Type: 'AWS::SNS::Topic' | ||
Properties: {} | ||
|
||
FunctionFailingEmailSubscription: | ||
Condition: HasAlertEmail | ||
Type: 'AWS::SNS::Subscription' | ||
Properties: | ||
TopicArn: !Ref FunctionFailingTopic | ||
Protocol: email | ||
Endpoint: !Ref ErrorAlertEmail | ||
|
||
Outputs: | ||
LambdaFunction: | ||
Description: 'Lambda function name created by this stack.' | ||
Value: !Ref SlackFunction | ||
|
||
LambdaFunctionArn: | ||
Description: 'Lambda function ARN created by this stack.' | ||
Value: !GetAtt SlackFunction.Arn |
Oops, something went wrong.