Challenge: Create DynamoDB Table using Python (Boto3)
This was an idea from the AWS Certified Global Community to create challenges by breaking a certain part of code, so the users can learn by fixing the code.
I have written this challenge for the AWS Certified Global Community and have also used it to mentor some of my mentees.
I thought that it might be a good idea to share with the broader community as well.
Boto3 is the name of the Python SDK for AWS. It allows you to directly create, update, and delete AWS resources from your Python scripts.
Amazon DynamoDB is a fully managed, serverless, key-value NoSQL database designed to run high-performance applications at any scale. DynamoDB offers built-in security, continuous backups, automated multi-Region replication, in-memory caching, and data import and export tools.
Let's start the challenge!
Pre-Requisites:
- Lambda with Python 3.9
- Lambda Execution role with access to DynamoDB and CloudWatch access (IAM)
Step 1. Create an IAM Policy:
Note: Replace [Region] and [AccountID] in the Policy
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:CreateTable",
"dynamodb:DescribeTable",
"dynamodb:BatchGetItem",
"dynamodb:GetItem",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:BatchWriteItem",
"dynamodb:PutItem",
"dynamodb:UpdateItem"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:[Region]:[AccountID]:*"
},
{
"Effect": "Allow",
"Action": "logs:CreateLogGroup",
"Resource": "*"
}
]
}
Step 2. Create a role for Lambda and attach the policy created in Step 1
Trusted entities:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
Step 3. Broken Code: Create a Lambda function with the following code and execute the function.
import boto3
import json
def lambda_handler(event, context):
client = boto3.resource('dynamodb')
table_name = 'VisitsCount'
params = {
'TableName': table_name,
'KeySchema': [
{'AttributeName': 'partition_key', 'KeyType': 'HASH'},
{'AttributeName': 'sort_key', 'KeyType': 'RANGE'},
],
'AttributeDefinitions': [
{'AttributeName': 'Date', 'AttributeType': 'S'},
{'AttributeName': 'Count', 'AttributeType': 'N'}
],
'ProvisionedThroughput': {
'ReadCapacityUnits': 10,
'WriteCapacityUnits': 10
}
}
table = client.create_table(**params)
print(f"Creating {table_name}...")
table.wait_until_exists()
return {
'statusCode': 200,
'body': table
}
First Error: We get the following error when we execute the code.
'KeySchema': [
{'AttributeName': 'partition_key', 'KeyType': 'HASH'},
{'AttributeName': 'sort_key', 'KeyType': 'RANGE'},
],
Error Message:
{
"errorMessage": "An error occurred (ValidationException) when calling the CreateTable operation: One or more parameter values were invalid: Some index key attributes are not defined in AttributeDefinitions. Keys: [partition_key, sort_key], AttributeDefinitions: [Date, Count]",
"errorType": "ClientError",
"requestId": "b4f6cf89-8efb-470b-a52e-81d364d5e7a2",
"stackTrace": [
" File \"/var/task/lambda_function.py\", line 23, in lambda_handler\n table = client.create_table(**params)\n",
" File \"/var/runtime/boto3/resources/factory.py\", line 520, in do_action\n response = action(self, *args, **kwargs)\n",
" File \"/var/runtime/boto3/resources/action.py\", line 83, in __call__\n response = getattr(parent.meta.client, operation_name)(*args, **params)\n",
" File \"/var/runtime/botocore/client.py\", line 391, in _api_call\n return self._make_api_call(operation_name, kwargs)\n",
" File \"/var/runtime/botocore/client.py\", line 719, in _make_api_call\n raise error_class(parsed_response, operation_name)\n"
]
}
Fix for the error: Upate the following code block.
'KeySchema': [
{'AttributeName': 'Date', 'KeyType': 'HASH'},
{'AttributeName': 'Count', 'KeyType': 'RANGE'},
],
So when we execute the code, this time we get another error as following
Second Error: We get the following error when we execute the code after applying fix for the first error.
The issue is while prinintg the table name in the output
'body': table
Error Message:
{
"errorMessage": "Unable to marshal response: Object of type dynamodb.Table is not JSON serializable",
"errorType": "Runtime.MarshalError",
"requestId": "e60ca4f2-eccd-410c-a93a-7cf7499a0a9d",
"stackTrace": []
}
Fix for the error: Upate the following code block.
'body': json.dumps(table_name)
Reference/Hint: AWS Lambda "Unable to marshal response" Error
Step 4. Final Code:
import boto3
import json
def lambda_handler(event, context):
client = boto3.resource('dynamodb')
table_name = 'VisitsCount'
params = {
'TableName': table_name,
'KeySchema': [
{'AttributeName': 'Date', 'KeyType': 'HASH'},
{'AttributeName': 'Count', 'KeyType': 'RANGE'},
],
'AttributeDefinitions': [
{'AttributeName': 'Date', 'AttributeType': 'S'},
{'AttributeName': 'Count', 'AttributeType': 'N'}
],
'ProvisionedThroughput': {
'ReadCapacityUnits': 10,
'WriteCapacityUnits': 10
}
}
table = client.create_table(**params)
print(f"Creating {table_name}...")
table.wait_until_exists()
return {
'statusCode': 200,
'body': json.dumps(table_name)
}
Output:
Test Event Name
createtable
Response
{
"statusCode": 200,
"body": "\"VisitsCount\""
}
Hope you liked the challenge! Please let me know your thoughts in the comments section.
See you next time 👋
Top comments (0)