Coder Social home page Coder Social logo

millihq / terraform-aws-next-js Goto Github PK

View Code? Open in Web Editor NEW
1.4K 17.0 152.0 5.94 MB

Terraform module for building and deploying Next.js apps to AWS. Supports SSR (Lambda), Static (S3) and API (Lambda) pages.

Home Page: https://registry.terraform.io/modules/milliHQ/next-js/aws

License: Apache License 2.0

HCL 10.31% TypeScript 82.22% JavaScript 7.06% Shell 0.20% HTML 0.21%
aws serverless nextjs terraform-module

terraform-aws-next-js's Introduction

Note
The main branch currently contains the atomic deployments alpha preview.
For the lastest stable release, check out the v0.x branch.

Please see our blog post "The road to Atomic Deployments"
or watch the latest release review for more information:


Terraform Next.js module for AWS

CI status

A zero-config Terraform module for self-hosting Next.js sites serverless on AWS Lambda.

Features

Some features are still under development, here is a list of features that are currently supported and what we plan to bring with the next releases:

Architecture

The Next.js Terraform module is designed as a full stack AWS app. It relies on multiple AWS services and connects them to work as a single application:

Architecture overview diagram

Usage

Prerequisites

You should have the following tools installed:

Note: Additionally we assume here that you already have a public Route53 Hosted Zone associated with your AWS account.

This is a requirement in the preview phase of atomic deployments, where each deployment gets a unique subdomain assigned. It will change once atomic deployments become generally available.

Setup the Next.js Terraform module

The Terraform module contains the system that is later used for creating new deployments and managing the aliases (domains) for your Next.js app(s). Creating the Terraform stack is only required on initial setup and creates the global resources (CloudFront distributions, DynamoDB tables, S3 storage) that is used for handling incoming requests to your website.

Create a new main.tf file in an empty folder (or add it to your existing Terraform stack) and add the following content:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
  }
}

# Main region where the resources should be created in
# Should be close to the location of your viewers
provider "aws" {
  region = "us-west-2"
}

# Provider used for creating the Lambda@Edge function which must be deployed
# to us-east-1 region (Should not be changed)
provider "aws" {
  alias  = "global_region"
  region = "us-east-1"
}

###########
# Variables
###########

variable "custom_domain" {
  description = "Your custom domain"
  type        = string
  default     = "example.com"
}

# Assuming that the ZONE of your domain is already available in your AWS account (Route 53)
# https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/AboutHZWorkingWith.html
variable "custom_domain_zone_name" {
  description = "The Route53 zone name of the custom domain"
  type        = string
  default     = "example.com."
}

########
# Locals
########

locals {
  # A wildcard domain(ex: *.example.com) has to be added when using atomic deployments:
  aliases = [var.custom_domain, "*.${var.custom_domain}"]
}

#######################
# Route53 Domain record
#######################

# Get the hosted zone for the custom domain
data "aws_route53_zone" "custom_domain_zone" {
  name = var.custom_domain_zone_name
}

# Create a new record in Route 53 for the domain
resource "aws_route53_record" "cloudfront_alias_domain" {
  for_each = toset(local.aliases)

  zone_id = data.aws_route53_zone.custom_domain_zone.zone_id
  name    = each.key
  type    = "A"

  alias {
    name                   = module.tf_next.cloudfront_domain_name
    zone_id                = module.tf_next.cloudfront_hosted_zone_id
    evaluate_target_health = false
  }
}

##########
# SSL Cert
##########

# Creates a free SSL certificate for CloudFront distribution
# For more options (e.g. multiple domains) see:
# https://registry.terraform.io/modules/terraform-aws-modules/acm/aws/
module "cloudfront_cert" {
  source  = "terraform-aws-modules/acm/aws"
  version = "~> 3.0"

  domain_name               = var.custom_domain
  zone_id                   = data.aws_route53_zone.custom_domain_zone.zone_id
  subject_alternative_names = slice(local.aliases, 1, length(local.aliases))

  wait_for_validation = true

  tags = {
    Name = "CloudFront ${var.custom_domain}"
  }

  # CloudFront works only with certs stored in us-east-1
  providers = {
    aws = aws.global_region
  }
}

##########################
# Terraform Next.js Module
##########################

module "tf_next" {
  source  = "milliHQ/next-js/aws"
  version = "1.0.0-canary.4"

  cloudfront_aliases             = local.aliases
  cloudfront_acm_certificate_arn = module.cloudfront_cert.acm_certificate_arn

  deployment_name = "atomic-deployments"

  enable_multiple_deployments      = true
  multiple_deployments_base_domain = "*.${var.custom_domain}"

  providers = {
    aws.global_region = aws.global_region
  }
}

#########
# Outputs
#########

output "api_endpoint" {
  value = module.tf_next.api_endpoint
}

output "api_endpoint_access_policy_arn" {
  value = module.tf_next.api_endpoint_access_policy_arn
}

To create the resources in your AWS account, run the following commands:

terraform init    # Only needed on the first time running Terraform

terraform plan    # (Optional) See what resources Terraform will create
terraform apply   # Create the resources in your AWS account

