From c12355835449be55218914677d5fb5d8f2c2f4b0 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Thu, 14 Mar 2024 18:04:37 +0100 Subject: [PATCH] feat!: Added creation of CW Log Group. Bump AWS provider version. (#103) --- .pre-commit-config.yaml | 2 +- README.md | 26 +++++++++---- examples/complete-http/README.md | 5 +-- examples/complete-http/main.tf | 10 ++--- examples/complete-http/versions.tf | 2 +- examples/vpc-link-http/README.md | 2 +- examples/vpc-link-http/versions.tf | 2 +- main.tf | 27 +++++++++++--- variables.tf | 59 ++++++++++++++++++++++++------ versions.tf | 2 +- wrappers/main.tf | 8 ++++ 11 files changed, 105 insertions(+), 40 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7c0a310..998beca 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/antonbabenko/pre-commit-terraform - rev: v1.88.0 + rev: v1.88.2 hooks: - id: terraform_fmt - id: terraform_wrapper_module_for_each diff --git a/README.md b/README.md index 17da9f9..d38a9d1 100644 --- a/README.md +++ b/README.md @@ -84,12 +84,13 @@ module "api_gateway" { 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 + 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_access_log_group = false # to control creation of CloudWatch Access log group for "$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 /" = { @@ -116,13 +117,13 @@ module "api_gateway" { | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | +| [aws](#requirement\_aws) | >= 5.30 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 4.0 | +| [aws](#provider\_aws) | >= 5.30 | ## Modules @@ -140,6 +141,7 @@ No modules. | [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 | +| [aws_cloudwatch_log_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | ## Inputs @@ -154,6 +156,7 @@ No modules. | [create\_api\_domain\_name](#input\_create\_api\_domain\_name) | Whether to create API domain name resource | `bool` | `true` | no | | [create\_api\_gateway](#input\_create\_api\_gateway) | Whether to create API Gateway | `bool` | `true` | no | | [create\_default\_stage](#input\_create\_default\_stage) | Whether to create default stage | `bool` | `true` | no | +| [create\_default\_stage\_access\_log\_group](#input\_create\_default\_stage\_access\_log\_group) | Whether to create CloudWatch log group for Access logs | `bool` | `false` | no | | [create\_default\_stage\_api\_mapping](#input\_create\_default\_stage\_api\_mapping) | Whether to create default stage API mapping | `bool` | `true` | no | | [create\_routes\_and\_integrations](#input\_create\_routes\_and\_integrations) | Whether to create routes and integrations resources | `bool` | `true` | no | | [create\_vpc\_link](#input\_create\_vpc\_link) | Whether to create VPC links | `bool` | `true` | no | @@ -161,6 +164,13 @@ No modules. | [default\_route\_settings](#input\_default\_route\_settings) | Settings for default route | `map(string)` | `{}` | no | | [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 | | [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 | +| [default\_stage\_access\_log\_group\_class](#input\_default\_stage\_access\_log\_group\_class) | Specified the log class of the Access log group. Possible values are: STANDARD or INFREQUENT\_ACCESS | `string` | `null` | no | +| [default\_stage\_access\_log\_group\_kms\_key\_id](#input\_default\_stage\_access\_log\_group\_kms\_key\_id) | The ARN of the KMS Key to use when encrypting log data for Access logs | `string` | `null` | no | +| [default\_stage\_access\_log\_group\_name](#input\_default\_stage\_access\_log\_group\_name) | Specifies the name of CloudWatch Log Group for Access logs | `string` | `null` | no | +| [default\_stage\_access\_log\_group\_name\_suffix](#input\_default\_stage\_access\_log\_group\_name\_suffix) | Specifies the name suffix of CloudWatch Log Group for Access logs | `string` | `""` | no | +| [default\_stage\_access\_log\_group\_retention\_in\_days](#input\_default\_stage\_access\_log\_group\_retention\_in\_days) | Specifies the number of days you want to retain log events in the specified log group for Access logs | `number` | `null` | no | +| [default\_stage\_access\_log\_group\_skip\_destroy](#input\_default\_stage\_access\_log\_group\_skip\_destroy) | Set to true if you do not wish the log group (and any logs it may contain) to be deleted at destroy time, and instead just remove the log group from the Terraform state | `bool` | `false` | no | +| [default\_stage\_access\_log\_group\_tags](#input\_default\_stage\_access\_log\_group\_tags) | Additional tags for the Access logs | `map(string)` | `{}` | no | | [default\_stage\_tags](#input\_default\_stage\_tags) | A mapping of tags to assign to the default stage resource. | `map(string)` | `{}` | no | | [description](#input\_description) | The description of the API. | `string` | `null` | no | | [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 | diff --git a/examples/complete-http/README.md b/examples/complete-http/README.md index 99659fc..f56e7b7 100644 --- a/examples/complete-http/README.md +++ b/examples/complete-http/README.md @@ -21,7 +21,7 @@ Note that this example may create resources which cost money. Run `terraform des | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | +| [aws](#requirement\_aws) | >= 5.30 | | [null](#requirement\_null) | >= 2.0 | | [random](#requirement\_random) | >= 2.0 | | [tls](#requirement\_tls) | >= 3.1 | @@ -30,7 +30,7 @@ Note that this example may create resources which cost money. Run `terraform des | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 4.0 | +| [aws](#provider\_aws) | >= 5.30 | | [null](#provider\_null) | >= 2.0 | | [random](#provider\_random) | >= 2.0 | | [tls](#provider\_tls) | >= 3.1 | @@ -49,7 +49,6 @@ Note that this example may create resources which cost money. Run `terraform des | Name | Type | |------|------| | [aws_apigatewayv2_authorizer.some_authorizer](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/apigatewayv2_authorizer) | resource | -| [aws_cloudwatch_log_group.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | | [aws_cognito_user_pool.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cognito_user_pool) | resource | | [aws_route53_record.api](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource | | [aws_s3_bucket.truststore](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | diff --git a/examples/complete-http/main.tf b/examples/complete-http/main.tf index 6785ef3..6a5b53c 100644 --- a/examples/complete-http/main.tf +++ b/examples/complete-http/main.tf @@ -26,6 +26,8 @@ module "api_gateway" { description = "My awesome HTTP API Gateway" protocol_type = "HTTP" + create_default_stage_access_log_group = true + fail_on_warnings = false cors_configuration = { @@ -42,8 +44,7 @@ module "api_gateway" { domain_name = local.domain_name domain_name_certificate_arn = module.acm.acm_certificate_arn - default_stage_access_log_destination_arn = aws_cloudwatch_log_group.logs.arn - default_stage_access_log_format = "$context.identity.sourceIp - - [$context.requestTime] \"$context.httpMethod $context.routeKey $context.protocol\" $context.status $context.responseLength $context.requestId $context.integrationErrorMessage" + default_stage_access_log_format = "$context.identity.sourceIp - - [$context.requestTime] \"$context.httpMethod $context.routeKey $context.protocol\" $context.status $context.responseLength $context.requestId $context.integrationErrorMessage" default_route_settings = { detailed_metrics_enabled = true @@ -62,7 +63,6 @@ module "api_gateway" { } integrations = { - "ANY /" = { lambda_arn = module.lambda_function.lambda_function_arn payload_format_version = "2.0" @@ -244,10 +244,6 @@ resource "random_pet" "this" { length = 2 } -resource "aws_cloudwatch_log_group" "logs" { - name = random_pet.this.id -} - ############################################# # Using packaged function from Lambda module ############################################# diff --git a/examples/complete-http/versions.tf b/examples/complete-http/versions.tf index a6cc337..ee08846 100644 --- a/examples/complete-http/versions.tf +++ b/examples/complete-http/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 5.30" } random = { source = "hashicorp/random" diff --git a/examples/vpc-link-http/README.md b/examples/vpc-link-http/README.md index 4095b1b..fe6017f 100644 --- a/examples/vpc-link-http/README.md +++ b/examples/vpc-link-http/README.md @@ -21,7 +21,7 @@ Note that this example may create resources which cost money. Run `terraform des | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | +| [aws](#requirement\_aws) | >= 5.30 | | [null](#requirement\_null) | >= 2.0 | | [random](#requirement\_random) | >= 2.0 | diff --git a/examples/vpc-link-http/versions.tf b/examples/vpc-link-http/versions.tf index 54b77a9..fe73db4 100644 --- a/examples/vpc-link-http/versions.tf +++ b/examples/vpc-link-http/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 5.30" } random = { source = "hashicorp/random" diff --git a/main.tf b/main.tf index 0a332c4..3eecc0a 100644 --- a/main.tf +++ b/main.tf @@ -60,6 +60,19 @@ resource "aws_apigatewayv2_domain_name" "this" { tags = merge(var.domain_name_tags, var.tags) } +# Default stage log group +resource "aws_cloudwatch_log_group" "this" { + count = var.create && var.create_default_stage && var.create_default_stage_access_log_group ? 1 : 0 + + name = coalesce(var.default_stage_access_log_group_name, "${var.name}${var.default_stage_access_log_group_name_suffix}") + retention_in_days = var.default_stage_access_log_group_retention_in_days + kms_key_id = var.default_stage_access_log_group_kms_key_id + skip_destroy = var.default_stage_access_log_group_skip_destroy + log_group_class = var.default_stage_access_log_group_class + + tags = merge(var.tags, var.default_stage_access_log_group_tags) +} + # Default stage resource "aws_apigatewayv2_stage" "default" { count = var.create && var.create_default_stage ? 1 : 0 @@ -69,10 +82,10 @@ resource "aws_apigatewayv2_stage" "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] : [] + for_each = (var.default_stage_access_log_destination_arn != null || var.create_default_stage_access_log_group) && var.default_stage_access_log_format != null ? [true] : [] content { - destination_arn = var.default_stage_access_log_destination_arn + destination_arn = try(aws_cloudwatch_log_group.this[0].arn, var.default_stage_access_log_destination_arn) format = var.default_stage_access_log_format } } @@ -110,6 +123,8 @@ resource "aws_apigatewayv2_stage" "default" { lifecycle { ignore_changes = [deployment_id] } + + depends_on = [aws_apigatewayv2_integration.this] } # Default API mapping @@ -123,7 +138,7 @@ resource "aws_apigatewayv2_api_mapping" "this" { # Routes and integrations resource "aws_apigatewayv2_route" "this" { - for_each = var.create && var.create_routes_and_integrations ? var.integrations : {} + for_each = { for k, v in var.integrations : k => v if var.create && var.create_routes_and_integrations } api_id = aws_apigatewayv2_api.this[0].id route_key = each.key @@ -142,7 +157,7 @@ resource "aws_apigatewayv2_route" "this" { } resource "aws_apigatewayv2_integration" "this" { - for_each = var.create && var.create_routes_and_integrations ? var.integrations : {} + for_each = { for k, v in var.integrations : k => v if var.create && var.create_routes_and_integrations } api_id = aws_apigatewayv2_api.this[0].id description = try(each.value.description, null) @@ -186,7 +201,7 @@ resource "aws_apigatewayv2_integration" "this" { # Authorizers resource "aws_apigatewayv2_authorizer" "this" { - for_each = var.create && var.create_routes_and_integrations ? var.authorizers : {} + for_each = { for k, v in var.authorizers : k => v if var.create && var.create_routes_and_integrations } api_id = aws_apigatewayv2_api.this[0].id @@ -211,7 +226,7 @@ resource "aws_apigatewayv2_authorizer" "this" { # VPC Link (Private API) resource "aws_apigatewayv2_vpc_link" "this" { - for_each = var.create && var.create_vpc_link ? var.vpc_links : {} + for_each = { for k, v in var.vpc_links : k => v if var.create && var.create_vpc_link } name = try(each.value.name, each.key) security_group_ids = each.value["security_group_ids"] diff --git a/variables.tf b/variables.tf index 25c1e8e..81345f4 100644 --- a/variables.tf +++ b/variables.tf @@ -22,17 +22,11 @@ variable "create_default_stage_api_mapping" { 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_default_stage_access_log_group" { + description = "Whether to create CloudWatch log group for Access logs" + type = bool + default = false +} variable "create_api_domain_name" { description = "Whether to create API domain name resource" @@ -163,6 +157,49 @@ variable "default_stage_tags" { default = {} } +# Log group for default stage +variable "default_stage_access_log_group_name" { + description = "Specifies the name of CloudWatch Log Group for Access logs" + type = string + default = null +} + +variable "default_stage_access_log_group_name_suffix" { + description = "Specifies the name suffix of CloudWatch Log Group for Access logs" + type = string + default = "" +} + +variable "default_stage_access_log_group_retention_in_days" { + description = "Specifies the number of days you want to retain log events in the specified log group for Access logs" + type = number + default = null +} + +variable "default_stage_access_log_group_kms_key_id" { + description = "The ARN of the KMS Key to use when encrypting log data for Access logs" + type = string + default = null +} + +variable "default_stage_access_log_group_skip_destroy" { + description = "Set to true if you do not wish the log group (and any logs it may contain) to be deleted at destroy time, and instead just remove the log group from the Terraform state" + type = bool + default = false +} + +variable "default_stage_access_log_group_class" { + description = "Specified the log class of the Access log group. Possible values are: STANDARD or INFREQUENT_ACCESS" + type = string + default = null +} + +variable "default_stage_access_log_group_tags" { + description = "Additional tags for the Access logs" + type = map(string) + default = {} +} + ##### # default stage API mapping diff --git a/versions.tf b/versions.tf index d8dd1a4..22111ba 100644 --- a/versions.tf +++ b/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 5.30" } } } diff --git a/wrappers/main.tf b/wrappers/main.tf index 24216b4..51fc120 100644 --- a/wrappers/main.tf +++ b/wrappers/main.tf @@ -12,6 +12,7 @@ module "wrapper" { 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_access_log_group = try(each.value.create_default_stage_access_log_group, var.defaults.create_default_stage_access_log_group, false) 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) @@ -19,6 +20,13 @@ module "wrapper" { 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_access_log_group_class = try(each.value.default_stage_access_log_group_class, var.defaults.default_stage_access_log_group_class, null) + default_stage_access_log_group_kms_key_id = try(each.value.default_stage_access_log_group_kms_key_id, var.defaults.default_stage_access_log_group_kms_key_id, null) + default_stage_access_log_group_name = try(each.value.default_stage_access_log_group_name, var.defaults.default_stage_access_log_group_name, null) + default_stage_access_log_group_name_suffix = try(each.value.default_stage_access_log_group_name_suffix, var.defaults.default_stage_access_log_group_name_suffix, "") + default_stage_access_log_group_retention_in_days = try(each.value.default_stage_access_log_group_retention_in_days, var.defaults.default_stage_access_log_group_retention_in_days, null) + default_stage_access_log_group_skip_destroy = try(each.value.default_stage_access_log_group_skip_destroy, var.defaults.default_stage_access_log_group_skip_destroy, false) + default_stage_access_log_group_tags = try(each.value.default_stage_access_log_group_tags, var.defaults.default_stage_access_log_group_tags, {}) 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)