Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
December 27, 2022 11:21 pm GMT

AWS Budgets: Update alert thresholds unlimitedly with Lambda

Motivation

When I use AWS Budgets, I can receive notifications when the billing is above a threshold.

The facts are that:

  1. We can set only 10 alert thresholds for one budget.
  2. Two budgets are free but we are charged for using more budgets.

For personal use, usually, I'm not charged so much (around 10 USD per month). I want to be notified every 1 USD but there is a limitation of 10 alerts like above fact.

I this post, I share how to update the alert threshold incremently when AWS Budgets triggers.

Architecture

image of Budgets threshold increment

  1. EventBridge triggers Lambda(setBudget) at the 1st of months. This Lambda delete and re-create the budget to initialize for a month and create 10 alert thresholds: 1, 2, 3, ... 10 USD thresholds, for example.

  2. When the billing is above the threshold, the user is notified via SNS email. At the same time, the second Lambda function is triggered and updates the threshold. If the triggered threshold is 1 USD, the Lambda function update the threshold to 11 USD.

CloudFormation template

  • Deploy from the Console. Create stack -> Upload a template -> Choose this file.
  • Enter Stack name and parameters: budget name, email, increment.
  • Leave the rest default. Choose Next, Next, check IAM acknowledge, and Submit.
  • When starting in the middle of the month, manually run the SetBudgetHandler Lambda function. Note that you'll get notified of all alerts of the current used cost.
