1
0

initial commit

This commit is contained in:
xpk
2026-02-13 15:44:24 +08:00
parent 66be8224f4
commit 09ce4c881a
570 changed files with 61807 additions and 0 deletions
@@ -0,0 +1,77 @@
<!-- This readme file is generated with terraform-docs -->
## Requirements
| Name | Version |
|------|---------|
| terraform | >= 1.3.0 |
| aws | >= 5.0 |
## Providers
| Name | Version |
|------|---------|
| aws | >= 5.0 |
| random | n/a |
## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [aws_api_gateway_deployment.apigw-deployment](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_deployment) | resource |
| [aws_api_gateway_integration.api-integration](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_integration) | resource |
| [aws_api_gateway_integration_response.integration-response](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_integration_response) | resource |
| [aws_api_gateway_method.api-method](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_method) | resource |
| [aws_api_gateway_method_response.response_200](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_method_response) | resource |
| [aws_api_gateway_method_settings.apigw-method-settings](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_method_settings) | resource |
| [aws_api_gateway_resource.api-res](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_resource) | resource |
| [aws_api_gateway_rest_api.api](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_rest_api) | resource |
| [aws_api_gateway_rest_api_policy.api-policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_rest_api_policy) | resource |
| [aws_api_gateway_stage.apigw-stage](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_stage) | resource |
| [aws_api_gateway_vpc_link.api-vpc-link](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_vpc_link) | resource |
| [aws_cloudwatch_log_group.lambda-logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |
| [aws_cloudwatch_log_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |
| [aws_iam_role.lambda-exec-role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
| [aws_lambda_function.function](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function) | resource |
| [aws_lambda_permission.allow_api_gateway](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission) | resource |
| [aws_vpc_endpoint.apigw-vpcep](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_endpoint) | resource |
| [random_id.this](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) | resource |
| [aws_caller_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
| [aws_iam_policy_document.api-policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
| [aws_vpc.vpc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc) | data source |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| apigw-security-group-id | Security group id of apigateway | `string` | n/a | yes |
| apigw-subnet-ids | Subnet IDs of apigateway | `list(string)` | n/a | yes |
| apigw-type | Type of apigateway: private or regional | `string` | n/a | yes |
| apigw-vpc-id | VPC id of apigateway | `string` | n/a | yes |
| apigw-vpc-link-target-arns | Target arns for apigateway VPC link | `list(string)` | `[]` | no |
| cloudwatchlog-retention | Cloudwatch log group retention days | `number` | `14` | no |
| create-vpc-link | Set true to create vpc link for outbound access to specific targets | `bool` | n/a | yes |
| cwl-cmk-key-id | CMK arn for cloudwatch logs encryption | `string` | `null` | no |
| description | Description of apigateway | `string` | n/a | yes |
| lambda-archive-file | Path to lambda zip archive | `string` | n/a | yes |
| lambda-main-function-name | Main python file without the .py extension | `string` | n/a | yes |
| lambda-runtime-version | Lambda runtime version | `string` | `"python3.12"` | no |
| name | Name of apigateway | `string` | n/a | yes |
| resources | Apigateway resources (path\_part) | <pre>map(object({<br> method = string<br> authorization = string<br> integration_type = string<br> content_handling = string<br> }))</pre> | n/a | yes |
| stages | apigateway stages | <pre>map(object({<br> description = string<br> variables = map(string)<br> }))</pre> | n/a | yes |
## Outputs
| Name | Description |
|------|-------------|
| apigw-id | n/a |
| apigw-vpc-endpoints | n/a |
---
## Authorship
This module was developed by UPDATE_THIS.
@@ -0,0 +1,256 @@
resource "aws_api_gateway_rest_api" "api" {
name = var.name
description = var.description
dynamic "endpoint_configuration" {
for_each = [1]
content {
types = var.apigw-type == "private" ? ["PRIVATE"] : ["REGIONAL"]
vpc_endpoint_ids = var.apigw-type == "private" ? [aws_vpc_endpoint.apigw-vpcep[0].id] : null
}
}
}
# private endpoint for inbound access
# to connect to the private api gateway, provide the header x-apigw-api-id
# curl -IX GET -H 'x-apigw-api-id:<YOUR_API_ID>' https://<YOUR_VPCE_HOSTNAME>/<STAGE>
data "aws_region" "current" {}
resource "aws_vpc_endpoint" "apigw-vpcep" {
count = var.apigw-type == "private" ? 1 : 0
private_dns_enabled = false
security_group_ids = [var.apigw-security-group-id]
service_name = "com.amazonaws.${data.aws_region.current.name}.execute-api"
subnet_ids = var.apigw-subnet-ids
vpc_endpoint_type = "Interface"
vpc_id = var.apigw-vpc-id
}
# vpc link for outbound access
resource "aws_api_gateway_vpc_link" "api-vpc-link" {
count = var.create-vpc-link ? 1 : 0
name = "${var.name}-vpclink"
description = "VPC link for apigateway ${var.name}"
target_arns = var.apigw-vpc-link-target-arns
}
# Apigw resources, integration, method, responses
resource "aws_api_gateway_resource" "api-res" {
depends_on = [aws_api_gateway_rest_api_policy.api-policy]
for_each = var.resources
path_part = each.key
parent_id = aws_api_gateway_rest_api.api.root_resource_id
rest_api_id = aws_api_gateway_rest_api.api.id
}
resource "aws_api_gateway_method" "api-method" {
depends_on = [aws_api_gateway_resource.api-res]
for_each = var.resources
rest_api_id = aws_api_gateway_rest_api.api.id
resource_id = aws_api_gateway_resource.api-res[each.key].id
http_method = each.value["method"]
authorization = each.value["authorization"]
}
# non-proxy integration
resource "aws_api_gateway_integration" "api-integration" {
depends_on = [aws_api_gateway_method.api-method]
for_each = var.resources
rest_api_id = aws_api_gateway_rest_api.api.id
resource_id = aws_api_gateway_resource.api-res[each.key].id
http_method = aws_api_gateway_method.api-method[each.key].http_method
integration_http_method = each.value["method"]
type = each.value["integration-type"]
content_handling = each.value["content-handling"]
uri = aws_lambda_function.function.invoke_arn
}
resource "aws_api_gateway_method_response" "response_200" {
depends_on = [aws_api_gateway_method.api-method]
for_each = var.resources
rest_api_id = aws_api_gateway_rest_api.api.id
resource_id = aws_api_gateway_resource.api-res[each.key].id
http_method = aws_api_gateway_method.api-method[each.key].http_method
status_code = "200"
}
resource "aws_api_gateway_integration_response" "integration-response" {
depends_on = [aws_api_gateway_integration.api-integration]
for_each = var.resources
rest_api_id = aws_api_gateway_rest_api.api.id
resource_id = aws_api_gateway_resource.api-res[each.key].id
http_method = aws_api_gateway_method.api-method[each.key].http_method
status_code = aws_api_gateway_method_response.response_200[each.key].status_code
}
# apigw deployment and stage
resource "aws_api_gateway_deployment" "apigw-deployment" {
depends_on = [aws_api_gateway_integration.api-integration]
rest_api_id = aws_api_gateway_rest_api.api.id
triggers = {
redeployment = sha1(jsonencode(aws_api_gateway_rest_api.api.body))
}
lifecycle {
create_before_destroy = true
}
}
resource "aws_api_gateway_stage" "apigw-stage" {
for_each = var.stages
depends_on = [aws_api_gateway_rest_api_policy.api-policy, aws_cloudwatch_log_group.this]
deployment_id = aws_api_gateway_deployment.apigw-deployment.id
rest_api_id = aws_api_gateway_rest_api.api.id
stage_name = each.key
description = each.value["description"]
variables = each.value["variables"]
access_log_settings {
destination_arn = aws_cloudwatch_log_group.this[each.key].arn
# https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-logging.html
format = jsonencode({
"requestId" : "$context.requestId",
"extendedRequestId" : "$context.extendedRequestId",
"ip" : "$context.identity.sourceIp",
"caller" : "$context.identity.caller",
"user" : "$context.identity.user",
"requestTime" : "$context.requestTime",
"httpMethod" : "$context.httpMethod",
"resourcePath" : "$context.resourcePath",
"status" : "$context.status",
"protocol" : "$context.protocol",
"responseLength" : "$context.responseLength"
}
)
}
}
resource "aws_api_gateway_method_settings" "apigw-method-settings" {
depends_on = [aws_api_gateway_rest_api_policy.api-policy]
for_each = aws_api_gateway_stage.apigw-stage
rest_api_id = aws_api_gateway_rest_api.api.id
stage_name = each.value.stage_name
method_path = "*/*"
settings {
metrics_enabled = true
logging_level = "INFO"
}
}
# Cloudwatch log group path: API-Gateway-Execution-Logs_{rest-api-id}/{stage_name}
resource "aws_cloudwatch_log_group" "this" {
for_each = var.stages
name = "API-Gateway-Execution-Logs_${aws_api_gateway_rest_api.api.id}/${each.key}"
retention_in_days = var.cloudwatchlog-retention
kms_key_id = var.cwl-cmk-key-id
}
# lambda function
resource "aws_cloudwatch_log_group" "lambda-logs" {
name = "/aws/lambda/${var.name}-lambda-function"
retention_in_days = var.cloudwatchlog-retention
kms_key_id = var.cwl-cmk-key-id
}
resource "aws_lambda_function" "function" {
filename = var.lambda-archive-file
function_name = "${var.name}-lambda-function"
handler = "main.lambda_handler"
role = aws_iam_role.lambda-exec-role.arn
runtime = var.lambda-runtime-version
# source_code_hash = local.source_code_hash
}
resource "aws_lambda_permission" "allow_api_gateway" {
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.function.function_name
principal = "apigateway.amazonaws.com"
}
# Lambda execution role
data "aws_caller_identity" "this" {}
resource "random_id" "this" {
byte_length = 4
}
resource "aws_iam_role" "lambda-exec-role" {
name = "lambda-apigw-${var.name}-${random_id.this.dec}"
assume_role_policy = jsonencode(
{
"Version" : "2012-10-17",
"Statement" : [
{
"Effect" : "Allow",
"Principal" : {
"Service" : "lambda.amazonaws.com"
},
"Action" : "sts:AssumeRole"
}
]
}
)
}
resource "aws_iam_role_policy" "this" {
policy = jsonencode(
{
"Version" : "2012-10-17",
"Statement" : [
{
"Sid" : "AllowCreationOfCloudwatchLogGroup",
"Effect" : "Allow",
"Action" : "logs:CreateLogGroup",
"Resource" : "arn:aws:logs:ap-east-1:${data.aws_caller_identity.this.account_id}:*"
},
{
"Sid" : "AllowWritingToCloudwatchLogGroup",
"Effect" : "Allow",
"Action" : [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource" : [
"arn:aws:logs:ap-east-1:${data.aws_caller_identity.this.account_id}:log-group:/aws/lambda/*"
]
}
]
}
)
role = aws_iam_role.lambda-exec-role.id
name = "LambdaExecutionPolicy"
}
# apigateway policy
data "aws_iam_policy_document" "api-policy" {
statement {
effect = "Allow"
principals {
type = "AWS"
identifiers = ["*"]
}
actions = ["execute-api:Invoke"]
resources = [aws_api_gateway_rest_api.api.execution_arn]
condition {
test = "IpAddress"
variable = "aws:SourceIp"
values = [data.aws_vpc.vpc.cidr_block]
}
}
}
data "aws_vpc" "vpc" {
id = var.apigw-vpc-id
}
resource "aws_api_gateway_rest_api_policy" "api-policy" {
rest_api_id = aws_api_gateway_rest_api.api.id
policy = data.aws_iam_policy_document.api-policy.json
}
@@ -0,0 +1,7 @@
output "apigw-id" {
value = aws_api_gateway_rest_api.api.id
}
output "apigw-vpc-endpoints" {
value = try(aws_vpc_endpoint.apigw-vpcep.*.dns_entry[0].*.dns_name, null)
}
@@ -0,0 +1,90 @@
variable "name" {
type = string
description = "Name of apigateway"
}
variable "description" {
type = string
description = "Description of apigateway"
}
variable "apigw-type" {
type = string
description = "Type of apigateway: private or regional"
validation {
condition = can(regex("^(private|regional)$", var.apigw-type))
error_message = "Invalid apigw type, only allowed types are: 'private', 'regional'"
}
}
variable "resources" {
type = map(object({
method = string
authorization = string
integration_type = string
content_handling = string
}))
description = "Apigateway resources (path_part)"
}
variable "stages" {
type = map(object({
description = string
variables = map(string)
}))
description = "apigateway stages"
}
variable "lambda-archive-file" {
type = string
description = "Path to lambda zip archive"
}
variable "lambda-runtime-version" {
type = string
description = "Lambda runtime version"
default = "python3.12"
}
variable "lambda-main-function-name" {
type = string
description = "Main python file without the .py extension"
}
variable "apigw-vpc-id" {
type = string
description = "VPC id of apigateway"
}
variable "apigw-subnet-ids" {
type = list(string)
description = "Subnet IDs of apigateway"
}
variable "apigw-security-group-id" {
type = string
description = "Security group id of apigateway"
}
variable "create-vpc-link" {
type = bool
description = "Set true to create vpc link for outbound access to specific targets"
}
variable "apigw-vpc-link-target-arns" {
type = list(string)
description = "Target arns for apigateway VPC link"
default = []
}
variable "cloudwatchlog-retention" {
type = number
description = "Cloudwatch log group retention days"
default = 14
}
variable "cwl-cmk-key-id" {
type = string
description = "CMK arn for cloudwatch logs encryption"
default = null
}
@@ -0,0 +1,9 @@
terraform {
required_version = ">= 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0"
}
}
}
@@ -0,0 +1,33 @@
# EditorConfig is awesome: http://EditorConfig.org
# Uses editorconfig to maintain consistent coding styles
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
max_line_length = 80
trim_trailing_whitespace = true
[*.py]
indent_size = 4
[*.{tf,tfvars}]
indent_size = 2
indent_style = space
[*.md]
max_line_length = 0
trim_trailing_whitespace = false
[Makefile]
tab_width = 2
indent_style = tab
[COMMIT_EDITMSG]
max_line_length = 0
@@ -0,0 +1,21 @@
name: 'Lock Threads'
on:
schedule:
- cron: '50 1 * * *'
jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v4
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
issue-comment: >
I'm going to lock this issue because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues.
If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.
issue-inactive-days: '30'
pr-comment: >
I'm going to lock this pull request because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues.
If you have found a problem that seems related to this change, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.
pr-inactive-days: '30'
@@ -0,0 +1,52 @@
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.0.2
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 an uppercase character.
subjectPattern: ^[A-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 an uppercase 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
# When using "Squash and merge" on a PR with only one commit, GitHub
# will suggest using that commit message instead of the PR title for the
# merge commit, and it's easy to commit this by mistake. Enable this option
# to also validate the commit message for one commit PRs.
validateSingleCommit: false
@@ -0,0 +1,83 @@
name: Pre-Commit
on:
pull_request:
branches:
- main
- master
env:
TERRAFORM_DOCS_VERSION: v0.16.0
TFLINT_VERSION: v0.44.1
jobs:
collectInputs:
name: Collect workflow inputs
runs-on: ubuntu-latest
outputs:
directories: ${{ steps.dirs.outputs.directories }}
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Get root directories
id: dirs
uses: clowdhaus/terraform-composite-actions/directories@v1.8.3
preCommitMinVersions:
name: Min TF pre-commit
needs: collectInputs
runs-on: ubuntu-latest
strategy:
matrix:
directory: ${{ fromJson(needs.collectInputs.outputs.directories) }}
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Terraform min/max versions
id: minMax
uses: clowdhaus/terraform-min-max@v1.2.4
with:
directory: ${{ matrix.directory }}
- name: Pre-commit Terraform ${{ steps.minMax.outputs.minVersion }}
# Run only validate pre-commit check on min version supported
if: ${{ matrix.directory != '.' }}
uses: clowdhaus/terraform-composite-actions/pre-commit@v1.8.3
with:
terraform-version: ${{ steps.minMax.outputs.minVersion }}
tflint-version: ${{ env.TFLINT_VERSION }}
args: 'terraform_validate --color=always --show-diff-on-failure --files ${{ matrix.directory }}/*'
- name: Pre-commit Terraform ${{ steps.minMax.outputs.minVersion }}
# Run only validate pre-commit check on min version supported
if: ${{ matrix.directory == '.' }}
uses: clowdhaus/terraform-composite-actions/pre-commit@v1.8.3
with:
terraform-version: ${{ steps.minMax.outputs.minVersion }}
tflint-version: ${{ env.TFLINT_VERSION }}
args: 'terraform_validate --color=always --show-diff-on-failure --files $(ls *.tf)'
preCommitMaxVersion:
name: Max TF pre-commit
runs-on: ubuntu-latest
needs: collectInputs
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Terraform min/max versions
id: minMax
uses: clowdhaus/terraform-min-max@v1.2.4
- name: Pre-commit Terraform ${{ steps.minMax.outputs.maxVersion }}
uses: clowdhaus/terraform-composite-actions/pre-commit@v1.8.3
with:
terraform-version: ${{ steps.minMax.outputs.maxVersion }}
tflint-version: ${{ env.TFLINT_VERSION }}
terraform-docs-version: ${{ env.TERRAFORM_DOCS_VERSION }}
install-hcledit: true
@@ -0,0 +1,37 @@
name: Release
on:
workflow_dispatch:
push:
branches:
- main
- master
paths:
- '**/*.tpl'
- '**/*.py'
- '**/*.tf'
- '.github/workflows/release.yml'
jobs:
release:
name: Release
runs-on: ubuntu-latest
# Skip running release workflow on forks
if: github.repository_owner == 'terraform-aws-modules'
steps:
- name: Checkout
uses: actions/checkout@v3
with:
persist-credentials: false
fetch-depth: 0
- name: Release
uses: cycjimmy/semantic-release-action@v3
with:
semantic_version: 18.0.0
extra_plugins: |
@semantic-release/changelog@6.0.0
@semantic-release/git@10.0.0
conventional-changelog-conventionalcommits@4.6.3
env:
GITHUB_TOKEN: ${{ secrets.SEMANTIC_RELEASE_TOKEN }}
@@ -0,0 +1,32 @@
name: 'Mark or close stale issues and PRs'
on:
schedule:
- cron: '0 0 * * *'
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v6
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
# Staling issues and PR's
days-before-stale: 30
stale-issue-label: stale
stale-pr-label: stale
stale-issue-message: |
This issue has been automatically marked as stale because it has been open 30 days
with no activity. Remove stale label or comment or this issue will be closed in 10 days
stale-pr-message: |
This PR has been automatically marked as stale because it has been open 30 days
with no activity. Remove stale label or comment or this PR will be closed in 10 days
# Not stale if have this labels or part of milestone
exempt-issue-labels: bug,wip,on-hold
exempt-pr-labels: bug,wip,on-hold
exempt-all-milestones: true
# Close issue operations
# Label will be automatically removed if the issues are no longer closed nor locked.
days-before-close: 10
delete-branch: true
close-issue-message: This issue was automatically closed because of stale in 10 days
close-pr-message: This PR was automatically closed because of stale in 10 days
@@ -0,0 +1,32 @@
# Local .terraform directories
**/.terraform/*
# Terraform lockfile
.terraform.lock.hcl
# .tfstate files
*.tfstate
*.tfstate.*
# Crash log files
crash.log
# Exclude all .tfvars files, which are likely to contain sentitive data, such as
# password, private keys, and other secrets. These should not be part of version
# control as they are data points which are potentially sensitive and subject
# to change depending on the environment.
*.tfvars
# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json
# Ignore CLI configuration files
.terraformrc
terraform.rc
# Zip archive
*.zip
@@ -0,0 +1,30 @@
repos:
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.86.0
hooks:
- id: terraform_fmt
- id: terraform_wrapper_module_for_each
- id: terraform_validate
- id: terraform_docs
args:
- "--args=--lockfile=false"
- id: terraform_tflint
args:
- "--args=--only=terraform_deprecated_interpolation"
- "--args=--only=terraform_deprecated_index"
- "--args=--only=terraform_unused_declarations"
- "--args=--only=terraform_comment_syntax"
- "--args=--only=terraform_documented_outputs"
- "--args=--only=terraform_documented_variables"
- "--args=--only=terraform_typed_variables"
- "--args=--only=terraform_module_pinned_source"
- "--args=--only=terraform_naming_convention"
- "--args=--only=terraform_required_version"
- "--args=--only=terraform_required_providers"
- "--args=--only=terraform_standard_module_structure"
- "--args=--only=terraform_workspace_remote"
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: check-merge-conflict
- id: end-of-file-fixer
@@ -0,0 +1,45 @@
{
"branches": [
"main",
"master"
],
"ci": false,
"plugins": [
[
"@semantic-release/commit-analyzer",
{
"preset": "conventionalcommits"
}
],
[
"@semantic-release/release-notes-generator",
{
"preset": "conventionalcommits"
}
],
[
"@semantic-release/github",
{
"successComment": "This ${issue.pull_request ? 'PR is included' : 'issue has been resolved'} in version ${nextRelease.version} :tada:",
"labels": false,
"releasedLabels": false
}
],
[
"@semantic-release/changelog",
{
"changelogFile": "CHANGELOG.md",
"changelogTitle": "# Changelog\n\nAll notable changes to this project will be documented in this file."
}
],
[
"@semantic-release/git",
{
"assets": [
"CHANGELOG.md"
],
"message": "chore(release): version ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
]
]
}
@@ -0,0 +1,268 @@
# Changelog
All notable changes to this project will be documented in this file.
## [3.1.0](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v3.0.0...v3.1.0) (2024-02-12)
### Features
* Add variable create_route to control creation of route ([#98](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/98)) ([68ad2b1](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/commit/68ad2b183a4872e29890784290b5925beee1dd29))
## [3.0.0](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v2.2.2...v3.0.0) (2024-02-12)
### ⚠ BREAKING CHANGES
* Added the fail_on_warnings variable, bumped Terraform version to 1.0+ (#96)
### Features
* Added the fail_on_warnings variable, bumped Terraform version to 1.0+ ([#96](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/96)) ([26859db](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/commit/26859db0a5d651903469f6fabeda1e341867c45d))
### [2.2.2](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v2.2.1...v2.2.2) (2023-01-24)
### Bug Fixes
* Use a version for to avoid GitHub API rate limiting on CI workflows ([#85](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/85)) ([9229fa2](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/commit/9229fa2ec7f01f9187b0ddcae3a29929e8146417))
### [2.2.1](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v2.2.0...v2.2.1) (2022-10-27)
### Bug Fixes
* Update CI configuration files to use latest version ([#84](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/84)) ([13bd65e](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/commit/13bd65e192cb39f25a7e7bd281470dd1f7c073bd))
## [2.2.0](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v2.1.0...v2.2.0) (2022-09-12)
### Features
* Enable route_settings in default stage ([#80](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/80)) ([a13ef33](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/commit/a13ef3308fc0b4efef53f7fefb8bc7f8012a8336))
## [2.1.0](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v2.0.0...v2.1.0) (2022-08-15)
### Features
* Added API GW Authorizer IDs to outputs ([#76](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/76)) ([05da6b1](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/commit/05da6b19a0150d2d39db97968fa7214e9e4fa580))
## [2.0.0](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v1.9.0...v2.0.0) (2022-06-15)
### ⚠ BREAKING CHANGES
* Upgraded AWS provider to v4 everywhere (#74)
### Features
* Upgraded AWS provider to v4 everywhere ([#74](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/74)) ([2f8ad61](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/commit/2f8ad61a8668eb7559115f31e62c502ed2ea09c3))
## [1.9.0](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v1.8.0...v1.9.0) (2022-06-13)
### Features
* Add pass-through of ownership_verification_certificate_arn to domain_name ([#72](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/72)) ([5709873](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/commit/57098730dc48785f0836f9eb322008b054f91134))
## [1.8.0](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v1.7.0...v1.8.0) (2022-05-27)
### Features
* Added enable_simple_responses and authorizer_credentials_arn to authorizers ([#71](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/71)) ([1467b95](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/commit/1467b95c8309d82332857a9c2790ca25f2edc2ec))
## [1.7.0](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v1.6.1...v1.7.0) (2022-04-06)
### Features
* Added support for ttl in authorizer resource ([#68](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/68)) ([ce1faf9](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/commit/ce1faf96b191228bf891864f50d284078f54b0a0))
### [1.6.1](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v1.6.0...v1.6.1) (2022-04-01)
### Bug Fixes
* Add support for passing authorization_scopes on routes with JWT authorizer ([#67](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/67)) ([c2b8d6b](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/commit/c2b8d6bd8b11fa83ccd13f3ccc74844d328f59ad))
## [1.6.0](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v1.5.1...v1.6.0) (2022-03-25)
### Features
* Added support for Authorizers ([#64](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/64)) ([5cd32e0](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/commit/5cd32e0360c866c25d8a8c6300e638143335a665))
## [1.5.1](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v1.5.0...v1.5.1) (2021-11-22)
### Bug Fixes
* update CI/CD process to enable auto-release workflow ([#60](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/60)) ([55f5c1d](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/commit/55f5c1d74b4b61cf228f66c2d4a7f940e90f01b7))
<a name="v1.5.0"></a>
## [v1.5.0] - 2021-11-01
- feat: Add support for response_parameters in integrations ([#58](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/58))
<a name="v1.4.0"></a>
## [v1.4.0] - 2021-09-23
- Feat: Add create_before_destroy lifecycle to integration resources ([#55](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/55))
<a name="v1.3.0"></a>
## [v1.3.0] - 2021-09-16
- fix: Fixed output when create_api_domain_name is false ([#44](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/44))
- chore: Fixed GH Action with terraform-docs
<a name="v1.2.0"></a>
## [v1.2.0] - 2021-08-26
- feat: Add support for tls_config in integrations ([#51](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/51))
- fix: Fixed source_arn in example (fixes [#41](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/41))
<a name="v1.1.0"></a>
## [v1.1.0] - 2021-05-25
- chore: Remove check boxes that don't render properly in module doc ([#40](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/40))
- chore: update CI/CD to use stable `terraform-docs` release artifact and discoverable Apache2.0 license ([#38](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/38))
- chore: Updated versions in README ([#37](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/37))
<a name="v1.0.0"></a>
## [v1.0.0] - 2021-04-26
- feat: Shorten outputs (removing this_), added domain name to outputs ([#34](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/34))
<a name="v0.16.0"></a>
## [v0.16.0] - 2021-04-25
- feat: Add support for default_route_settings ([#32](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/32))
<a name="v0.15.0"></a>
## [v0.15.0] - 2021-04-24
- feat: Added support for mTLS and the ability to disable default endpoint ([#29](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/29))
- chore: update documentation and pin `terraform_docs` version to avoid future changes ([#28](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/28))
<a name="v0.14.0"></a>
## [v0.14.0] - 2021-03-16
- feat: Add support for OpenAPI definition ([#27](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/27))
<a name="v0.13.0"></a>
## [v0.13.0] - 2021-03-10
- feat: Added example of step-functions integration ([#26](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/26))
<a name="v0.12.0"></a>
## [v0.12.0] - 2021-03-09
- feat: Added VPC integration ([#25](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/25))
<a name="v0.11.0"></a>
## [v0.11.0] - 2021-03-06
- fix: Remove workaround related to passthrough_behavior for older providers ([#24](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/24))
- chore: align ci-cd static checks to use individual minimum Terraform versions ([#23](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/23))
- fix: bump min supported version due to types unsupported on current ([#22](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/22))
- chore: add ci-cd workflow for pre-commit checks ([#21](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/21))
<a name="v0.10.0"></a>
## [v0.10.0] - 2021-02-20
- chore: update documentation based on latest `terraform-docs` which includes module and resource sections ([#19](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/19))
<a name="v0.9.0"></a>
## [v0.9.0] - 2021-02-14
- feat: support authorization_type and authorizer_id on routes ([#17](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/17))
<a name="v0.8.0"></a>
## [v0.8.0] - 2021-01-14
- feat: New useful outputs to use with route53 ([#11](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/11))
<a name="v0.7.0"></a>
## [v0.7.0] - 2021-01-14
- chore: Updated example after lambda module has been updated ([#16](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/16))
- fix: default API Gateway integration method is POST ([#14](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/14))
<a name="v0.6.0"></a>
## [v0.6.0] - 2021-01-14
- fix: Integration URI is not always a lambda_arn ([#15](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/15))
<a name="v0.5.0"></a>
## [v0.5.0] - 2020-11-24
- fix: Updated supported Terraform versions
<a name="v0.4.0"></a>
## [v0.4.0] - 2020-09-08
- feat: add VPC Links resource to allow access to private resources ([#3](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/3))
<a name="v0.3.0"></a>
## [v0.3.0] - 2020-08-14
- feat: Updated version requirements for AWS provider v3 and Terraform 0.13
- Added route53 record into example (closes [#1](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/issues/1))
- Improved complete example
<a name="v0.2.0"></a>
## [v0.2.0] - 2020-06-07
- Added support for access logs
<a name="v0.1.0"></a>
## v0.1.0 - 2020-06-05
- Adding API Gateway module
[Unreleased]: https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v1.5.0...HEAD
[v1.5.0]: https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v1.4.0...v1.5.0
[v1.4.0]: https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v1.3.0...v1.4.0
[v1.3.0]: https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v1.2.0...v1.3.0
[v1.2.0]: https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v1.1.0...v1.2.0
[v1.1.0]: https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v1.0.0...v1.1.0
[v1.0.0]: https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v0.16.0...v1.0.0
[v0.16.0]: https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v0.15.0...v0.16.0
[v0.15.0]: https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v0.14.0...v0.15.0
[v0.14.0]: https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v0.13.0...v0.14.0
[v0.13.0]: https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v0.12.0...v0.13.0
[v0.12.0]: https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v0.11.0...v0.12.0
[v0.11.0]: https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v0.10.0...v0.11.0
[v0.10.0]: https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v0.9.0...v0.10.0
[v0.9.0]: https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v0.8.0...v0.9.0
[v0.8.0]: https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v0.7.0...v0.8.0
[v0.7.0]: https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v0.6.0...v0.7.0
[v0.6.0]: https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v0.5.0...v0.6.0
[v0.5.0]: https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v0.4.0...v0.5.0
[v0.4.0]: https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v0.3.0...v0.4.0
[v0.3.0]: https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v0.2.0...v0.3.0
[v0.2.0]: https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/compare/v0.1.0...v0.2.0
@@ -0,0 +1,176 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
@@ -0,0 +1,216 @@
# AWS API Gateway v2 (HTTP/Websocket) Terraform module
Terraform module which creates API Gateway version 2 with HTTP/Websocket capabilities.
This Terraform module is part of [serverless.tf framework](https://serverless.tf), which aims to simplify all operations when working with the serverless in Terraform.
## Supported Features
- Support many of features of HTTP API Gateway, but rather limited support for WebSocket API Gateway
- Conditional creation for many types of resources
## Feature Roadmap
- Some features are still missing (especially for WebSocket support)
## Usage
### HTTP API Gateway
```hcl
module "api_gateway" {
source = "terraform-aws-modules/apigateway-v2/aws"
name = "dev-http"
description = "My awesome HTTP API Gateway"
protocol_type = "HTTP"
cors_configuration = {
allow_headers = ["content-type", "x-amz-date", "authorization", "x-api-key", "x-amz-security-token", "x-amz-user-agent"]
allow_methods = ["*"]
allow_origins = ["*"]
}
# Custom domain
domain_name = "terraform-aws-modules.modules.tf"
domain_name_certificate_arn = "arn:aws:acm:eu-west-1:052235179155:certificate/2b3a7ed9-05e1-4f9e-952b-27744ba06da6"
# Access logs
default_stage_access_log_destination_arn = "arn:aws:logs:eu-west-1:835367859851:log-group:debug-apigateway"
default_stage_access_log_format = "$context.identity.sourceIp - - [$context.requestTime] \"$context.httpMethod $context.routeKey $context.protocol\" $context.status $context.responseLength $context.requestId $context.integrationErrorMessage"
# Routes and integrations
integrations = {
"POST /" = {
lambda_arn = "arn:aws:lambda:eu-west-1:052235179155:function:my-function"
payload_format_version = "2.0"
timeout_milliseconds = 12000
}
"GET /some-route-with-authorizer" = {
integration_type = "HTTP_PROXY"
integration_uri = "some url"
authorizer_key = "azure"
}
"$default" = {
lambda_arn = "arn:aws:lambda:eu-west-1:052235179155:function:my-default-function"
}
}
authorizers = {
"azure" = {
authorizer_type = "JWT"
identity_sources = "$request.header.Authorization"
name = "azure-auth"
audience = ["d6a38afd-45d6-4874-d1aa-3c5c558aqcc2"]
issuer = "https://sts.windows.net/aaee026e-8f37-410e-8869-72d9154873e4/"
}
}
tags = {
Name = "http-apigateway"
}
}
```
## Conditional creation
Sometimes you need to have a way to create resources conditionally but Terraform does not allow usage of `count` inside `module` block, so the solution is to specify `create` arguments.
```hcl
module "api_gateway" {
source = "terraform-aws-modules/apigateway-v2/aws"
create = false # to disable all resources
create_api_gateway = false # to control creation of API Gateway
create_api_domain_name = false # to control creation of API Gateway Domain Name
create_default_stage = false # to control creation of "$default" stage
create_default_stage_api_mapping = false # to control creation of "$default" stage and API mapping
create_routes_and_integrations = false # to control creation of routes and integrations
create_vpc_link = false # to control creation of VPC link
integrations= {
"GET /" = {
create_route = false # to control creation of route
}
}
# ... omitted
}
```
## Notes:
- Make sure provider block has the setting of `skip_requesting_account_id` disabled (`false`) to produce correct value in the `execution_arn`.
## Examples
- [Complete HTTP](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/tree/master/examples/complete-http) - Create API Gateway, authorizer, domain name, stage and other resources in various combinations
- [HTTP with VPC Link](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/tree/master/examples/vpc-link-http) - Create API Gateway with VPC link and integration with resources in VPC (eg. ALB)
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 4.0 |
## Providers
| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 4.0 |
## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [aws_apigatewayv2_api.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_api) | resource |
| [aws_apigatewayv2_api_mapping.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_api_mapping) | resource |
| [aws_apigatewayv2_authorizer.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_authorizer) | resource |
| [aws_apigatewayv2_domain_name.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_domain_name) | resource |
| [aws_apigatewayv2_integration.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_integration) | resource |
| [aws_apigatewayv2_route.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_route) | resource |
| [aws_apigatewayv2_stage.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_stage) | resource |
| [aws_apigatewayv2_vpc_link.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_vpc_link) | resource |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_api_key_selection_expression"></a> [api\_key\_selection\_expression](#input\_api\_key\_selection\_expression) | An API key selection expression. Valid values: $context.authorizer.usageIdentifierKey, $request.header.x-api-key. | `string` | `"$request.header.x-api-key"` | no |
| <a name="input_api_version"></a> [api\_version](#input\_api\_version) | A version identifier for the API | `string` | `null` | no |
| <a name="input_authorizers"></a> [authorizers](#input\_authorizers) | Map of API gateway authorizers | `map(any)` | `{}` | no |
| <a name="input_body"></a> [body](#input\_body) | An OpenAPI specification that defines the set of routes and integrations to create as part of the HTTP APIs. Supported only for HTTP APIs. | `string` | `null` | no |
| <a name="input_cors_configuration"></a> [cors\_configuration](#input\_cors\_configuration) | The cross-origin resource sharing (CORS) configuration. Applicable for HTTP APIs. | `any` | `{}` | no |
| <a name="input_create"></a> [create](#input\_create) | Controls if API Gateway resources should be created | `bool` | `true` | no |
| <a name="input_create_api_domain_name"></a> [create\_api\_domain\_name](#input\_create\_api\_domain\_name) | Whether to create API domain name resource | `bool` | `true` | no |
| <a name="input_create_api_gateway"></a> [create\_api\_gateway](#input\_create\_api\_gateway) | Whether to create API Gateway | `bool` | `true` | no |
| <a name="input_create_default_stage"></a> [create\_default\_stage](#input\_create\_default\_stage) | Whether to create default stage | `bool` | `true` | no |
| <a name="input_create_default_stage_api_mapping"></a> [create\_default\_stage\_api\_mapping](#input\_create\_default\_stage\_api\_mapping) | Whether to create default stage API mapping | `bool` | `true` | no |
| <a name="input_create_routes_and_integrations"></a> [create\_routes\_and\_integrations](#input\_create\_routes\_and\_integrations) | Whether to create routes and integrations resources | `bool` | `true` | no |
| <a name="input_create_vpc_link"></a> [create\_vpc\_link](#input\_create\_vpc\_link) | Whether to create VPC links | `bool` | `true` | no |
| <a name="input_credentials_arn"></a> [credentials\_arn](#input\_credentials\_arn) | Part of quick create. Specifies any credentials required for the integration. Applicable for HTTP APIs. | `string` | `null` | no |
| <a name="input_default_route_settings"></a> [default\_route\_settings](#input\_default\_route\_settings) | Settings for default route | `map(string)` | `{}` | no |
| <a name="input_default_stage_access_log_destination_arn"></a> [default\_stage\_access\_log\_destination\_arn](#input\_default\_stage\_access\_log\_destination\_arn) | Default stage's ARN of the CloudWatch Logs log group to receive access logs. Any trailing :* is trimmed from the ARN. | `string` | `null` | no |
| <a name="input_default_stage_access_log_format"></a> [default\_stage\_access\_log\_format](#input\_default\_stage\_access\_log\_format) | Default stage's single line format of the access logs of data, as specified by selected $context variables. | `string` | `null` | no |
| <a name="input_default_stage_tags"></a> [default\_stage\_tags](#input\_default\_stage\_tags) | A mapping of tags to assign to the default stage resource. | `map(string)` | `{}` | no |
| <a name="input_description"></a> [description](#input\_description) | The description of the API. | `string` | `null` | no |
| <a name="input_disable_execute_api_endpoint"></a> [disable\_execute\_api\_endpoint](#input\_disable\_execute\_api\_endpoint) | Whether clients can invoke the API by using the default execute-api endpoint. To require that clients use a custom domain name to invoke the API, disable the default endpoint | `string` | `false` | no |
| <a name="input_domain_name"></a> [domain\_name](#input\_domain\_name) | The domain name to use for API gateway | `string` | `null` | no |
| <a name="input_domain_name_certificate_arn"></a> [domain\_name\_certificate\_arn](#input\_domain\_name\_certificate\_arn) | The ARN of an AWS-managed certificate that will be used by the endpoint for the domain name | `string` | `null` | no |
| <a name="input_domain_name_ownership_verification_certificate_arn"></a> [domain\_name\_ownership\_verification\_certificate\_arn](#input\_domain\_name\_ownership\_verification\_certificate\_arn) | ARN of the AWS-issued certificate used to validate custom domain ownership (when certificate\_arn is issued via an ACM Private CA or mutual\_tls\_authentication is configured with an ACM-imported certificate.) | `string` | `null` | no |
| <a name="input_domain_name_tags"></a> [domain\_name\_tags](#input\_domain\_name\_tags) | A mapping of tags to assign to API domain name resource. | `map(string)` | `{}` | no |
| <a name="input_fail_on_warnings"></a> [fail\_on\_warnings](#input\_fail\_on\_warnings) | Whether warnings should return an error while API Gateway is creating or updating the resource using an OpenAPI specification. Defaults to false. Applicable for HTTP APIs. | `bool` | `false` | no |
| <a name="input_integrations"></a> [integrations](#input\_integrations) | Map of API gateway routes with integrations | `map(any)` | `{}` | no |
| <a name="input_mutual_tls_authentication"></a> [mutual\_tls\_authentication](#input\_mutual\_tls\_authentication) | An Amazon S3 URL that specifies the truststore for mutual TLS authentication as well as version, keyed at uri and version | `map(string)` | `{}` | no |
| <a name="input_name"></a> [name](#input\_name) | The name of the API | `string` | `""` | no |
| <a name="input_protocol_type"></a> [protocol\_type](#input\_protocol\_type) | The API protocol. Valid values: HTTP, WEBSOCKET | `string` | `"HTTP"` | no |
| <a name="input_route_key"></a> [route\_key](#input\_route\_key) | Part of quick create. Specifies any route key. Applicable for HTTP APIs. | `string` | `null` | no |
| <a name="input_route_selection_expression"></a> [route\_selection\_expression](#input\_route\_selection\_expression) | The route selection expression for the API. | `string` | `"$request.method $request.path"` | no |
| <a name="input_tags"></a> [tags](#input\_tags) | A mapping of tags to assign to API gateway resources. | `map(string)` | `{}` | no |
| <a name="input_target"></a> [target](#input\_target) | Part of quick create. Quick create produces an API with an integration, a default catch-all route, and a default stage which is configured to automatically deploy changes. For HTTP integrations, specify a fully qualified URL. For Lambda integrations, specify a function ARN. The type of the integration will be HTTP\_PROXY or AWS\_PROXY, respectively. Applicable for HTTP APIs. | `string` | `null` | no |
| <a name="input_vpc_link_tags"></a> [vpc\_link\_tags](#input\_vpc\_link\_tags) | A map of tags to add to the VPC Link | `map(string)` | `{}` | no |
| <a name="input_vpc_links"></a> [vpc\_links](#input\_vpc\_links) | Map of VPC Links details to create | `map(any)` | `{}` | no |
## Outputs
| Name | Description |
|------|-------------|
| <a name="output_apigatewayv2_api_api_endpoint"></a> [apigatewayv2\_api\_api\_endpoint](#output\_apigatewayv2\_api\_api\_endpoint) | The URI of the API |
| <a name="output_apigatewayv2_api_arn"></a> [apigatewayv2\_api\_arn](#output\_apigatewayv2\_api\_arn) | The ARN of the API |
| <a name="output_apigatewayv2_api_execution_arn"></a> [apigatewayv2\_api\_execution\_arn](#output\_apigatewayv2\_api\_execution\_arn) | The ARN prefix to be used in an aws\_lambda\_permission's source\_arn attribute or in an aws\_iam\_policy to authorize access to the @connections API. |
| <a name="output_apigatewayv2_api_id"></a> [apigatewayv2\_api\_id](#output\_apigatewayv2\_api\_id) | The API identifier |
| <a name="output_apigatewayv2_api_mapping_id"></a> [apigatewayv2\_api\_mapping\_id](#output\_apigatewayv2\_api\_mapping\_id) | The API mapping identifier. |
| <a name="output_apigatewayv2_authorizer_id"></a> [apigatewayv2\_authorizer\_id](#output\_apigatewayv2\_authorizer\_id) | The map of API Gateway Authorizer identifiers |
| <a name="output_apigatewayv2_domain_name_api_mapping_selection_expression"></a> [apigatewayv2\_domain\_name\_api\_mapping\_selection\_expression](#output\_apigatewayv2\_domain\_name\_api\_mapping\_selection\_expression) | The API mapping selection expression for the domain name |
| <a name="output_apigatewayv2_domain_name_arn"></a> [apigatewayv2\_domain\_name\_arn](#output\_apigatewayv2\_domain\_name\_arn) | The ARN of the domain name |
| <a name="output_apigatewayv2_domain_name_configuration"></a> [apigatewayv2\_domain\_name\_configuration](#output\_apigatewayv2\_domain\_name\_configuration) | The domain name configuration |
| <a name="output_apigatewayv2_domain_name_hosted_zone_id"></a> [apigatewayv2\_domain\_name\_hosted\_zone\_id](#output\_apigatewayv2\_domain\_name\_hosted\_zone\_id) | The Amazon Route 53 Hosted Zone ID of the endpoint |
| <a name="output_apigatewayv2_domain_name_id"></a> [apigatewayv2\_domain\_name\_id](#output\_apigatewayv2\_domain\_name\_id) | The domain name identifier |
| <a name="output_apigatewayv2_domain_name_target_domain_name"></a> [apigatewayv2\_domain\_name\_target\_domain\_name](#output\_apigatewayv2\_domain\_name\_target\_domain\_name) | The target domain name |
| <a name="output_apigatewayv2_vpc_link_arn"></a> [apigatewayv2\_vpc\_link\_arn](#output\_apigatewayv2\_vpc\_link\_arn) | The map of VPC Link ARNs |
| <a name="output_apigatewayv2_vpc_link_id"></a> [apigatewayv2\_vpc\_link\_id](#output\_apigatewayv2\_vpc\_link\_id) | The map of VPC Link identifiers |
| <a name="output_default_apigatewayv2_stage_arn"></a> [default\_apigatewayv2\_stage\_arn](#output\_default\_apigatewayv2\_stage\_arn) | The default stage ARN |
| <a name="output_default_apigatewayv2_stage_domain_name"></a> [default\_apigatewayv2\_stage\_domain\_name](#output\_default\_apigatewayv2\_stage\_domain\_name) | Domain name of the stage (useful for CloudFront distribution) |
| <a name="output_default_apigatewayv2_stage_execution_arn"></a> [default\_apigatewayv2\_stage\_execution\_arn](#output\_default\_apigatewayv2\_stage\_execution\_arn) | The ARN prefix to be used in an aws\_lambda\_permission's source\_arn attribute or in an aws\_iam\_policy to authorize access to the @connections API. |
| <a name="output_default_apigatewayv2_stage_id"></a> [default\_apigatewayv2\_stage\_id](#output\_default\_apigatewayv2\_stage\_id) | The default stage identifier |
| <a name="output_default_apigatewayv2_stage_invoke_url"></a> [default\_apigatewayv2\_stage\_invoke\_url](#output\_default\_apigatewayv2\_stage\_invoke\_url) | The URL to invoke the API pointing to the stage |
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Authors
Module managed by [Anton Babenko](https://github.com/antonbabenko). Check out [serverless.tf](https://serverless.tf) to learn more about doing serverless with Terraform.
Please reach out to [Betajob](https://www.betajob.com/) if you are looking for commercial support for your Terraform, AWS, or serverless project.
## License
Apache 2 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-apigateway-v2/tree/master/LICENSE) for full details.
@@ -0,0 +1,221 @@
# API Gateway
resource "aws_apigatewayv2_api" "this" {
count = var.create && var.create_api_gateway ? 1 : 0
name = var.name
description = var.description
protocol_type = var.protocol_type
version = var.api_version
body = var.body
route_selection_expression = var.route_selection_expression
api_key_selection_expression = var.api_key_selection_expression
disable_execute_api_endpoint = var.disable_execute_api_endpoint
fail_on_warnings = var.fail_on_warnings
/* Start of quick create */
route_key = var.route_key
credentials_arn = var.credentials_arn
target = var.target
/* End of quick create */
dynamic "cors_configuration" {
for_each = length(keys(var.cors_configuration)) == 0 ? [] : [var.cors_configuration]
content {
allow_credentials = try(cors_configuration.value.allow_credentials, null)
allow_headers = try(cors_configuration.value.allow_headers, null)
allow_methods = try(cors_configuration.value.allow_methods, null)
allow_origins = try(cors_configuration.value.allow_origins, null)
expose_headers = try(cors_configuration.value.expose_headers, null)
max_age = try(cors_configuration.value.max_age, null)
}
}
tags = var.tags
}
# Domain name
resource "aws_apigatewayv2_domain_name" "this" {
count = var.create && var.create_api_domain_name ? 1 : 0
domain_name = var.domain_name
domain_name_configuration {
certificate_arn = var.domain_name_certificate_arn
ownership_verification_certificate_arn = var.domain_name_ownership_verification_certificate_arn
endpoint_type = "REGIONAL"
security_policy = "TLS_1_2"
}
dynamic "mutual_tls_authentication" {
for_each = length(keys(var.mutual_tls_authentication)) == 0 ? [] : [var.mutual_tls_authentication]
content {
truststore_uri = mutual_tls_authentication.value.truststore_uri
truststore_version = try(mutual_tls_authentication.value.truststore_version, null)
}
}
tags = merge(var.domain_name_tags, var.tags)
}
# Default stage
resource "aws_apigatewayv2_stage" "default" {
count = var.create && var.create_default_stage ? 1 : 0
api_id = aws_apigatewayv2_api.this[0].id
name = "$default"
auto_deploy = true
dynamic "access_log_settings" {
for_each = var.default_stage_access_log_destination_arn != null && var.default_stage_access_log_format != null ? [true] : []
content {
destination_arn = var.default_stage_access_log_destination_arn
format = var.default_stage_access_log_format
}
}
dynamic "default_route_settings" {
for_each = length(keys(var.default_route_settings)) == 0 ? [] : [var.default_route_settings]
content {
data_trace_enabled = try(default_route_settings.value.data_trace_enabled, false) # supported in Websocket APIGateway only
logging_level = try(default_route_settings.value.logging_level, null) # supported in Websocket APIGateway only
detailed_metrics_enabled = try(default_route_settings.value.detailed_metrics_enabled, false)
throttling_burst_limit = try(default_route_settings.value.throttling_burst_limit, null)
throttling_rate_limit = try(default_route_settings.value.throttling_rate_limit, null)
}
}
dynamic "route_settings" {
for_each = { for k, v in var.integrations : k => v if var.create_routes_and_integrations && try(tobool(v.create_route), true) && length(setintersection(["data_trace_enabled", "detailed_metrics_enabled", "logging_level", "throttling_burst_limit", "throttling_rate_limit"], keys(v))) > 0 }
content {
route_key = route_settings.key
data_trace_enabled = try(route_settings.value.data_trace_enabled, var.default_route_settings["data_trace_enabled"], false) # supported in Websocket APIGateway only
logging_level = try(route_settings.value.logging_level, var.default_route_settings["logging_level"], null) # supported in Websocket APIGateway only
detailed_metrics_enabled = try(route_settings.value.detailed_metrics_enabled, var.default_route_settings["detailed_metrics_enabled"], false)
throttling_burst_limit = try(route_settings.value.throttling_burst_limit, var.default_route_settings["throttling_burst_limit"], null)
throttling_rate_limit = try(route_settings.value.throttling_rate_limit, var.default_route_settings["throttling_rate_limit"], null)
}
}
tags = merge(var.default_stage_tags, var.tags)
# Bug in terraform-aws-provider with perpetual diff
lifecycle {
ignore_changes = [deployment_id]
}
}
# Default API mapping
resource "aws_apigatewayv2_api_mapping" "this" {
count = var.create && var.create_api_domain_name && var.create_default_stage && var.create_default_stage_api_mapping ? 1 : 0
api_id = aws_apigatewayv2_api.this[0].id
domain_name = aws_apigatewayv2_domain_name.this[0].id
stage = aws_apigatewayv2_stage.default[0].id
}
# Routes and integrations
resource "aws_apigatewayv2_route" "this" {
for_each = var.create && var.create_routes_and_integrations ? var.integrations : {}
api_id = aws_apigatewayv2_api.this[0].id
route_key = each.key
api_key_required = try(each.value.api_key_required, null)
authorization_scopes = try(split(",", each.value.authorization_scopes), null)
authorization_type = try(each.value.authorization_type, "NONE")
authorizer_id = try(aws_apigatewayv2_authorizer.this[each.value.authorizer_key].id, each.value.authorizer_id, null)
model_selection_expression = try(each.value.model_selection_expression, null)
operation_name = try(each.value.operation_name, null)
route_response_selection_expression = try(each.value.route_response_selection_expression, null)
target = "integrations/${aws_apigatewayv2_integration.this[each.key].id}"
# Have been added to the docs. But is WEBSOCKET only(not yet supported)
# request_models = try(each.value.request_models, null)
}
resource "aws_apigatewayv2_integration" "this" {
for_each = var.create && var.create_routes_and_integrations ? var.integrations : {}
api_id = aws_apigatewayv2_api.this[0].id
description = try(each.value.description, null)
integration_type = try(each.value.integration_type, try(each.value.lambda_arn, "") != "" ? "AWS_PROXY" : "MOCK")
integration_subtype = try(each.value.integration_subtype, null)
integration_method = try(each.value.integration_method, try(each.value.integration_subtype, null) == null ? "POST" : null)
integration_uri = try(each.value.lambda_arn, try(each.value.integration_uri, null))
connection_type = try(each.value.connection_type, "INTERNET")
connection_id = try(aws_apigatewayv2_vpc_link.this[each.value["vpc_link"]].id, try(each.value.connection_id, null))
payload_format_version = try(each.value.payload_format_version, null)
timeout_milliseconds = try(each.value.timeout_milliseconds, null)
passthrough_behavior = try(each.value.passthrough_behavior, null)
content_handling_strategy = try(each.value.content_handling_strategy, null)
credentials_arn = try(each.value.credentials_arn, null)
request_parameters = try(jsondecode(each.value["request_parameters"]), each.value["request_parameters"], null)
dynamic "tls_config" {
for_each = flatten([try(jsondecode(each.value["tls_config"]), each.value["tls_config"], [])])
content {
server_name_to_verify = tls_config.value["server_name_to_verify"]
}
}
dynamic "response_parameters" {
for_each = flatten([try(jsondecode(each.value["response_parameters"]), each.value["response_parameters"], [])])
content {
status_code = response_parameters.value["status_code"]
mappings = response_parameters.value["mappings"]
}
}
lifecycle {
create_before_destroy = true
}
}
# Authorizers
resource "aws_apigatewayv2_authorizer" "this" {
for_each = var.create && var.create_routes_and_integrations ? var.authorizers : {}
api_id = aws_apigatewayv2_api.this[0].id
authorizer_type = try(each.value.authorizer_type, null)
identity_sources = try(flatten([each.value.identity_sources]), null)
name = try(each.value.name, null)
authorizer_uri = try(each.value.authorizer_uri, null)
authorizer_payload_format_version = try(each.value.authorizer_payload_format_version, null)
authorizer_result_ttl_in_seconds = try(each.value.authorizer_result_ttl_in_seconds, null)
authorizer_credentials_arn = try(each.value.authorizer_credentials_arn, null)
enable_simple_responses = try(each.value.enable_simple_responses, null)
dynamic "jwt_configuration" {
for_each = length(try(each.value.audience, [each.value.issuer], [])) > 0 ? [true] : []
content {
audience = try(each.value.audience, null)
issuer = try(each.value.issuer, null)
}
}
}
# VPC Link (Private API)
resource "aws_apigatewayv2_vpc_link" "this" {
for_each = var.create && var.create_vpc_link ? var.vpc_links : {}
name = try(each.value.name, each.key)
security_group_ids = each.value["security_group_ids"]
subnet_ids = each.value["subnet_ids"]
tags = merge(var.tags, var.vpc_link_tags, try(each.value.tags, {}))
}
@@ -0,0 +1,104 @@
output "apigatewayv2_api_id" {
description = "The API identifier"
value = try(aws_apigatewayv2_api.this[0].id, "")
}
output "apigatewayv2_api_api_endpoint" {
description = "The URI of the API"
value = try(aws_apigatewayv2_api.this[0].api_endpoint, "")
}
output "apigatewayv2_api_arn" {
description = "The ARN of the API"
value = try(aws_apigatewayv2_api.this[0].arn, "")
}
output "apigatewayv2_api_execution_arn" {
description = "The ARN prefix to be used in an aws_lambda_permission's source_arn attribute or in an aws_iam_policy to authorize access to the @connections API."
value = try(aws_apigatewayv2_api.this[0].execution_arn, "")
}
# default stage
output "default_apigatewayv2_stage_id" {
description = "The default stage identifier"
value = try(aws_apigatewayv2_stage.default[0].id, "")
}
output "default_apigatewayv2_stage_arn" {
description = "The default stage ARN"
value = try(aws_apigatewayv2_stage.default[0].arn, "")
}
output "default_apigatewayv2_stage_execution_arn" {
description = "The ARN prefix to be used in an aws_lambda_permission's source_arn attribute or in an aws_iam_policy to authorize access to the @connections API."
value = try(aws_apigatewayv2_stage.default[0].execution_arn, "")
}
output "default_apigatewayv2_stage_invoke_url" {
description = "The URL to invoke the API pointing to the stage"
value = try(aws_apigatewayv2_stage.default[0].invoke_url, "")
}
output "default_apigatewayv2_stage_domain_name" {
description = "Domain name of the stage (useful for CloudFront distribution)"
value = replace(try(aws_apigatewayv2_stage.default[0].invoke_url, ""), "/^https?://([^/]*).*/", "$1")
}
# domain name
output "apigatewayv2_domain_name_id" {
description = "The domain name identifier"
value = try(aws_apigatewayv2_domain_name.this[0].id, "")
}
output "apigatewayv2_domain_name_arn" {
description = "The ARN of the domain name"
value = try(aws_apigatewayv2_domain_name.this[0].arn, "")
}
output "apigatewayv2_domain_name_api_mapping_selection_expression" {
description = "The API mapping selection expression for the domain name"
value = try(aws_apigatewayv2_domain_name.this[0].api_mapping_selection_expression, "")
}
output "apigatewayv2_domain_name_configuration" {
description = "The domain name configuration"
value = try(aws_apigatewayv2_domain_name.this[0].domain_name_configuration, "")
}
output "apigatewayv2_domain_name_target_domain_name" {
description = "The target domain name"
value = try(aws_apigatewayv2_domain_name.this[0].domain_name_configuration[0].target_domain_name, "")
}
output "apigatewayv2_domain_name_hosted_zone_id" {
description = "The Amazon Route 53 Hosted Zone ID of the endpoint"
value = try(aws_apigatewayv2_domain_name.this[0].domain_name_configuration[0].hosted_zone_id, "")
}
# api mapping
output "apigatewayv2_api_mapping_id" {
description = "The API mapping identifier."
value = try(aws_apigatewayv2_api_mapping.this[0].id, "")
}
# route
# output "apigatewayv2_route_id" {
# description = "The default route identifier."
# value = try(aws_apigatewayv2_route.this[0].id, "")
# }
# VPC link
output "apigatewayv2_vpc_link_id" {
description = "The map of VPC Link identifiers"
value = { for k, v in aws_apigatewayv2_vpc_link.this : k => v.id }
}
output "apigatewayv2_vpc_link_arn" {
description = "The map of VPC Link ARNs"
value = { for k, v in aws_apigatewayv2_vpc_link.this : k => v.arn }
}
output "apigatewayv2_authorizer_id" {
description = "The map of API Gateway Authorizer identifiers"
value = { for k, v in aws_apigatewayv2_authorizer.this : k => v.id }
}
@@ -0,0 +1,227 @@
variable "create" {
description = "Controls if API Gateway resources should be created"
type = bool
default = true
}
variable "create_api_gateway" {
description = "Whether to create API Gateway"
type = bool
default = true
}
variable "create_default_stage" {
description = "Whether to create default stage"
type = bool
default = true
}
variable "create_default_stage_api_mapping" {
description = "Whether to create default stage API mapping"
type = bool
default = true
}
# variable "create_stage" {
# description = "Whether to create custom stage"
# type = bool
# default = false
# }
#
# variable "create_stage_api_mapping" {
# description = "Whether to create stage API mapping"
# type = bool
# default = false
# }
variable "create_api_domain_name" {
description = "Whether to create API domain name resource"
type = bool
default = true
}
variable "create_routes_and_integrations" {
description = "Whether to create routes and integrations resources"
type = bool
default = true
}
variable "create_vpc_link" {
description = "Whether to create VPC links"
type = bool
default = true
}
# API Gateway
variable "name" {
description = "The name of the API"
type = string
default = ""
}
variable "description" {
description = "The description of the API."
type = string
default = null
}
variable "default_route_settings" {
description = "Settings for default route"
type = map(string)
default = {}
}
variable "disable_execute_api_endpoint" {
description = "Whether clients can invoke the API by using the default execute-api endpoint. To require that clients use a custom domain name to invoke the API, disable the default endpoint"
type = string
default = false
}
variable "fail_on_warnings" {
description = "Whether warnings should return an error while API Gateway is creating or updating the resource using an OpenAPI specification. Defaults to false. Applicable for HTTP APIs."
type = bool
default = false
}
variable "protocol_type" {
description = "The API protocol. Valid values: HTTP, WEBSOCKET"
type = string
default = "HTTP"
}
variable "api_key_selection_expression" {
description = "An API key selection expression. Valid values: $context.authorizer.usageIdentifierKey, $request.header.x-api-key."
type = string
default = "$request.header.x-api-key"
}
variable "route_key" {
description = "Part of quick create. Specifies any route key. Applicable for HTTP APIs."
type = string
default = null
}
variable "route_selection_expression" {
description = "The route selection expression for the API."
type = string
default = "$request.method $request.path"
}
variable "cors_configuration" {
description = "The cross-origin resource sharing (CORS) configuration. Applicable for HTTP APIs."
type = any
default = {}
}
variable "credentials_arn" {
description = "Part of quick create. Specifies any credentials required for the integration. Applicable for HTTP APIs."
type = string
default = null
}
variable "target" {
description = "Part of quick create. Quick create produces an API with an integration, a default catch-all route, and a default stage which is configured to automatically deploy changes. For HTTP integrations, specify a fully qualified URL. For Lambda integrations, specify a function ARN. The type of the integration will be HTTP_PROXY or AWS_PROXY, respectively. Applicable for HTTP APIs."
type = string
default = null
}
variable "body" {
description = "An OpenAPI specification that defines the set of routes and integrations to create as part of the HTTP APIs. Supported only for HTTP APIs."
type = string
default = null
}
variable "api_version" {
description = "A version identifier for the API"
type = string
default = null
}
variable "tags" {
description = "A mapping of tags to assign to API gateway resources."
type = map(string)
default = {}
}
#####
# default stage
variable "default_stage_access_log_destination_arn" {
description = "Default stage's ARN of the CloudWatch Logs log group to receive access logs. Any trailing :* is trimmed from the ARN."
type = string
default = null
}
variable "default_stage_access_log_format" {
description = "Default stage's single line format of the access logs of data, as specified by selected $context variables."
type = string
default = null
}
variable "default_stage_tags" {
description = "A mapping of tags to assign to the default stage resource."
type = map(string)
default = {}
}
#####
# default stage API mapping
####
# domain name
variable "domain_name" {
description = "The domain name to use for API gateway"
type = string
default = null
}
variable "domain_name_certificate_arn" {
description = "The ARN of an AWS-managed certificate that will be used by the endpoint for the domain name"
type = string
default = null
}
variable "domain_name_ownership_verification_certificate_arn" {
description = "ARN of the AWS-issued certificate used to validate custom domain ownership (when certificate_arn is issued via an ACM Private CA or mutual_tls_authentication is configured with an ACM-imported certificate.)"
type = string
default = null
}
variable "domain_name_tags" {
description = "A mapping of tags to assign to API domain name resource."
type = map(string)
default = {}
}
variable "mutual_tls_authentication" {
description = "An Amazon S3 URL that specifies the truststore for mutual TLS authentication as well as version, keyed at uri and version"
type = map(string)
default = {}
}
####
# routes and integrations
variable "integrations" {
description = "Map of API gateway routes with integrations"
type = map(any)
default = {}
}
# authorrizers
variable "authorizers" {
description = "Map of API gateway authorizers"
type = map(any)
default = {}
}
# vpc link
variable "vpc_links" {
description = "Map of VPC Links details to create"
type = map(any)
default = {}
}
variable "vpc_link_tags" {
description = "A map of tags to add to the VPC Link"
type = map(string)
default = {}
}
@@ -0,0 +1,10 @@
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.0"
}
}
}
@@ -0,0 +1,100 @@
# Wrapper for the root module
The configuration in this directory contains an implementation of a single module wrapper pattern, which allows managing several copies of a module in places where using the native Terraform 0.13+ `for_each` feature is not feasible (e.g., with Terragrunt).
You may want to use a single Terragrunt configuration file to manage multiple resources without duplicating `terragrunt.hcl` files for each copy of the same module.
This wrapper does not implement any extra functionality.
## Usage with Terragrunt
`terragrunt.hcl`:
```hcl
terraform {
source = "tfr:///terraform-aws-modules/apigateway-v2/aws//wrappers"
# Alternative source:
# source = "git::git@github.com:terraform-aws-modules/terraform-aws-apigateway-v2.git//wrappers?ref=master"
}
inputs = {
defaults = { # Default values
create = true
tags = {
Terraform = "true"
Environment = "dev"
}
}
items = {
my-item = {
# omitted... can be any argument supported by the module
}
my-second-item = {
# omitted... can be any argument supported by the module
}
# omitted...
}
}
```
## Usage with Terraform
```hcl
module "wrapper" {
source = "terraform-aws-modules/apigateway-v2/aws//wrappers"
defaults = { # Default values
create = true
tags = {
Terraform = "true"
Environment = "dev"
}
}
items = {
my-item = {
# omitted... can be any argument supported by the module
}
my-second-item = {
# omitted... can be any argument supported by the module
}
# omitted...
}
}
```
## Example: Manage multiple S3 buckets in one Terragrunt layer
`eu-west-1/s3-buckets/terragrunt.hcl`:
```hcl
terraform {
source = "tfr:///terraform-aws-modules/s3-bucket/aws//wrappers"
# Alternative source:
# source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git//wrappers?ref=master"
}
inputs = {
defaults = {
force_destroy = true
attach_elb_log_delivery_policy = true
attach_lb_log_delivery_policy = true
attach_deny_insecure_transport_policy = true
attach_require_latest_tls_policy = true
}
items = {
bucket1 = {
bucket = "my-random-bucket-1"
}
bucket2 = {
bucket = "my-random-bucket-2"
tags = {
Secure = "probably"
}
}
}
}
```
@@ -0,0 +1,40 @@
module "wrapper" {
source = "../"
for_each = var.items
api_key_selection_expression = try(each.value.api_key_selection_expression, var.defaults.api_key_selection_expression, "$request.header.x-api-key")
api_version = try(each.value.api_version, var.defaults.api_version, null)
authorizers = try(each.value.authorizers, var.defaults.authorizers, {})
body = try(each.value.body, var.defaults.body, null)
cors_configuration = try(each.value.cors_configuration, var.defaults.cors_configuration, {})
create = try(each.value.create, var.defaults.create, true)
create_api_domain_name = try(each.value.create_api_domain_name, var.defaults.create_api_domain_name, true)
create_api_gateway = try(each.value.create_api_gateway, var.defaults.create_api_gateway, true)
create_default_stage = try(each.value.create_default_stage, var.defaults.create_default_stage, true)
create_default_stage_api_mapping = try(each.value.create_default_stage_api_mapping, var.defaults.create_default_stage_api_mapping, true)
create_routes_and_integrations = try(each.value.create_routes_and_integrations, var.defaults.create_routes_and_integrations, true)
create_vpc_link = try(each.value.create_vpc_link, var.defaults.create_vpc_link, true)
credentials_arn = try(each.value.credentials_arn, var.defaults.credentials_arn, null)
default_route_settings = try(each.value.default_route_settings, var.defaults.default_route_settings, {})
default_stage_access_log_destination_arn = try(each.value.default_stage_access_log_destination_arn, var.defaults.default_stage_access_log_destination_arn, null)
default_stage_access_log_format = try(each.value.default_stage_access_log_format, var.defaults.default_stage_access_log_format, null)
default_stage_tags = try(each.value.default_stage_tags, var.defaults.default_stage_tags, {})
description = try(each.value.description, var.defaults.description, null)
disable_execute_api_endpoint = try(each.value.disable_execute_api_endpoint, var.defaults.disable_execute_api_endpoint, false)
domain_name = try(each.value.domain_name, var.defaults.domain_name, null)
domain_name_certificate_arn = try(each.value.domain_name_certificate_arn, var.defaults.domain_name_certificate_arn, null)
domain_name_ownership_verification_certificate_arn = try(each.value.domain_name_ownership_verification_certificate_arn, var.defaults.domain_name_ownership_verification_certificate_arn, null)
domain_name_tags = try(each.value.domain_name_tags, var.defaults.domain_name_tags, {})
fail_on_warnings = try(each.value.fail_on_warnings, var.defaults.fail_on_warnings, false)
integrations = try(each.value.integrations, var.defaults.integrations, {})
mutual_tls_authentication = try(each.value.mutual_tls_authentication, var.defaults.mutual_tls_authentication, {})
name = try(each.value.name, var.defaults.name, "")
protocol_type = try(each.value.protocol_type, var.defaults.protocol_type, "HTTP")
route_key = try(each.value.route_key, var.defaults.route_key, null)
route_selection_expression = try(each.value.route_selection_expression, var.defaults.route_selection_expression, "$request.method $request.path")
tags = try(each.value.tags, var.defaults.tags, {})
target = try(each.value.target, var.defaults.target, null)
vpc_link_tags = try(each.value.vpc_link_tags, var.defaults.vpc_link_tags, {})
vpc_links = try(each.value.vpc_links, var.defaults.vpc_links, {})
}
@@ -0,0 +1,5 @@
output "wrapper" {
description = "Map of outputs of a wrapper."
value = module.wrapper
# sensitive = false # No sensitive module output found
}
@@ -0,0 +1,11 @@
variable "defaults" {
description = "Map of default values which will be used for each item."
type = any
default = {}
}
variable "items" {
description = "Maps of items to create a wrapper from. Values are passed through to the module."
type = any
default = {}
}
@@ -0,0 +1,3 @@
terraform {
required_version = ">= 0.13.1"
}
+42
View File
@@ -0,0 +1,42 @@
<!-- This readme file is generated with terraform-docs -->
## Requirements
| Name | Version |
|------|---------|
| terraform | >= 1.3.0 |
| aws | >= 5.0 |
## Providers
| Name | Version |
|------|---------|
| aws | >= 5.0 |
## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [aws_sesv2_configuration_set.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sesv2_configuration_set) | resource |
| [aws_sesv2_email_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sesv2_email_identity) | resource |
| [aws_sesv2_email_identity_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sesv2_email_identity_policy) | resource |
| [aws_caller_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
| [aws_iam_policy_document.ses-policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| emails | Email addresses to be added to SES | `list(string)` | n/a | yes |
| reputation\_metrics\_enabled | Enable reputation metrics | `bool` | `true` | no |
## Outputs
No outputs.
---
## Authorship
This module was developed by UPDATE_THIS.
+63
View File
@@ -0,0 +1,63 @@
data "aws_caller_identity" "this" {}
data "aws_region" "this" {}
resource "aws_sesv2_email_identity" "this" {
for_each = toset(var.emails)
email_identity = each.value
configuration_set_name = aws_sesv2_configuration_set.this.configuration_set_name
}
resource "aws_sesv2_configuration_set" "this" {
configuration_set_name = "default-sesv2-configuration-set"
delivery_options {
tls_policy = var.require_tls ? "REQUIRE" : "OPTIONAL"
}
reputation_options {
reputation_metrics_enabled = var.reputation_metrics_enabled
}
sending_options {
sending_enabled = true
}
}
# The exact same policy can be created successfully on console!
#resource "aws_sesv2_email_identity_policy" "this" {
# for_each = aws_sesv2_email_identity.this
# email_identity = each.value.arn
# policy_name = "default-policy"
# # policy = data.aws_iam_policy_document.ses-policy[each.key].json
# policy = jsonencode({
# "Version" : "2012-10-17",
# "Statement" : [
# {
# "Sid" : "default",
# "Effect" : "Allow",
# "Principal" : {
# "AWS" : "arn:aws:iam::${data.aws_caller_identity.this.account_id}:root"
# },
# "Action" : [
# "ses:SendEmail",
# "ses:SendRawEmail"
# ],
# "Resource" : each.value.arn,
# "Condition" : {}
# }
# ]
# })
#}
#data "aws_iam_policy_document" "ses-policy" {
# for_each = aws_sesv2_email_identity.this
# statement {
# sid = "default"
# actions = ["SES:SendEmail", "SES:SendRawEmail"]
# resources = [each.value.arn]
# principals {
# identifiers = [data.aws_caller_identity.this.account_id]
# type = "AWS"
# }
# }
#}
@@ -0,0 +1,16 @@
variable "emails" {
type = list(string)
description = "Email addresses to be added to SES"
}
variable "reputation_metrics_enabled" {
type = bool
description = "Enable reputation metrics"
default = true
}
variable "require_tls" {
type = bool
description = "Require TLS delivery option"
default = true
}
@@ -0,0 +1,9 @@
terraform {
required_version = ">= 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0, < 5.39"
}
}
}
@@ -0,0 +1,50 @@
<!-- This readme file is generated with terraform-docs -->
This module installs Cloudwatch agent via SSM State Manager.
It creates an association and install the agent to all instances every 1 day.
Then a default cloudwatch agent config is generated using amazon-cloudwatch-agent-config-wizard,
saved on /opt/aws/amazon-cloudwatch-agent/bin/config.json, supplemented with additional collections,
and uploaded on SSM parameter store as ```AmazonCloudWatch-linux```.
Note that for cloudwatch agent to fully function, the instance needs an instance profile with the
following managed policies attached:
* CloudWatchAgentServerPolicy
* AmazonSSMManagedInstanceCore
## Requirements
| Name | Version |
|------|---------|
| terraform | >= 1.3.0 |
| aws | >= 5.0 |
## Providers
| Name | Version |
|------|---------|
| aws | >= 5.0 |
## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [aws_ssm_association.ConfigCwAgent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_association) | resource |
| [aws_ssm_association.InstallCwAgent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_association) | resource |
| [aws_ssm_parameter.CwAgentConfigLinux](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
## Inputs
No inputs.
## Outputs
No outputs.
---
## Authorship
This module was developed by UPDATE_THIS.
@@ -0,0 +1,135 @@
resource "aws_ssm_association" "InstallCwAgent" {
name = "AWS-ConfigureAWSPackage"
association_name = "CwAgentInstall"
schedule_expression = "cron(0 00 01 ? * * *)"
max_concurrency = 10
parameters = {
name = "AmazonCloudWatchAgent"
action = "Install"
installationType = "Uninstall and reinstall"
additionalArguments = "{}"
}
targets {
key = "InstanceIds"
values = ["*"]
}
}
resource "aws_ssm_association" "ConfigCwAgent" {
name = "AmazonCloudWatch-ManageAgent"
association_name = "CwAgentConfiguration"
schedule_expression = "cron(0 00 02 ? * * *)"
max_concurrency = 10
parameters = {
action = "configure"
optionalConfigurationLocation = "AmazonCloudWatch-linux"
optionalConfigurationSource = "ssm"
mode = "ec2"
optionalRestart = "yes"
}
targets {
key = "InstanceIds"
values = ["*"]
}
}
resource "aws_ssm_parameter" "CwAgentConfigLinux" {
name = "AmazonCloudWatch-linux"
description = "Cloudwatch agent Standard config for Linux"
type = "String"
value = local.CwAgentLinuxConfig
}
locals {
CwAgentLinuxConfig = jsonencode(
{
"agent" : {
"metrics_collection_interval" : 60,
"run_as_user" : "root"
},
"metrics" : {
"aggregation_dimensions" : [
[
"InstanceId"
]
],
"append_dimensions" : {
"AutoScalingGroupName" : "$${aws:AutoScalingGroupName}",
"ImageId" : "$${aws:ImageId}",
"InstanceId" : "$${aws:InstanceId}",
"InstanceType" : "$${aws:InstanceType}"
},
"metrics_collected" : {
"cpu" : {
"measurement" : [
"cpu_usage_idle",
"cpu_usage_iowait",
"cpu_usage_user",
"cpu_usage_system"
],
"metrics_collection_interval" : 60,
"resources" : [
"*"
],
"totalcpu" : false
},
"disk" : {
"measurement" : [
"used_percent",
"inodes_free"
],
"metrics_collection_interval" : 60,
"resources" : [
"*"
],
"ignore_file_system_types" : [
"devtmpfs",
"overlay",
"sysfs",
"tmpfs"
]
},
"diskio" : {
"measurement" : [
"io_time"
],
"metrics_collection_interval" : 60,
"resources" : [
"*"
]
},
"mem" : {
"measurement" : [
"mem_used_percent"
],
"metrics_collection_interval" : 60
},
"statsd" : {
"metrics_aggregation_interval" : 60,
"metrics_collection_interval" : 10,
"service_address" : ":8125"
},
"swap" : {
"measurement" : [
"swap_used_percent"
],
"metrics_collection_interval" : 60
},
"net": {
"measurement": [
"net_err_in",
"net_err_out"
],
"metrics_collection_interval": 60
},
"processes": {
"measurement": [
"processes_total"
],
"metrics_collection_interval": 60
}
}
}
}
)
}
@@ -0,0 +1,9 @@
terraform {
required_version = ">= 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0"
}
}
}
@@ -0,0 +1,61 @@
<!-- This readme file is generated with terraform-docs -->
This module configure CloudwatchLog and stream logs to s3 bucket via Kinesis Firehose
## Requirements
| Name | Version |
|------|---------|
| terraform | ~> 1.3.0 |
| aws | >= 5.0 |
## Providers
| Name | Version |
|------|---------|
| aws | >= 5.0 |
| random | n/a |
## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [aws_cloudwatch_log_group.firehose-log](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |
| [aws_cloudwatch_log_subscription_filter.cwl-sub-filter](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_subscription_filter) | resource |
| [aws_iam_policy.cwlog-role-policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_policy.firehose-role-policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_role.cwlog-stream-role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role.firehose-stream-iam-role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role_policy_attachment.cwlog-role-policy-attachment](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_iam_role_policy_attachment.firehose-role-policy-attachment](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_kinesis_firehose_delivery_stream.cwl-s3-firehose-stream](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kinesis_firehose_delivery_stream) | resource |
| [random_id.rid](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) | resource |
| [aws_caller_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| cwl-region | AWS region where Cloudwatch LogGroup resides. Needed for setting up cwlog-stream-role | `string` | n/a | yes |
| dest-bucket-arn | Destination S3 bucket ARN | `string` | n/a | yes |
| dest-bucket-kmskey-arn | KMS key ARN for destination bucket | `string` | n/a | yes |
| dest-bucket-prefix | S3 object prefix for this stream. Please do not start with / end with a /. For example, r53-log/acme.local/ | `string` | n/a | yes |
| enable-firehose-errorlog | Enable firehose errorlog | `bool` | `false` | no |
| firehose-kmskey-arn | KMS Key arn for Firehose | `string` | n/a | yes |
| source-cwlgroup-name | Name of source CloudwatchLog group | `string` | n/a | yes |
| stream-name | Name of Kinesis Data Firehose delivery stream | `string` | n/a | yes |
## Outputs
| Name | Description |
|------|-------------|
| cloudwatchstream-iam-role-arn | n/a |
| firehose-iam-role-arn | n/a |
---
## Authorship
This module was developed by Rackspace.
@@ -0,0 +1,162 @@
resource "aws_kinesis_firehose_delivery_stream" "cwl-s3-firehose-stream" {
name = var.stream-name
destination = "extended_s3"
extended_s3_configuration {
role_arn = aws_iam_role.firehose-stream-iam-role.arn
bucket_arn = var.dest-bucket-arn
prefix = trimprefix(var.dest-bucket-prefix, "/")
error_output_prefix = "FirehoseErrors/"
kms_key_arn = var.dest-bucket-kmskey-arn
compression_format = "GZIP"
cloudwatch_logging_options {
enabled = var.enable-firehose-errorlog
log_group_name = try(aws_cloudwatch_log_group.firehose-log[0].name, null)
log_stream_name = "DestinationDelivery"
}
}
server_side_encryption {
enabled = true
key_type = "CUSTOMER_MANAGED_CMK"
key_arn = var.firehose-kmskey-arn
}
}
resource "aws_cloudwatch_log_group" "firehose-log" {
count = var.enable-firehose-errorlog ? 1 : 0
name = "/aws/kinesisfirehose/${var.stream-name}"
retention_in_days = 365
}
resource "aws_cloudwatch_log_subscription_filter" "cwl-sub-filter" {
log_group_name = var.source-cwlgroup-name
name = "stream-to-s3"
role_arn = aws_iam_role.cwlog-stream-role.arn
filter_pattern = ""
destination_arn = aws_kinesis_firehose_delivery_stream.cwl-s3-firehose-stream.arn
}
resource "random_id" "rid" {
byte_length = 4
}
resource "aws_iam_role" "firehose-stream-iam-role" {
name = "firehose-stream-role-${var.stream-name}-${random_id.rid.dec}"
description = "Kinesis Firehose IAM role for streaming logs from CloudwatchLog to S3"
assume_role_policy = jsonencode(
{
"Version" : "2012-10-17",
"Statement" : [
{
"Sid" : "FirehoseStreaming",
"Effect" : "Allow",
"Principal" : {
"Service" : "firehose.amazonaws.com"
},
"Action" : "sts:AssumeRole"
}
]
}
)
}
resource "aws_iam_role_policy_attachment" "firehose-role-policy-attachment" {
role = aws_iam_role.firehose-stream-iam-role.name
policy_arn = aws_iam_policy.firehose-role-policy.arn
}
resource "aws_iam_policy" "firehose-role-policy" {
name = "kinesis-firehose-log-stream-${var.stream-name}-${random_id.rid.dec}"
description = "Policy for Kinesis Firehose streaming logs to s3"
policy = jsonencode(
{
"Version" : "2012-10-17",
"Statement" : [
{
"Effect" : "Allow",
"Action" : [
"s3:AbortMultipartUpload",
"s3:GetBucketLocation",
"s3:GetObject",
"s3:ListBucket",
"s3:ListBucketMultipartUploads",
"s3:PutObject"
],
"Resource" : [
var.dest-bucket-arn,
"${var.dest-bucket-arn}/*"
]
},
{
"Effect" : "Allow",
"Action" : [
"kms:Decrypt",
"kms:GenerateDataKey"
],
"Resource" : [
var.dest-bucket-kmskey-arn
]
},
{
"Effect" : "Allow",
"Action" : [
"logs:PutLogEvents",
"logs:PutLogEventsBatch",
"logs:CreateLogStream"
],
"Resource" : [
"arn:aws:logs:*:*:log-group:/aws/kinesisfirehose/${var.stream-name}/*"
]
}
]
}
)
}
resource "aws_iam_role" "cwlog-stream-role" {
name = "cloudwatchlog-stream-role-${var.stream-name}-${random_id.rid.dec}"
description = "CloudwatchLog role for streaming to firehose"
assume_role_policy = jsonencode(
{
"Version" : "2012-10-17",
"Statement" : [
{
"Sid" : "CloudwatchLogStreaming",
"Effect" : "Allow",
"Principal" : {
"Service" : "logs.${var.cwl-region}.amazonaws.com"
},
"Action" : "sts:AssumeRole"
}
]
}
)
}
resource "aws_iam_role_policy_attachment" "cwlog-role-policy-attachment" {
role = aws_iam_role.cwlog-stream-role.name
policy_arn = aws_iam_policy.cwlog-role-policy.arn
}
resource "aws_iam_policy" "cwlog-role-policy" {
name = "cloudwatchlog-stream-${var.stream-name}-${random_id.rid.dec}"
description = "Policy for CloudWatch Logs streaming to Kinesis Firehose"
policy = jsonencode(
{
"Version" : "2012-10-17",
"Statement" : [
{
"Effect" : "Allow",
"Action" : ["firehose:PutRecord"],
"Resource" : [
"arn:aws:firehose:${var.cwl-region}:${data.aws_caller_identity.this.account_id}:deliverystream/${var.stream-name}"
]
}
]
}
)
}
data "aws_caller_identity" "this" {}
@@ -0,0 +1,7 @@
output firehose-iam-role-arn {
value = aws_iam_role.firehose-stream-iam-role.arn
}
output cloudwatchstream-iam-role-arn {
value = aws_iam_role.cwlog-stream-role.arn
}
@@ -0,0 +1,40 @@
variable "stream-name" {
type = string
description = "Name of Kinesis Data Firehose delivery stream"
}
variable "firehose-kmskey-arn" {
type = string
description = "KMS Key arn for Firehose"
}
variable "dest-bucket-arn" {
type = string
description = "Destination S3 bucket ARN"
}
variable "dest-bucket-prefix" {
type = string
description = "S3 object prefix for this stream. Please do not start with / end with a /. For example, r53-log/acme.local/"
}
variable "dest-bucket-kmskey-arn" {
type = string
description = "KMS key ARN for destination bucket"
}
variable "source-cwlgroup-name" {
type = string
description = "Name of source CloudwatchLog group"
}
variable "cwl-region" {
type = string
description = "AWS region where Cloudwatch LogGroup resides. Needed for setting up cwlog-stream-role"
}
variable "enable-firehose-errorlog" {
type = bool
description = "Enable firehose errorlog"
default = false
}
@@ -0,0 +1,9 @@
terraform {
required_version = "~> 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0"
}
}
}
@@ -0,0 +1,22 @@
# Monitoring module
This module deploys the default cloudwatch metric monitoring
## Notes
Terraform lifecycle ignores tags to speed up terraform subsequent update. Cloudwatch alarm tags cannot be read on aws console anyway.
## Example
```terraform
module "alb-arns" {
source = "../../modules/util/resource-list"
resource-type = "alb"
}
module "alb-monitoring" {
for_each = toset(split(" ", data.external.alb-arns.result.result))
source = "../../modules/ManagementGovernance/Monitoring.ALB"
default-tags = local.default-tags
load-balancer = each.value
threshold-HealthHostCountMin = 1
}
```
@@ -0,0 +1,6 @@
#!/bin/bash
eval "$(jq -r '@sh "lb=\(.lb)"')"
RESULTS=$(aws elbv2 describe-target-groups --load-balancer-arn $lb --query TargetGroups[*].TargetGroupArn --output text --no-cli-pager | sed 's/\t/\n/g' | sort | xargs)
jq -n --arg result "$RESULTS" '{"result":$result}'
@@ -0,0 +1,110 @@
locals {
alb-name = "app/${split("/", var.load-balancer)[2]}/${split("/", var.load-balancer)[3]}"
}
resource "aws_cloudwatch_metric_alarm" "alb-HTTPCode_ELB_5XX_Count" {
alarm_name = "${var.settings.HTTPCode_ELB_5XX_Count.ecccode}-ALB_${local.alb-name}-HTTPCode_ELB_5XX_Count"
comparison_operator = var.settings.HTTPCode_ELB_5XX_Count.comparison_operator
evaluation_periods = var.settings.HTTPCode_ELB_5XX_Count.evaluation_periods
metric_name = "HTTPCode_ELB_5XX_Count"
period = var.settings.HTTPCode_ELB_5XX_Count.period
statistic = var.settings.HTTPCode_ELB_5XX_Count.statistic
threshold = var.settings.HTTPCode_ELB_5XX_Count.threshold
alarm_description = "ALB:HTTPCode_ELB_5XX_Count"
namespace = "AWS/ApplicationELB"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.HTTPCode_ELB_5XX_Count.action]
ok_actions = [var.settings.HTTPCode_ELB_5XX_Count.action]
dimensions = {
LoadBalancer = local.alb-name
}
}
resource "aws_cloudwatch_metric_alarm" "alb-TargetConnectionErrorCount" {
alarm_name = "${var.settings.TargetConnectionErrorCount.ecccode}-ALB_${local.alb-name}-TargetConnectionErrorCount"
comparison_operator = var.settings.TargetConnectionErrorCount.comparison_operator
evaluation_periods = var.settings.TargetConnectionErrorCount.evaluation_periods
metric_name = "TargetConnectionErrorCount"
period = var.settings.TargetConnectionErrorCount.period
statistic = var.settings.TargetConnectionErrorCount.statistic
threshold = var.settings.TargetConnectionErrorCount.threshold
alarm_description = "ALB:TargetConnectionErrorCount"
namespace = "AWS/ApplicationELB"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.TargetConnectionErrorCount.action]
ok_actions = [var.settings.TargetConnectionErrorCount.action]
dimensions = {
LoadBalancer = local.alb-name
}
}
resource "aws_cloudwatch_metric_alarm" "alb-TargetResponseTime" {
alarm_name = "${var.settings.TargetResponseTime.ecccode}-ALB_${local.alb-name}-TargetResponseTime"
comparison_operator = var.settings.TargetResponseTime.comparison_operator
evaluation_periods = var.settings.TargetResponseTime.evaluation_periods
metric_name = "TargetResponseTime"
period = var.settings.TargetResponseTime.period
statistic = var.settings.TargetResponseTime.statistic
threshold = var.settings.TargetResponseTime.threshold
alarm_description = "ALB:TargetResponseTime"
namespace = "AWS/ApplicationELB"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.TargetResponseTime.action]
ok_actions = [var.settings.TargetResponseTime.action]
dimensions = {
LoadBalancer = local.alb-name
}
}
/*
module "alb-targetgroups" {
source = "../../util/resource-list"
resource-type = "alb-targetgroups"
query-input = var.load-balancer
asrolearn = var.asrolearn
}
*/
// causes Rate exceeded error, maybe because of adaptive AWS_RETRY_MODE?
/*
module "alb_tgs" {
assume_role_arn = var.asrolearn
role_session_name = "terraform-resource-list"
source = "../../util/terraform-aws-cli"
aws_cli_commands = ["elbv2", "describe-target-groups", "--load-balancer-arn", var.load-balancer]
aws_cli_query = "TargetGroups[*].TargetGroupArn"
}
*/
module alb_tgs {
source = "../../util/awscli"
access_key = var.target-account-ak
aws_cli_commands = "elbv2 describe-target-groups --load-balancer-arn ${var.load-balancer} --query TargetGroups[*].TargetGroupArn"
secret_key = var.target-account-sk
session_token = var.target-account-token
}
resource "aws_cloudwatch_metric_alarm" "alb-HealthyHostCount" {
# for_each = module.alb-targetgroups.result-set
for_each = toset(module.alb_tgs.awscliout)
alarm_name = "${var.settings.HealthHostCountMin.ecccode}-ALBTG_:${split(":", each.value)[5]}-HealthyHostCount"
comparison_operator = var.settings.HealthHostCountMin.comparison_operator
evaluation_periods = var.settings.HealthHostCountMin.evaluation_periods
metric_name = "HealthyHostCount"
period = var.settings.HealthHostCountMin.period
statistic = var.settings.HealthHostCountMin.statistic
threshold = var.settings.HealthHostCountMin.threshold
alarm_description = "ALBTG:HealthyHostCount"
namespace = "AWS/ApplicationELB"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.HealthHostCountMin.action]
ok_actions = [var.settings.HealthHostCountMin.action]
dimensions = {
TargetGroup = split(":", each.value)[5]
LoadBalancer = "app/${split("/", var.load-balancer)[2]}/${split("/", var.load-balancer)[3]}"
}
}
@@ -0,0 +1,4 @@
output alb-tg-count {
# value = length(module.alb-targetgroups.result-set)
value = length(flatten(module.alb_tgs.awscliout))
}
@@ -0,0 +1,9 @@
terraform {
required_version = "~> 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.36.1"
}
}
}
@@ -0,0 +1,8 @@
variable cw-alarm-prefix {}
variable actions-enabled {}
variable load-balancer {}
variable settings {}
# variable asrolearn {}
variable target-account-ak {}
variable target-account-sk {}
variable target-account-token {}
@@ -0,0 +1,24 @@
# Monitoring module
This module deploys the default cloudwatch metric monitoring
## Notes
Terraform lifecycle ignores tags to speed up terraform subsequent update. Cloudwatch alarm tags cannot be read on aws console anyway.
## Example
```terraform
module "asg" {
source = "../../modules/util/resource-list"
resource-type = "asg"
}
module "asg-monitoring" {
cw-alarm-prefix = local.cw-alarm-prefix
for_each = module.asg.result-set
source = "../../modules/ManagementGovernance/Monitoring.ASG"
default-tags = local.default-tags
asg-name = each.value
threshold-CPUUtilization = 90
actions-enabled = var.actions-enabled
sns-targets = var.sns-targets
}
```
@@ -0,0 +1,41 @@
data "aws_autoscaling_group" "asg" {
name = var.asg-name
}
resource "aws_cloudwatch_metric_alarm" "asg-CPUUtilization" {
alarm_name = "${var.settings.CPUUtilization.ecccode}-ASG_${var.asg-name}-CPUUtilization"
comparison_operator = var.settings.CPUUtilization.comparison_operator
evaluation_periods = var.settings.CPUUtilization.evaluation_periods
metric_name = "CPUUtilization"
period = var.settings.CPUUtilization.period
statistic = var.settings.CPUUtilization.statistic
threshold = var.settings.CPUUtilization.threshold
alarm_description = "ASG:CPUUtilization"
namespace = "AWS/EC2"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.CPUUtilization.action]
ok_actions = [var.settings.CPUUtilization.action]
dimensions = {
AutoScalingGroupName =var.asg-name
}
}
resource "aws_cloudwatch_metric_alarm" "asg-GroupInServiceCapacity" {
alarm_name = "${var.settings.GroupInServiceCapacity.ecccode}-ASG_${var.asg-name}-GroupInServiceCapacity"
comparison_operator = "LessThanThreshold"
evaluation_periods = var.settings.GroupInServiceCapacity.evaluation_periods
metric_name = "GroupInServiceCapacity"
period = var.settings.GroupInServiceCapacity.period
statistic = "Minimum"
threshold = data.aws_autoscaling_group.asg.min_size
alarm_description = "ASG:GroupInServiceCapacity"
namespace = "AWS/AutoScaling"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.GroupInServiceCapacity.action]
ok_actions = [var.settings.GroupInServiceCapacity.action]
dimensions = {
AutoScalingGroupName = var.asg-name
}
}
@@ -0,0 +1,9 @@
terraform {
required_version = "~> 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.36.1"
}
}
}
@@ -0,0 +1,5 @@
variable cw-alarm-prefix {}
variable actions-enabled {}
variable asg-name {}
variable settings {}
variable ecccode {}
@@ -0,0 +1,74 @@
# Monitoring module
This module deploys the default cloudwatch metric monitoring
## Notes
Terraform lifecycle ignores tags to speed up terraform subsequent update. Cloudwatch alarm tags cannot be read on aws console anyway.
## Example
```terraform
module "ec2-instances" {
source = "../../modules/util/resource-list"
resource-type = "ec2"
}
module "ec2-monitoring" {
cw-alarm-prefix = local.cw-alarm-prefix
for_each = module.ec2-instances.result-set
source = "../../modules/ManagementGovernance/Monitoring.EC2"
default-tags = local.default-tags
ec2-instance-id = each.value
threshold-CPUUtilization = 90
threshold-mem_free = 100000
threshold-swap_free = 100000
threshold-disk_free = 1 * 1000 * 1000 * 1000
threshold-disk_inodes_free = 10000
threshold-processes_total = 500
threshold-LogicalDiskFreePct = 10
threshold-MemoryCommittedPct = 90
actions-enabled = var.actions-enabled
sns-targets = var.sns-targets
}
```
## Sample cloudwatch alarm email notification
```
Subject: ALARM: "TestAlarmPleaseIgnore" in Asia Pacific (Hong Kong)
You are receiving this email because your Amazon CloudWatch Alarm "TestAlarmPleaseIgnore" in the
Asia Pacific (Hong Kong) region has entered the ALARM state, because "Threshold Crossed: 1 out of
the last 1 datapoints [864.0 (24/01/24 00:56:00)] was less than or equal to the threshold (900.0)
(minimum 1 datapoint for OK -> ALARM transition)." at "Wednesday 24 January, 2024 01:01:34 UTC".
View this alarm in the AWS Management Console:
https://ap-east-1.console.aws.amazon.com%2Fcloudwatch...
Alarm Details:
- Name: TestAlarmPleaseIgnore
- Description: Cloudwatch alarm for the following resource
- Instance ID: xxx
- Instance Name: yyy
- Instance IP: zz.zz.zz.zz
- State Change: OK -> ALARM
- Reason for State Change: Threshold Crossed: 1 out of the last 1 datapoints [864.0 (24/01/24 00:56:00)] was less than or equal to the threshold (900.0) (minimum 1 datapoint for OK -> ALARM transition).
- Timestamp: Wednesday 24 January, 2024 01:01:34 UTC
- AWS Account: 111122223333
- Alarm Arn: arn:aws:cloudwatch:ap-east-1:111122223333:alarm:TestAlarmPleaseIgnore
Threshold:
- The alarm is in the ALARM state when the metric is LessThanOrEqualToThreshold 900.0 for at least 1 of the last 1 period(s) of 300 seconds.
Monitored Metric:
- MetricNamespace: AWS/EC2
- MetricName: CPUCreditBalance
- Dimensions: [InstanceId = i-050d4adeafaa53cd0]
- Period: 300 seconds
- Statistic: Average
- Unit: not specified
- TreatMissingData: missing
State Change Actions:
- OK:
- ALARM: [arn:aws:sns:ap-east-1:111122223333:CWA-SNS-Email-KenFong]
- INSUFFICIENT_DATA:
```
@@ -0,0 +1,22 @@
#!/bin/bash
eval "$(jq -r '@sh "export id=\(.input) asrolearn=\(.asrolearn)"')"
eval $(aws sts assume-role --role-arn $asrolearn --role-session-name awscli | jq -cr '"export AWS_ACCESS_KEY_ID=" + .Credentials.AccessKeyId, "export AWS_SECRET_ACCESS_KEY=" + .Credentials.SecretAccessKey, "export AWS_SESSION_TOKEN=" + .Credentials.SessionToken, "export AWS_SESSION_EXPIRATION=" + .Credentials.Expiration')
aws cloudwatch list-metrics --namespace CWAgent --metric-name disk_inodes_free \
--dimensions Name=InstanceId,Value=$id Name=path,Value=/ | \
jq '.Metrics[] | .Dimensions[] | select ((.Name=="device") or (.Name=="fstype")) | { (.Name): (.Value)}' | \
jq -s 'add // {"device":"unknown", "fstype":"unknown"}'
exit 0
DEVICE=$(aws cloudwatch list-metrics --namespace CWAgent --metric-name disk_inodes_free \
--dimensions Name=InstanceId,Value=$id Name=path,Value=/ \
--query 'Metrics[].Dimensions[?Name==`device`].Value' --output text)
FSTYPE=$(aws cloudwatch list-metrics --namespace CWAgent --metric-name disk_inodes_free \
--dimensions Name=InstanceId,Value=$id Name=path,Value=/ \
--query 'Metrics[].Dimensions[?Name==`fstype`].Value' --output text)
jq -n --arg device "$DEVICE" --arg fstype "$FSTYPE" '{"device":$device,"fstype":$fstype}'
@@ -0,0 +1,25 @@
#!/bin/bash
# Get the query
TERRAFORM_QUERY=$(jq -Mc .)
# Extract the query attributes
access_key=$(echo "${TERRAFORM_QUERY}" | jq -r '.access_key')
secret_key=$(echo "${TERRAFORM_QUERY}" | jq -r '.secret_key')
session_token=$(echo "${TERRAFORM_QUERY}" | jq -r '.session_token')
iid=$(echo "${TERRAFORM_QUERY}" | jq -r '.iid')
# eval "$(jq -r '@sh "export id=\(.input) asrolearn=\(.asrolearn)"')"
# eval $(aws sts assume-role --role-arn $asrolearn --role-session-name awscli | jq -cr '"export AWS_ACCESS_KEY_ID=" + .Credentials.AccessKeyId, "export AWS_SECRET_ACCESS_KEY=" + .Credentials.SecretAccessKey, "export AWS_SESSION_TOKEN=" + .Credentials.SessionToken, "export AWS_SESSION_EXPIRATION=" + .Credentials.Expiration')
export AWS_ACCESS_KEY_ID=$access_key
export AWS_SECRET_ACCESS_KEY=$secret_key
export AWS_SESSION_TOKEN=$session_token
#aws cloudwatch list-metrics --namespace CWAgent --metric-name disk_inodes_free \
#--dimensions Name=InstanceId,Value=$iid Name=path,Value=/ | \
#jq '.Metrics[] | .Dimensions[] | {(.Name):(.Value)}' | jq -s 'add'
# when there are multiple metrics with the same name...
aws cloudwatch list-metrics --namespace CWAgent --metric-name disk_inodes_free \
--dimensions Name=InstanceId,Value=$iid Name=path,Value=/ --query Metrics[] | \
jq '. | last | .Dimensions[] | {(.Name):(.Value)}' | jq -s 'add'
@@ -0,0 +1,12 @@
#!/bin/bash
eval "$(jq -r '@sh "export id=\(.input) asrolearn=\(.asrolearn)"')"
eval $(aws sts assume-role --role-arn $asrolearn --role-session-name awscli | jq -cr '"export AWS_ACCESS_KEY_ID=" + .Credentials.AccessKeyId, "export AWS_SECRET_ACCESS_KEY=" + .Credentials.SecretAccessKey, "export AWS_SESSION_TOKEN=" + .Credentials.SessionToken, "export AWS_SESSION_EXPIRATION=" + .Credentials.Expiration')
EC2OS=$(aws ec2 describe-instances --instance-ids $id | jq -r '.Reservations[].Instances[].PlatformDetails')
if [ $EC2OS == "Windows" ]; then
echo '{"os": "Windows"}'
else
echo '{"os": "Linux"}'
fi
@@ -0,0 +1,395 @@
locals {
# alarm-message limited to 1024 characters
alarm-message = <<EOF
Cloudwatch alarm for the following resource
- Instance ID: ${var.ec2-instance-id}
- Instance Name: ${data.aws_instance.ec2-instance.tags["Name"]}
- Instance IP: ${data.aws_instance.ec2-instance.private_ip}
- Instance Type: ${data.aws_instance.ec2-instance.instance_type}
EOF
}
resource "aws_cloudwatch_metric_alarm" "ec2-StatusCheckFailed_System" {
alarm_name = "${var.settings.StatusCheckFailed_System.ecccode}-EC2_${var.ec2-instance-id}-StatusCheckFailed_System"
comparison_operator = var.settings.StatusCheckFailed_System.comparison_operator
evaluation_periods = var.settings.StatusCheckFailed_System.evaluation_periods
metric_name = "StatusCheckFailed_System"
period = var.settings.StatusCheckFailed_System.period
statistic = var.settings.StatusCheckFailed_System.statistic
threshold = var.settings.StatusCheckFailed_System.threshold
# alarm_description = "EC2:StatusCheckFailed_System"
alarm_description = local.alarm-message
namespace = "AWS/EC2"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.StatusCheckFailed_System.action]
ok_actions = [var.settings.StatusCheckFailed_System.action]
dimensions = {
InstanceId = var.ec2-instance-id
}
}
resource "aws_cloudwatch_metric_alarm" "ec2-StatusCheckFailed_Instance" {
alarm_name = "${var.settings.StatusCheckFailed_Instance.ecccode}-EC2_${var.ec2-instance-id}-StatusCheckFailed_Instance"
comparison_operator = var.settings.StatusCheckFailed_Instance.comparison_operator
evaluation_periods = var.settings.StatusCheckFailed_Instance.evaluation_periods
metric_name = "StatusCheckFailed_Instance"
period = var.settings.StatusCheckFailed_Instance.period
statistic = var.settings.StatusCheckFailed_Instance.statistic
threshold = var.settings.StatusCheckFailed_Instance.threshold
# alarm_description = "EC2:StatusCheckFailed_Instance"
alarm_description = local.alarm-message
namespace = "AWS/EC2"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.StatusCheckFailed_Instance.action]
ok_actions = [var.settings.StatusCheckFailed_Instance.action]
dimensions = {
InstanceId = var.ec2-instance-id
}
}
resource "aws_cloudwatch_metric_alarm" "ec2-CPUUtilization" {
alarm_name = "${var.settings.CPUUtilization.ecccode}-EC2_${var.ec2-instance-id}-CPUUtilization"
comparison_operator = var.settings.CPUUtilization.comparison_operator
evaluation_periods = var.settings.CPUUtilization.evaluation_periods
metric_name = "CPUUtilization"
period = var.settings.CPUUtilization.period
statistic = var.settings.CPUUtilization.statistic
threshold = var.settings.CPUUtilization.threshold
# alarm_description = "EC2:CPUUtilization"
alarm_description = local.alarm-message
namespace = "AWS/EC2"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.CPUUtilization.action]
ok_actions = [var.settings.CPUUtilization.action]
treat_missing_data = "notBreaching"
dimensions = {
InstanceId = var.ec2-instance-id
}
}
# cwagent metrics
data "aws_instance" "ec2-instance" {
instance_id = var.ec2-instance-id
}
# put instance name or ip in alarm name
locals {
instance-ip = data.aws_instance.ec2-instance.private_ip
instance-name = data.aws_instance.ec2-instance.tags["Name"]
}
module "ec2_os" {
source = "../../util/awscli"
access_key = var.target-account-ak
aws_cli_commands = "ec2 describe-instances --instance-ids ${var.ec2-instance-id} --query Reservations[].Instances[].PlatformDetails"
secret_key = var.target-account-sk
session_token = var.target-account-token
}
# Linux specific checks
# default cw agent uses mem_used_percent metric
# detect presense of cloudwatch agent
module "detect_cloudwatch_agent" {
source = "../../util/awscli"
access_key = var.target-account-ak
secret_key = var.target-account-sk
session_token = var.target-account-token
aws_cli_commands = "cloudwatch list-metrics --namespace CWAgent --dimensions Name=InstanceId,Value=${var.ec2-instance-id} --query Metrics[].MetricName --max-items 1"
}
resource "aws_cloudwatch_metric_alarm" "ec2-mem_used_percent" {
count = module.ec2_os.awscliout[0] != "Windows" && length(module.detect_cloudwatch_agent.awscliout) > 0 ? 1 : 0
alarm_name = "${var.settings.mem_used_percent.ecccode}-EC2_${var.ec2-instance-id}-mem_used_percent"
comparison_operator = var.settings.mem_used_percent.comparison_operator
evaluation_periods = var.settings.mem_used_percent.evaluation_periods
metric_name = "mem_used_percent"
period = var.settings.mem_used_percent.period
statistic = var.settings.mem_used_percent.statistic
threshold = var.settings.mem_used_percent.threshold
# alarm_description = "EC2:mem_used_percent"
alarm_description = local.alarm-message
namespace = "CWAgent"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.mem_used_percent.action]
ok_actions = [var.settings.mem_used_percent.action]
dimensions = {
InstanceId = var.ec2-instance-id
ImageId = data.aws_instance.ec2-instance.ami
InstanceType = data.aws_instance.ec2-instance.instance_type
}
}
data "external" "cw-dimensions" {
program = ["bash", "${path.module}/get-cwagent-dimensions.sh"]
query = {
iid = var.ec2-instance-id
access_key = var.target-account-ak
secret_key = var.target-account-sk
session_token = var.target-account-token
}
}
/* module returns blank
module "cw-dimensions" {
source = "../../util/awscli"
access_key = var.target-account-ak
aws_cli_commands = "cloudwatch list-metrics --namespace CWAgent --metric-name disk_inodes_free --dimensions Name=InstanceId,Value=${var.ec2-instance-id} Name=path,Value=/ --query Metrics[].Dimensions[] | jq '.[] | {(.Name):(.Value)}' | jq -s 'add'"
secret_key = var.target-account-sk
session_token = var.target-account-token
}
*/
resource "aws_cloudwatch_metric_alarm" "ec2-swap_used_percent" {
count = module.ec2_os.awscliout[0] != "Windows" && length(module.detect_cloudwatch_agent.awscliout) > 0 ? 1 : 0
alarm_name = "${var.settings.swap_used_percent.ecccode}-EC2_${var.ec2-instance-id}-swap_used_percent"
comparison_operator = var.settings.swap_used_percent.comparison_operator
evaluation_periods = var.settings.swap_used_percent.evaluation_periods
metric_name = "swap_used_percent"
period = var.settings.swap_used_percent.period
statistic = var.settings.swap_used_percent.statistic
threshold = var.settings.swap_used_percent.threshold
# alarm_description = "EC2:swap_used_percent"
alarm_description = local.alarm-message
namespace = "CWAgent"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.swap_used_percent.action]
ok_actions = [var.settings.swap_used_percent.action]
dimensions = {
InstanceId = var.ec2-instance-id
ImageId = data.aws_instance.ec2-instance.ami
InstanceType = data.aws_instance.ec2-instance.instance_type
}
}
resource "aws_cloudwatch_metric_alarm" "ec2-disk_used_percent_warn" {
count = module.ec2_os.awscliout[0] != "Windows" && data.external.cw-dimensions.result != null ? 1 : 0
alarm_name = "${var.settings.disk_used_percent_warn.ecccode}-EC2_${var.ec2-instance-id}-disk_used_percent"
comparison_operator = var.settings.disk_used_percent_warn.comparison_operator
evaluation_periods = var.settings.disk_used_percent_warn.evaluation_periods
metric_name = "disk_used_percent"
period = var.settings.disk_used_percent_warn.period
statistic = var.settings.disk_used_percent_warn.statistic
threshold = var.settings.disk_used_percent_warn.threshold
# alarm_description = "EC2:disk_used_percent"
alarm_description = local.alarm-message
namespace = "CWAgent"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.disk_used_percent_warn.action]
ok_actions = [var.settings.disk_used_percent_warn.action]
dimensions = data.external.cw-dimensions.result
}
resource "aws_cloudwatch_metric_alarm" "ec2-disk_used_percent_crit" {
count = module.ec2_os.awscliout[0] != "Windows" && data.external.cw-dimensions.result != null ? 1 : 0
alarm_name = "${var.settings.disk_used_percent_crit.ecccode}-EC2_${var.ec2-instance-id}-disk_used_percent"
comparison_operator = var.settings.disk_used_percent_crit.comparison_operator
evaluation_periods = var.settings.disk_used_percent_crit.evaluation_periods
metric_name = "disk_used_percent"
period = var.settings.disk_used_percent_crit.period
statistic = var.settings.disk_used_percent_crit.statistic
threshold = var.settings.disk_used_percent_crit.threshold
# alarm_description = "EC2:disk_used_percent"
alarm_description = local.alarm-message
namespace = "CWAgent"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.disk_used_percent_crit.action]
ok_actions = [var.settings.disk_used_percent_crit.action]
dimensions = data.external.cw-dimensions.result
}
resource "aws_cloudwatch_metric_alarm" "ec2-disk_inodes_free" {
count = module.ec2_os.awscliout[0] != "Windows" && data.external.cw-dimensions.result != null ? 1 : 0
alarm_name = "${var.settings.disk_inodes_free.ecccode}-EC2_${var.ec2-instance-id}-disk_inodes_free"
comparison_operator = var.settings.disk_inodes_free.comparison_operator
evaluation_periods = var.settings.disk_inodes_free.evaluation_periods
metric_name = "disk_inodes_free"
period = var.settings.disk_inodes_free.period
statistic = var.settings.disk_inodes_free.statistic
threshold = var.settings.disk_inodes_free.threshold
# alarm_description = "EC2:disk_inodes_free"
alarm_description = local.alarm-message
namespace = "CWAgent"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.disk_inodes_free.action]
ok_actions = [var.settings.disk_inodes_free.action]
dimensions = data.external.cw-dimensions.result
}
# process metric not published by default cw agent config
resource "aws_cloudwatch_metric_alarm" "ec2-processes_total" {
count = module.ec2_os.awscliout[0] != "Windows" && length(module.detect_cloudwatch_agent.awscliout) > 0 ? 1 : 0
alarm_name = "${var.settings.processes_total.ecccode}-EC2_${var.ec2-instance-id}-processes_total"
comparison_operator = var.settings.processes_total.comparison_operator
evaluation_periods = var.settings.processes_total.evaluation_periods
metric_name = "processes_total"
period = var.settings.processes_total.period
statistic = var.settings.processes_total.statistic
threshold = var.settings.processes_total.threshold
# alarm_description = "EC2:processes_total"
alarm_description = local.alarm-message
namespace = "CWAgent"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.processes_total.action]
ok_actions = [var.settings.processes_total.action]
dimensions = {
InstanceId = var.ec2-instance-id
ImageId = data.aws_instance.ec2-instance.ami
InstanceType = data.aws_instance.ec2-instance.instance_type
}
}
resource "aws_cloudwatch_metric_alarm" "ec2-net_err" {
count = module.ec2_os.awscliout[0] != "Windows" && length(module.detect_cloudwatch_agent.awscliout) > 0 ? 1 : 0
alarm_name = "${var.settings.net_err_in.ecccode}-EC2_${var.ec2-instance-id}-net_err"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = var.settings.net_err_in.evaluation_periods
threshold = 0
# alarm_description = "EC2:net_err_in or EC2:net_err_out exceeds threshold"
alarm_description = local.alarm-message
insufficient_data_actions = []
actions_enabled = false
alarm_actions = [var.settings.net_err_in.action]
ok_actions = [var.settings.net_err_in.action]
treat_missing_data = "notBreaching"
metric_query {
id = "e1"
expression = "IF(m1 > ${var.settings.net_err_in.threshold} OR m2 > ${var.settings.net_err_out.threshold}, 1, 0)"
label = "net_err_exceeds_threshold"
return_data = "true"
}
metric_query {
id = "m1"
metric {
metric_name = "net_err_in"
namespace = "CWAgent"
period = var.settings.net_err_in.period
stat = var.settings.net_err_in.statistic
dimensions = {
InstanceId = var.ec2-instance-id
ImageId = data.aws_instance.ec2-instance.ami
InstanceType = data.aws_instance.ec2-instance.instance_type
interface = "eth0"
}
}
}
metric_query {
id = "m2"
metric {
metric_name = "net_err_out"
namespace = "CWAgent"
period = var.settings.net_err_out.period
stat = var.settings.net_err_out.statistic
dimensions = {
InstanceId = var.ec2-instance-id
ImageId = data.aws_instance.ec2-instance.ami
InstanceType = data.aws_instance.ec2-instance.instance_type
interface = "eth0"
}
}
}
}
resource "aws_cloudwatch_metric_alarm" "ec2-NetworkIn" {
count = try(var.settings.NetworkIn.monitor, false) ? 1 : 0
alarm_name = "${var.settings.NetworkIn.ecccode}-EC2_${var.ec2-instance-id}-NetworkIn"
comparison_operator = var.settings.NetworkIn.comparison_operator
evaluation_periods = var.settings.NetworkIn.evaluation_periods
metric_name = "NetworkIn"
period = var.settings.NetworkIn.period
statistic = var.settings.NetworkIn.statistic
threshold = var.settings.NetworkIn.threshold
# alarm_description = "EC2:NetworkIn"
alarm_description = local.alarm-message
namespace = "AWS/EC2"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.NetworkIn.action]
ok_actions = [var.settings.NetworkIn.action]
dimensions = {
InstanceId = var.ec2-instance-id
}
}
resource "aws_cloudwatch_metric_alarm" "ec2-NetworkOut" {
count = try(var.settings.NetworkIn.monitor, false) ? 1 : 0
alarm_name = "${var.settings.NetworkOut.ecccode}-EC2_${var.ec2-instance-id}-NetworkOut"
comparison_operator = var.settings.NetworkOut.comparison_operator
evaluation_periods = var.settings.NetworkOut.evaluation_periods
metric_name = "NetworkOut"
period = var.settings.NetworkOut.period
statistic = var.settings.NetworkOut.statistic
threshold = var.settings.NetworkOut.threshold
# alarm_description = "EC2:NetworkOut"
alarm_description = local.alarm-message
namespace = "AWS/EC2"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.NetworkOut.action]
ok_actions = [var.settings.NetworkOut.action]
dimensions = {
InstanceId = var.ec2-instance-id
}
}
# Windows specific checks
resource "aws_cloudwatch_metric_alarm" "ec2-MemoryCommittedPct" {
count = module.ec2_os.awscliout[0] == "Windows" && length(module.detect_cloudwatch_agent.awscliout) > 0 ? 1 : 0
alarm_name = "${var.settings.MemoryCommittedPct.ecccode}-EC2_${var.ec2-instance-id}-MemoryCommittedPct"
comparison_operator = var.settings.MemoryCommittedPct.comparison_operator
evaluation_periods = var.settings.MemoryCommittedPct.evaluation_periods
metric_name = "Memory % Committed Bytes In Use"
period = var.settings.MemoryCommittedPct.period
statistic = var.settings.MemoryCommittedPct.statistic
threshold = var.settings.MemoryCommittedPct.threshold
# alarm_description = "EC2:MemoryCommittedBytes"
alarm_description = local.alarm-message
namespace = "CWAgent"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.MemoryCommittedPct.action]
ok_actions = [var.settings.MemoryCommittedPct.action]
dimensions = {
objectname = "Memory"
InstanceId = var.ec2-instance-id
ImageId = data.aws_instance.ec2-instance.ami
InstanceType = data.aws_instance.ec2-instance.instance_type
}
}
resource "aws_cloudwatch_metric_alarm" "ec2-LogicalDiskFreePct" {
count = module.ec2_os.awscliout[0] == "Windows" && length(module.detect_cloudwatch_agent.awscliout) > 0 ? 1 : 0
alarm_name = "${var.settings.LogicalDiskFreePct.ecccode}-EC2_${var.ec2-instance-id}-LogicalDiskFreePct"
comparison_operator = var.settings.LogicalDiskFreePct.comparison_operator
evaluation_periods = var.settings.LogicalDiskFreePct.evaluation_periods
metric_name = "LogicalDisk % Free Space"
period = var.settings.LogicalDiskFreePct.period
statistic = var.settings.LogicalDiskFreePct.statistic
threshold = var.settings.LogicalDiskFreePct.threshold
# alarm_description = "EC2:OsDiskFreePct"
alarm_description = local.alarm-message
namespace = "CWAgent"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.LogicalDiskFreePct.action]
ok_actions = [var.settings.LogicalDiskFreePct.action]
dimensions = {
instance = "C:"
objectname = "LogicalDisk"
InstanceId = var.ec2-instance-id
ImageId = data.aws_instance.ec2-instance.ami
InstanceType = data.aws_instance.ec2-instance.instance_type
}
}
@@ -0,0 +1,9 @@
terraform {
required_version = "~> 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.36.1"
}
}
}
@@ -0,0 +1,8 @@
variable "cw-alarm-prefix" {}
variable "actions-enabled" {}
variable "ec2-instance-id" {}
variable "settings" {}
# variable asrolearn {}
variable target-account-ak {}
variable target-account-sk {}
variable target-account-token {}
@@ -0,0 +1,27 @@
# Monitoring module
This module deploys the default cloudwatch metric monitoring
## Notes
Terraform lifecycle ignores tags to speed up terraform subsequent update. Cloudwatch alarm tags cannot be read on aws console anyway.
Unlike other monitoring modules which discovers resources details automatically, EKS pod name need to be supplied to this module.
AWS cli does not provide pod information.
## Example
```terraform
data "aws_eks_clusters" "eks-clusters" {}
module "eks-monitoring" {
cw-alarm-prefix = local.cw-alarm-prefix
for_each = data.aws_eks_clusters.eks-clusters.names
source = "../../modules/ManagementGovernance/Monitoring.EKS"
default-tags = local.default-tags
cluster-name = each.value
eks-namespace = "default"
pod-names = ["depl-nginx", "depl-alpine"]
threshold-pod_cpu_utilization = 85
threshold-pod_memory_utilization = 85
threshold-pod_number_of_container_restarts = 5
actions-enabled = var.actions-enabled
sns-targets = local.sns-targets
}
```
@@ -0,0 +1,69 @@
// The following checks requires container insights
resource "aws_cloudwatch_metric_alarm" "eks-pod_cpu_utilization" {
for_each = toset(var.pod-names)
alarm_name = "${each.value["ecccode"]}:${var.cw-alarm-prefix}:EKS:${var.cluster-name}:${each.value}:${var.settings.alarm1.metric}"
comparison_operator = var.settings.alarm1.comparison_operator
evaluation_periods = var.settings.alarm1.evaluation_periods
metric_name = var.settings.alarm1.metric
period = var.settings.alarm1.period
statistic = var.settings.alarm1.statistic
threshold = var.settings.alarm1.threshold
alarm_description = "EKS:${var.settings.alarm1.metric}"
namespace = "ContainerInsights"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.alarm1.action]
ok_actions = [var.settings.alarm1.action]
dimensions = {
"PodName" = each.value
"ClusterName" = var.cluster-name
"Namespace" = var.eks-namespace
}
}
resource "aws_cloudwatch_metric_alarm" "eks-pod_memory_utilization" {
for_each = toset(var.pod-names)
alarm_name = "${each.value["ecccode"]}:${var.cw-alarm-prefix}:EKS:${var.cluster-name}:${each.value}:${var.settings.alarm2.metric}"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = "3"
metric_name = var.settings.alarm2.metric
period = var.settings.alarm2.period
statistic = var.settings.alarm2.statistic
threshold = var.settings.alarm2.threshold
alarm_description = "EKS:${var.settings.alarm2.metric}"
namespace = "ContainerInsights"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.alarm2.action]
ok_actions = [var.settings.alarm2.action]
dimensions = {
"PodName" = each.value
"ClusterName" = var.cluster-name
"Namespace" = var.eks-namespace
}
}
resource "aws_cloudwatch_metric_alarm" "eks-pod_number_of_container_restarts" {
for_each = toset(var.pod-names)
alarm_name = "${each.value["ecccode"]}:${var.cw-alarm-prefix}:EKS:${var.cluster-name}:${each.value}:${var.settings.alarm3.metric}"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = "3"
metric_name = var.settings.alarm3.metric
period = var.settings.alarm3.period
statistic = var.settings.alarm3.statistic
threshold = var.settings.alarm3.threshold
alarm_description = "EKS:${var.settings.alarm3.metric}"
namespace = "ContainerInsights"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.alarm3.action]
ok_actions = [var.settings.alarm3.action]
dimensions = {
"PodName" = each.value
"ClusterName" = var.cluster-name
"Namespace" = var.eks-namespace
}
}
@@ -0,0 +1,9 @@
terraform {
required_version = "~> 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.36.1"
}
}
}
@@ -0,0 +1,8 @@
variable cw-alarm-prefix {}
variable actions-enabled {}
variable cluster-name {}
variable eks-namespace {}
variable pod-names {
type = list
}
variable settings {}
@@ -0,0 +1,25 @@
# Monitoring module
This module deploys the default cloudwatch metric monitoring
## Notes
Terraform lifecycle ignores tags to speed up terraform subsequent update. Cloudwatch alarm tags cannot be read on aws console anyway.
## Example
```terraform
module "emr-clusters" {
source = "../../modules/util/resource-list"
resource-type = "emr"
}
module "emr-monitoring" {
cw-alarm-prefix = local.cw-alarm-prefix
for_each = module.emr-clusters.result-set
source = "../../modules/ManagementGovernance/Monitoring.EMR"
default-tags = local.default-tags
job-flow-id = split("/", each.value)[1]
threshold-AppsPending = 2
threshold-CapacityRemainingGB = 100
actions-enabled = var.actions-enabled
sns-targets = var.sns-targets
}
```
@@ -0,0 +1,19 @@
resource "aws_cloudwatch_metric_alarm" "emr-alarms" {
for_each = var.settings
alarm_name = "${each.value["ecccode"]}-EMR_${var.job-flow-id}-${each.value["metric"]}"
comparison_operator = each.value["comparison_operator"]
evaluation_periods = each.value["evaluation_periods"]
metric_name = each.value["metric"]
period = each.value["period"]
statistic = each.value["statistic"]
threshold = each.value["threshold"]
alarm_description = "EMR:${each.value["metric"]}"
namespace = "AWS/ElasticMapReduce"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [each.value["action"]]
ok_actions = [each.value["action"]]
dimensions = {
JobFlowId = var.job-flow-id
}
}
@@ -0,0 +1,9 @@
terraform {
required_version = "~> 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.36.1"
}
}
}
@@ -0,0 +1,4 @@
variable cw-alarm-prefix {}
variable actions-enabled {}
variable job-flow-id {}
variable settings {}
@@ -0,0 +1,5 @@
# Monitoring module
This module deploys the default cloudwatch metric monitoring
## Notes
Terraform lifecycle ignores tags to speed up terraform subsequent update. Cloudwatch alarm tags cannot be read on aws console anyway.
@@ -0,0 +1,46 @@
resource "aws_cloudwatch_event_rule" "EventRule" {
name = "${var.cw-alarm-prefix}-health-events"
description = "A CloudWatch Event Rule that triggers on changes in the status of AWS Personal Health Dashboard (AWS Health) and forwards the events to an SNS topic."
state = var.actions-enabled
event_pattern = <<PATTERN
{
"detail": {
"service": ["DIRECTCONNECT", "VPN", "LAMBDA", "EC2", "RDS"]
},
"detail-type": [
"AWS Health Event"
],
"source": [
"aws.health"
]
}
PATTERN
lifecycle {
ignore_changes = [tags["LastModified"]]
}
}
resource "aws_cloudwatch_event_target" "TargetForEventRule" {
rule = aws_cloudwatch_event_rule.EventRule.name
# target_id = "health-event-notification-sns"
arn = var.settings.healthEvents.action
input_transformer {
input_paths = {
"account" : "$.account",
"endTime" : "$.detail.endTime",
"message" : "$.detail.eventDescription[0].latestDescription",
"resources" : "$.resources",
"service" : "$.detail.service",
"startTime" : "$.detail.startTime"
}
input_template = <<EOF
"A maintenance has been scheduled for <service> on AWS account <account>."
"Resources: <resources>"
"Start time: <startTime>"
"End time: <endTime>"
"Detail: <message>"
EOF
}
}
@@ -0,0 +1,9 @@
terraform {
required_version = "~> 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.36.1"
}
}
}
@@ -0,0 +1,3 @@
variable cw-alarm-prefix {}
variable actions-enabled {}
variable settings {}
@@ -0,0 +1,24 @@
# Monitoring module
This module deploys the default cloudwatch metric monitoring
## Notes
Terraform lifecycle ignores tags to speed up terraform subsequent update. Cloudwatch alarm tags cannot be read on aws console anyway.
## Example
```terraform
module "kafka-clusters" {
source = "../../modules/util/resource-list"
resource-type = "kafka"
}
module "kafka-monitoring" {
cw-alarm-prefix = local.cw-alarm-prefix
for_each = module.kafka-clusters.result-set
source = "../../modules/ManagementGovernance/Monitoring.Kafka"
default-tags = local.default-tags
cluster-name = each.value
threshold-ZooKeeperRequestLatencyMsMean = 30
actions-enabled = var.actions-enabled
sns-targets = var.sns-targets
}
```
@@ -0,0 +1,116 @@
resource "aws_cloudwatch_metric_alarm" "Kafka-ZooKeeperRequestLatencyMsMean" {
alarm_name = "${var.settings.ZooKeeperRequestLatencyMsMean.ecccode}-Kafka_${var.cluster-name}-ZooKeeperRequestLatencyMsMean"
comparison_operator = var.settings.ZooKeeperRequestLatencyMsMean.comparison_operator
evaluation_periods = var.settings.ZooKeeperRequestLatencyMsMean.evaluation_periods
metric_name = "ZooKeeperRequestLatencyMsMean"
period = var.settings.ZooKeeperRequestLatencyMsMean.period
statistic = var.settings.ZooKeeperRequestLatencyMsMean.statistic
threshold = var.settings.ZooKeeperRequestLatencyMsMean.threshold
alarm_description = "Kafka:ZooKeeperRequestLatencyMsMean"
namespace = "AWS/Kafka"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.ZooKeeperRequestLatencyMsMean.action]
ok_actions = [var.settings.ZooKeeperRequestLatencyMsMean.action]
dimensions = {
"Cluster Name" = var.cluster-name
}
}
data "aws_msk_cluster" "msk-cluster" {
cluster_name = var.cluster-name
}
data "aws_msk_broker_nodes" "msk-broker" {
cluster_arn = data.aws_msk_cluster.msk-cluster.arn
}
resource "aws_cloudwatch_metric_alarm" "Kafka-CpuUserSystem" {
for_each = toset([for i in data.aws_msk_broker_nodes.msk-broker.node_info_list[*].broker_id : tostring(i)])
alarm_name = "${var.settings.CpuUserSystem.ecccode}-Kafka_${var.cluster-name}-${each.value}-CpuUsage"
comparison_operator = var.settings.CpuUserSystem.comparison_operator
evaluation_periods = var.settings.CpuUserSystem.evaluation_periods
threshold = var.settings.CpuUserSystem.threshold
alarm_description = "Kafka:ZooKeeperRequestLatencyMsMean"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.CpuUserSystem.action]
ok_actions = [var.settings.CpuUserSystem.action]
metric_query {
id = "m1"
metric {
metric_name = "CpuUser"
namespace = "AWS/Kafka"
period = var.settings.CpuUserSystem.period
stat = var.settings.CpuUserSystem.statistic
dimensions = {
"Cluster Name" = var.cluster-name
"Broker ID" = each.value
}
}
}
metric_query {
id = "m2"
metric {
metric_name = "CpuSystem"
namespace = "AWS/Kafka"
period = var.settings.CpuUserSystem.period
stat = var.settings.CpuUserSystem.statistic
dimensions = {
"Cluster Name" = var.cluster-name
"Broker ID" = each.value
}
}
}
metric_query {
id = "e1"
expression = "m1 + m2"
label = "CpuUserSystem"
return_data = "true"
}
}
resource "aws_cloudwatch_metric_alarm" "Kafka-KafkaDataLogsDiskUsed" {
for_each = toset([for i in data.aws_msk_broker_nodes.msk-broker.node_info_list[*].broker_id : tostring(i)])
alarm_name = "${var.settings.KafkaDataLogsDiskUsed.ecccode}-Kafka_${var.cluster-name}-${each.value}-KafkaDataLogsDiskUsed"
comparison_operator = var.settings.KafkaDataLogsDiskUsed.comparison_operator
evaluation_periods = var.settings.KafkaDataLogsDiskUsed.evaluation_periods
metric_name = "KafkaDataLogsDiskUsed"
period = var.settings.KafkaDataLogsDiskUsed.period
statistic = var.settings.KafkaDataLogsDiskUsed.statistic
threshold = var.settings.KafkaDataLogsDiskUsed.threshold
alarm_description = "Kafka:KafkaDataLogsDiskUsed"
namespace = "AWS/Kafka"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.KafkaDataLogsDiskUsed.action]
ok_actions = [var.settings.KafkaDataLogsDiskUsed.action]
dimensions = {
"Cluster Name" = var.cluster-name
"Broker ID" = each.value
}
}
resource "aws_cloudwatch_metric_alarm" "Kafka-HeapMemoryAfterGC" {
for_each = toset([for i in data.aws_msk_broker_nodes.msk-broker.node_info_list[*].broker_id : tostring(i)])
alarm_name = "${var.settings.HeapMemoryAfterGC.ecccode}-Kafka_${var.cluster-name}-${each.value}-HeapMemoryAfterGC"
comparison_operator = var.settings.HeapMemoryAfterGC.comparison_operator
evaluation_periods = var.settings.HeapMemoryAfterGC.evaluation_periods
metric_name = "HeapMemoryAfterGC"
period = var.settings.HeapMemoryAfterGC.period
statistic = var.settings.HeapMemoryAfterGC.statistic
threshold = var.settings.HeapMemoryAfterGC.threshold
alarm_description = "Kafka:HeapMemoryAfterGC"
namespace = "AWS/Kafka"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.HeapMemoryAfterGC.action]
ok_actions = [var.settings.HeapMemoryAfterGC.action]
dimensions = {
"Cluster Name" = var.cluster-name
"Broker ID" = each.value
}
}
@@ -0,0 +1,9 @@
terraform {
required_version = "~> 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.36.1"
}
}
}
@@ -0,0 +1,4 @@
variable cw-alarm-prefix {}
variable actions-enabled {}
variable cluster-name {}
variable settings {}
@@ -0,0 +1,26 @@
# Monitoring module
This module deploys the default cloudwatch metric monitoring
## Notes
Terraform lifecycle ignores tags to speed up terraform subsequent update. Cloudwatch alarm tags cannot be read on aws console anyway.
## Example
```terraform
module "ngw" {
source = "../../modules/util/resource-list"
resource-type = "ngw"
}
module "ngw-monitoring" {
cw-alarm-prefix = local.cw-alarm-prefix
for_each = module.ngw.result-set
source = "../../modules/ManagementGovernance/Monitoring.NGW"
default-tags = local.default-tags
job-flow-id = split("/", each.value)[1]
threshold-ErrorPortAllocation = 2
threshold-ConnectionEstablishedCount = 1000
threshold-PacketsDropCount = 10
actions-enabled = var.actions-enabled
sns-targets = var.sns-targets
}
```
@@ -0,0 +1,19 @@
resource "aws_cloudwatch_metric_alarm" "ngw-alarms" {
for_each = var.settings
alarm_name = "${each.value["ecccode"]}-NGW_${var.res-id}-${each.value["metric"]}"
comparison_operator = each.value["comparison_operator"]
evaluation_periods = each.value["evaluation_periods"]
metric_name = each.value["metric"]
period = each.value["period"]
statistic = each.value["statistic"]
threshold = each.value["threshold"]
alarm_description = "NGW:${each.value["metric"]}"
namespace = "AWS/NATGateway"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [each.value["action"]]
ok_actions = [each.value["action"]]
dimensions = {
NatGatewayId = var.res-id
}
}
@@ -0,0 +1,9 @@
terraform {
required_version = "~> 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.36.1"
}
}
}
@@ -0,0 +1,4 @@
variable cw-alarm-prefix {}
variable actions-enabled {}
variable res-id {}
variable settings {}
@@ -0,0 +1,24 @@
# Monitoring module
This module deploys the default cloudwatch metric monitoring
## Notes
Terraform lifecycle ignores tags to speed up terraform subsequent update. Cloudwatch alarm tags cannot be read on aws console anyway.
## Example
```terraform
module "nlb-arns" {
source = "../../modules/util/resource-list"
resource-type = "nlb"
}
module "nlb-monitoring" {
cw-alarm-prefix = local.cw-alarm-prefix
for_each = module.nlb-arns.result-set
source = "../../modules/ManagementGovernance/Monitoring.NLB"
default-tags = local.default-tags
load-balancer = each.value
threshold-HealthHostCountMin = 1
actions-enabled = var.actions-enabled
sns-targets = var.sns-targets
}
```
@@ -0,0 +1,105 @@
/*
data "external" "nlb-targetgroups" {
program = ["bash", "${path.module}/list-nlb-targetgroups.sh"]
query = {
parameter = var.load-balancer
}
}
*/
locals {
nlb-name = "net/${split("/", var.load-balancer)[2]}/${split("/", var.load-balancer)[3]}"
}
resource "aws_cloudwatch_metric_alarm" "nlb-TCP_Target_Reset_Count" {
alarm_name = "${var.settings.TCP_Target_Reset_Count.ecccode}-NLB_${local.nlb-name}-TCP_Target_Reset_Count"
comparison_operator = var.settings.TCP_Target_Reset_Count.comparison_operator
evaluation_periods = var.settings.TCP_Target_Reset_Count.evaluation_periods
metric_name = "TCP_Target_Reset_Count"
period = var.settings.TCP_Target_Reset_Count.period
statistic = var.settings.TCP_Target_Reset_Count.statistic
threshold = var.settings.TCP_Target_Reset_Count.threshold
alarm_description = "NLB:TCP_Target_Reset_Count"
namespace = "AWS/NetworkELB"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.TCP_Target_Reset_Count.action]
ok_actions = [var.settings.TCP_Target_Reset_Count.action]
dimensions = {
LoadBalancer = local.nlb-name
}
}
/*
module "nlb-targetgroups" {
source = "../../util/resource-list"
resource-type = "nlb-targetgroups"
query-input = var.load-balancer
asrolearn = var.asrolearn
}
*/
// causes Rate exceeded error, maybe because of adaptive AWS_RETRY_MODE?
/*
module "nlb_tgs" {
assume_role_arn = var.asrolearn
role_session_name = "terraform-resource-list"
source = "../../util/terraform-aws-cli"
aws_cli_commands = ["elbv2", "describe-target-groups", "--load-balancer-arn", var.load-balancer]
aws_cli_query = "TargetGroups[*].TargetGroupArn"
}
*/
module nlb_tgs {
source = "../../util/awscli"
access_key = var.target-account-ak
aws_cli_commands = "elbv2 describe-target-groups --load-balancer-arn ${var.load-balancer} --query TargetGroups[*].TargetGroupArn"
secret_key = var.target-account-sk
session_token = var.target-account-token
}
resource "aws_cloudwatch_metric_alarm" "nlb-HealthyHostCount" {
# for_each = module.nlb-targetgroups.result-set
for_each = toset(module.nlb_tgs.awscliout)
alarm_name = "${var.settings.HealthHostCountMin.ecccode}-NLBTG_${split(":", each.value)[5]}-HealthyHostCount"
comparison_operator = var.settings.HealthHostCountMin.comparison_operator
evaluation_periods = var.settings.HealthHostCountMin.evaluation_periods
metric_name = "HealthyHostCount"
period = var.settings.HealthHostCountMin.period
statistic = var.settings.HealthHostCountMin.statistic
threshold = var.settings.HealthHostCountMin.threshold
alarm_description = "NLBTG:HealthyHostCount"
namespace = "AWS/NetworkELB"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.HealthHostCountMin.action]
ok_actions = [var.settings.HealthHostCountMin.action]
dimensions = {
TargetGroup = split(":", each.value)[5]
LoadBalancer = "net/${split("/", var.load-balancer)[2]}/${split("/", var.load-balancer)[3]}"
}
}
resource "aws_cloudwatch_metric_alarm" "nlb-UnHealthyHostCount" {
# for_each = module.nlb-targetgroups.result-set
for_each = toset(module.nlb_tgs.awscliout)
alarm_name = "${var.settings.UnHealthyHostCount.ecccode}-NLBTG_${split(":", each.value)[5]}-UnHealthyHostCount"
comparison_operator = var.settings.UnHealthyHostCount.comparison_operator
evaluation_periods = var.settings.UnHealthyHostCount.evaluation_periods
metric_name = "UnHealthyHostCount"
period = var.settings.UnHealthyHostCount.period
statistic = var.settings.UnHealthyHostCount.statistic
threshold = var.settings.UnHealthyHostCount.threshold
alarm_description = "NLBTG:UnHealthyHostCount"
namespace = "AWS/NetworkELB"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [var.settings.UnHealthyHostCount.action]
ok_actions = [var.settings.UnHealthyHostCount.action]
dimensions = {
TargetGroup = split(":", each.value)[5]
LoadBalancer = "net/${split("/", var.load-balancer)[2]}/${split("/", var.load-balancer)[3]}"
}
}
@@ -0,0 +1,4 @@
output nlb-tg-count {
# value = length(module.nlb-targetgroups.result-set)
value = length(flatten(module.nlb_tgs.awscliout))
}
@@ -0,0 +1,9 @@
terraform {
required_version = "~> 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.36.1"
}
}
}
@@ -0,0 +1,8 @@
variable cw-alarm-prefix {}
variable actions-enabled {}
variable load-balancer {}
variable settings {}
# variable asrolearn {}
variable target-account-ak {}
variable target-account-sk {}
variable target-account-token {}
@@ -0,0 +1,27 @@
# Monitoring module
This module deploys the default cloudwatch metric monitoring
## Notes
Terraform lifecycle ignores tags to speed up terraform subsequent update. Cloudwatch alarm tags cannot be read on aws console anyway.
## Example
```terraform
module "es-domains" {
source = "../../modules/util/resource-list"
resource-type = "opensearch"
}
module "es-monitoring" {
cw-alarm-prefix = local.cw-alarm-prefix
for_each = module.es-domains.result-set
source = "../../modules/ManagementGovernance/Monitoring.OpenSearch"
default-tags = local.default-tags
domain-name = each.value
threshold-CPUUtilization = 90
threshold-IndexingLatency = 3
threshold-SearchLatency = 3
# threshold-KibanaHealthyNodes = 1
actions-enabled = var.actions-enabled
sns-targets = var.sns-targets
}
```
@@ -0,0 +1,22 @@
data "aws_caller_identity" "this" {}
resource "aws_cloudwatch_metric_alarm" "ES-alarms" {
for_each = var.settings
alarm_name = "${each.value["ecccode"]}-ES_${var.domain-name}-${each.value["metric"]}"
comparison_operator = each.value["comparison_operator"]
evaluation_periods = each.value["evaluation_periods"]
metric_name = each.value["metric"]
period = each.value["period"]
statistic = each.value["statistic"]
threshold = each.value["threshold"]
alarm_description = "ES:${each.value["metric"]}"
namespace = "AWS/ES"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [each.value["action"]]
ok_actions = [each.value["action"]]
dimensions = {
DomainName = var.domain-name
ClientId = data.aws_caller_identity.this.id
}
}
@@ -0,0 +1,9 @@
terraform {
required_version = "~> 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.36.1"
}
}
}
@@ -0,0 +1,4 @@
variable "cw-alarm-prefix" {}
variable "actions-enabled" {}
variable "domain-name" {}
variable "settings" {}
@@ -0,0 +1,31 @@
# Monitoring module
This module deploys the default cloudwatch metric monitoring
## Notes
Terraform lifecycle ignores tags to speed up terraform subsequent update. Cloudwatch alarm tags cannot be read on aws console anyway.
AWS provider 4.47.0 or above is needed for datasource aws_db_instances (https://github.com/hashicorp/terraform-provider-aws/blob/main/CHANGELOG.md)
## Example
```terraform
module "rds-instances" {
source = "../../modules/util/resource-list"
resource-type = "rds"
}
module "rds-monitoring" {
# for_each = toset(var.rds-instance-ids)
cw-alarm-prefix = local.cw-alarm-prefix
for_each = module.rds-instances.result-set
source = "../../modules/ManagementGovernance/Monitoring.RDS"
default-tags = local.default-tags
rds-instance-name = each.value
threshold-CpuUtilization = 90
threshold-FreeableMemory = 512 * 1024 * 1024
threshold-FreeStorageSpace = 5 * 1024 * 1024 * 1024
threshold-DiskQueueDepth = 30
threshold-ReadLatency = 0.03
threshold-WriteLatency = 0.03
actions-enabled = var.actions-enabled
sns-targets = var.sns-targets
}
```
@@ -0,0 +1,19 @@
resource "aws_cloudwatch_metric_alarm" "rds-alarms" {
for_each = var.settings
alarm_name = "${each.value["ecccode"]}-RDS_${var.rds-instance-name}-${each.value["metric"]}"
comparison_operator = each.value["comparison_operator"]
evaluation_periods = each.value["evaluation_periods"]
metric_name = each.value["metric"]
period = each.value["period"]
statistic = each.value["statistic"]
threshold = each.value["threshold"]
alarm_description = "RDS:${each.value["metric"]}"
namespace = "AWS/RDS"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [each.value["action"]]
ok_actions = [each.value["action"]]
dimensions = {
DBInstanceIdentifier = var.rds-instance-name
}
}
@@ -0,0 +1,9 @@
terraform {
required_version = "~> 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.47.0"
}
}
}
@@ -0,0 +1,4 @@
variable cw-alarm-prefix {}
variable actions-enabled {}
variable rds-instance-name {}
variable settings {}
@@ -0,0 +1,26 @@
# Monitoring module
This module deploys the default cloudwatch metric monitoring
## Notes
Terraform lifecycle ignores tags to speed up terraform subsequent update. Cloudwatch alarm tags cannot be read on aws console anyway.
## Example
```terraform
module "redis-instances" {
source = "../../modules/util/resource-list"
resource-type = "redis"
}
module "redis-monitoring" {
cw-alarm-prefix = local.cw-alarm-prefix
for_each = module.redis-instances.result-set
source = "../../modules/ManagementGovernance/Monitoring.Redis"
default-tags = local.default-tags
redis-cluster-id = each.value
threshold-EngineCPUUtilization = 90
threshold-DatabaseMemoryUsagePercentage = 90
threshold-CacheHitRate = 3
actions-enabled = var.actions-enabled
sns-targets = var.sns-targets
}
```
@@ -0,0 +1,21 @@
resource "aws_cloudwatch_metric_alarm" "redis-alarms" {
for_each = var.settings
alarm_name = "${each.value["ecccode"]}-Redis_${var.redis-cluster-id}-${each.value["metric"]}"
comparison_operator = each.value["comparison_operator"]
evaluation_periods = each.value["evaluation_periods"]
metric_name = each.value["metric"]
period = each.value["period"]
statistic = each.value["statistic"]
threshold = each.value["threshold"]
alarm_description = "ElastiCache:${each.value["metric"]}"
namespace = "AWS/ElastiCache"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [each.value["action"]]
ok_actions = [each.value["action"]]
treat_missing_data = "notBreaching"
dimensions = {
CacheClusterId = var.redis-cluster-id
}
}
@@ -0,0 +1,9 @@
terraform {
required_version = "~> 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.36.1"
}
}
}
@@ -0,0 +1,4 @@
variable "cw-alarm-prefix" {}
variable "actions-enabled" {}
variable "redis-cluster-id" {}
variable "settings" {}
@@ -0,0 +1,24 @@
# Monitoring module
This module deploys the default cloudwatch metric monitoring
## Notes
Terraform lifecycle ignores tags to speed up terraform subsequent update. Cloudwatch alarm tags cannot be read on aws console anyway.
## Example
```terraform
module "tgw" {
source = "../../modules/util/resource-list"
resource-type = "tgw"
}
module "tgw-monitoring" {
cw-alarm-prefix = local.cw-alarm-prefix
for_each = module.tgw.result-set
source = "../../modules/ManagementGovernance/Monitoring.TGW"
default-tags = local.default-tags
job-flow-id = split("/", each.value)[1]
threshold-PacketDropCountNoRoute = 1
actions-enabled = var.actions-enabled
sns-targets = var.sns-targets
}
```
@@ -0,0 +1,19 @@
resource "aws_cloudwatch_metric_alarm" "tgw-PacketDropCountNoRoute" {
for_each = var.settings
alarm_name = "${each.value["ecccode"]}-TGW_${var.tgw-id}-PacketDropCountNoRoute"
comparison_operator = each.value["comparison_operator"]
evaluation_periods = each.value["evaluation_periods"]
metric_name = each.value["metric"]
period = each.value["period"]
statistic = each.value["statistic"]
threshold = each.value["threshold"]
alarm_description = "TGW:${each.value["metric"]}"
namespace = "AWS/TransitGateway"
insufficient_data_actions = []
actions_enabled = var.actions-enabled
alarm_actions = [each.value["action"]]
ok_actions = [each.value["action"]]
dimensions = {
TransitGateway = var.tgw-id
}
}
@@ -0,0 +1,9 @@
terraform {
required_version = "~> 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.47.0"
}
}
}
@@ -0,0 +1,4 @@
variable cw-alarm-prefix {}
variable actions-enabled {}
variable tgw-id {}
variable settings {}
@@ -0,0 +1,47 @@
<!-- This readme file is generated with terraform-docs -->
## Requirements
| Name | Version |
|------|---------|
| terraform | >= 1.3.0 |
| aws | >= 5.0 |
## Providers
| Name | Version |
|------|---------|
| aws | >= 5.0 |
## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [aws_sns_topic.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource |
| [aws_sns_topic_subscription.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource |
| [aws_caller_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
| [aws_region.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| email-addresses | Email recipients of SNS notifications | `set(string)` | n/a | yes |
| kms-key-id | KMS key id for SNS topic at-rest encryption. Make sure the sender has access to this key | `string` | n/a | yes |
| sender | ARN of SNS sender or sending service name | `string` | n/a | yes |
| sender-type | Sender principal type. Value should be either *AWS* or *Service* | `string` | n/a | yes |
| sns-topic-description | SNS topic display name | `string` | n/a | yes |
| sns-topic-name | Name of SNS topic | `string` | n/a | yes |
## Outputs
| Name | Description |
|------|-------------|
| sns-topic-arn | n/a |
---
## Authorship
This module was developed by xpk.
@@ -0,0 +1,69 @@
data "aws_caller_identity" "this" {}
data "aws_region" "this" {}
resource "aws_sns_topic" "this" {
name = var.sns-topic-name
display_name = var.sns-topic-description
kms_master_key_id = var.kms-key-id
policy = jsonencode(
{
"Version" : "2008-10-17",
"Id" : "SnsTopicPolicy",
"Statement" : [
{
"Sid" : "SnsTopicAdmin",
"Effect" : "Allow",
"Principal" : {
"AWS" : data.aws_caller_identity.this.account_id
},
"Action" : [
"SNS:GetTopicAttributes",
"SNS:SetTopicAttributes",
"SNS:AddPermission",
"SNS:RemovePermission",
"SNS:DeleteTopic",
"SNS:Subscribe",
"SNS:ListSubscriptionsByTopic",
"SNS:Publish",
"SNS:Receive"
],
"Resource" : "arn:aws:sns:${data.aws_region.this.name}:${data.aws_caller_identity.this.account_id}:${var.sns-topic-name}",
"Condition" : {
"StringEquals" : {
"AWS:SourceOwner" : data.aws_caller_identity.this.account_id
}
}
},
{
"Sid" : "AllowPublishing",
"Effect" : "Allow",
"Principal" : {
"${var.sender-type}" : var.sender
},
"Action" : "sns:Publish",
"Resource" : "arn:aws:sns:${data.aws_region.this.name}:${data.aws_caller_identity.this.account_id}:${var.sns-topic-name}"
},
{
"Sid" : "AllowPublishThroughSSLOnly",
"Action" : "SNS:Publish",
"Effect" : "Deny",
"Resource" : "arn:aws:sns:${data.aws_region.this.name}:${data.aws_caller_identity.this.account_id}:${var.sns-topic-name}",
"Condition" : {
"Bool" : {
"aws:SecureTransport" : "false"
}
},
"Principal" : "*"
}
]
}
)
}
resource "aws_sns_topic_subscription" "this" {
for_each = var.email-addresses
topic_arn = aws_sns_topic.this.arn
protocol = "email"
endpoint = each.value
}
@@ -0,0 +1,3 @@
output "sns-topic-arn" {
value = aws_sns_topic.this.arn
}
@@ -0,0 +1,33 @@
variable "sender" {
type = string
description = "ARN of SNS sender or sending service name"
}
variable "sender-type" {
type = string
description = "Sender principal type. Value should be either *AWS* or *Service*"
validation {
condition = var.sender-type == "AWS" || var.sender-type == "Service"
error_message = "Valid values are AWS or Service"
}
}
variable "sns-topic-name" {
type = string
description = "Name of SNS topic"
}
variable "sns-topic-description" {
type = string
description = "SNS topic display name"
}
variable "kms-key-id" {
type = string
description = "KMS key id for SNS topic at-rest encryption. Make sure the sender has access to this key"
}
variable "email-addresses" {
type = set(string)
description = "Email recipients of SNS notifications"
}

Some files were not shown because too many files have changed in this diff Show More