> Apply complete!
>
> Outputs:
>
> api_endpoint = "https://<api-id>.execute-api.us-west-2.amazonaws.com"
> api_endpoint_access_policy_arn = "arn:aws:iam::123456789012:policy/access-api"

The api_endpoint is later used by the CLI tool to create new deployments.

With the api_endpoint_access_policy_arn AWS policy you can create new users (and assign that policy) that only can use the CLI tool tf-next but cannot access other resources inside of your AWS account.

After the successful deployment your Next.js app is publicly available at the CloudFront subdomain from the cloudfront_domain_name output.

Deploy a Next.js App

For building and deploying Next.js apps to the system we created a CLI tool called tf-next.

It is a npm package that can be installed with:

npm i -g tf-next@canary

Next, we need to build the Next.js so that it can run in a serverless environment (with AWS Lambda). This is archived by running tf-next build in the same directory where your Next.js app is located (Right where your package.json or next.config.js files are located):

tf-next build

> All serverless functions created in: 20.791ms
> 1752924 total bytes
> Build successful!

Now deploy the Next.js app by running tf-next deploy from the same directory. The deploy command communicates through a secured (and authenticated with your AWS credentials) API with the Terraform module.

To tell the command where to deploy the app, an additional --endpoint flag must be provided, which should use the value from the api_endpoint output from the terraform apply step:

tf-next deploy --endpoint https://<api-id>.execute-api.us-west-2.amazonaws.com

> Available at: https://3edade7a2bf7bb0343699af6b851bbfa.example.com/

The preview deployment can now be accessed by the displayed url.
To make the deployment available from a more readable url, you can use the tf-next alias subcommand:

tf-next alias set my-app.example.com 3edade7a2bf7bb0343699af6b851bbfa.example.com

> Available at: https://my-app.example.com/

For a full list of available commands that can be used with tf-next, check the command reference.

Examples

  • Atomic Deployments
    Each deployment gets a unique url from where it can be previewed.
  • Complete
    Complete example with SSR, API and static pages.
  • Static
    Example that uses static pages only (No SSR).
  • Next Image
    Images are optimized on the fly by AWS Lambda.
  • Existing CloudFront
    Use the module together with an existing CloudFront distribution that can be fully customized.
  • Custom Domain
    Use the module with your own domain from Route 53.

Requirements

Name Version
terraform >= 0.15
aws >= 4.8

Providers

Name Version
aws >= 4.8

Inputs

Name Description Type Default Required
cloudfront_acm_certificate_arn ACM certificate arn for custom_domain string null no
cloudfront_aliases Aliases for custom_domain list(string) [] no
cloudfront_cache_key_headers Header keys that should be used to calculate the cache key in CloudFront. list(string)
[
"Authorization"
]
no
cloudfront_create_distribution Controls whether the main CloudFront distribution should be created. bool true no
cloudfront_external_arn When using an external CloudFront distribution provide its arn. string null no
cloudfront_external_id When using an external CloudFront distribution provide its id. string null no
cloudfront_minimum_protocol_version The minimum version of the SSL protocol that you want CloudFront to use for HTTPS connections. One of SSLv3, TLSv1, TLSv1_2016, TLSv1.1_2016, TLSv1.2_2018 or TLSv1.2_2019. string "TLSv1" no
cloudfront_origin_request_policy Id of a custom request policy that overrides the default policy (AllViewer). Can be custom or managed. string null no
cloudfront_price_class Price class for the CloudFront distributions (main & proxy config). One of PriceClass_All, PriceClass_200, PriceClass_100. string "PriceClass_100" no
cloudfront_response_headers_policy Id of a response headers policy. Can be custom or managed. Default is empty. string null no
cloudfront_webacl_id An optional webacl2 arn or webacl id to associate with the cloudfront distribution string null no
create_image_optimization Controls whether resources for image optimization support should be created or not. bool true no
debug_use_local_packages Use locally built packages rather than download them from npm. bool false no
deployment_name Identifier for the deployment group (only lowercase alphanumeric characters and hyphens are allowed). string "tf-next" no
enable_multiple_deployments Controls whether it should be possible to run multiple deployments in parallel (requires multiple_deployments_base_domain). bool false no
image_optimization_lambda_memory_size Amount of memory in MB the worker Lambda Function for image optimization can use. Valid value between 128 MB to 10,240 MB, in 1 MB increments. number 2048 no
lambda_attach_policy_json Whether to deploy additional lambda JSON policies. If false, lambda_policy_json will not be attached to the lambda function. (Necessary since policy strings are only known after apply when using Terraforms data.aws_iam_policy_document) bool false no
lambda_attach_to_vpc Set to true if the Lambda functions should be attached to a VPC. Use this setting if VPC resources should be accessed by the Lambda functions. When setting this to true, use vpc_security_group_ids and vpc_subnet_ids to specify the VPC networking. Note that attaching to a VPC would introduce a delay on to cold starts bool false no
lambda_policy_json Additional policy document as JSON to attach to the Lambda Function role string null no
lambda_role_permissions_boundary ARN of IAM policy that scopes aws_iam_role access for the lambda string null no
multiple_deployments_base_domain Default wildcard domain where new deployments should be available. Should be in the form of *.example.com. string null no
tags Tag metadata to label AWS resources that support tags. map(string) {} no
tags_s3_bucket Tag metadata to label AWS S3 buckets. Overrides tags with the same name in input variable tags. map(string) {} no
vpc_security_group_ids The list of Security Group IDs to be used by the Lambda functions. lambda_attach_to_vpc should be set to true for these to be applied. list(string) [] no
vpc_subnet_ids The list of VPC subnet IDs to attach the Lambda functions. lambda_attach_to_vpc should be set to true for these to be applied. list(string) [] no