AWSTemplateFormatVersion: "2010-09-09"Description: "Increment alert threshold of AWS Budgets"Parameters:  budgetname:    Type: String    Default: increment-notification  increment:    Type: String    Default: 1  email:    Type: String    AllowedPattern: "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$"    ConstraintDescription: Must be a valid email address.Resources:  MyTopic:    Type: AWS::SNS::Topic  MyTopicTokenSubscription:    Type: AWS::SNS::Subscription    Properties:      Protocol: email      TopicArn: !Ref MyTopic      Endpoint: !Ref email  MyTopicPolicy:    Type: AWS::SNS::TopicPolicy    Properties:      PolicyDocument:        Statement:          - Action: sns:Publish            Effect: Allow            Principal:              Service: budgets.amazonaws.com            Resource: !Ref MyTopic            Sid: "0"        Version: "2012-10-17"      Topics:        - !Ref MyTopic  UpdateBudgetHandlerServiceRole:    Type: AWS::IAM::Role    Properties:      AssumeRolePolicyDocument:        Statement:          - Action: sts:AssumeRole            Effect: Allow            Principal:              Service: lambda.amazonaws.com        Version: "2012-10-17"      ManagedPolicyArns:        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole  UpdateBudgetHandlerServiceRoleDefaultPolicy:    Type: AWS::IAM::Policy    Properties:      PolicyDocument:        Statement:          - Action: budgets:*            Effect: Allow            Resource: "*"        Version: "2012-10-17"      PolicyName: UpdateBudgetHandlerServiceRoleDefaultPolicy      Roles:        - !Ref UpdateBudgetHandlerServiceRole  UpdateBudgetHandler:    Type: AWS::Lambda::Function    Properties:      Code:        ZipFile: |          import os          import re          import boto3          budget_name = os.environ['BUDGET_NAME']          increment = os.environ['INCREMENT']          def handler(event, context):              print(event)              client = boto3.client('budgets')              account_id = context.invoked_function_arn.split(":")[4]              print("account_id", account_id)              message = event['Records'][0]['Sns']['Message']              print("message", message)              # catch 1 from "Alert Threshold: > $1.00"              match = re.search(r"Alert Threshold: > \$(\d{1,})\.00", message)              if not match:                  print("No Budget Notification. Exit.")                  return              current_value_str = match.groups()[0]              print("current_value", current_value_str)              current_value = int(current_value_str)              next_value = current_value + 10 * int(increment)              response = client.update_notification(                  AccountId=account_id,                  BudgetName=budget_name,                  OldNotification={                      "NotificationType": "ACTUAL",                      "ComparisonOperator": "GREATER_THAN",                      "Threshold": current_value,                      "ThresholdType": "ABSOLUTE_VALUE",                  },                  NewNotification={                      "NotificationType": "ACTUAL",                      "ComparisonOperator": "GREATER_THAN",                      "Threshold": next_value,                      "ThresholdType": "ABSOLUTE_VALUE",                  },              )      Role: !GetAtt UpdateBudgetHandlerServiceRole.Arn      Environment:        Variables:          BUDGET_NAME: !Ref budgetname          INCREMENT: !Ref increment      Handler: index.handler      Runtime: python3.9      Timeout: 10    DependsOn:      - UpdateBudgetHandlerServiceRoleDefaultPolicy      - UpdateBudgetHandlerServiceRole  UpdateBudgetHandlerEventInvokeConfig:    Type: AWS::Lambda::EventInvokeConfig    Properties:      FunctionName: !Ref UpdateBudgetHandler      Qualifier: $LATEST      MaximumRetryAttempts: 0  UpdateBudgetHandlerAllowInvokeStackMyTopic:    Type: AWS::Lambda::Permission    Properties:      Action: lambda:InvokeFunction      FunctionName: !GetAtt UpdateBudgetHandler.Arn      Principal: sns.amazonaws.com      SourceArn: !Ref MyTopic  UpdateBudgetHandlerMyTopic:    Type: AWS::SNS::Subscription    Properties:      Protocol: lambda      TopicArn: !Ref MyTopic      Endpoint: !GetAtt UpdateBudgetHandler.Arn  SetBudgetHandlerServiceRole:    Type: AWS::IAM::Role    Properties:      AssumeRolePolicyDocument:        Statement:          - Action: sts:AssumeRole            Effect: Allow            Principal:              Service: lambda.amazonaws.com        Version: "2012-10-17"      ManagedPolicyArns:        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole  SetBudgetHandlerServiceRoleDefaultPolicy:    Type: AWS::IAM::Policy    Properties:      PolicyDocument:        Statement:          - Action: budgets:*            Effect: Allow            Resource: "*"        Version: "2012-10-17"      PolicyName: SetBudgetHandlerServiceRoleDefaultPolicy      Roles:        - !Ref SetBudgetHandlerServiceRole  SetBudgetHandler:    Type: AWS::Lambda::Function    Properties:      Code:        ZipFile: |          import os          import boto3          budget_name = os.environ['BUDGET_NAME']          sns_arn = os.environ['SNS_ARN']          increment = os.environ['INCREMENT']          client = boto3.client("budgets")          def handler(event, context):              account_id = context.invoked_function_arn.split(":")[4]              # Delete if the budget exists              try:                  client.delete_budget(AccountId=account_id, BudgetName=budget_name)                  print("Deleted the old budget.")              except client.exceptions.NotFoundException:                  pass              # Create new budget              client.create_budget(                  AccountId=account_id,                  Budget={                      "BudgetName": budget_name,                      "BudgetLimit": {"Amount": "100.0", "Unit": "USD"},                      "CostTypes": {                          "IncludeTax": True,                          "IncludeSubscription": True,                          "UseBlended": False,                          "IncludeRefund": False,                          "IncludeCredit": False,                          "IncludeUpfront": True,                          "IncludeRecurring": True,                          "IncludeOtherSubscription": True,                          "IncludeSupport": True,                          "IncludeDiscount": True,                          "UseAmortized": False,                      },                      "TimeUnit": "MONTHLY",                      "BudgetType": "COST",                  },                  NotificationsWithSubscribers=[                      {                          "Notification": {                              "NotificationType": "ACTUAL",                              "ComparisonOperator": "GREATER_THAN",                              "Threshold": threshold,                              "ThresholdType": "ABSOLUTE_VALUE",                              "NotificationState": "OK",                          },                          "Subscribers": [                              {"SubscriptionType": "SNS", "Address": sns_arn},                          ],                      }                      for threshold in range(1, 1 + 10 * int(increment), int(increment))                  ],              )              print("Created a new budget.")      Role: !GetAtt SetBudgetHandlerServiceRole.Arn      Environment:        Variables:          BUDGET_NAME: !Ref budgetname          SNS_ARN: !Ref MyTopic          INCREMENT: !Ref increment      Handler: index.handler      Runtime: python3.9      Timeout: 10    DependsOn:      - SetBudgetHandlerServiceRoleDefaultPolicy      - SetBudgetHandlerServiceRole  SetBudgetHandlerEventInvokeConfig:    Type: AWS::Lambda::EventInvokeConfig    Properties:      FunctionName: !Ref SetBudgetHandler      Qualifier: $LATEST      MaximumRetryAttempts: 0  ScheduleRule:    Type: AWS::Events::Rule    Properties:      ScheduleExpression: cron(0 0 1 * ? *)      State: ENABLED      Targets:        - Arn: !GetAtt SetBudgetHandler.Arn          Id: Target0  ScheduleRuleAllowEventRuleStackSetBudgetHandler:    Type: AWS::Lambda::Permission    Properties:      Action: lambda:InvokeFunction      FunctionName: !GetAtt SetBudgetHandler.Arn      Principal: events.amazonaws.com      SourceArn: !GetAtt ScheduleRule.Arn

Summary

I've shared how to update AWS Budgets threshold incrementally.


Original Link: https://dev.to/aws-builders/aws-budgets-update-alert-thresholds-unlimitedly-with-lambda-5e7h

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To