Website
Next.js
A Next.js website hosted on AWS utilizing OpenNext architecture.
Copy
/**
* Common Data Sources
***/
data "aws_region" "current" {}
/**
* Server Function
***/
resource "aws_lambda_function" "server" {
description = "Server function for the website"
filename = var.server_function_config.filename
function_name = var.server_function_config.function_name
handler = var.server_function_config.handler
memory_size = var.server_function_config.memory_size
role = aws_iam_role.server.arn
runtime = var.server_function_config.runtime
source_code_hash = filebase64sha256(var.server_function_config.filename)
timeout = var.server_function_config.timeout
dynamic "tracing_config" {
for_each = var.server_function_config.tracing_enabled ? [1] : []
content {
mode = "Active"
}
}
dynamic "vpc_config" {
for_each = var.vpc_config.enabled ? [1] : []
content {
subnet_ids = var.vpc_config.subnet_ids
security_group_ids = var.vpc_config.security_group_ids
}
}
environment {
variables = {
CACHE_BUCKET_NAME = aws_s3_bucket.cache_bucket.id
CACHE_BUCKET_REGION = data.aws_region.current.name
CACHE_DYNAMO_TABLE = "INSERT_CACHE_DYNAMO_TABLE_HERE"
REVALIDATION_QUEUE_URL = aws_sqs_queue.revalidation_queue.id
REVALIDATION_QUEUE_REGION = data.aws_region.current.name
}
}
tags = merge(var.shared_tags, {
Name = var.server_function_config.function_name
})
}
resource "aws_iam_role" "server_lambda" {
name = var.server_function_config.function_name
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "lambda.amazonaws.com"
}
}
]
})
}
resource "aws_iam_role_policy" "server_lambda" {
name = var.server_function_config.function_name
policy = jsondecode({
Version = "2012-10-17"
Statement = [
{
Action = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
]
Effect = "Allow"
Resource = "arn:aws:logs:*:*:*"
},
{
Action = [
"s3:GetObject",
"s3:PutObject"
]
Effect = "Allow"
Resource = "${aws_s3_bucket.cache_bucket.arn}/*"
},
{
Action = [
"sqs:SendMessage"
]
Effect = "Allow"
Resource = aws_sqs_queue.revalidation_queue.arn
},
{
Action = [
"dynamodb:PutItem",
"dynamodb:GetItem"
]
Effect = "Allow"
Resource = "INSERT CACHE DYNAMO TABLE ARNS"
}
]
})
role = aws_iam_role.server_lambda.id
}
/**
* Image Optimization Function
***/
resource "aws_lambda_function" "image_optimization" {
architectures = ["arm64"]
description = "Image Optimization function for the website"
filename = var.image_optimization_function_config.filename
function_name = var.image_optimization_function_config.function_name
handler = var.image_optimization_function_config.handler
memory_size = var.image_optimization_function_config.memory_size
role = aws_iam_role.image_optimization.arn
runtime = var.image_optimization_function_config.runtime
source_code_hash = filebase64sha256(var.image_optimization_function_config.filename)
timeout = var.image_optimization_function_config.timeout
dynamic "tracing_config" {
for_each = var.image_optimization_function_config.tracing_enabled ? [1] : []
content {
mode = "Active"
}
}
dynamic "vpc_config" {
for_each = var.vpc_config.enabled ? [1] : []
content {
subnet_ids = var.vpc_config.subnet_ids
security_group_ids = var.vpc_config.security_group_ids
}
}
environment {
variables = {
BUCKET_NAME = aws_s3_bucket.asset_files_bucket.id
}
}
tags = merge(var.shared_tags, {
Name = var.image_optimization_function_config.function_name
})
}
resource "aws_iam_role" "image_optimization" {
name = var.image_optimization_function_config.function_name
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "lambda.amazonaws.com"
}
}
]
})
}
resource "aws_iam_role_policy" "image_optimization" {
name = var.image_optimization_function_config.function_name
policy = jsondecode({
Version = "2012-10-17"
Statement = [
{
Action = [
"s3:GetObject"
]
Effect = "Allow"
Resource = "${aws_s3_bucket.asset_files_bucket.arn}/*"
}
]
})
role = aws_iam_role.image_optimization.id
}
/**
* Asset Files Bucket
***/
resource "aws_s3_bucket" "asset_bucket" {
bucket = var.asset_bucket_config.bucket_name
tags = merge(var.shared_tags, {
Name = var.asset_bucket_config.bucket_name
})
}
/**
* Revalidation Function
***/
resource "aws_lambda_function" "revalidation_function" {
description = "Revalidation function for the website"
filename = var.revalidation_function_config.filename
function_name = var.revalidation_function_config.function_name
handler = var.revalidation_function_config.handler
memory_size = var.revalidation_function_config.memory_size
role = aws_iam_role.revalidation_function.arn
runtime = var.revalidation_function_config.runtime
source_code_hash = filebase64sha256(var.revalidation_function_config.filename)
timeout = var.revalidation_function_config.timeout
dynamic "tracing_config" {
for_each = var.revalidation_function_config.tracing_enabled ? [1] : []
content {
mode = "Active"
}
}
dynamic "vpc_config" {
for_each = var.vpc_config.enabled ? [1] : []
content {
subnet_ids = var.vpc_config.subnet_ids
security_group_ids = var.vpc_config.security_group_ids
}
}
tags = merge(var.shared_tags, {
Name = var.revalidation_function_config.function_name
})
}
resource "aws_iam_role" "revalidation_function" {
name = var.revalidation_function_config.function_name
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "lambda.amazonaws.com"
}
}
]
})
}
resource "aws_iam_role_policy" "revalidation_function" {
name = var.revalidation_function_config.function_name
role = aws_iam_role.revalidation_function.id
policy = jsondecode({
Version = "2012-10-17"
Statement = [
{
Action = [
"sqs:ReceiveMessage",
"sqs:DeleteMessage"
],
Effect = "Allow",
Resource = aws_sqs_queue.revalidation_queue.arn
}
]
})
}
resource "aws_lambda_event_source_mapping" "revalidation_queue" {
event_source_arn = aws_sqs_queue.revalidation_queue.arn
function_name = aws_lambda_function.revalidation_function.function_name
}
/**
* Revalidation Queue
***/
resource "aws_sqs_queue" "revalidation_queue" {
name = "revalidation-queue.fifo"
fifo_queue = true
tags = merge(var.shared_tags, {
Name = "revalidation-queue.fifo"
})
}
/**
* Revalidation Tag-to-path Mapping DynamoDB Table
***/
/**
* Cache Files Bucket
***/
resource "aws_s3_bucket" "cache_bucket" {
bucket = var.cache_bucket_config.bucket_name
tags = merge(var.shared_tags, {
Name = var.cache_bucket_config.bucket_name
})
}
/**
* Warmer Function
***/
resource "aws_lambda_function" "warmer" {
description = "Warmer function for the website"
filename = var.warmer_function_config.filename
function_name = var.warmer_function_config.function_name
handler = var.warmer_function_config.handler
memory_size = var.warmer_function_config.memory_size
role = aws_iam_role.warmer.arn
runtime = var.warmer_function_config.runtime
source_code_hash = filebase64sha256(var.warmer_function_config.filename)
timeout = var.warmer_function_config.timeout
dynamic "tracing_config" {
for_each = var.warmer_function_config.tracing_enabled ? [1] : []
content {
mode = "Active"
}
}
dynamic "vpc_config" {
for_each = var.vpc_config.enabled ? [1] : []
content {
subnet_ids = var.vpc_config.subnet_ids
security_group_ids = var.vpc_config.security_group_ids
}
}
environment {
variables = {
FUNCTION_NAME = var.server_function_config.function_name
CONCURRENCY = var.warmer_function_config.concurrency
}
}
tags = merge(var.shared_tags, {
Name = var.warmer_function_config.function_name
})
}
resource "aws_iam_role" "warmer_lambda" {
name = var.warmer_function_config.function_name
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "lambda.amazonaws.com"
}
}
]
})
}
resource "aws_iam_role_policy" "warmer_lambda" {
name = var.warmer_function_config.function_name
policy = jsondecode({
Version = "2012-10-17"
Statement = [
{
Action = [
"lambda:InvokeFunction"
]
Effect = "Allow"
Resource = aws_lambda_function.server.arn
}
]
})
role = aws_iam_role.warmer_lambda.id
}
/**
* Warmer Eventbridge Cron
***/
About
This is an opinated way to launch a Next.js based website on AWS.
This architecture is based on utilizing OpenNext for generation of the site bundles. Refer to OpenNext for more information on what Next.js features it supports.
Installation
1
Copy and paste all code files above into a folder.
Copy the main.tf, variables.tf, outputs.tf, versions.tf, and README.md into a folder (ex. website).
2
Reference the new module as a local or remote module.
Copy
module "website" {
source = "path-to-copied-website-module"
server_function_config = {
filename = "server.zip"
}
image_optimization_function_config = {
filename = "image-optimization.zip"
}
revlidation_function_config = {
filename = "revlidation.zip"
}
warmer_function_config = {
filename = "warmer.zip"
}
}
Usage
The options available to can be seen in the variables.tf file as well as in the Next.js module README.
Was this page helpful?
On this page
Assistant
Responses are generated using AI and may contain mistakes.