AWS Lambda offers a powerful way to execute code in response to various events within the AWS ecosystem. One practical use case is to trigger a Lambda function when your AWS Budgets threshold alarm is breached. This enables you to automate actions like sending notifications, invoking other AWS services, or implementing custom logic to manage your costs proactively.

AWS Budget and Lambda Integration

Terraform

# Terraform backend
# Replace to your own configuration:
# https://github.com/hands-on-cloud/cloudformation-templates/tree/master/terraform-remote-state-infrastructure
terraform {
  backend "s3" {
    bucket  = "hands-on-cloud-terraform-remote-state-s3"
    key     = "budget-lambda-trigger-demo.tfstate"
    region  = "us-west-2"
    encrypt = "true"
    dynamodb_table = "hands-on-cloud-terraform-remote-state-dynamodb"
  }
}

provider "aws" {
  region = "us-east-1"
}
locals {
  prefix = "budget-lambda-trigger-demo"
}
data "archive_file" "lambda_zip" {
  type        = "zip"
  source_file = "${path.module}/lambda_function.py"
  output_path = "${path.module}/lambda_function.zip"
}
resource "aws_lambda_function" "budget_alert_lambda" {
  filename         = data.archive_file.lambda_zip.output_path
  function_name    = "${local.prefix}-lambda"
  role             = aws_iam_role.lambda_exec_role.arn
  handler          = "lambda_function.lambda_handler"
  source_code_hash = data.archive_file.lambda_zip.output_base64sha256
  runtime          = "python3.11"
}
resource "aws_iam_role" "lambda_exec_role" {
  name = "${local.prefix}-lambda-exec-role"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "lambda.amazonaws.com"
        }
      },
    ]
  })
}
resource "aws_sns_topic" "budget_alert_topic" {
  name = "${local.prefix}-budget-alert-topic"
}
resource "aws_sns_topic_subscription" "budget_alert_subscription" {
  topic_arn = aws_sns_topic.budget_alert_topic.arn
  protocol  = "lambda"
  endpoint  = aws_lambda_function.budget_alert_lambda.arn
}
resource "aws_lambda_permission" "allow_sns_to_call_lambda" {
  statement_id  = "AllowExecutionFromSNS"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.budget_alert_lambda.function_name
  principal     = "sns.amazonaws.com"
  source_arn    = aws_sns_topic.budget_alert_topic.arn
}
resource "aws_budgets_budget" "account" {
  name              = "${local.prefix}-budget-aws-monthly"
  budget_type       = "COST"
  limit_amount      = "10"
  limit_unit        = "USD"
  time_period_end   = "2087-06-15_00:00"
  time_period_start = "2024-01-01_00:00"
  time_unit         = "MONTHLY"
  notification {
    comparison_operator        = "GREATER_THAN"
    threshold                  = 90
    threshold_type             = "PERCENTAGE"
    notification_type          = "FORECASTED"
    subscriber_sns_topic_arns = [ aws_sns_topic.budget_alert_topic.arn ]
  }
}

Lambda function code

import json
def lambda_handler(event, context):
    message = json.loads(event['Records'][0]['Sns']['Message'])
    print(f"Budget Alert: {message}")
    return {
        'statusCode': 200,
        'body': json.dumps('Budget alert processed.')
    }

CloudFormation Stack

AWSTemplateFormatVersion: '2010-09-09'
Description: An AWS CloudFormation stack to trigger a Lambda function on AWS budget alerts.
Resources:
  AccountBudget:
    Type: AWS::Budgets::Budget
    Properties:
      Budget:
        BudgetName: budget-lambda-trigger-demo-budget-aws-monthly
        BudgetLimit:
          Amount: 10
          Unit: USD
        TimeUnit: MONTHLY
        BudgetType: COST
        TimePeriod:
          Start: '1609459200' # Equivalent to 2024-01-01_00:00 in epoch seconds
          End: '3666288000'   # Equivalent to 2087-06-15_00:00 in epoch seconds
      NotificationsWithSubscribers:
        - Notification:
            NotificationType: FORECASTED
            ComparisonOperator: GREATER_THAN
            Threshold: 90
            ThresholdType: PERCENTAGE
          Subscribers:
            - SubscriptionType: SNS
              Address: !Ref BudgetAlertTopic
  BudgetAlertLambda:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.lambda_handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Runtime: python3.8
      Code:
        ZipFile: |
          import json
          def lambda_handler(event, context):
              message = json.loads(event['Records'][0]['Sns']['Message'])
              print(f"Budget Alert: {message}")
              return {
                  'statusCode': 200,
                  'body': json.dumps('Budget alert processed.')
              }
      Timeout: 10
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: LambdaLoggingPolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource: arn:aws:logs:*:*:*
  BudgetAlertTopic:
    Type: AWS::SNS::Topic
  LambdaInvokePermission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !Ref BudgetAlertLambda
      Action: lambda:InvokeFunction
      Principal: sns.amazonaws.com
      SourceArn: !Ref BudgetAlertTopic
  BudgetAlertSubscription:
    Type: AWS::SNS::Subscription
    Properties:
      Protocol: lambda
      TopicArn: !Ref BudgetAlertTopic
      Endpoint: !GetAtt BudgetAlertLambda.Arn

Manual Configuration

Here’s a straightforward guide to setting this up:

  1. Create an AWS Budget:
  • Navigate to the AWS Budgets console.
  • Click on “Create budget” and choose the type of budget you need (e.g., Cost budget, Usage budget).
  • Define your budget details, including the threshold percentage at which you want to trigger the alarm.
  1. Set Up an SNS Topic:
  • Go to the Amazon SNS console.
  • Create a new topic for budget notifications.
  • Note the ARN (Amazon Resource Name) of the SNS topic; you’ll need it for setting up notifications and linking to your Lambda function.
  1. Configure Budget Notifications:
  • In the AWS Budgets console, find your budget and navigate to the “Alerts” section.
  • Create a new alert. Select “Actual” for alert type, and specify the threshold based on your preference.
  • Under “Notification,” choose “Select an SNS topic,” and specify the ARN of the SNS topic you created earlier.
  1. Create a Lambda Function:
  • Head over to the AWS Lambda console.
  • Click “Create function.” Give it a name, choose a runtime (e.g., Python, Node.js), and define the necessary execution role.
  • Implement your custom logic within the Lambda function. This could be sending an email, logging a message, or any action you deem necessary upon breaching the budget threshold.
  1. Subscribe Lambda to the SNS Topic:
  • In the Amazon SNS console, find your topic and create a new subscription.
  • Choose “AWS Lambda” as the protocol and select your Lambda function.
  • This links your Lambda function to the SNS topic, allowing it to be triggered whenever a message is published to the topic.
  1. Test Your Setup:
  • It’s crucial to verify that everything works as intended.
  • You might simulate the budget alarm (if possible) or publish a test message to your SNS topic to ensure the Lambda function is triggered correctly.
  1. Monitor and Adjust:
  • Keep an eye on the execution of your Lambda function and the notifications generated by AWS Budgets.
  • Adjust your budget, the threshold, or the Lambda function logic as necessary based on your observations and evolving requirements.

This approach allows you to automate responses to budget alarms, helping manage costs effectively without constant manual oversight. Remember, this is just the foundation. Depending on your needs, you can expand this setup to include more complex logic, integrate with other AWS services, or handle different types of budget alerts.