From d77d4b8f6643ff8f2ec510b355cc75dfffae900f Mon Sep 17 00:00:00 2001 From: Paul Zietsman Date: Wed, 27 Dec 2023 13:10:44 +0200 Subject: [PATCH] feat: Code needed for REST API CORS --- .github/contributing.md | 34 ++++++ .github/workflows/pr-title.yml | 54 ++++++++++ .github/workflows/pre-commit-and-tests.yml | 58 ++++++++++ .github/workflows/release.yml | 39 +++++++ .gitignore | 26 +++++ .pre-commit-config.yaml | 87 +++++++++++++++ .tfdocs-config.yml | 70 ++++++++++++ .tflint.hcl | 55 ++++++++++ README.md | 117 ++++++++++++++++++++- examples/.tfdocs-examples-config.yml | 76 +++++++++++++ examples/terraform-docs.py | 26 +++++ local-modules/HTTP/README.md | 37 +++++++ local-modules/HTTP/main.tf | 1 + local-modules/HTTP/outputs.tf | 0 local-modules/HTTP/variables.tf | 0 local-modules/HTTP/versions.tf | 10 ++ local-modules/REST/README.md | 50 +++++++++ local-modules/REST/main.tf | 54 ++++++++++ local-modules/REST/outputs.tf | 0 local-modules/REST/variables.tf | 34 ++++++ local-modules/REST/versions.tf | 10 ++ locals.tf | 17 +++ main.tf | 18 ++++ outputs.tf | 0 variables.tf | 79 ++++++++++++++ versions.tf | 14 +++ 26 files changed, 961 insertions(+), 5 deletions(-) create mode 100644 .github/contributing.md create mode 100644 .github/workflows/pr-title.yml create mode 100644 .github/workflows/pre-commit-and-tests.yml create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 .pre-commit-config.yaml create mode 100644 .tfdocs-config.yml create mode 100644 .tflint.hcl create mode 100644 examples/.tfdocs-examples-config.yml create mode 100644 examples/terraform-docs.py create mode 100644 local-modules/HTTP/README.md create mode 100644 local-modules/HTTP/main.tf create mode 100644 local-modules/HTTP/outputs.tf create mode 100644 local-modules/HTTP/variables.tf create mode 100644 local-modules/HTTP/versions.tf create mode 100644 local-modules/REST/README.md create mode 100644 local-modules/REST/main.tf create mode 100644 local-modules/REST/outputs.tf create mode 100644 local-modules/REST/variables.tf create mode 100644 local-modules/REST/versions.tf create mode 100644 locals.tf create mode 100644 main.tf create mode 100644 outputs.tf create mode 100644 variables.tf create mode 100644 versions.tf diff --git a/.github/contributing.md b/.github/contributing.md new file mode 100644 index 0000000..84575cd --- /dev/null +++ b/.github/contributing.md @@ -0,0 +1,34 @@ +# Contributing + +When contributing to this repository, please first discuss the change you wish to make via issue, +email, or any other method with the owners of this repository before making a change. + +Please note we have a code of conduct, please follow it in all your interactions with the project. + +## Pull Request Process + +1. Update the README.md with details of changes including example hcl blocks and [example files](./examples) if appropriate. +2. Run pre-commit hooks `pre-commit run -a`. +3. Once all outstanding comments and checklist items have been addressed, your contribution will be merged! Merged PRs will be included in the next release. The terraform-aws-vpc maintainers take care of updating the CHANGELOG as they merge. + +## Checklists for contributions + +- [ ] Add [semantics prefix](#semantic-pull-requests) to your PR or Commits (at least one of your commit groups) +- [ ] CI tests are passing +- [ ] README.md has been updated after any changes to variables and outputs. See https://github.com/cloudandthings/terraform-aws-cors/#doc-generation +- [ ] ~~Run pre-commit hooks `pre-commit run -a`~~ TODO + +## Semantic Pull Requests + +To generate changelog, Pull Requests or Commits must have semantic and must follow conventional specs below: + +- `feat:` for new features +- `fix:` for bug fixes +- `improvement:` for enhancements +- `docs:` for documentation and examples +- `refactor:` for code refactoring +- `test:` for tests +- `ci:` for CI purpose +- `chore:` for chores stuff + +The `chore` prefix skipped during changelog generation. It can be used for `chore: update changelog` commit message by example. diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml new file mode 100644 index 0000000..1640d12 --- /dev/null +++ b/.github/workflows/pr-title.yml @@ -0,0 +1,54 @@ +name: Validate PR title + +on: + pull_request_target: + types: + - opened + - edited + - synchronize + +jobs: + main: + name: 👀 Validate PR title + runs-on: ubuntu-latest + steps: + # Please look up the latest version from + # https://github.com/amannn/action-semantic-pull-request/releases + - uses: amannn/action-semantic-pull-request@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + # Configure which types are allowed. + # Default: https://github.com/commitizen/conventional-commit-types + types: | + fix + feat + docs + ci + chore + # Configure that a scope must always be provided. + requireScope: false + # Configure additional validation for the subject based on a regex. + # This example ensures the subject starts with a character. + subjectPattern: ^[A-Za-z].+$ + # If `subjectPattern` is configured, you can use this property to override + # the default error message that is shown when the pattern doesn't match. + # The variables `subject` and `title` can be used within the message. + subjectPatternError: | + The subject "{subject}" found in the pull request title "{title}" + didn't match the configured pattern. Please ensure that the subject + starts with a character. + # For work-in-progress PRs you can typically use draft pull requests + # from Github. However, private repositories on the free plan don't have + # this option and therefore this action allows you to opt-in to using the + # special "[WIP]" prefix to indicate this state. This will avoid the + # validation of the PR title and the pull request checks remain pending. + # Note that a second check will be reported if this is enabled. + wip: true + # See: https://github.com/amannn/action-semantic-pull-request#legacy-configuration + # If the PR only contains a single commit, the action will validate that + # it matches the configured pattern. + validateSingleCommit: true + # Related to `validateSingleCommit` you can opt-in to validate that the PR + # title matches a single commit to avoid confusion. + validateSingleCommitMatchesPrTitle: true diff --git a/.github/workflows/pre-commit-and-tests.yml b/.github/workflows/pre-commit-and-tests.yml new file mode 100644 index 0000000..2397467 --- /dev/null +++ b/.github/workflows/pre-commit-and-tests.yml @@ -0,0 +1,58 @@ +name: pre-commit & tests + +on: + workflow_dispatch: + pull_request: + branches: + - main + - master + - develop + +permissions: + id-token: write + contents: read + +defaults: + # Set shell for steps inside containers (default=sh) + run: + shell: bash + +jobs: + #-------------------------------------------------------------- + # PRE-COMMIT + #-------------------------------------------------------------- + pre_commit: + name: 💍 pre-commit + # For public repos use runs-on: ubuntu-latest + # For private repos use runs-on: self-hosted + runs-on: ubuntu-latest + container: bjorncloudandthings/terraform-aws-github:latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: 3.11 + - uses: hashicorp/setup-terraform@v2 + - name: Install requirements + run: | + python -m venv .venv + source .venv/bin/activate + pip install pre-commit + - name: pre-commit run + run: | + source .venv/bin/activate + if [[ "${{ env.GITHUB_REPOSITORY}}" == "cloudandthings/terraform-aws-template" ]]; then + export SKIP=no-vanilla-readme + fi + echo ...running developer pre-commit checks... + pre-commit run --config .pre-commit-config.yaml --all-files --show-diff-on-failure + if [[ -f .github/.pre-commit-config.yaml ]]; then + echo ...running remote pre-commit checks... + pip install checkov + pre-commit run --config .github/.pre-commit-config.yaml --all-files --show-diff-on-failure + fi + - name: cat pre-commit log + if: failure() + run: | + LOG_FILE=~/.cache/pre-commit/pre-commit.log + [ -f $LOG_FILE ] && cat $LOG_FILE diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..dc55d2a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,39 @@ +name: release + +# Overview ref: https://github.com/googleapis/release-please +# Configuration ref: https://github.com/google-github-actions/release-please-action + +on: + workflow_dispatch: + push: + branches: + - main + - master + - develop + +defaults: + # Set shell for steps inside containers (default=sh) + run: + shell: bash + +jobs: + release: + # For public repos use runs-on: ubuntu-latest + # For private repos use runs-on: self-hosted + runs-on: ubuntu-latest + outputs: + release_created: ${{ steps.release-please.outputs.release_created }} + tag_name: ${{ steps.release-please.outputs.tag_name }} + steps: + - name: release-please + uses: google-github-actions/release-please-action@v3 + id: release-please + with: + release-type: simple + # These bumps are honoured only if there is an + # initial tag of v0.1.0 . Create it manually if needed. + # + # BREAKING CHANGE only bumps semver minor if version < 1.0.0 + bump-minor-pre-major: true + # feat commits bump semver patch instead of minor if version < 1.0.0 + bump-patch-for-minor-pre-major: true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2748153 --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# Ignore Terraform state files +*.tfstate +*.tfstate.backup +*.tfplan + +# Ignore any .terraform directories +**/.terraform/* + +# Ignore .terraform.lock.hcl file +.terraform.lock.hcl + +# Ignore crash log files +crash.log + +# Ignore .tfvars files that contain sensitive information +*.tfvars + +# Ignore override files as they are usually used to override resources locally and don't need to be committed +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Ignore CLI configuration files +.terraformrc +terraform.rc diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..f37580c --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,87 @@ +# https://pre-commit.com/ +# More hooks available at: +# https://pre-commit.com/hooks.html + +# INSTALL ON MAC: +# brew install pre-commit + +# INSTALL ON WINDOWS: +# pip install pre-commit + +# INSTALL hooks for this workspace +# pre-commit install + +exclude: '^modules/external/.*$' + +repos: +# TERRAFORM + # If running tf-docs it must be run first to avoid race condition relating to version constraints. + # BUG: terraform_docs hook conflicts when used more than once. + # Workaround with Python + - repo: local + hooks: + - id: Terraform docs + name: Terraform docs + entry: python examples/terraform-docs.py + language: system + types: [python] + always_run: true + pass_filenames: false + + - repo: https://github.com/antonbabenko/pre-commit-terraform + rev: v1.74.1 + hooks: + # - id: terraform_docs + # args: + # - --args=--lockfile=false + # - --args=--config=.tfdocs-config.yml + - id: terraform_fmt + - id: terraform_validate + # [^/]+: Exclude root module. Validation is done via examples/basic + # see https://github.com/antonbabenko/pre-commit-terraform#terraform_validate + exclude: | + (?x)^( + tests/.*| + [^/]+| + )$ + - id: terraform_tflint + # Configure tflint by creating and updating the config file below. + args: ['--args=--config=__GIT_WORKING_DIR__/.tflint.hcl'] + #----------------------------------------- + # Moved to .github/.pre-commit-config.yaml + #----------------------------------------- + # - id: terraform_tfsec + # files: ^.*.tf$ + # # Configure tfsec by creating and updating the config file below. + # args: ['--args=--config-file=__GIT_WORKING_DIR__/.tfsec-config.yml'] + # - id: terraform_checkov + # # Configure checkov by creating and updating the config file below. + # args: ['--args=--config-file __GIT_WORKING_DIR__/.checkov-config.yml'] + +# PRECOMMIT - GENERAL + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: check-added-large-files + args: ['--maxkb=1024'] + - id: check-case-conflict + - id: check-json + exclude: tsconfig.json|devcontainer.json + # - id: pretty-format-json + # exclude: tsconfig.json|package-lock.json|package.json + - id: check-merge-conflict + - id: check-symlinks + - id: check-toml + - id: check-vcs-permalinks + - id: check-xml + - id: check-yaml + - id: detect-aws-credentials + args: ['--allow-missing-credentials'] + - id: detect-private-key + - id: end-of-file-fixer + - id: mixed-line-ending + args: ['--fix=lf'] + - id: no-commit-to-branch + args: ['--branch', 'develop', '--branch', 'main', '--branch', 'master'] + - id: requirements-txt-fixer + - id: trailing-whitespace diff --git a/.tfdocs-config.yml b/.tfdocs-config.yml new file mode 100644 index 0000000..2bf8f26 --- /dev/null +++ b/.tfdocs-config.yml @@ -0,0 +1,70 @@ +formatter: "markdown table" # this is required + +version: ">= 0.13.0, < 1.0.0" + +# header-from: main.tf +# footer-from: "" + +recursive: + enabled: true + path: local-modules + include: "**/*.tf" + +sections: + hide: [] + show: [] + + +content: |- + ## Terraform Documentation + {{ .Inputs }} + + ---- + {{ .Modules }} + + ---- + {{ .Outputs }} + + ---- + {{ .Providers }} + + ---- + {{ .Requirements }} + + ---- + {{ .Resources }} + + ---- + + +output: + file: README.md + mode: inject + template: |- + + {{ .Content }} + + + +output-values: + enabled: false + from: "" + +sort: + enabled: true + by: name + +settings: + anchor: true + color: true + default: true + description: false + escape: true + hide-empty: false + html: true + indent: 3 + lockfile: false + read-comments: true + required: true + sensitive: true + type: true diff --git a/.tflint.hcl b/.tflint.hcl new file mode 100644 index 0000000..f9d22bf --- /dev/null +++ b/.tflint.hcl @@ -0,0 +1,55 @@ +config { + # disabled_by_default = true +} + +rule "terraform_deprecated_interpolation" { + enabled = true +} + +rule "terraform_deprecated_index" { + enabled = true +} + +rule "terraform_unused_declarations" { + enabled = true +} + +rule "terraform_comment_syntax" { + enabled = true +} + +rule "terraform_documented_outputs" { + enabled = true +} + +rule "terraform_documented_variables" { + enabled = true +} + +rule "terraform_typed_variables" { + enabled = true +} + +rule "terraform_module_pinned_source" { + enabled = true +} + +rule "terraform_naming_convention" { + enabled = true +} + +rule "terraform_required_version" { + enabled = true +} + +rule "terraform_required_providers" { + enabled = true +} + +rule "terraform_standard_module_structure" { + enabled = true +} + +rule "terraform_workspace_remote" { + enabled = true +} diff --git a/README.md b/README.md index d5c38c8..80c3690 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,119 @@ # Terraform AWS CORS -The last module you'll need to setup CORS for AWS API Gateway. +A single module to setup CORS for both REST and HTTP APIs. ## Headers -The following commonly used headers are set by default: 'Content-Type', 'X-Amz-Date', 'Authorization', 'X-Api-Key', 'X-Amz-Security-Token'. These can be disbaled using the `disable_default_headers` variable. +The following commonly used headers are set by default: `Content-Type`, `X-Amz-Date`, `Authorization`, `X-Api-Key`, `X-Amz-Security-Token`. These can be disbaled using the `disable_default_headers` variable. Additional headers can be set using the `allowed_headers` variable. -## TF Docs +## Usage with REST API (v1) +### Basic usage +`REST` APIs only support a single origin, if more than one is supplied the module will throw a validation error. + +```hcl +module "basic_cors_rest" { + source = "cloudandthings/cors/aws" + version = "1.0.0" + + api_gateway_type = 'REST' + api = aws_api_gateway_rest_api.your_api.id + + resources = [ + aws_api_gateway_resource.a.id, + aws_api_gateway_resource.b.id + ] + + methods = [ + "GET", + "POST" + ] + + allowed_origins = ['*'] +} +``` + +### All features +```hcl +module "basic_cors_rest" { + source = "cloudandthings/cors/aws" + version = "1.0.0" + + api_gateway_type = 'REST' + api = aws_api_gateway_rest_api.your_api.id + + resources = [ + aws_api_gateway_resource.a.id, + aws_api_gateway_resource.b.id + ] + + methods = [ + "GET", + "POST", + "PUT", + "DELETE", + "HEAD", + "PATCH", + "OPTIONS" + ] + + allowed_origins = ['http://your_domain'] + + disable_default_headers = true + + allowed_headers = [ + "Authentication" + ] +} +``` + +## Usage with HTTP API (v2) + + -{{ .Content }} - \ No newline at end of file +## Terraform Documentation +### Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [allowed\_headers](#input\_allowed\_headers) | A list of additionally allowed headers. If you are using Access-Control-Allow-Headers as a wildcard, you must specify ['*'] explicitly. | `list(string)` | `[]` | no | +| [allowed\_origins](#input\_allowed\_origins) | A list of allowed origins. REST API only support a single origin. | `list(string)` | n/a | yes | +| [api](#input\_api) | ID of the API Gateway. | `string` | n/a | yes | +| [api\_gateway\_type](#input\_api\_gateway\_type) | The type of the API Gateway to create. Valid values are REST (v1) or HTTP (v2). Defaults to REST. | `string` | n/a | yes | +| [disable\_default\_headers](#input\_disable\_default\_headers) | Whether to disable the default headers. Defaults to false. | `bool` | `false` | no | +| [methods](#input\_methods) | List of permitted HTTP methods. OPTIONS is added by default. | `list(string)` | n/a | yes | +| [rest\_resources](#input\_rest\_resources) | List of the IDs of an aws\_api\_gateway\_resource resource. This must be set if api\_gateway\_type is REST. | `list(string)` | `[]` | no | + +---- +### Modules + +| Name | Source | Version | +|------|--------|---------| +| [http](#module\_http) | ./local-modules/HTTP | n/a | +| [rest](#module\_rest) | ./local-modules/REST | n/a | + +---- +### Outputs + +No outputs. + +---- +### Providers + +No providers. + +---- +### Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 0.15.0 | +| [aws](#requirement\_aws) | >= 4.9 | +| [null](#requirement\_null) | >= 3.1 | + +---- +### Resources + +No resources. + +---- + diff --git a/examples/.tfdocs-examples-config.yml b/examples/.tfdocs-examples-config.yml new file mode 100644 index 0000000..97ca2e8 --- /dev/null +++ b/examples/.tfdocs-examples-config.yml @@ -0,0 +1,76 @@ +formatter: "markdown table" # this is required + +version: ">= 0.13.0, < 1.0.0" + +header-from: main.tf +footer-from: "" + +# recursive: +# enabled: false +# path: modules + +sections: + hide: [] + show: [] + +content: |- + ---- + ## main.tf + ```hcl + {{ include "main.tf" }} + ``` + ---- + + ## Documentation + + ---- + {{ .Inputs }} + + ---- + {{ .Modules }} + + ---- + {{ .Outputs }} + + ---- + {{ .Providers }} + + ---- + {{ .Requirements }} + + ---- + {{ .Resources }} + + ---- + + +output: + file: README.md + mode: inject + template: |- + + {{ .Content }} + + +output-values: + enabled: false + from: "" + +sort: + enabled: true + by: name + +settings: + anchor: true + color: true + default: true + description: false + escape: true + hide-empty: false + html: true + indent: 3 + lockfile: false + read-comments: true + required: true + sensitive: true + type: true diff --git a/examples/terraform-docs.py b/examples/terraform-docs.py new file mode 100644 index 0000000..27ae05f --- /dev/null +++ b/examples/terraform-docs.py @@ -0,0 +1,26 @@ +import subprocess +import os + +root_module_dir = os.getcwd() + +r = subprocess.run( + ["terraform-docs", root_module_dir, "-c", ".tfdocs-config.yml"], + capture_output=True, +) +print(r.stderr.decode()) +print(r.stdout.decode()) +if r.returncode != 0: + exit(r.returncode) + +examples_dir = os.path.join(os.getcwd(), "examples") + +example_dirs = [f.path for f in os.scandir(examples_dir) if f.is_dir()] +for example_dir in example_dirs: + r = subprocess.run( + ["terraform-docs", example_dir, "-c", "examples/.tfdocs-examples-config.yml"], + capture_output=True, + ) + print(r.stderr.decode()) + print(r.stdout.decode()) + if r.returncode != 0: + exit(r.returncode) diff --git a/local-modules/HTTP/README.md b/local-modules/HTTP/README.md new file mode 100644 index 0000000..fc6b0bb --- /dev/null +++ b/local-modules/HTTP/README.md @@ -0,0 +1,37 @@ +# Local Module for the HTTP API CORS setup + +## Terraform Documentation +### Inputs + +No inputs. + +---- +### Modules + +No modules. + +---- +### Outputs + +No outputs. + +---- +### Providers + +No providers. + +---- +### Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 0.15.0 | +| [aws](#requirement\_aws) | >= 4.9 | + +---- +### Resources + +No resources. + +---- + diff --git a/local-modules/HTTP/main.tf b/local-modules/HTTP/main.tf new file mode 100644 index 0000000..3bc873c --- /dev/null +++ b/local-modules/HTTP/main.tf @@ -0,0 +1 @@ +# create a terraform module to set cors on an http (v2) api gateway diff --git a/local-modules/HTTP/outputs.tf b/local-modules/HTTP/outputs.tf new file mode 100644 index 0000000..e69de29 diff --git a/local-modules/HTTP/variables.tf b/local-modules/HTTP/variables.tf new file mode 100644 index 0000000..e69de29 diff --git a/local-modules/HTTP/versions.tf b/local-modules/HTTP/versions.tf new file mode 100644 index 0000000..1a2e74d --- /dev/null +++ b/local-modules/HTTP/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 0.15.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.9" + } + } +} diff --git a/local-modules/REST/README.md b/local-modules/REST/README.md new file mode 100644 index 0000000..472d38e --- /dev/null +++ b/local-modules/REST/README.md @@ -0,0 +1,50 @@ +# Local Module for the REST API CORS setup + +## Terraform Documentation +### Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [api](#input\_api) | ID of the API Gateway. | `string` | n/a | yes | +| [headers](#input\_headers) | Concatenated list of allowed headers. | `string` | n/a | yes | +| [methods](#input\_methods) | Concatenated list of allowed methods. | `string` | n/a | yes | +| [origin](#input\_origin) | A list of allowed origins. Defaults to '*'. | `list(string)` | n/a | yes | +| [resources](#input\_resources) | List of the IDs of an aws\_api\_gateway\_resource resource. This must be set if api\_gateway\_type is REST. | `list(string)` | n/a | yes | + +---- +### Modules + +No modules. + +---- +### Outputs + +No outputs. + +---- +### Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 4.9 | + +---- +### Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 0.15.0 | +| [aws](#requirement\_aws) | >= 4.9 | + +---- +### Resources + +| Name | Type | +|------|------| +| [aws_api_gateway_integration.cors_integration](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_integration) | resource | +| [aws_api_gateway_integration_response.cors_integration_response](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_integration_response) | resource | +| [aws_api_gateway_method.cors_method](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_method) | resource | +| [aws_api_gateway_method_response.cors_method_response](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_method_response) | resource | + +---- + diff --git a/local-modules/REST/main.tf b/local-modules/REST/main.tf new file mode 100644 index 0000000..a3eff9c --- /dev/null +++ b/local-modules/REST/main.tf @@ -0,0 +1,54 @@ +resource "aws_api_gateway_method" "cors_method" { + for_each = toset(var.resources) + rest_api_id = var.api + resource_id = each.value + http_method = "OPTIONS" + authorization = "NONE" +} + +resource "aws_api_gateway_integration" "cors_integration" { + for_each = toset(var.resources) + rest_api_id = var.api + resource_id = each.value + http_method = aws_api_gateway_method.cors_method[each.key].http_method + type = "MOCK" + + request_templates = { + "application/json" = < 0 + error_message = "At least one REST resource must be specified." + } +} diff --git a/local-modules/REST/versions.tf b/local-modules/REST/versions.tf new file mode 100644 index 0000000..1a2e74d --- /dev/null +++ b/local-modules/REST/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 0.15.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.9" + } + } +} diff --git a/locals.tf b/locals.tf new file mode 100644 index 0000000..21b013b --- /dev/null +++ b/locals.tf @@ -0,0 +1,17 @@ +locals { + + options_method = "OPTIONS" + + default_headers = var.disable_default_headers ? [] : [ + "Content-Type", + "X-Amz-Date", + "Authorization", + "X-Api-Key", + "X-Amz-Security-Token" + ] + all_headers_list = distinct(concat(var.allowed_headers, local.default_headers)) + all_headers = join(",", local.all_headers_list) + + methods_list = distinct(concat(var.methods, list(local.options_method))) + methods = join(",", local.methods_list) +} diff --git a/main.tf b/main.tf new file mode 100644 index 0000000..f2abe4d --- /dev/null +++ b/main.tf @@ -0,0 +1,18 @@ +module "rest" { + + for_each = var.api_gateway_type == "REST" ? toset([var.api_gateway_type]) : toset([]) + + source = "./local-modules/REST" + + api = var.api + origin = var.allowed_origins + methods = local.methods + headers = local.all_headers + resources = var.rest_resources +} + +module "http" { + for_each = var.api_gateway_type == "HTTP" ? toset([var.api_gateway_type]) : toset([]) + + source = "./local-modules/HTTP" +} diff --git a/outputs.tf b/outputs.tf new file mode 100644 index 0000000..e69de29 diff --git a/variables.tf b/variables.tf new file mode 100644 index 0000000..792f96a --- /dev/null +++ b/variables.tf @@ -0,0 +1,79 @@ +variable "api" { + description = "ID of the API Gateway." + type = string +} + +variable "api_gateway_type" { + description = "The type of the API Gateway to create. Valid values are REST (v1) or HTTP (v2). Defaults to REST." + type = string + + validation { + condition = contains(["REST", "HTTP"], var.api_gateway_type) + error_message = "api_gateway_type must be either REST or HTTP." + } +} + +variable "methods" { + description = "List of permitted HTTP methods. OPTIONS is added by default." + type = list(string) + + validation { + condition = alltrue([for method in var.methods : contains(["GET", "POST", "PUT", "DELETE", "HEAD", "PATCH", "OPTIONS"], var.method)]) + error_message = "Methods must be a list of valid HTTP methods." + } + + validation { + condition = length(var.methods) == 0 + error_message = "At least one method needs to be supplied." + } +} + +variable "allowed_origins" { + description = "A list of allowed origins. REST API only support a single origin." + type = list(string) + + validation { + condition = length(var.allowed_origins) >= 1 + error_message = "At least one allowed origin must be specified." + } +} + +variable "disable_default_headers" { + description = "Whether to disable the default headers. Defaults to false." + type = bool + + default = false + +} + +variable "allowed_headers" { + description = "A list of additionally allowed headers. If you are using Access-Control-Allow-Headers as a wildcard, you must specify ['*'] explicitly." + type = list(string) + + default = [] +} + +# ______ _____ _____ _____ +# | ___ \ ___/ ___|_ _| +# | |_/ / |__ \ `--. | | +# | /| __| `--. \ | | +# | |\ \| |___/\__/ / | | +# \_| \_\____/\____/ \_/ +# +# These variables are only used for REST (v1) API Gateways. +variable "rest_resources" { + description = "List of the IDs of an aws_api_gateway_resource resource. This must be set if api_gateway_type is REST." + type = list(string) + default = [] + + # This validation sits in the local REST module. +} + +# _ _ _____ ___________ +# | | | |_ _|_ _| ___ \ +# | |_| | | | | | | |_/ / +# | _ | | | | | | __/ +# | | | | | | | | | | +# \_| |_/ \_/ \_/ \_| +# +# These variables are only used for HTTP (v2) API Gateways. diff --git a/versions.tf b/versions.tf new file mode 100644 index 0000000..4708518 --- /dev/null +++ b/versions.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 0.15.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.9" + } + null = { + source = "hashicorp/null" + version = ">= 3.1" + } + } +}