Skip to content

Commit

Permalink
Merge pull request guardian#1 from JCotton1123/monitor-fxn
Browse files Browse the repository at this point in the history
Numerous enhancements and updates
  • Loading branch information
JCotton1123 authored May 12, 2017
2 parents 3cbf724 + 1c9daeb commit d0d3040
Show file tree
Hide file tree
Showing 10 changed files with 244 additions and 78 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.DS_Store
*.pyc
slack.py
cf-notify.zip
vendor
17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
# CF Notify

## What?
An AWS Lambda function that will post Cloud Formation status updates to a Slack channel via a Slack Web Hook.
An AWS Lambda function that will post Cloud Formation status updates to a Slack channel via a Slack Web Hook. Additionally it will notify you of stacks that do not have notifications enabled.


## Why?
To give visibility of Cloud Formation changes to the whole team in a quick and simple manner. For example:

![example Slack messages](./example.jpeg)
![example Slack messages](./misc/example.jpeg)


## How?
CF Notify has a stack of AWS resources consisting of:
- An SNS Topic
- A Lambda function, which uses the SNS Topic as an event source
- CloudWatch Rule
- A Lambda function, which uses the SNS Topic and CloudWatch Scheduled Event as event sources
- An IAM Role to execute the Lambda function

We add the SNS Topic of CF Notify to the notification ARNs of the Stack we want to monitor.
Expand All @@ -35,15 +36,17 @@ You can create an incoming webhook [here](https://my.slack.com/services/new/inco
This is done using the script [deploy.sh](./deploy.sh).

```sh
./deploy.sh $CHANNEL $WEBHOOK $AWS_PROFILE
./deploy.sh $CHANNEL $WEBHOOK $BUCKET $TOPIC $AWS_PROFILE
```

Where:
- CHANNEL is the Slack channel or user to send messages to. It will be used in the naming of the Lambda artifact file stored in S3.
- CHANNEL is the Slack channel or user to send messages to.
- WEBHOOK is the Web Hook URL of an Incoming Web Hook (see https://api.slack.com/incoming-webhooks).
- AWS_PROFILE is the aws cli profile you want to use for deploy. Default profile is "default"
- BUCKET is the S3 bucket where the Lambda artifacts are deployed.
- TOPIC is the SNS topic name.
- AWS_PROFILE is the aws cli profile you want to use for deploy. The default profile is "default".

`deploy.sh` will create a zip file and upload it to S3 and also create a cloud formation stack using the [template](./cf-notify.json).
`deploy.sh` will create a zip file and upload it to S3 and also create a cloud formation stack using the [template](./cloudformation/cf-notify.json).

## Usage

Expand Down
68 changes: 60 additions & 8 deletions cf-notify.json → cloudformation/cf-notify.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,26 @@
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "cf notify stack",
"Parameters": {
"Bucket": {
"Release": {
"Description": "The release/version of cf-notify",
"Type": "String"
},
"ArtifactBucket": {
"Description": "S3 bucket to locate lambda function (cf-notify.zip)",
"Type": "String"
}
},
"TopicName": {
"Description": "The SNS topic name used by cf-notify",
"Type": "String"
},
"SlackChannel": {
"Description": "The Slack channel that notifications are delivered to",
"Type": "String"
},
"SlackWebhook": {
"Description": "The Slack webhook used to handle Slack integration",
"Type": "String"
}
},
"Resources": {
"CFNotifyRole": {
Expand Down Expand Up @@ -42,9 +58,17 @@
{
"Effect": "Allow",
"Action": [
"cloudformation:DescribeStacks",
"cloudformation:DescribeStackResources"
],
"Resource": "arn:aws:cloudformation:*:*:*/*/*"
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"iam:ListUsers"
],
"Resource": "*"
}
]
}
Expand All @@ -60,7 +84,8 @@
"Endpoint": { "Fn::GetAtt": [ "CFNotifyFunction", "Arn" ] },
"Protocol": "lambda"
}
]
],
"TopicName": { "Ref": "TopicName" }
}
},
"CFNotifyFunction": {
Expand All @@ -72,21 +97,48 @@
"Fn::GetAtt": [ "CFNotifyRole", "Arn" ]
},
"Code": {
"S3Bucket": { "Ref": "Bucket" },
"S3Key": "cf-notify.zip"
"S3Bucket": { "Ref": "ArtifactBucket" },
"S3Key": {"Fn::Join": ["/", [{ "Ref": "Release" }, "cf-notify.zip"]]}
},
"Runtime": "python2.7",
"Timeout": "30"
"Timeout": "30",
"Environment": {
"Variables": {
"CHANNEL": { "Ref": "SlackChannel" },
"WEBHOOK": { "Ref": "SlackWebhook" }
}
}
}
},
"CFNotifyScheduledRule": {
"Type": "AWS::Events::Rule",
"Properties": {
"Description": "CF Notify check stacks for notification config",
"ScheduleExpression": "rate(12 hours)",
"State": "ENABLED",
"Targets": [{
"Arn": { "Fn::GetAtt": ["CFNotifyFunction", "Arn"] },
"Id": { "Fn::Join": ["", ["CFNotifyFunction", { "Ref": "Release" }]]}
}]
}
},
"CFNotifyInvokePermission": {
"CFNotifySnsInvokePermission": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName" : { "Ref" : "CFNotifyFunction" },
"Action": "lambda:InvokeFunction",
"Principal": "sns.amazonaws.com",
"SourceArn": { "Ref": "CFNotifyTopic" }
}
},
"CFNotifyEventsInvokePermissions": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName" : { "Ref" : "CFNotifyFunction" },
"Action": "lambda:InvokeFunction",
"Principal": "events.amazonaws.com",
"SourceArn": { "Fn::GetAtt": ["CFNotifyScheduledRule", "Arn"] }
}
}
},
"Outputs": {
Expand Down
62 changes: 29 additions & 33 deletions deploy.sh
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
#!/usr/bin/env bash

test $(which pwgen)
if [ $? != "0" ]; then
echo -e "pwgen not found. Please install using 'sudo apt-get install pwgen' (GNU/Linux) or 'brew install pwgen' (OSX)"
exit 1
fi

if [ $# -lt 1 ]
then
echo "usage: $0 <CHANNEL> <WEBHOOK> <AWS_PROFILE>"
echo "usage: $0 <CHANNEL> <WEBHOOK> <BUCKET> [TOPIC] [PROFILE]"
exit 1
fi

CHANNEL=$1
WEBHOOK=$2
PROFILE=$3
BUCKET=$3
TOPIC=${4:-"cf-notify"}
PROFILE=${5:-"default"}

RELEASE=$(date +%Y-%m-%d-%H%M)

if [ -z $BUCKET ];
then
echo "Please specify a bucket name";
exit 1
fi

if [ -z $CHANNEL ];
then
Expand All @@ -28,10 +32,6 @@ then
exit 1
fi

if [ -z $PROFILE ];
then
PROFILE="default"
fi

if [[ $(aws configure --profile $PROFILE list) && $? -ne 0 ]];
then
Expand All @@ -48,40 +48,36 @@ fi

CHANNEL_NAME=`echo ${CHANNEL:1} | tr '[:upper:]' '[:lower:]'`

echo 'Creating bucket'
BUCKET="cf-notify-`pwgen -1 --no-capitalize 5`"
echo $BUCKET
aws s3 mb "s3://$BUCKET" --profile $PROFILE
echo "Creating bucket $BUCKET"
aws s3 mb "s3://$BUCKET" --profile $PROFILE || exit 1
echo "Bucket $BUCKET created"


echo 'Creating lambda zip artifact'

if [ ! -f slack.py ]; then
cat > slack.py <<EOL
WEBHOOK='$WEBHOOK'
CHANNEL='$CHANNEL'
CUSTOM_CHANNELS={}
EOL
fi

zip cf-notify.zip lambda_notify.py slack.py
zip -j cf-notify.zip src/*
echo 'Lambda artifact created'


echo 'Moving lambda artifact to S3'
aws s3 cp cf-notify.zip s3://$BUCKET/cf-notify.zip --profile $PROFILE

aws s3 cp cf-notify.zip s3://$BUCKET/$RELEASE/cf-notify.zip --profile $PROFILE
rm cf-notify.zip
echo 'Lambda artifact moved'

echo 'Deleting existing stack if it exists'
aws cloudformation delete-stack --stack-name cf-notify || true
aws cloudformation wait stack-delete-complete --stack-name cf-notify
echo 'Stack deleted if it existed'

echo 'Creating stack'
aws cloudformation create-stack \
--template-body file://cf-notify.json \
--template-body file://cloudformation/cf-notify.json \
--stack-name cf-notify \
--capabilities CAPABILITY_IAM \
--parameters ParameterKey=Bucket,ParameterValue=$BUCKET \
--profile $PROFILE
--parameters ParameterKey=ArtifactBucket,ParameterValue=$BUCKET \
ParameterKey=Release,ParameterValue=$RELEASE \
ParameterKey=TopicName,ParameterValue=$TOPIC \
ParameterKey=SlackChannel,ParameterValue=$CHANNEL \
ParameterKey=SlackWebhook,ParameterValue=$WEBHOOK \
--profile $PROFILE \
--output text

if [[ $? != 0 ]];
then
Expand Down
File renamed without changes
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
boto3==1.4.4
Loading

0 comments on commit d0d3040

Please sign in to comment.