Outputs

Name Description
api_endpoint API endpoint that is used by the CLI.
api_endpoint_access_policy_arn ARN of the policy that grants access to the API endpoint.
cloudfront_custom_error_response Preconfigured custom error response the CloudFront distribution should use.
cloudfront_default_cache_behavior Preconfigured default cache behavior the CloudFront distribution should use.
cloudfront_default_root_object Preconfigured root object the CloudFront distribution should use.
cloudfront_domain_name Domain of the main CloudFront distribution (When created).
cloudfront_hosted_zone_id Zone id of the main CloudFront distribution (When created).
cloudfront_ordered_cache_behaviors Preconfigured ordered cache behaviors the CloudFront distribution should use.
cloudfront_origins Preconfigured origins the CloudFront distribution should use.
upload_bucket_id n/a

Known issues

Under the hood this module uses a lot of Vercel's build pipeline. So issues that exist on Vercel are likely to occur on this project too.

  • Stack deletion (terraform destroy) fails on first run (terraform-provider-aws#1721)

    This is intentional because we cannot delete a Lambda@Edge function (Used by proxy module) in a synchronous way. It can take up to an hour for AWS to unbind a Lambda@Edge function from it's CloudFront distribution even when the distribution is already destroyed.

    Workaround:

    After running the initial terraform destroy command (that failed) wait ~1 hour and run the command again. This time it should run successfully and delete the rest of the stack.

  • Initial apply fails with error message Error: error creating Lambda Event Source Mapping (#138)

    There is some race condition when the permissions are created for the static deployment Lambda. This should only happen on the first deployment.

    Workaround:

    You should be able to runterraform apply again and the stack creation would proceed without this error.

Contributing

Contributions are welcome!
If you want to improve this module, please take a look at our contributing guidelines to get started.

About

This project is maintained by milliVolt infrastructure.
We build custom infrastructure solutions for any cloud provider.

License

Apache-2.0 - see LICENSE for details.

Note: All sample projects in examples/* are licensed as MIT to comply with the official Next.js examples.

terraform-aws-next-js's People

Contributors

andybitz avatar aquanow-jeffen avatar b6pzeusbc54tvhw5jgpyw8pwz2x6gs avatar chamilad avatar christophebeling avatar dav-is avatar dependabot[bot] avatar flybayer avatar github-actions[bot] avatar ijjk avatar jamesla avatar jeantil avatar ky0615 avatar lcswillems avatar leo avatar lucleray avatar maael avatar mohalsherif avatar nimish-gupta avatar nkzawa avatar ofhouse avatar rdev avatar shuding avatar styfle avatar thasophearak avatar thecallsign avatar timer avatar timneutkens avatar tootallnate avatar underyx avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

terraform-aws-next-js's Issues

terraform plan/apply: Error: failed to execute "bash"

I'm getting this while executing terraform plan or terraform apply:

$ terraform plan

Error: failed to execute "bash": .terraform/modules/tf_next.next_image.lambda_content/download.sh: line 7: wget: command not found
Failed to download https://cdn.jsdelivr.net/npm/@dealmore/[email protected]/dist.zip

Error: failed to execute "bash": .terraform/modules/tf_next.statics_deploy.lambda_content/download.sh: line 7: wget: command not found
Failed to download https://cdn.jsdelivr.net/npm/@dealmore/[email protected]/dist.zip

Error: failed to execute "bash": .terraform/modules/tf_next.proxy.proxy_package/download.sh: line 7: wget: command not found
Failed to download https://cdn.jsdelivr.net/npm/@dealmore/[email protected]/dist.zip

The referenced zips appear to be fine when accessing via HTTP.

tf-next build and terraform init completed with no issues.

Any ideas?

--
Node 14.16
macOS 11.2.3

Add support for custom headers

Idea

CloudFront Functions

It seems like CloudFront functions includes both, the full request and the full response object.
So the idea from Original Idea 2.0 could be picked up, but instead of Lambda or API-Gateway make the header change, the CloudFront function does it for S3 and API Gateway response:

Request             -->     Response
x-tfn-foo: "bar"            foo: "bar"

Original Idea 2.0

Lambda (API-Gateway)

The proxy adds special headers in the form of x-tfn-<header-name> to the request that are then converted by node-bridge component to response headers.

Request             -->     Response
x-tfn-foo: "bar"            foo: "bar"

For security reasons proxy component has to ensure that no viewer headers with x-tfn-prefix are passed to the origin.

Static pages (S3)

The request of static pages from S3 should move from CloudFront to Lambda@Edge.
So instead of CloudFront requesting the resource, it is then requested inside the Proxy through aws-sdk and returned to the viewer via an generated response.
This may increase the cost for Lambda@Edge when requesting static pages from S3, but it's the most efficient way to modify the response from S3.

When requesting it from Lambda@Edge the max payload size can be 1mb.
While it is sufficient for most HTML pages, this could cause problems with bigger static files, e.g. images.

Next.js reference: https://nextjs.org/docs/api-reference/next.config.js/headers


Original idea 1.0

This was the original idea to bring support for custom headers, but this does not work since Lambda@Edge does not allow to switch the behaviors in the middle of the request

This requires another Lambda@Edge which acts as afterburner for the CloudFront distribution.
So the response from the Next.js lambda or S3 bucket is modified by the Lambd@Edge and the required headers are added.
The modified request is then sent back to the client.

The uncool part is that this adds another layer to each request that is not cached by CloudFront:
Cloudfront -> Lambda@Edge (Proxy) -> Origin (Lambda or S3) -> Lambda@Edge (Afterburner) -> Client

A possible workaround would be that we create 2 Origin groups in CloudFront, One with the afterburner and one without.
Then the proxy decides (based on if headers should be added) if the default origin (no headers) or the afterburner origin (modifies headers) should be triggered.

How to adapt this module for Blitz.js?

Hi,

This module looks very interesting!!

I am using Blitz.js, a framework built on top of Next.js. Would it be easy for me to adapt this module to make it working with Blitz.js?

Thank you for your time!

How to customize cloudfront events

Hi, I'm new at terraform I want to ask if I can customize the cloudfront events. I'm gonna add basic http authentication. I found that terraform able to attach lambda function to cloudfront event like,

resource "aws_cloudfront_distribution" "example" {
  # ... other configuration ...

  # lambda_function_association is also supported by default_cache_behavior
  ordered_cache_behavior {
    # ... other configuration ...

    lambda_function_association {
      event_type   = "viewer-request"
      lambda_arn   = aws_lambda_function.example.qualified_arn
      include_body = false
    }
  }
}


How can I achieve this with this plugin ?

Documenting the internals/how this works (for Pulumi implementation)

This repo is really fantastic - thank you @ofhouse for your work!

I'm trying to understand the "small" picture - how things work under the hood - with the goal of implementing a version of this in Pulumi. This has been rather challenging due to my lack of familiarity with the Vercel stack and how it's used in this library. Vercel/Next.js documentations don't cover the internals well either - they are meant to be consumed by the users, not (new) developers. It would help me tremendously if there's some description of how this library works by integrating with the Vercel tools. Thanks!

Custom domain certificates are created in the wrong region

Having configured region = "us-west-2" for my app, I get the following error:

Error: error creating CloudFront Distribution: InvalidViewerCertificate: The specified SSL certificate doesn't exist, isn't in us-east-1 region, isn't valid, or doesn't include a valid certificate chain.
	status code: 400, request id: a8ae8b5a-ad66-45e4-ad90-dc9928eff55a

  on .terraform/modules/tf_next/modules/proxy/main.tf line 61, in resource "aws_cloudfront_distribution" "distribution":
  61: resource "aws_cloudfront_distribution" "distribution" {

Seems like the certificate creation process should always use us-east-1 regardless of provider region, as otherwise the cert cannot be used with CloudFront.

Add support for fallbacks

In #17 we had a failing test where a SSR lambda imiediatly renders the site instead of returning a fallback.
Vercel uses a fallback on this route.

See test/fixtures/00-shared-lambdas/probes.json for more information.

Create own Next.js runtime

Since Vercel pulled the trigger and moved (vercel/vercel#5640) their Next.js runtime into a private repository, we no longer have access to their builder and can use it in the future. 🙈

So we have to move on (like we already did with the bridge package) and develop our own runtime for doing this.

Required target for deployment

image

Hello, (I am new to terraform) i would like to know what is the most recommended target for this deployment?
Server or Serverless?

Remove provider proxy from proxy-config module

Follow up from #50.
For an easier upgrade path the internal proxy-config also uses the aws.global_region provider.

This means that all proxy config ressources (S3 bucket and Config Object in S3) are also created in us-east-1.
This should change with the v0.9.x release.

Idea here is to give

  • resource "aws_s3_bucket" "proxy_config"
  • resource "aws_s3_bucket_policy" "origin_access"
  • resource "aws_s3_bucket_object" "proxy_config"

a different identifier and remove the provider attribute from them.
So that they are recreated by Terraform in the correct region.

Add option to set the image optimizer version

Currently by default the image-optimizer uses a semver range (^10.0.5-beta) to determine which version of the lambda package to use.
Since this seems to cause trouble while applying, we should give users as a workaround the possibility to manually set version to a fixed one.

Error creating S3 bucket: TooManyBuckets

Hello,

I'm receiving an error when using your module,

Error: Error creating S3 bucket: TooManyBuckets: You have attempted to create more buckets than allowed
	status code: 400, request id: 89C5F67086889131, host id: wPdV165nEmLi92mhhOCI9nHNt+jUk+zjIFTMbv/Se08jx9y/YJa9S1VRigzKN/0h81bgiCXr8M4=



Error: Error creating S3 bucket: TooManyBuckets: You have attempted to create more buckets than allowed
	status code: 400, request id: 4B5FF648E1B79D5F, host id: lbtIJiUVxVAspB1El4JxvFOz4IXHc4hmnxVUy4OCFmibrrjkjnlEoqIS0DQJmsggJD+9wUOw3DE=



Error: Error creating S3 bucket: TooManyBuckets: You have attempted to create more buckets than allowed
	status code: 400, request id: 1DC100A1565DABF8, host id: dH5aVMqbDQE9MnVMXpK9RzMjGTuJPF7CJXoduXUVrgKDQjq3O8RN5zotxCWISlhLdQSzYAgRx/A=

My TF file is the following,

provider "aws" {
  version = "~> 3.0"
  region = "us-east-1"
}

module "lambdas" {
  source  = "dealmore/next-js/aws"

  deployment_name = "test-app-sandbox"
  create_domain_name_records = false
}

I've followed the following steps,

  1. terraform init
  2. yarn tf-next
  3. terraform plan
  4. terraform apply

Not sure if I'm doing something wrong and any help is appreciated! Thank you in advance!

Error: no matching Route53Zone found

Following the custom domain example, I get this error:

Error: no matching Route53Zone found

  on .terraform/modules/tf_next/main.tf line 240, in data "aws_route53_zone" "alias_domains":
 240: data "aws_route53_zone" "alias_domains" {

Note how this data block just picks up the first item from domain names, which is set to domain_names = [local.custom_domain] in the example, and thus has no trailing dot. Could that be the issue? Although updating to domain_names = ["${local.custom_domain}."] does not fix it.

`create` variable

many terraform module support create or create_xx variables to controls whether resource should be created. I think, one of the reasons for doing this is, it's hard to use count or for_each in module block.

terraform-aws-next-js also has create_domain_name_records and create_image_optimization. But no option to skip for other resources.

Do you have any plans to add a create variable that controls the creation of the entire resource? Or can I create a PR?

Support for Terraform 0.14

With the release of Terraform 0.14 we should add support for it.
We should also drop support for Terraform 0.12 as part of this.

Dynamic routes not working

Hello!

I'm seeing issues with dynamic pages and I just wanted to make sure that it's not something I'm doing incorrectly. Here's my tf-next build output,

image

You can see that I have a mix of dynamic routes, catch-all routes and optional catch-all routes.

Going to https://d1hjov1nk12qxr.cloudfront.net/products/one results in a 404, but going to https://d1hjov1nk12qxr.cloudfront.net/products/[pid] results in rendered HTML.

Here's a zip of my test repo,

nextjs-aws-demo.zip

Thanks for the help in advance,

Andrew

Update packaging for components

We currently rely on an internal tool called @dealmore/funny for building the components (proxy, static-deployment) that are deployed as lambdas.
Since we currently only use the packaging part of the tool, we should replace it with ncc-zip.

ToDo

  • Replace all occurrences of @dealmore/funny with ncc-zip
  • Republish all affected components
  • Release new Terraform module version with the updated components

terraform plan: Error: Invalid function argument

I'm getting this error running terraform plan

Any help?

Error: Invalid function argument

  on .terraform/modules/tf_next/main.tf line 4, in locals:
   4:   config_file          = jsondecode(file("${local.config_dir}/config.json"))
    |----------------
    | local.config_dir is "../.next-tf"

Invalid value for "path" parameter: no file exists at ../.next-tf/config.json;
this function works only with files that are distributed as part of the
configuration source code, so if this file will be created by a resource in
this configuration you must instead obtain this result from an attribute of
that resource.

s3-put bad request error

Hi, when i tried to run the command "terraform apply" in my project
with my windows 10 machine

Everything was ok, until this bug appeared

Error: Error running command './s3-put -r us-east-1 -T C:/apps/web/mynextapp/client/.next-tf/static-website-files.zip /next-tf-deploy-source20210331135505030700000001/static-website-files.zip': exit status 1. Output: '.' is not recognized as an internal or external command, operable program or batch file.

image
It created the 3 buckets successfully

image
It created both CDN distribution successfully

image
And it also created both functions, and other needed resources

That's the error i got:

Error: Error running command './s3-put -r us-east-1 -T C:/apps/web/mynextapp/client/.next-tf/static-website-files.zip /next-tf-deploy-source20210331135505030700000001/static-website-files.zip': exit status 1. Output: '.' is not recognized as an internal or external command, operable program or batch file.

s3-put fails with error 403

The s3-put bash script seems to fail to upload the static files:

Error: Error running command './s3-put -r us-east-1 -T /home/thecallsign/code/.next-tf/static-website-files.zip /next-tf-deploy-source20210118142043963400000001/static-website-files.zip': exit status 22. 
Output:   
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0 1618k    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
curl: (22) The requested URL returned error: 403 Forbidden

All the env vars are set correctly, and after a couple hours trying to figure out what was causing the issue I forked this repo to bypass s3-put completely and just use a terraform config block. Source

Things seems to be working. Is there a reason to be using the bash script instead?

Error: Call to function "merge" failed using cloudfront_origins

Using the input cloudfront_origins results in a terraform error:

Error: Error in function call

  on .terraform/modules/tf_next/main.tf line 173, in locals:
 173:   proxy_cloudfront_origins = var.cloudfront_origins != null ? merge(
 174:     local.next_image_cloudfront_origins,
 175:     var.cloudfront_origins
 176:   ) : local.next_image_cloudfront_origins
    |----------------
    | local.next_image_cloudfront_origins is list of object with 1 element
    | var.cloudfront_origins is list of object with 1 element

Call to function "merge" failed: arguments must be maps or objects, got "list
of object".

Triggering terraform resource updates when nextjs code changes

I have the following file main.tf which sits in the root of my repo and deploys my next app great via terraform cloud.

provider "aws" {
  region  = "us-east-1"
}

module "lambdas" {
  source  = "dealmore/next-js/aws"
  deployment_name          = "codecitizen"
  lambda_memory_size = 128
}

It deploys fine but I can't seem to figure out how to make it update the underlying terraform resources when my nextjs application code changes as the module doesn't seem to expose any triggers etc.

I did try hacking it by doing the following:

resource "null_resource" "hack" {
  triggers = {
    policy_sha1 = "${sha1(file("src/index.js"))}"
  }
}

module "lambdas" {
  source  = "dealmore/next-js/aws"
  deployment_name          = "codecitizen"
  lambda_memory_size = 128

  depends_on = [null_resource.hack]
}

but since the module still has terraform 12 support then it doesn't support module depends_on

When the nextjs code changes how can I get the module to pick up on the change and then update the underlying terraform resources?

Runtime: Use `server` target

Instead of the legacy experimental-serverless-trace we should switch to the server target for building and bundling the Next.js app beginning with the v10.0.9-canary.4 version of Next.js.

History

Default root object not being set on s3 cloudfront distribution

This doesn't set the default root object in cloud front which means that everything 404's unless you manually specify /index after the url.

This can be fixed by manually adding a default_root_object path of index in cloudfront once it has been deployed.

Ideally default_root_object = "index" should be configured on the cloudfront distribution here:

https://github.com/dealmore/terraform-aws-next-js/blob/2d88ce0fb9f93ff49eb43570d6d455e2a1a3ff2e/modules/proxy/main.tf#L62

Use sammy for e2e tests

For our AWS Lambda e2e tests we have now extracted the wrapper around AWS SAM CLI into a new package called sammy (@dealmore/sammy).
We should now remove the test utils from this package and use sammy instead.

Error: Invalid function argument - var.module_version is null

Following the custom domain example, I get this error:

Error: Invalid function argument

  on .terraform/modules/tf_next.next_image.lambda_content/main.tf line 10, in locals:
  10:   cdn_module_version = length(var.module_version) > 0 ? "@${var.module_version}" : ""
    |----------------
    | var.module_version is null

Invalid value for "value" parameter: argument must not be null.

Compatibility with Next 9.5+

  • Slug-Routes are broken for proxy module
  • Add e2e test to make sure that proxy is compatible with current next version

s3-put 400 bad request error

Hi, I am getting the following error when running terraform apply:

Error: Error running command './s3-put -r ca-central-1 -T /Users/a/Workspace/a/.next-tf/static-website-files.zip /next-tf-deploy-source20210329045608925600000002/static-website-files.zip': exit status 22. Output: unknown option '-mac'

curl: (22) The requested URL returned error: 400 Bad Request

I am running on macOS Sierra.

Thanks!

Possible to use in a Nx monorepo?

I have a Nx monorepo consisting of:

web/
├── apps/
│   ├── app1/
│   │   ├── pages/
│   │   ├── tsconfig.json
│   │   ├── next.config.json
│   │   └── .babelrc
│   ├── app2/
│   │   ├── pages/
│   │   ├── tsconfig.json
│   │   ├── next.config.json
│   │   └── .babelrc
├── libs/
│   ├── common/
│   │   ├── components/
│   │   ├── index.ts
├── workspace.json
├── nx.json
├── package.json
├── tools/
├── tsconfig.base.json
└── .eslintrc.json

app1 and app2 use common components exported from the common lib. Everything is in typescript.

Building and serving one of the apps with nx is done with nx commands instead of the next.js commands. E.g.: nx build app1 and nx serve app1.

It seems like this terraform module assumes there is a single next.js app without any external dependencies in a sibling folder and that there is a package.json in the next.js app folder instead of a single shared package.json at the monorepo root, which is how Nx structures things. There doesn't seem to be a way to force the module to use the nx build commands instead of the normal next.js build commands.

Is it possible to use this terraform module with my app structure? It's okay if it's only possible to deploy one of the next.js apps (e.g. app1) at a time.

Remove internal providers

Follow up from #49.

We currently define an internal provider alias for the aws provider to force the creation of some resources in us-east-1 region.
This behaviour is generally not recommended by Terraform and can cause trouble for users that work with different AWS account profiles.
e.g. you have a private (default) and work profile (alias), the internal provider would always use the default profile even when you explicitly initialize the aws provider with the alias profile.

Our initial approach here was to reduce configuration effort since the us-east-1 alias must be created and passed by the user into the module but we should follow the official recommendation from Terraform here and move it into userland.

Failure when creating Proxy CF

Hey, thank you for this module 👍

I can't deploy the app using steps described in tutorial (I also checked all examples). proxy.aws_cloudfront_distribution can't be created because referenced Origin Access Identity is invalid:

module.tf_next.module.proxy.aws_cloudfront_distribution.distribution: Creating...

Error: error creating CloudFront Distribution: AccessDenied: CloudFront cannot access the specified Amazon S3 bucket using the specified origin access identity.
        status code: 403, request id: 48abad2a-9f92-451f-bfbc-448e348450d0

I was checking latest commits and I've found this one ad7047c. I'm using eu-west-1 as my default provider, but I also have us-east-1 for global resources:

locals {
  aws_region  = "eu-west-1"
  aws_profile = "my-profile"
}

provider "aws" {
  region  = local.aws_region
  profile = local.aws_profile
}

// us-east-1 provider for global resources (.e.g Certificate Manager, Lambda@Edge)
provider "aws" {
  alias   = "ue1"
  region  = "us-east-1"
  profile = local.aws_profile
}

I pass this provider to the tf_next module:

module "tf_next" {
  source      = "dealmore/next-js/aws"
  next_tf_dir = "../.next-tf"

  domain_names                      = [local.domain]
  create_domain_name_records        = false
  cloudfront_viewer_certificate_arn = data.aws_acm_certificate.hawkker_com.arn

  providers = {
    // tried both with and without this line. Code completition suggests only `global_region`
    // aws               = aws
    aws.global_region = aws.ue1
  }

  tags = local.default_tags
}

Could it be related to this commit?

Sign requests to HTTP API Gateway

By design the HTTP API Gateway is currently exposed publicly.
It's not a major security flaw because it uses a random API Gateway subdomain that are hard to guess and accessing it would only give an attacker the benefit of bypassing CloudFront security rules (like DDOS protection).

Anyway since every other resource (like S3, Lambdas) are not accessible from the outside we should enforce this policy to the API Gateway too.

Idea: Sign requests in proxy component with IAM authorization: https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-access-control-iam.html

https://stackoverflow.com/a/54298963/831465

Use with external CloudFront resource

Currently the main CloudFront resource is created & managed by the module itself.
While this is great as a quick setup for new users, it can become difficult for advanced use cases to integrate the module into an existing setup.

So we should switch to a similar approach as we did on the image optimizer module:

  • Create a managed CloudFront distribution by default (Quick setup)
  • Prevent creation of CloudFront distribution and export all configuration (Cache & Origin Policies), so that it can be integrated into an existing CloudFront Distribution

ToDo

  • Rework Custom Domain Example to use external CloudFront distribution

tf-next build ignores build errors

When tf-next build stops with exit code 1 caused by build errors the error is not passed through and the main process exits with code 0.

next.js v10 images dosen't seem to work

Request URL: https://my.domain/_next/image?url=%2FImages%2FInvest_1.jpg&w=2048&q=75
Request Method: GET
Status Code: 404 
Remote Address: 143.204.215.108:443
Referrer Policy: strict-origin-when-cross-origin
age: 5842
cache-control: public,max-age=0,must-revalidate,s-maxage=31536000
content-encoding: gzip
content-type: text/html
date: Tue, 12 Jan 2021 10:05:59 GMT
etag: W/"a6a3ead7fe34e552f0cf9d3344ae209d"
last-modified: Sun, 03 Jan 2021 19:33:17 GMT
server: AmazonS3
vary: Accept-Encoding
via: 1.1 a4a46c5a6cdf81ec1d08cf6e63389765.cloudfront.net (CloudFront)
x-amz-cf-id: tP9pWfQhy--Y6LPpZVsTIvLtWMx9hw6rTAwMxCAYMreefFbvXKcylQ==
x-amz-cf-pop: FRA53-C1
x-cache: Error from cloudfront

Is this a known issue or did I do something wrong with my configuration?

Prisma client fails build

Build fails for projects using prisma client.

Type error: Module '"../../../node_modules/@prisma/client"' has no exported member 'MyModel'.

import { MyModel } from '@prisma/client';

Build failed:
NowBuildError: Command "yarn run build" exited with 1
at ChildProcess. (/Users/jeremygottfried/Development/sweepstakes-app/sweepstakes-next-app/node_modules/@vercel/build-utils/dist/index.js:29311:20)
at ChildProcess.emit (events.js:310:20)
at ChildProcess.EventEmitter.emit (domain.js:482:12)
at maybeClose (internal/child_process.js:1021:16)
at Process.ChildProcess._handle.onexit (internal/child_process.js:286:5) {
hideStackTrace: true,
code: 'BUILD_UTILS_SPAWN_1',
link: undefined,
action: undefined
}

NEXT_NO_ROUTES_MANIFEST error during `npm run tf-next`

I tried following the setup guide but my build failed:

package.json excerpts:

{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "tf-next": "tf-next build"
  },
...
  "dependencies": {
    "next": "^10.0.6",
    "react": "17.0.1",
  }
...
}

Running $ npm run tf-next produces this output:

> [email protected] tf-next /Users/sebastian/development/app
> tf-next build

Installing dependencies...
yarn install v1.22.4
warning package-lock.json found. Your project contains lock files generated by tools other than Yarn. It is advised not to mix package managers in order to avoid resolution inconsistencies caused by unsynchronized lock files. To clear this warning, remove package-lock.json.
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...
success Saved lockfile.
✨  Done in 12.19s.
Running "yarn run build"
yarn run v1.22.4
$ next build
Loaded env from /private/var/folders/k0/41_ltq_d1v571z67g9bjb2h00000gn/T/tmp-75327-inHXi38AbvJA/.env.local
info  - Creating an optimized production build
info  - Compiled successfully
info  - Collecting page data
info  - Generating static pages (6/6)
info  - Finalizing page optimization

Page                                Size     First Load JS
┌ ○ /                               806 B          68.4 kB
├   /_app                           0 B            62.7 kB
├ ○ /404                            2.77 kB        65.5 kB
├ λ /api/s3-upload-token            0 B            62.7 kB
├ ○ /checkout                   5.18 kB        72.8 kB
├ ○ /reading                    95 kB           163 kB
├   └ css/4f6d1999152a411e8da4.css  135 B
└ ○ /review                     1.47 kB        69.1 kB
+ First Load JS shared by all       62.7 kB
  ├ chunks/commons.2ac29a.js        13.3 kB
  ├ chunks/framework.9d5241.js      41.8 kB
  ├ chunks/main.3ff250.js           6.32 kB
  ├ chunks/pages/_app.ce9584.js     545 B
  ├ chunks/webpack.50bee0.js        751 B
  └ css/a0d15b181925e64a7258.css    2.7 kB

λ  (Lambda)  server-side renders at runtime (uses getInitialProps or getServerSideProps)
○  (Static)  automatically rendered as static HTML (uses no initial props)
●  (SSG)     automatically generated as static HTML + JSON (uses getStaticProps)
   (ISR)     incremental static regeneration (uses revalidate in getStaticProps)

✨  Done in 20.38s.
Build failed:
NowBuildError: A "routes-manifest.json" couldn't be found. This is normally caused by a misconfiguration in your project.
Please check the following, and reach out to support if you cannot resolve the problem:
  1. If present, be sure your `build` script in "package.json" calls `next build`.  2. Navigate to your project's settings in the Vercel dashboard, and verify that the "Build Command" is not overridden, or that it calls `next build`.  3. Navigate to your project's settings in the Vercel dashboard, and verify that the "Output Directory" is not overridden. Note that `next export` does **not** require you change this setting, even if you customize the `next export` output directory.
    at Object.getRoutesManifest (/Users/sebastian/development/app/node_modules/@dealmore/next-tf/dist/index.js:37244:15)
    at async Object.build (/Users/sebastian/development/app/node_modules/@dealmore/next-tf/dist/index.js:35211:28)
    at async Object.buildCommand [as default] (/Users/sebastian/development/app/node_modules/tf-next/dist/commands/build.js:121:29) {
  hideStackTrace: true,
  code: 'NEXT_NO_ROUTES_MANIFEST',
  link: 'https://err.sh/vercel/vercel/now-next-routes-manifest',
  action: undefined
}
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] tf-next: `tf-next build`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] tf-next script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/sebastian/.npm/_logs/2021-02-14T00_24_15_009Z-debug.log

Other local versions:

$ npm -v
6.14.5
$ node -v
v12.18.1

Internal server error when setting multiple header values

Hi,

I've been debugging an issue we've been having using nextauth routes. The api routes (/api/auth/[...nextauth]) served by the nextauth library were all responding with an HTTP error 500. I've narrowed down the issue to nextauth setting two cookies, which in turn sets the header set-cookie to contain two values in the header object returned to API Gateway (see source).

AWS API Gateway v1 requires headers with multiple values to be set in multiValueHeaders key and rejects multi value headers that are set in headers.

I would write a patch myself but I am struggling to test the changes to the respective bridge function.

Please let me know if I have missed something.

Yarn workspace issue with node-fetch

Hey all,

Thanks for the great tool, when using this from a yarn workspace structured like the following:

root
 |  ...otherWorkspaces
 | web --> contains next, ...

When I run cd web && yarn tf-next --skipDownload and do a terraform apply I get the following error in the PAGE_LAMBDA:

2021-03-30T07:16:17.284Z	7dbd5f95-e964-4bd1-9ea0-ef2ddfd92413	ERROR	Unhandled error during request: Error: Cannot find module 'node-fetch'
Require stack:
- /var/task/node_modules/next/dist/next-server/server/node-polyfill-fetch.js

Is there some noHoist that I have to do to account for the workspace or me deploying from it?

EDIT: I've tried forcing node-fetch as a dep by using noHoist on it but now the error has moved on to the next dependency.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.