Website
Next.js
A Next.js website hosted on AWS utilizing OpenNext architecture.
/**
* 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.
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