Skip to content

New serverless pattern - apigw-lambda-bedrock-novacanvas #2719

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions apigw-bedrock-nova-canvas/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Serverless Text-to-Image Generation with Amazon Bedrock Nova Canvas

![architecture](architecture/architecture.png)

This pattern implements a serverless text-to-image generation service using Amazon API Gateway, AWS Lambda and Amazon Bedrock's Nova Canvas model. It provides a REST API endpoint where users can submit text prompts.

This invokes a Lambda function containing the request and the function makes a call to Amazon Bedrock's Nova Canvas model to generate an image based on the text description. Once the image is generated, the Lambda function saves it to an S3 bucket and returns this filename to the user through the API Gateway API.

Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/apigw-bedrock-nova-canvas

Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example.

## Requirements

* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources.
* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured
* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
* [Terraform](https://learn.hashicorp.cxom/tutorials/terraform/install-cli?in=terraform/aws-get-started) installed
* [Amazon Bedrock Nova Canvas Foundation Model Access](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access.html#add-model-access)

## Deployment Instructions

For this pattern, you would need access only to Amazon Nova Canvas foundation model (Model ID: amazon.nova-canvas-v1:0) in us-east-1 region, since the pattern uses us-east-1 region by default.

You must request access to the model before you can use it. If you try to use the model before you have requested access to it, you will receive an error message.

1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository:
```
git clone https://github.com/aws-samples/serverless-patterns
```
1. Change directory to the pattern directory:
```
cd apigw-bedrock-nova-canvas
```
1. From the command line, initialize terraform to downloads and installs the providers defined in the configuration:
```
terraform init
```
1. From the command line, apply the configuration in the main.tf file:
```
terraform apply
```
1. During the prompts
```
#var.prefix
- Enter a value: {enter any prefix to associate with resources}
```

## Testing

1. Make a POST request to the API using the following cURL command:

```
curl -X POST 'API_ENDPOINT' --header "Content-Type: application/json" --data '{"prompt": "YOUR_PROMPT"}'
```

Note: Replace `API_ENDPOINT` with the generated `api_endpoint` from Terraform (refer to the Terraform Outputs section), "YOUR_PROMPT" with your desired prompt. For ex,

```
curl -X POST 'https://1234abcde.execute-api.us-east-1.amazonaws.com/dev/image_gen' --header "Content-Type: application/json" --data '{"prompt": "Kitten playing the piano"}'
```

1. Once the API Gateway responds with the image ID, you can navigate to the S3 bucket (refer to the Terraform Outputs section for the bucket name) and select the correct image ID to view the generated image.

## Cleanup

1. Change directory to the pattern directory:
```
cd serverless-patterns/apigw-bedrock-nova-canvas
```

1. Delete all created resources
```
terraform destroy
```

1. During the prompts:
```
Enter all details as entered during creation.
```

1. Confirm all created resources has been deleted
```
terraform show
```
----
Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.

SPDX-License-Identifier: MIT-0
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
58 changes: 58 additions & 0 deletions apigw-bedrock-nova-canvas/example-pattern.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"title": "Serverless Text-to-Image Generation with Amazon Bedrock Nova Canvas",
"description": "This pattern implements a serverless text-to-image generation service using Amazon API Gateway, AWS Lambda and Amazon Bedrock's Nova Canvas model. It provides a REST API endpoint where users can submit text prompts and receive generated images whic are stored in an S3 bucket.",
"language": "Python",
"level": "200",
"framework": "Terraform",
"introBox": {
"headline": "How it works",
"text": [
"The solution works by receiving a text prompt to an API Gateway endpoint, which triggers a Lambda function containing the request. The Lambda function then formats this prompt and makes a call to Amazon Bedrock's Nova Canvas model to generate an image based on the text description and the generated image is saved to the S3 bucket."
]
},
"gitHub": {
"template": {
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/apigw-bedrock-nova-canvas",
"templateURL": "serverless-patterns/apigw-bedrock-nova-canvas",
"projectFolder": "apigw-bedrock-nova-canvas",
"templateFile": "main.tf"
}
},
"resources": {
"bullets": [
{
"text": "Amazon Nova Canvas",
"link": "https://docs.aws.amazon.com/ai/responsible-ai/nova-canvas/overview.html"
},
{
"text": "Invoke Amazon Nova Canvas on Amazon Bedrock to generate an image",
"link": "https://docs.aws.amazon.com/bedrock/latest/userguide/bedrock-runtime_example_bedrock-runtime_InvokeModel_AmazonNovaImageGeneration_section.html"
}
]
},
"deploy": {
"text": [
"terraform init",
"terraform apply"
]
},
"testing": {
"text": [
"See the GitHub repo for detailed testing instructions."
]
},
"cleanup": {
"text": [
"terraform destroy",
"terraform show"
]
},
"authors": [
{
"name": "Archana V",
"image": "https://media.licdn.com/dms/image/v2/D5603AQGhkVtEhllFEw/profile-displayphoto-shrink_400_400/B56ZZH3LL6H0Ag-/0/1744962369913?e=1750291200&v=beta&t=R0hX6jzWC03OyoWKvYJ0jDDTuPocobPSy0lAJY-3XfA",
"bio": "Solutions Architect at AWS",
"linkedin": "archana-venkat-9b80b7184"
}
]
}
Binary file added apigw-bedrock-nova-canvas/index.zip
Binary file not shown.
188 changes: 188 additions & 0 deletions apigw-bedrock-nova-canvas/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
provider "aws" {
region = "us-east-1"
}

variable "prefix" {
description = "Prefix to associate with the resources"
type = string
}

resource "random_string" "suffix" {
length = 8
special = false
upper = false
}

# Create Lambda layer for Pillow from provided zip file
resource "aws_lambda_layer_version" "pillow_layer" {
filename = "pillow.zip" # Make sure this zip file exists in your terraform directory
layer_name = "pillow_layer"
compatible_runtimes = ["python3.11"]
description = "Pillow library layer for image processing"
}

# IAM Policy for invoking Bedrock model
resource "aws_iam_policy" "invoke_model_policy" {
name = "${lower(var.prefix)}-InvokeModelPolicy-${random_string.suffix.result}"
path = "/"
description = "Policy to invoke Bedrock model"

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"bedrock:InvokeModel",
]
Effect = "Allow"
Resource = [
"arn:aws:bedrock:${data.aws_region.current.name}::foundation-model/amazon.nova-canvas-v1:0"
]
},
]
})
}

