Skip to content

Commit

Permalink
Add Slack posting option
Browse files Browse the repository at this point in the history
  • Loading branch information
jimzucker committed Sep 7, 2020
1 parent 717f30b commit a4e6270
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 11 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.idea/
info.log
# Byte-compiled / optimized / DLL files
__pycache__/
Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ So I set out to automate this as a slack post daily to save time. While doing t
1. Numbers generated include percent change must be consistent with the numbers in Cost Explorer UI.
2. Application must produce a cleanly formatted one line output.
3. Code must be written as python functions that we can re-use to integrate into a slack-bot.
4. Post to slack if url is defined as an AWS secret (see below)

# Technical Notes

Expand All @@ -20,6 +21,18 @@ So I set out to automate this as a slack post daily to save time. While doing t
### Sample Output
![Sample Output of get_forecast](https://github.com/jimzucker/aws-forecast/blob/master/images/get_forecast_sample_output.png)

### Enabling Slack
To enable posting the message to slack instead of outputing to the command line you must define a secret in secrets manager called 'awsgenie_forecast_slack_url' with key=slack_url and value=<slack url>, if this secret is not found then the output is to the console.

![Sample Output of get_forecast](https://github.com/jimzucker/aws-forecast/blob/master/images/aws_secret.png)


#### SSL Errors posting message to slack
If you get SSL Cert errors defining this environment varialbe may help you:
```
export SSL_CERT_FILE=$(python -m certifi)
```

### AWS API Used
1. get_cost_forecast - used to get current month forecast. (note we exclude credits)
2. get_cost_and_usage - used to get prior & current month actuals (note we exclude credits)
Expand Down
41 changes: 30 additions & 11 deletions get_forecast.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,16 @@
"""

import argparse
import os
import sys
import logging
import boto3
from datetime import datetime
from dateutil.relativedelta import relativedelta
from botocore.exceptions import ClientError
import json
from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError

# noinspection All
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(threadName)s] [%(levelname)s] %(message)s",
Expand Down Expand Up @@ -87,11 +89,13 @@ def get_secret(client, secret_name):
# Secrets Manager decrypts the secret value using the associated KMS CMK
# Depending on whether the secret was a string or binary, only one of these fields will be populated
if 'SecretString' in get_secret_value_response:
text_secret_data = get_secret_value_response['SecretString']
text_secret_data = json.loads(get_secret_value_response['SecretString']).get('slack_url')
else:
binary_secret_data = get_secret_value_response['SecretBinary']
#binary_secret_data = get_secret_value_response['SecretBinary']
logger.error("Binary Secrets not supported")

# Your code goes here.
return text_secret_data

#
# Calculates forecast, ignoring credits
Expand Down Expand Up @@ -203,22 +207,37 @@ def calc_forecast(costs_explorer_client):

def display_output(forecast_slack_url,message):
if forecast_slack_url != "":
logger.info("slack" + message)
send_slack(forecast_slack_url, message)
else:
logger.info(message)


def main():
def send_slack(slack_hook_url, message):
slack_message = {
'text': message
}

req = Request(slack_hook_url, json.dumps(slack_message).encode('utf-8'))

try:
response = urlopen(req)
response.read()
logger.debug("Message posted to slack")
except HTTPError as e:
logger.error("Request failed: %d %s", e.code, e.reason)
except URLError as e:
logger.error("Server connection failed: %s", e.reason)

def get_forecast():
try:
cmdline_params = arg_parser()
boto3_session = boto3.Session(profile_name=cmdline_params.profile)
costs_explorer_client = boto3_session.client('ce')

forecast_slack_url=""
try:
secrets_manager_client = session.client('secretsmanager')
forecast_slack_url=get_secret(secrets_manager_client, "awsgenie:forecast_slack_url")
except Exception:
secrets_manager_client = boto3_session.client('secretsmanager')
forecast_slack_url='https://' + get_secret(secrets_manager_client, "awsgenie_forecast_slack_url")
except Exception as e:
logger.warning("Disabling Slack URL not found")

if cmdline_params.type in ['FORECAST']:
Expand All @@ -240,4 +259,4 @@ def main():
sys.exit(0)

if __name__ == '__main__':
main()
get_forecast()
Binary file added images/aws_secret.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit a4e6270

Please sign in to comment.