Desktop and Application Streaming
Automated Alerting on Amazon WorkSpaces Service Limits
Amazon Web Services (AWS) recently announced the addition of Amazon WorkSpaces Service Limits within Service Quotas. This now allows customers to have visibility into what their current limits are for the various types of WorkSpaces in a given AWS Region. Combined with the API operations to query deployed WorkSpaces, customers now have the tools at their disposal to proactively monitor their WorkSpaces deployments.
Overview
In this article, we walk through the setup of an automation that will alert you via email when you reach a defined percentage of your WorkSpaces quota. Each of the three types of WorkSpaces, Graphics, GraphicsPro, and Non-GPU, are monitored and alerted on independently. This solution leverages an AWS Lambda function to gather the total number of WorkSpaces deployed in each selected region. It then checks this against the current service limits for the given region. Finally, it sends an Amazon Simple Notification Service alert if any quota threshold is breached. The Lambda function is triggered by an Amazon EventBridge rule at a determined time of day. This solution can either be deployed using the provided AWS CloudFormation template or manually by creating each component yourself by following the below steps.
Time to read | 15 minutes |
Time to complete | 30 minutes |
Cost to complete (estimated) | $0 |
Learning level | Advanced (300) |
Services used | Amazon EventBridge Amazon Simple Notification service Amazon Workspaces AWS CloudFormation AWS Lambda Service Quotas |
Walkthrough
In this walkthrough, you complete the following tasks:
- Create an Amazon Simple Notification Service (Amazon SNS) topic and subscription.
- Create an AWS Identity and Access Management (IAM) policy and role.
- Create an AWS Lambda function.
- Create an Amazon EventBridge rule.
Prerequisites
This article assumes that you have the following already in place:
- An AWS account.
- Amazon WorkSpaces deployed into at least one AWS Region.
- Permissions to create EventBridge rules, Lambda functions, SNS topics and subscriptions, IAM Roles and Policies, and optionally CloudFormation stacks.
Option 1: CloudFormation Deployment
The provided CloudFormation template can be used to quickly deploy all of the parts of this solution with a few clicks and after entering a few required parameters. Once deployed, you have the option to modify various aspects to meet your needs.
- Copy the contents of the below YAML CloudFormation template, paste into the text editor of your choice, and save.
- Open the CloudFormation console.
- In the navigation pane, choose Stacks.
- Choose Create stack, then select With new resources (standard).
- Keep Template is ready selected, under Specify template select Upload a template file.
- Choose Choose file and select the file created in step 1, and choose Next.
- Enter a name for the stack and fill out the parameters:
- AlertThreshold – at what percentage of a limit to send a notification email; 0-100.
- AlertTime – at what time of day to run the limit check in UTC format; 0-23.
- MonitorRegions – a comma-separated list of which AWS Regions to check WorkSpace limits.
- NotificationEmail – email address that alerts are sent to.
- Choose Next.
- Optionally add tags that will be applied to the resources deployed with the template as required by your environment, then choose Next.
- On the Review page, check the box I acknowledge that AWS CloudFormation might create IAM resources with custom names and then click Create stack.
The CloudFormation template will now begin building the resources for this solution. Once finished the status will change from the CREATE_IN_PROGRESS to CREATE_COMPLETE. At this point the solution is fully deployed and will run once per day at the time set by AlertTime parameter.
AWSTemplateFormatVersion: '2010-09-09'
Description: >-
This template creates the components to setup automatic alerting on Amazon WorkSpaces Service Limits. By default, this solution will monitor the limits once per day at the configured time. The schedule can be modified in the Amazon EventBridge console if a different interval is required (such as hourly or monthly).
Parameters:
NotificationEmail:
Type: String
Description: The email address that receives the alarm notifications. Additional email addresses can be added by subscribing them to the topic created with the template.
AllowedPattern: '[^\s@]+@[^\s@]+\.[^\s@]+'
ConstraintDescription: "NotificationEmail must be a valid email address."
Default: youruser@yourdomain.com
AlertThreshold:
Type: String
Description: At what percentage of the quota used will an alert be triggered.
AllowedPattern: '^([0-9]{1,2}|100){1}(\.[0-9]{1,2})?$'
ConstraintDescription: "AlertThredhold should be between 0 and 100."
Default: 80
AlertTime:
Type: String
Description: Time to run the service limit check each day (UTC).
AllowedPattern: '^\d$|^1\d$|^2[0-3]$'
ConstraintDescription: "AlertTime must be between 0 and 23 UTC."
Default: 12
MonitorRegions:
Type: String
AllowedPattern: '\"[a-zA-Z0-9-]+\"+(,\"[a-zA-Z0-9-]+\"+)*'
ConstraintDescription: 'Must be a comma separated and quoted list of regions with no spaces between entries. ("us-east-1","us-west-2" or "us-east-1").'
Description: Comma separated and quoted list of regions to monitor WorkSpaces service limits. ("us-east-1","us-west-2" or "us-east-1")
Resources:
SNSTopic:
Type: AWS::SNS::Topic
Properties:
TopicName: "WorkSpaces_Service_Limit_Alerting"
SNSSubscription:
Type: AWS::SNS::Subscription
Properties:
Endpoint:
Ref: NotificationEmail
Protocol: email
TopicArn:
Ref: SNSTopic
LambdaFunctionIAMRole:
Type: 'AWS::IAM::Role'
Properties:
RoleName: WorkSpaces_Service_Limit_Alerting_Role
Description: IAM role for WorkSpaces Service Limit Alerting Lambda Function
AssumeRolePolicyDocument: # What service can assume this role
Version: '2012-10-17'
Statement:
-
Effect: Allow
Principal:
Service:
- 'lambda.amazonaws.com'
Action:
- 'sts:AssumeRole'
LambdaFunctionIAMPolicy:
Type: 'AWS::IAM::ManagedPolicy'
Properties:
Description: "Permissions needed by the WorkSpaces Service Limits Lambda function to read the required information from Service Quotas and WorkSpaces."
ManagedPolicyName: WorkSpaces_Service_Limit_Alerting_Policy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- servicequotas:GetServiceQuota
- workspaces:DescribeWorkspaces
Resource: '*'
- Effect: Allow
Action:
- sns:Publish
Resource:
- Ref: SNSTopic
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*'
Roles:
- !Ref LambdaFunctionIAMRole
LambdaFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: WorkSpaces_Service_Limit_Alerting
Handler: index.lambda_handler
Code:
ZipFile: |
import json
import boto3
import logging
import os
import textwrap
from botocore import config
LOGGER = logging.getLogger()
LOGGER.setLevel(logging.INFO)
CONFIG = config.Config(
retries = {
'max_attempts': 10,
'mode': 'standard'
})
SNS = boto3.client('sns')
def send_notification():
#Publish image information to SNS Topic
try :
SNS_RESPONSE = SNS.publish(
TopicArn=ALERT_TOPIC_ARN,
Message=MESSAGE,
Subject=SUBJECT
)
except Exception as e:
LOGGER.error(e)
return()
def lambda_handler(event, context):
#Notification email variables
global SUBJECT, MESSAGE, ALERT_TOPIC_ARN
#Get alert threshold from event data and convert to percentage, or default to 80% if not found
if 'AlertThreshold' in event :
ALERT_THRESHOLD = float(event['AlertThreshold']) / 100
else :
ALERT_THRESHOLD = .80
ALERT_THRESHOLD_PERCENT = ALERT_THRESHOLD * 100
LOGGER.info(f"Alert threshold set to {ALERT_THRESHOLD_PERCENT}%")
#Get list of regions from event data to check WorkSpaces Service Quotas in, or default to current region if not found
if 'AlertRegions' in event :
REGION_LIST = event['AlertRegions']
else :
REGION_LIST = [os.environ['AWS_REGION']]
ALERT_TOPIC_ARN = event['AlertTopicArn']
#Trackers for quota alarms
WORKSPACE_ALARM_STATUS = False
WORKSPACE_GLOBAL_COUNT = 0
WORKSPACE_ALARM_LIST = []
for ALERT_REGION in REGION_LIST :
LOGGER.info(f"Begin WorkSpaces quota check in {ALERT_REGION}")
SQ = boto3.client('service-quotas',region_name=ALERT_REGION)
WS = boto3.client('workspaces',region_name=ALERT_REGION,config=CONFIG)
#Retreive current quota limit for standard WorkSpaces
RESPONSE = SQ.get_service_quota(
ServiceCode='workspaces',
QuotaCode='L-34278094'
)
STANDARD_WORKSPACE_QUOTA = int(RESPONSE['Quota']['Value'])
#Retreive current quota limit for graphics g4dn WorkSpaces
RESPONSE = SQ.get_service_quota(
ServiceCode='workspaces',
QuotaCode='L-BCACAEBC'
)
GRAPHICS_G4DN_WORKSPACE_QUOTA = int(RESPONSE['Quota']['Value'])
#Retreive current quota limit for graphics pro WorkSpaces
RESPONSE = SQ.get_service_quota(
ServiceCode='workspaces',
QuotaCode='L-254B485B'
)
GRAPHICSPRO_WORKSPACE_QUOTA = int(RESPONSE['Quota']['Value'])
#Retreive current quota limit for graphicspro.g4dn WorkSpaces
RESPONSE = SQ.get_service_quota(
ServiceCode='workspaces',
QuotaCode='L-BE9A8466'
)
GRAPHICSPRO_G4DN_WORKSPACE_QUOTA = int(RESPONSE['Quota']['Value'])
#Return all WorkSpaces in current region
WS_PAGINATOR = WS.get_paginator('describe_workspaces')
WS_RESPONSE_ITERATOR = WS_PAGINATOR.paginate()
#Counter for the number of WorkSpaces by compute type
GRAPHICS_G4DN_COUNT = 0
GRAPHICSPRO_COUNT = 0
GRAPHICSPRO_G4DN_COUNT = 0
STANDARD_COUNT = 0
#WorkSpaces API returns pages of 25 WorkSpaces, loop through all pages and count all WorkSpaces by quota type
for WS_PAGE in WS_RESPONSE_ITERATOR:
for WORKSPACE in WS_PAGE.get('Workspaces'):
if (WORKSPACE['WorkspaceProperties']['ComputeTypeName'] == 'GRAPHICS_G4DN'):
GRAPHICS_G4DN_COUNT += 1
elif (WORKSPACE['WorkspaceProperties']['ComputeTypeName'] == 'GRAPHICSPRO'):
GRAPHICSPRO_COUNT += 1
elif (WORKSPACE['WorkspaceProperties']['ComputeTypeName'] == 'GRAPHICSPRO_G4DN'):
GRAPHICSPRO_G4DN_COUNT += 1
else:
STANDARD_COUNT += 1
#Check each WorkSpace type count against alarm threshold
if (STANDARD_COUNT > 0):
if (STANDARD_COUNT >= (ALERT_THRESHOLD * STANDARD_WORKSPACE_QUOTA)):
LOGGER.info(f"ALERT: Approaching regular WorkSpace quota in {ALERT_REGION}")
PERCENT = (STANDARD_COUNT / STANDARD_WORKSPACE_QUOTA) * 100
WORKSPACE_ALARM_LIST.append([ALERT_REGION,"Standard",STANDARD_WORKSPACE_QUOTA,STANDARD_COUNT,PERCENT])
WORKSPACE_ALARM_STATUS = True
if (GRAPHICS_G4DN_COUNT > 0):
if (GRAPHICS_G4DN_COUNT >= (ALERT_THRESHOLD * GRAPHICS_G4DN_WORKSPACE_QUOTA)):
LOGGER.info(f"ALERT: Approaching graphics.g4dn WorkSpace quota in {ALERT_REGION}")
PERCENT = (GRAPHICS_G4DN_COUNT / GRAPHICS_G4DN_WORKSPACE_QUOTA) * 100
WORKSPACE_ALARM_LIST.append([ALERT_REGION,"Graphics.g4dnboto3",GRAPHICS_G4DN_WORKSPACE_QUOTA,GRAPHICS_G4DN_COUNT,PERCENT])
WORKSPACE_ALARM_STATUS = True
if (GRAPHICSPRO_COUNT > 0):
if (GRAPHICSPRO_COUNT >= (ALERT_THRESHOLD * GRAPHICSPRO_WORKSPACE_QUOTA)):
LOGGER.info(f"ALERT: Approaching graphics pro WorkSpace quota in {ALERT_REGION}")
PERCENT = (GRAPHICSPRO_COUNT / GRAPHICSPRO_WORKSPACE_QUOTA) * 100
WORKSPACE_ALARM_LIST.append([ALERT_REGION,"GraphicsPro",GRAPHICSPRO_WORKSPACE_QUOTA,GRAPHICSPRO_COUNT,PERCENT])
WORKSPACE_ALARM_STATUS = True
if (GRAPHICSPRO_G4DN_COUNT > 0):
if (GRAPHICSPRO_G4DN_COUNT >= (ALERT_THRESHOLD * GRAPHICSPRO_G4DN_WORKSPACE_QUOTA)):
LOGGER.info(f"ALERT: Approaching graphicspro.g4dn WorkSpace quota in {ALERT_REGION}")
PERCENT = (GRAPHICSPRO_G4DN_COUNT / GRAPHICSPRO_G4DN_WORKSPACE_QUOTA) * 100
WORKSPACE_ALARM_LIST.append([ALERT_REGION,"GraphicsPro.g4dn",GRAPHICSPRO_G4DN_WORKSPACE_QUOTA,GRAPHICSPRO_G4DN_COUNT,PERCENT])
WORKSPACE_ALARM_STATUS = True
WORKSPACE_COUNT = GRAPHICS_G4DN_COUNT + GRAPHICSPRO_COUNT + GRAPHICSPRO_G4DN_COUNT + STANDARD_COUNT
#Tally of all WorkSpaces across all regions checked
WORKSPACE_GLOBAL_COUNT = WORKSPACE_GLOBAL_COUNT + WORKSPACE_COUNT
#Print data to retain in CloudWatch log of Lambda execution
print("########################################")
print(f"# Report for {ALERT_REGION}")
print("########################################")
print(f"Graphics.g4dn WorkSpace limit: {GRAPHICS_G4DN_WORKSPACE_QUOTA}")
print(f"Graphics.g4dn WorkSpace count: {GRAPHICS_G4DN_COUNT}")
print("------------------------------------")
print(f"GraphicsPro.g4dn WorkSpace limit: {GRAPHICSPRO_G4DN_WORKSPACE_QUOTA}")
print(f"GraphicsPro.g4dn WorkSpace count: {GRAPHICSPRO_G4DN_COUNT}")
print("------------------------------------")
print(f"GraphicsPro WorkSpace limit: {GRAPHICSPRO_WORKSPACE_QUOTA}")
print(f"GraphicsPro WorkSpace count: {GRAPHICSPRO_COUNT}")
print("------------------------------------")
print(f"WorkSpace limit: {STANDARD_WORKSPACE_QUOTA}")
print(f"WorkSpace count: {STANDARD_COUNT}")
print("------------------------------------")
print(f"Total WorkSpaces in region: {WORKSPACE_COUNT}")
print("########################################")
LOGGER.info(f"End WorkSpaces quota check in {ALERT_REGION}")
LOGGER.info(f"Total WorkSpaces across all regions: {WORKSPACE_GLOBAL_COUNT}")
#Only generate and send SNS notification if there are any quotas breaching the alarm threshold
if (WORKSPACE_ALARM_STATUS) :
LOGGER.info("Due to alarm, Service Limit Notification published to SNS.")
SUBJECT = "ALERT: Amazon WorkSpace Service Limits"
MESSAGE = 'The below Amazon WorkSpaces Service Limits are beyond the configured threshold of {}% and are in an alarm state:\n\n'.format(ALERT_THRESHOLD_PERCENT)
MESSAGE = MESSAGE + '{: <21} {: <20} {}/{}\n'.format('Region','Desktop Type','Count','Quota')
for ALARM in WORKSPACE_ALARM_LIST:
REGION, TYPE, QUOTA, COUNT, PERCENT = ALARM
MESSAGE = MESSAGE + '{: <21} {: <23} {}/{} ({}%)\n'.format(REGION,TYPE,COUNT,QUOTA,PERCENT)
send_notification()
else :
LOGGER.info("No service limits are in alarm state. No notification published.")
return {
'WorkSpaceAlarm': WORKSPACE_ALARM_STATUS,
}
Runtime: python3.9
Role: !GetAtt 'LambdaFunctionIAMRole.Arn'
Timeout: 30
EventBridgeScheduledRule:
Type: AWS::Events::Rule
Properties:
Description: "Trigger Lambda function for WorkSpace Service Limit alerting."
Name: "WorkSpaces_Service_Limit_Alerting_Trigger"
ScheduleExpression: !Sub "cron(0 ${AlertTime} * * ? *)"
State: "ENABLED"
Targets:
-
Arn:
Fn::GetAtt:
- "LambdaFunction"
- "Arn"
Id: "TargetLambdaFunction"
Input: !Sub "{ \"AlertThreshold\": ${AlertThreshold}, \"AlertRegions\": [ ${MonitorRegions} ], \"AlertTopicArn\": \"${SNSTopic}\" }"
PermissionForEventsToInvokeLambda:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !Ref "LambdaFunction"
Action: "lambda:InvokeFunction"
Principal: "events.amazonaws.com"
SourceArn:
Fn::GetAtt:
- "EventBridgeScheduledRule"
- "Arn"
Option 2: Manual Solution Deployment
The following steps describe how to create the solution manually. You will create each of the resources from the CloudFormation template using the AWS console.
Step 1. Create the SNS Topic and Subscription(s)
In this step, you create the Amazon SNS topic and subscription. The Lambda function will use the topic to send alert emails when your configured service limit threshold has been breached.
- Open the Amazon SNS console.
- In the navigation pane, choose Topics.
- Choose Create topic.
- Select Standard type.
- Give the topic a name, for example, WorkSpaces-Limits-Alerting.
- Choose Create topic.
- Copy the ARN for use in later steps.
- Under Subscriptions, choose Create subscription.
- For Protocol, select Email.
- For Endpoint, enter the email address that will receive the limit alerts.
- Choose Create subscription.
- Repeat steps 7-10 to add any additional addresses that will receive the notifications.
- Each subscription will receive an AWS Notification email that has a link to Confirm subscription. The address will not receive the alert emails from this solution until that confirm link is opened.
Step 2. Create the IAM Policy and Role
In this step, you create an IAM policy and role that the Lambda function will assume. This provides Lambda the permissions needed to read the required information from Service Quotas and WorkSpaces. The policy also contains the basic Lambda permissions required for logging and to publish to SNS.
- Open the IAM console.
- In the navigation pane, choose Policies.
- Choose Create Policy.
- Choose the JSON tab.
- Copy and paste the following JSON policy.
- Replace the following values in the policy to match your environment:
- Replace <aws-region> with the AWS Region code in which the solution is being built.
- Replace <account-id> with the account ID number of the account in which the solution is being built.
- Replace <sns-topic-arn> with the ARN from the SNS Topic created in the previous step.
- Once finished, choose Next: Tags.
- Add any required tags for your environment, then choose Next: Review.
- Enter a name of your choosing, for example, WorkSpaces_Service_Limit_Alerting_Policy.
- Choose Create policy.
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"servicequotas:GetServiceQuota",
"workspaces:DescribeWorkspaces"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"sns:Publish"
],
"Resource": [
"<sns-topic-arn>"
],
"Effect": "Allow"
},
{
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:<aws-region>:<account-id>:*",
"Effect": "Allow"
}
]
}
- In the navigation pane, choose Roles.
- Click Create role.
- For Select type of trusted entity, keep AWS service selected.
- Choose Lambda, and then click Next: Permissions.
- Browse for or search in the filter policies search box for the name of the policy created in the previous step. Select the check box next to the policy name.
- Choose Next: Tags.
- Add any required tags for your environment, then choose Next: Review.
- Enter a name for your Role to help you identify it, for example, WorkSpaces_Service_Limit_Alerting_Role.
- Choose Create role.
Step 3. Create the Lambda function
In this step, we create the Lambda function that is responsible for reading the Service Quota limits and currently deployed WorkSpace counts in each of the AWS Regions defined in a later section.
- Open the Lambda console.
- Choose Create function.
- Keep Author from scratch selected and enter a Function name, for example, WorkSpaces_Service_Limit_Alerting.
- Select Python 3.9 as the Runtime.
- Expand the Permissions section, select Use an existing role, and from the list choose the role created in step 2.
- Choose Create function.
- Within the Code source section, replace the placeholder code with the below Python script.
- Once replaced, click Deploy to save the changes.
- Choose Configuration.
- Choose General configuration, then choose Edit.
- Set the Timeout to 30 seconds.
- Choose Save.
import json
import boto3
import logging
import os
import textwrap
from botocore import config
LOGGER = logging.getLogger()
LOGGER.setLevel(logging.INFO)
CONFIG = config.Config(
retries = {
'max_attempts': 10,
'mode': 'standard'
})
SNS = boto3.client('sns')
def send_notification():
#Publish image information to SNS Topic
try :
SNS_RESPONSE = SNS.publish(
TopicArn=ALERT_TOPIC_ARN,
Message=MESSAGE,
Subject=SUBJECT
)
except Exception as e:
LOGGER.error(e)
return()
def lambda_handler(event, context):
#Notification email variables
global SUBJECT, MESSAGE, ALERT_TOPIC_ARN
#Get alert threshold from event data and convert to percentage, or default to 80% if not found
if 'AlertThreshold' in event :
ALERT_THRESHOLD = float(event['AlertThreshold']) / 100
else :
ALERT_THRESHOLD = .80
ALERT_THRESHOLD_PERCENT = ALERT_THRESHOLD * 100
LOGGER.info(f"Alert threshold set to {ALERT_THRESHOLD_PERCENT}%")
#Get list of regions from event data to check WorkSpaces Service Quotas in, or default to current region if not found
if 'AlertRegions' in event :
REGION_LIST = event['AlertRegions']
else :
REGION_LIST = [os.environ['AWS_REGION']]
ALERT_TOPIC_ARN = event['AlertTopicArn']
#Trackers for quota alarms
WORKSPACE_ALARM_STATUS = False
WORKSPACE_GLOBAL_COUNT = 0
WORKSPACE_ALARM_LIST = []
for ALERT_REGION in REGION_LIST :
LOGGER.info(f"Begin WorkSpaces quota check in {ALERT_REGION}")
SQ = boto3.client('service-quotas',region_name=ALERT_REGION)
WS = boto3.client('workspaces',region_name=ALERT_REGION,config=CONFIG)
#Retreive current quota limit for standard WorkSpaces
RESPONSE = SQ.get_service_quota(
ServiceCode='workspaces',
QuotaCode='L-34278094'
)
STANDARD_WORKSPACE_QUOTA = int(RESPONSE['Quota']['Value'])
#Retreive current quota limit for graphics g4dn WorkSpaces
RESPONSE = SQ.get_service_quota(
ServiceCode='workspaces',
QuotaCode='L-BCACAEBC'
)
GRAPHICS_G4DN_WORKSPACE_QUOTA = int(RESPONSE['Quota']['Value'])
#Retreive current quota limit for graphics pro WorkSpaces
RESPONSE = SQ.get_service_quota(
ServiceCode='workspaces',
QuotaCode='L-254B485B'
)
GRAPHICSPRO_WORKSPACE_QUOTA = int(RESPONSE['Quota']['Value'])
#Retreive current quota limit for graphicspro.g4dn WorkSpaces
RESPONSE = SQ.get_service_quota(
ServiceCode='workspaces',
QuotaCode='L-BE9A8466'
)
GRAPHICSPRO_G4DN_WORKSPACE_QUOTA = int(RESPONSE['Quota']['Value'])
#Return all WorkSpaces in current region
WS_PAGINATOR = WS.get_paginator('describe_workspaces')
WS_RESPONSE_ITERATOR = WS_PAGINATOR.paginate()
#Counter for the number of WorkSpaces by compute type
GRAPHICS_G4DN_COUNT = 0
GRAPHICSPRO_COUNT = 0
GRAPHICSPRO_G4DN_COUNT = 0
STANDARD_COUNT = 0
#WorkSpaces API returns pages of 25 WorkSpaces, loop through all pages and count all WorkSpaces by quota type
for WS_PAGE in WS_RESPONSE_ITERATOR:
for WORKSPACE in WS_PAGE.get('Workspaces'):
if (WORKSPACE['WorkspaceProperties']['ComputeTypeName'] == 'GRAPHICS_G4DN'):
GRAPHICS_G4DN_COUNT += 1
elif (WORKSPACE['WorkspaceProperties']['ComputeTypeName'] == 'GRAPHICSPRO'):
GRAPHICSPRO_COUNT += 1
elif (WORKSPACE['WorkspaceProperties']['ComputeTypeName'] == 'GRAPHICSPRO_G4DN'):
GRAPHICSPRO_G4DN_COUNT += 1
else:
STANDARD_COUNT += 1
#Check each WorkSpace type count against alarm threshold
if (STANDARD_COUNT > 0):
if (STANDARD_COUNT >= (ALERT_THRESHOLD * STANDARD_WORKSPACE_QUOTA)):
LOGGER.info(f"ALERT: Approaching regular WorkSpace quota in {ALERT_REGION}")
PERCENT = (STANDARD_COUNT / STANDARD_WORKSPACE_QUOTA) * 100
WORKSPACE_ALARM_LIST.append([ALERT_REGION,"Standard",STANDARD_WORKSPACE_QUOTA,STANDARD_COUNT,PERCENT])
WORKSPACE_ALARM_STATUS = True
if (GRAPHICS_G4DN_COUNT > 0):
if (GRAPHICS_G4DN_COUNT >= (ALERT_THRESHOLD * GRAPHICS_G4DN_WORKSPACE_QUOTA)):
LOGGER.info(f"ALERT: Approaching graphics.g4dn WorkSpace quota in {ALERT_REGION}")
PERCENT = (GRAPHICS_G4DN_COUNT / GRAPHICS_G4DN_WORKSPACE_QUOTA) * 100
WORKSPACE_ALARM_LIST.append([ALERT_REGION,"Graphics.g4dnboto3",GRAPHICS_G4DN_WORKSPACE_QUOTA,GRAPHICS_G4DN_COUNT,PERCENT])
WORKSPACE_ALARM_STATUS = True
if (GRAPHICSPRO_COUNT > 0):
if (GRAPHICSPRO_COUNT >= (ALERT_THRESHOLD * GRAPHICSPRO_WORKSPACE_QUOTA)):
LOGGER.info(f"ALERT: Approaching graphics pro WorkSpace quota in {ALERT_REGION}")
PERCENT = (GRAPHICSPRO_COUNT / GRAPHICSPRO_WORKSPACE_QUOTA) * 100
WORKSPACE_ALARM_LIST.append([ALERT_REGION,"GraphicsPro",GRAPHICSPRO_WORKSPACE_QUOTA,GRAPHICSPRO_COUNT,PERCENT])
WORKSPACE_ALARM_STATUS = True
if (GRAPHICSPRO_G4DN_COUNT > 0):
if (GRAPHICSPRO_G4DN_COUNT >= (ALERT_THRESHOLD * GRAPHICSPRO_G4DN_WORKSPACE_QUOTA)):
LOGGER.info(f"ALERT: Approaching graphicspro.g4dn WorkSpace quota in {ALERT_REGION}")
PERCENT = (GRAPHICSPRO_G4DN_COUNT / GRAPHICSPRO_G4DN_WORKSPACE_QUOTA) * 100
WORKSPACE_ALARM_LIST.append([ALERT_REGION,"GraphicsPro.g4dn",GRAPHICSPRO_G4DN_WORKSPACE_QUOTA,GRAPHICSPRO_G4DN_COUNT,PERCENT])
WORKSPACE_ALARM_STATUS = True
WORKSPACE_COUNT = GRAPHICS_G4DN_COUNT + GRAPHICSPRO_COUNT + GRAPHICSPRO_G4DN_COUNT + STANDARD_COUNT
#Tally of all WorkSpaces across all regions checked
WORKSPACE_GLOBAL_COUNT = WORKSPACE_GLOBAL_COUNT + WORKSPACE_COUNT
#Print data to retain in CloudWatch log of Lambda execution
print("########################################")
print(f"# Report for {ALERT_REGION}")
print("########################################")
print(f"Graphics.g4dn WorkSpace limit: {GRAPHICS_G4DN_WORKSPACE_QUOTA}")
print(f"Graphics.g4dn WorkSpace count: {GRAPHICS_G4DN_COUNT}")
print("------------------------------------")
print(f"GraphicsPro.g4dn WorkSpace limit: {GRAPHICSPRO_G4DN_WORKSPACE_QUOTA}")
print(f"GraphicsPro.g4dn WorkSpace count: {GRAPHICSPRO_G4DN_COUNT}")
print("------------------------------------")
print(f"GraphicsPro WorkSpace limit: {GRAPHICSPRO_WORKSPACE_QUOTA}")
print(f"GraphicsPro WorkSpace count: {GRAPHICSPRO_COUNT}")
print("------------------------------------")
print(f"WorkSpace limit: {STANDARD_WORKSPACE_QUOTA}")
print(f"WorkSpace count: {STANDARD_COUNT}")
print("------------------------------------")
print(f"Total WorkSpaces in region: {WORKSPACE_COUNT}")
print("########################################")
LOGGER.info(f"End WorkSpaces quota check in {ALERT_REGION}")
LOGGER.info(f"Total WorkSpaces across all regions: {WORKSPACE_GLOBAL_COUNT}")
#Only generate and send SNS notification if there are any quotas breaching the alarm threshold
if (WORKSPACE_ALARM_STATUS) :
LOGGER.info("Due to alarm, Service Limit Notification published to SNS.")
SUBJECT = "ALERT: Amazon WorkSpace Service Limits"
MESSAGE = 'The below Amazon WorkSpaces Service Limits are beyond the configured threshold of {}% and are in an alarm state:\n\n'.format(ALERT_THRESHOLD_PERCENT)
MESSAGE = MESSAGE + '{: <21} {: <20} {}/{}\n'.format('Region','Desktop Type','Count','Quota')
for ALARM in WORKSPACE_ALARM_LIST:
REGION, TYPE, QUOTA, COUNT, PERCENT = ALARM
MESSAGE = MESSAGE + '{: <21} {: <23} {}/{} ({}%)\n'.format(REGION,TYPE,COUNT,QUOTA,PERCENT)
send_notification()
else :
LOGGER.info("No service limits are in alarm state. No notification published.")
return {
'WorkSpaceAlarm': WORKSPACE_ALARM_STATUS,
}
Step 4. Create the EventBridge rule
In this final step, you create an EventBridge rule to invoke the Lambda function created in the previous step. This rule defines the schedule for when the Lambda function will run and check for WorkSpaces service limit breaches. This rule also contains the parameters that define the service limit threshold, the AWS Regions to monitor WorkSpaces limits, and the SNS top.
- Open the EventBridge console.
- Choose Create rule.
- Enter a rule Name, for example WorkSpaces_Service_Limit_Alerting_Trigger.
- Under Define pattern select Schedule.
- Define either a Fixed rate or a Cron expression to match the frequency you desire for the service limit checks.
- Under Select targets keep the Target set to Lambda function.
- In the Function dropdown, select the Lambda function created in step 3.
- Expand Configure input, then select Constant (JSON text).
- Copy and paste the following JSON statement into the box.
- Replace the following values in the statement to match your environment:
-
- Replace <alert-threshold> a number between 0 and 100. This value determines at what percentage of your service limit will an alert notification be sent, for example 80.
- Replace <alert-regions> with a comma separated list of which AWS Regions to check WorkSpace limits, for example us-east-1, us-west-2.
- Replace <sns-topic-arn> with the ARN from the SNS Topic created in step 1.
{ “AlertThreshold”: <alert-threshold>, “AlertRegions”: [ “<alert-regions>” ], “AlertTopicArn”: “<sns-topic-arn>“}
-
- Choose Create.
Clean up
In this blog post, you created several components that may generate costs based on usage. To avoid incurring future charges, remove the following resources.
- If the CloudFormation template was used:
- Navigate to the CloudFormation console.
- Select the stack created above.
- Choose Delete. This will automatically delete the other resources used in the solution.
- To prevent future executions of the solution:
- Navigate to the EventBridge console.
- Click Rules.
- Select the rule create above and click Delete. This will prevent future executions of the solution.
- Lambda functions do not incur a charge unless invoked. To remove this component completely:
- Open the Lambda console.
- Select your function.
- Choose Actions, then Delete.
- SNS charges for the number of messages sent. To remove the topic created as part of this solution:
- Open the SNS console.
- Click Topics.
- Select your topic, then Delete.
- There are no charges for IAM Policies and Roles. To remove the IAM entities created in this article:
- Navigate to the IAM console.
- Click Roles.
- Select the role created above, then Delete.
- Click Policies.
- Select the policy for this article, then choose Actions, then Delete.
Conclusion
You now have solution to proactively monitor your WorkSpaces usage and alert you as your approach your service limits. At a scheduled interval, an EventBridge rule triggers a Lambda function to gather the WorkSpaces Service Limits and currently deployed WorkSpaces counts, and automatically send an alert email via an Amazon SNS subscription to your administrators.
If changes are needed, you can go to the following locations to update or change different aspects of the solution:
- Change alerting time or frequency:
- In the EventBridge console, navigate to the Rules section, and edit the rule created in step 4.
- Define a new CRON expression or fixed rate.
- Modify or add additional email addresses for the notification:
- In the SNS console, navigate to the Topics section, then choose the topic created in step 1.
- Under Subscriptions, choose Create subscription, the in the dropdown Protocol select Email.
- Enter the new email address for Endpoint.
- Change or add the monitored AWS Regions or switch the alert threshold:
- In the EventBridge console, navigate to the Rules section, and edit the rule created in step 4.
- Under the Select targets section, expand Configure input.
- Change the JSON values for AlertThreshold or AlertRegions as required.
Justin Grego is a Senior End User Computing Specialist Solutions Architect. As part of the EUC Service Aligned SA Team, he helps enable both customers and fellow SAs get up to speed on and be successful with new AWS EUC features and services. |