# S3 bucket for storing images
resource "aws_s3_bucket" "image_bucket" {
bucket = "${lower(var.prefix)}-image-bucket-${random_string.suffix.result}"
force_destroy = true
}

# Create CloudWatch Log Group for Lambda
resource "aws_cloudwatch_log_group" "lambda_log_group" {
name = "/aws/lambda/${lower(var.prefix)}-invoke-bedrock"
retention_in_days = 14

lifecycle {
prevent_destroy = false
}
}

# Lambda function
resource "aws_lambda_function" "invoke_bedrock_function" {
filename = "index.zip" # Replace with your Lambda code zip file
function_name = "${lower(var.prefix)}-invoke-bedrock"
role = aws_iam_role.lambda_role.arn
handler = "index.handler"
runtime = "python3.11"
timeout = 30

layers = [
aws_lambda_layer_version.pillow_layer.arn
]

environment {
variables = {
BUCKET = aws_s3_bucket.image_bucket.id
}
}

depends_on = [aws_cloudwatch_log_group.lambda_log_group]
}

# IAM role for Lambda function
resource "aws_iam_role" "lambda_role" {
name = "${lower(var.prefix)}-lambda_role-${random_string.suffix.result}"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "lambda.amazonaws.com"
}
}
]
})
}

# Attach policies to Lambda role
resource "aws_iam_role_policy_attachment" "lambda_invoke_model_policy" {
policy_arn = aws_iam_policy.invoke_model_policy.arn
role = aws_iam_role.lambda_role.name
}

resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
role = aws_iam_role.lambda_role.name
}

resource "aws_iam_role_policy_attachment" "lambda_s3_access" {
policy_arn = "arn:aws:iam::aws:policy/AmazonS3FullAccess"
role = aws_iam_role.lambda_role.name
}

# API Gateway
resource "aws_api_gateway_rest_api" "bedrock_api" {
name = "${lower(var.prefix)}-BedrockImageAPI-${random_string.suffix.result}"
}

resource "aws_api_gateway_resource" "image_gen" {
rest_api_id = aws_api_gateway_rest_api.bedrock_api.id
parent_id = aws_api_gateway_rest_api.bedrock_api.root_resource_id
path_part = "image_gen"
}

resource "aws_api_gateway_method" "image_gen_post" {
rest_api_id = aws_api_gateway_rest_api.bedrock_api.id
resource_id = aws_api_gateway_resource.image_gen.id
http_method = "POST"
authorization = "NONE"
}

resource "aws_api_gateway_integration" "lambda_integration" {
rest_api_id = aws_api_gateway_rest_api.bedrock_api.id
resource_id = aws_api_gateway_resource.image_gen.id
http_method = aws_api_gateway_method.image_gen_post.http_method

integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.invoke_bedrock_function.invoke_arn
}

# API Gateway Deployment
resource "aws_api_gateway_deployment" "api_deployment" {
rest_api_id = aws_api_gateway_rest_api.bedrock_api.id

depends_on = [
aws_api_gateway_integration.lambda_integration
]
}

# API Gateway Stage
resource "aws_api_gateway_stage" "api_stage" {
deployment_id = aws_api_gateway_deployment.api_deployment.id
rest_api_id = aws_api_gateway_rest_api.bedrock_api.id
stage_name = "dev"
}

# Lambda permission for API Gateway
resource "aws_lambda_permission" "api_gateway_lambda" {
statement_id = "AllowAPIGatewayInvoke"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.invoke_bedrock_function.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_api_gateway_rest_api.bedrock_api.execution_arn}/*/*"
}

# Outputs
output "lambda_function" {
description = "The ARN of the Lambda function"
value = aws_lambda_function.invoke_bedrock_function.arn
}

output "api_endpoint" {
description = "The API Gateway endpoint URL "
value = "${aws_api_gateway_stage.api_stage.invoke_url}/image_gen"
}

output "s3_image_bucket" {
description = "The Output S3 bucket is "
value = aws_s3_bucket.image_bucket.id
}

# Data source for current region
data "aws_region" "current" {}
Binary file added apigw-bedrock-nova-canvas/pillow.zip
Binary file not shown.