- Overview
- Prerequisites
- Setup Steps
- 1. Create a Slack App
- 2. Add Bot Permissions
- 3. Get the Slack Channel ID
- 4. Invite the Bot to the Channel
- 5. Prepare the Lambda Function Code
- 6. Package and Deploy the Lambda Function
- 7. Create an IAM Role for Lambda
- 8. Set Up Environment Variables in AWS Lambda
- 9. Test the Lambda Function
- 10. GitHub Actions Setup
- Conclusion
The Bloodhound Lambda function is designed to scan AWS regions for EC2 and RDS instances and post a summary to a Slack channel. This README provides detailed steps to set up, deploy, and test the Lambda function, including Slack integration.
- AWS account with permissions to create IAM roles, Lambda functions, and access to EC2 and RDS.
- Slack workspace with permission to create and install Slack apps.
- Python 3.8 installed locally for testing and packaging the Lambda function.
- Go to the Slack API website: Slack API: Applications.
- Click on "Create New App".
- Choose "From scratch".
- Give your app a name and select the Slack workspace where you have permissions to install the app.
- Click "Create App".
- In your Slack app settings, go to "OAuth & Permissions".
- Under "OAuth Tokens & Redirect URLs", scroll down to "Scopes".
- Add the following bot token scopes:
chat:write: To post messages in channels.channels:read: To read information about channels.groups:read: To read information about private channels.
- At the top of the "OAuth & Permissions" page, click "Install App to Workspace".
- Review the permissions and click "Allow".
- Copy the OAuth Access Token; you'll need this as the
SLACK_BOT_TOKEN.
- Open Slack and navigate to the channel where you want the bot to post messages.
- Click on the channel name to open the channel details.
- Copy the channel ID from the URL or the channel details pane. The channel ID starts with
Cfor public channels orGfor private channels.
- In Slack, go to the channel where you want the bot to post messages.
- Type
/invite @your-bot-nameto invite the bot to the channel. Replaceyour-bot-namewith the actual name of your bot.
- Create a directory for your Lambda function code:
mkdir bloodhound_lambda
cd bloodhound_lambda- Create a file named
lambda_function.pyand add the following code:
import boto3
import os
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError
# Adjust based on the regions you want to scan
STUDENT_REGIONS = [
"us-east-1",
"us-east-2",
"us-west-1",
"us-west-2",
]
def create_session(region):
return boto3.Session(region_name=region)
def search_regions_for_rds_resources(session):
rds_instances = []
print(f"Sniffing out rds resources in {session.region_name}...")
rds = session.client("rds")
response = rds.describe_db_instances()
for dbinstance in response["DBInstances"]:
rds_instances.append(dbinstance["DBInstanceIdentifier"])
return {"rds": rds_instances}
def search_regions_for_ec2_resources(session):
ec2_instances = []
print(f"Sniffing out ec2 resources in {session.region_name}...")
ec2 = session.client("ec2")
response = ec2.describe_instances()
try:
if len(response["Reservations"]) == 1:
for ec2instance in response["Reservations"][0]["Instances"]:
if ec2instance["State"]["Name"] in ("stopped", "terminated"):
continue
ec2_instances.append(ec2instance["InstanceId"])
else:
for ec2instance in response["Reservations"]:
if ec2instance["Instances"][0]["State"]["Name"] in ("stopped", "terminated"):
continue
ec2_instances.append(ec2instance["Instances"][0]["InstanceId"])
except IndexError:
return {"ec2": []}
return {"ec2": ec2_instances}
def format_message(resources):
message = ":dog2: Woof! Woof! :dog2:\n Bloodhound found the following resources in use: \n"
for region in resources.keys():
if len(resources[region]["ec2"]) == 0 and len(resources[region]["rds"]) == 0:
continue
message += f'- *{region.upper()}*: {len(resources[region]["ec2"])} ec2 instances, {len(resources[region]["rds"])} rds instances\n'
message += f"Please stop or terminate all unneeded resources!"
return message
def send_slack_message(message):
try:
slack_bot_token = os.environ.get("SLACK_BOT_TOKEN")
channel_id = os.environ.get("CHANNEL_ID")
client = WebClient(token=slack_bot_token)
response = client.chat_postMessage(
channel=channel_id,
text=message
)
print(f"Slack message response: {response}")
except SlackApiError as e:
print(f"Error sending message to Slack: {e.response['error']}")
def lambda_handler(event, context):
resources_in_regions = {}
print(f"Going hunting in regions {STUDENT_REGIONS}")
for region in STUDENT_REGIONS:
session = create_session(region)
resources_in_regions[region] = search_regions_for_ec2_resources(session)
resources_in_regions[region].update(search_regions_for_rds_resources(session))
message = format_message(resources_in_regions)
print(message)
send_slack_message(message)
return message- Create a
requirements.txtfile with the following content:
slack_sdk
boto3
- Install the dependencies and create a deployment package:
pip install -r requirements.txt -t .
zip -r9 ../bloodhound_lambda.zip .- Create the Lambda function using the AWS CLI:
aws lambda create-function --function-name BloodhoundLambda \
--zip-file fileb://bloodhound_lambda.zip --handler lambda_function.lambda_handler --runtime python3.8 \
--role arn:aws:iam::<YOUR_ACCOUNT_ID>:role/BloodhoundLambdaRole --region us-west-2Replace <YOUR_ACCOUNT_ID> with your actual AWS account ID.
- Go to the IAM console.
- Create a new role:
- Choose the "Lambda" service.
- Attach the following policies:
AWSLambdaBasicExecutionRoleAmazonEC2ReadOnlyAccessAmazonRDSReadOnlyAccess
- Note the ARN of the created role.
- Navigate to the AWS Lambda Console.
- Select your BloodhoundLambda function.
- Go to the "Configuration" tab and then "Environment variables".
- Add the following environment variables:
SLACK_BOT_TOKEN: Your Slack bot token.CHANNEL_ID: Your Slack channel ID.
- Save the changes.
- Create a test event named
test_event.jsonwith the following content:
{}- Invoke the Lambda function:
aws lambda invoke --function-name BloodhoundLambda --payload file://test_event.json output.txt --region us-west-2- Check the contents of
output.txtand review the CloudWatch logs to ensure the function executed correctly.
Create the file .github/workflows/invoke_lambda.yml in your repository.
Add the following content to the invoke_lambda.yml file:
name: Invoke Bloodhound Lambda
on:
schedule:
# Run at 11 AM and 11 PM EST (4 PM and 4 AM UTC)
- cron: "0 16,4 * * *"
workflow_dispatch:
jobs: invoke
-lambda:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Set up AWS CLI
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-west-2
- name: Invoke Bloodhound Lambda function
run: |
echo '{}' > test_event.json
aws lambda invoke --function-name BloodhoundLambda --payload file://test_event.json output.txt --region us-west-2
cat output.txt- Go to your repository on GitHub.
- Click on "Settings".
- Click on "Secrets and variables" in the left sidebar, then click on "Actions".
- Click "New repository secret".
- Add the following secrets:
AWS_ACCESS_KEY_ID: Your AWS access key ID.AWS_SECRET_ACCESS_KEY: Your AWS secret access key.
Commit and push the .github/workflows/invoke_lambda.yml file to your repository.
- Go to the "Actions" tab in your GitHub repository.
- You should see the new workflow listed. It will run according to the schedule and can also be manually triggered.
By following these steps, you have successfully set up, deployed, and tested the Bloodhound Lambda function with Slack integration. The function scans AWS regions for EC2 and RDS instances and posts a summary to the specified Slack channel. Additionally, you have set up a GitHub Action to invoke the Lambda function twice a day at 11 AM and 11 PM EST. Ensure to review the logs and Slack messages to confirm the function's correct behavior.
