
Ways to Monitor AWS Free Tier Usage with AWS Free Tier API
In this article, we will explore numerous options to monitor AWS Free Tier usage with the newly released AWS Free Tier API
- Always free offers allow customers to use a service for free up to specified limits as long as they are AWS customers.
- 12-month free offers allow customers to use a service for free up to specified limits for one year from the date the account was activated.
- Short-term trials are free to use for a specified period or up to a one-time limit, depending on the service.
- The usage alerts in the Billing preferences of the AWS Billing and Cost Management console can send you emails when you exceed 85 percent of the Free Tier limit for each service.
- You can create a zero-spend or a monthly-cost budget in the Budgets section of the Billing and Cost Management console.
- The Free Tier page in the Billing and Cost Management console tells you the service, the type of offer, the current usage, and the forecasted usage for each offer in the current billing period.
- To check for free tier usage we can call
GetFreeTierUsage
API by typing the following command
1
aws freetier get-free-tier-usage
- It will then show all the free tier usage information that applies to your account like this.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
"freeTierUsages": [
{
"actualUsageAmount": 15.0,
"description": "1000000.0 Request are always free per month as part of AWS Free Usage Tier (Global-Catalog-Request)",
"forecastedUsageAmount": 232.5,
"freeTierType": "Always Free",
"limit": 1000000.0,
"operation": "Request",
"region": "global",
"service": "AWS Glue",
"unit": "Request",
"usageType": "Catalog-Request"
},
{
"actualUsageAmount": 7.0,
"description": "500000.0 API Request are always free per month as part of AWS Free Usage Tier (Global-API-Request)",
"forecastedUsageAmount": 108.5,
"freeTierType": "Always Free",
"limit": 500000.0,
"operation": "",
"region": "global",
"service": "AWS Migration Hub Refactor Spaces",
"unit": "API Request",
"usageType": "API-Request"
},
-- More --
- To get usage of a specific service, you can type like this
1
aws freetier get-free-tier-usage --query "freeTierUsages[?service=='AWS Lambda']"
- It will then return usage information of AWS Lambda service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[
{
"actualUsageAmount": 0.29625,
"description": "400000.0 seconds are always free per month as part of AWS Free Usage Tier (Global-Lambda-GB-Second)",
"forecastedUsageAmount": 0.36735,
"freeTierType": "Always Free",
"limit": 400000.0,
"operation": "",
"region": "global",
"service": "AWS Lambda",
"unit": "seconds",
"usageType": "Lambda-GB-Second"
},
{
"actualUsageAmount": 6.0,
"description": "1000000.0 Request are always free per month as part of AWS Free Usage Tier (Global-Request)",
"forecastedUsageAmount": 7.4399999999999995,
"freeTierType": "Always Free",
"limit": 1000000.0,
"operation": "",
"region": "global",
"service": "AWS Lambda",
"unit": "Request",
"usageType": "Request"
}
]
- To check for an actual usage amount that is greater than the limit, you can type like this.
1
aws freetier get-free-tier-usage --query 'freeTierUsages[?actualUsageAmount > limit]'
- It will return all the usage information if which actual usage amount is greater than the limit.
- To return output in a specific response format, you can type like this
1
aws freetier get-free-tier-usage --query "freeTierUsages[].{service: service, actualUsage: actualUsageAmount, limit: limit, unit: unit}"
- It will return a response that returns the response format according to the format we provided our query.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
[
{
"service": "AWS Glue",
"actualUsage": 336.0,
"limit": 1000000.0,
"unit": "Request"
},
{
"service": "AWS Lambda",
"actualUsage": 0.29625,
"limit": 400000.0,
"unit": "seconds"
},
{
"service": "AWS Lambda",
"actualUsage": 6.0,
"limit": 1000000.0,
"unit": "Request"
},
{
"service": "AWS Migration Hub Refactor Spaces",
"actualUsage": 170.0,
"limit": 500000.0,
"unit": "API Request"
},
{
"service": "Amazon Simple Queue Service",
"actualUsage": 169.0,
"limit": 1000000.0,
"unit": "Requests"
},
{
"service": "AmazonCloudWatch",
"actualUsage": 6.37634517,
"limit": 10.0,
"unit": "Alarms"
},
{
"service": "AmazonCloudWatch",
"actualUsage": 13.0,
"limit": 1000000.0,
"unit": "Requests"
},
In this use case, we will create a usage report notification that will notify our Lambda usage daily every day at 11:00 AM UTC or 06:00 PM GMT+7.
- To send Daily Email Usage Report Notifications we will utilize several AWS Services like Amazon SNS, Amazon Lambda, Amazon EventBridge Scheduler, and Amazon SNS.
- We will build our infrastructure using AWS CDK.
- First, create our project folder.
1
mkdir cdk-scheduled-reporting && cd cdk-scheduled-reporting
- Start initializing our CDK Project.
1
cdk init app --language javascript
- create
index.mjs
insidesrc
folder
1
touch src/index.mjs
- Start typing the following code into
src/index.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import {
FreeTierClient,
GetFreeTierUsageCommand,
} from "@aws-sdk/client-freetier";
import { SNSClient, PublishCommand } from "@aws-sdk/client-sns";
export const handler = async (event) => {
const snsClient = new SNSClient({
region: process.env.AWS_REGION,
});
try {
const snsResponse = await snsClient.send(
new PublishCommand({
Subject: "Lambda Free Tier Usage Report",
Message: await getLambdaFreeTierUsage(),
TopicArn: process.env.TOPIC_ARN,
})
);
console.log(snsResponse);
} catch (err) {
console.log("err", err);
}
return { message: "Successfully executed" };
};
async function getLambdaFreeTierUsage() {
const freeTierClient = new FreeTierClient({
region: process.env.AWS_REGION,
});
const responseFreeTier = await freeTierClient.send(
new GetFreeTierUsageCommand({
filter: {
Dimensions: {
Key: "SERVICE",
Values: ["AWS Lambda"],
MatchOptions: ["EQUALS"],
},
},
})
);
return responseFreeTier.freeTierUsages
.map((item) => {
return `${item.service} - ${item.usageType} - ${item.actualUsageAmount} / ${item.limit} ${item.unit} \n`;
})
.join("");
}
- Go to
lib/cdk-scheduled-reporting-stack.js
- Type the following code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
const { Stack, Duration, CfnParameter, CfnOutput } = require("aws-cdk-lib");
const lambda = require("aws-cdk-lib/aws-lambda");
const event = require("aws-cdk-lib/aws-events");
const target = require("aws-cdk-lib/aws-events-targets");
const sns = require("aws-cdk-lib/aws-sns");
const subscription = require("aws-cdk-lib/aws-sns-subscriptions");
const iam = require("aws-cdk-lib/aws-iam");
const path = require("path");
class CdkScheduledResportingStack extends Stack {
constructor(scope, id, props) {
super(scope, id, props);
const emailAddress = new CfnParameter(this, "EmailAddress", {
type: "String",
description: "Email address to send free tier usage report",
});
const topic = new sns.Topic(this, "UsageReportTopic", {
displayName: "ScheduledReportingTopic",
});
topic.addSubscription(
new subscription.EmailSubscription(emailAddress.valueAsString)
);
const usageReportFunction = new lambda.Function(
this,
"UsageReportFunction",
{
runtime: lambda.Runtime.NODEJS_LATEST,
code: lambda.Code.fromAsset(path.join(__dirname, "../src")),
handler: "index.handler",
timeout: Duration.seconds(300),
environment: {
TOPIC_ARN: topic.topicArn,
},
}
);
topic.grantPublish(usageReportFunction);
usageReportFunction.addToRolePolicy(
new iam.PolicyStatement({
actions: ["freetier:GetFreeTierUsage"],
resources: ["*"],
})
);
const rule = new event.Rule(this, "Rule", {
schedule: event.Schedule.expression("cron(0 11 * * ? *)"),
});
rule.addTarget(new target.LambdaFunction(usageReportFunction));
new CfnOutput(this, "LambdaFunction", {
value: usageReportFunction.functionName,
});
}
}
module.exports = { CdkScheduledResportingStack };
- Then start deploying our stack with your own
email address
1
cdk deploy --parameters EmailAddress=youremail@email.com
- CDK will then output something like this, take note of the function name.
1
2
Outputs:
CdkScheduledResportingStack.LambdaFunction = CdkScheduledResportingSta-UsageReportFunction40879-IjQDqOovyRy4
- Make sure you confirm your SNS Topic subscriptions first from your email account.
- You can test your lambda function
CdkScheduledResportingSta-UsageReportFunction40879-IjQDqOovyRy4
like this
1
aws lambda invoke --function-name CdkScheduledResportingSta-UsageReportFunction40879-IjQDqOovyRy --invocation-type Event response.json
- After running the above command, you will receive your sales notification in your email like this.
- By receiving the above email, that means our service is deployed and working perfectly. You will receive a notification similar to the above example every day at 11:00 AM UTC or 06:00 PM GMT+7.
In this use case we will utilize Free Tier API to build our own Dashboard that integrate into our own application.
- To solve this use case, we will utilize AWS Lambda with Function URL enabled.
- We will use AWS CDK to build our infrastructure.
- First, create our project folder name
free-tier-backend-api
.
1
mkdir free-tier-backend-api && cd free-tier-backend-api
- Then initialize our CDK project using the following command.
1
cdk init app -l javascript
- Then create our lambda function handler inside
src/index.mjs
with the following code.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import {
FreeTierClient,
GetFreeTierUsageCommand,
} from "@aws-sdk/client-freetier";
export const handler = async (event) => {
return await getFreeTierUsage();
};
async function getFreeTierUsage() {
const client = new FreeTierClient({});
// query only AWS Lambda Free Tier usages
const response = await client.send(
new GetFreeTierUsageCommand({
filter: {
Dimensions: {
Key: "SERVICE",
Values: ["AWS Lambda"],
MatchOptions: ["EQUALS"],
},
},
})
);
// return the data in specific format
return response.freeTierUsages.map((usage) => {
return {
serviceUsageType: `${usage.service} ${usage.usageType}`,
ActualUsage: usage.actualUsageAmount,
Limit: usage.limit,
};
});
}
- Then go to `lib/freetier-backend-api-stack.js to start creating our stack by typing the following code.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
const { Stack, Duration, CfnOutput } = require("aws-cdk-lib");
const lambda = require("aws-cdk-lib/aws-lambda");
const iam = require("aws-cdk-lib/aws-iam");
class FreetierBackendApiStack extends Stack {
constructor(scope, id, props) {
super(scope, id, props);
//create lambda function that load funcation handler from src/index.mjs
const freeTierLambdaUsageFunction = new lambda.Function(
this,
"freeTierLambdaFunction",
{
runtime: lambda.Runtime.NODEJS_LATEST,
handler: "index.handler",
code: lambda.Code.fromAsset("src"),
timeout: Duration.seconds(10),
}
);
// grant lambda to access Free Tier API
freeTierLambdaUsageFunction.addToRolePolicy(
new iam.PolicyStatement({
actions: ["freetier:GetFreeTierUsage"],
resources: ["*"],
})
);
// Enable Lambda's Function URL
const fnUrl = freeTierLambdaUsageFunction.addFunctionUrl({
authType: lambda.FunctionUrlAuthType.NONE,
invokeMode: lambda.InvokeMode.BUFFERED,
cors: {
allowedOrigins: ["*"],
allowedMethods: ["GET"],
},
});
// Return the function's URL
new CfnOutput(this, "FunctionUrl", {
value: fnUrl.url,
});
}
}
module.exports = { FreetierBackendApiStack };
- After that, we can start deploying our infrastructure by typing this command into our terminal.
1
cdk deploy
- After our deployment is completed, CDK will output our function URL like this
https://vzthe2pe7kloemnpoyxwrdej7i0hmwps.lambda-url.ap-southeast-1.on.aws/
(take note of this).
1
2
3
4
5
6
7
8
✨ Deployment time: 64.5s
Outputs:
FreetierBackendApiStack.FunctionUrl = https://vzthe2pe7kloemnpoyxwrdej7i0hmwps.lambda-url.ap-southeast-1.on.aws/
Stack ARN:
arn:aws:cloudformation:ap-southeast-1:498624870591:stack/FreetierBackendApiStack/ab485ce0-bde9-11ee-a8d9-0a63076ab1bd
✨ Total time: 65.11s
- You can try testing our function URL whether it can return our data like this.
1
2
$ curl -k https://vzthe2pe7kloemnpoyxwrdej7i0hmwps.lambda-url.ap-southeast-1.on.aws/
[{"serviceUsageType":"AWS Lambda Lambda-GB-Second","Limit":400000,"ActualUsage":2.84975},{"serviceUsageType":"AWS Lambda Request","Limit":1000000,"ActualUsage":21}]
- Then we can start integrating it into our own web by typing this code into our HTML file, for example like this.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/css/bootstrap.min.css"
integrity="sha512-b2QcS5SsA8tZodcDtGRELiGv5SaKSk1vDHDaQRda0htPYWZ6046lr3kJ5bAAQdpV2mmA/4v0wQF9MyU6/pDIAg=="
crossorigin="anonymous"
/>
</head>
<body>
<div class="container">
<h1>Lambda Free Tier Usage Report</h1>
<div id="chart" class="row"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script type="module">
const chart = document.getElementById("chart");
const usage = await fetch(
"https://72ar6w4esjs2sgskgyopob4qty0faiwm.lambda-url.ap-southeast-1.on.aws/"
).then((res) => res.json());
usage.map((item) => {
console.log(item);
const div = document.createElement("div");
div.classList.add("col-md-3");
const canvas = document.createElement("canvas");
canvas.id = item.serviceUsageType;
var ctx = canvas.getContext("2d");
var myChart = new Chart(ctx, {
type: "pie",
options: {
responsive: true,
plugins: {
legend: {
position: "bottom",
},
title: {
display: true,
text: item.serviceUsageType,
},
},
},
data: {
labels: ["Actual Usage", "Limit"],
datasets: [
{
label: item.serviceUsageType,
data: [item.ActualUsage, item.Limit],
backgroundColor: [
"rgba(255, 54, 162, 0.2)",
"rgba(99, 255, 132, 0.2)",
],
borderColor: ["rgba(255, 54, 162, 1)", "rgba(99, 255, 132, 1)"],
borderWidth: 1,
},
],
},
});
div.appendChild(canvas);
chart.appendChild(div);
});
</script>
</body>
</html>
- The html file will output something like this.
- If the output is correct, that means we successfully integrated Free Tier Usage API into our own dashboard.