diff --git a/README.md b/README.md index def6c61..c5b8347 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ see [documentation](https://www.terraform.io/docs/providers/aws/r/lambda_functio ### basic -see [example](examples/complete) for other configuration options +see [example](examples/complete) for more configuration options ```hcl provider "aws" { @@ -234,33 +234,68 @@ module "lambda" { ### with CloudWatch Logs configuration -The module will create a [CloudWatch Log Group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) -for your Lambda function. It's retention period and [CloudWatch Logs subscription filters](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_subscription_filter) -to stream logs to other Lambda functions (e.g. to forward logs to Amazon OpenSearch Service) can be declared inline. +By default, the module will create and manage a [CloudWatch Log Group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) for your Lambda function. +It's possible to configure settings like retention time and [KMS encryption](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/encrypt-log-data-kms.html) +for this log group. -The module will create the required [Lambda permissions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission) automatically. -Sending logs to CloudWatch can be disabled with `cloudwatch_logs_enabled = false` +In addition, the module also supports [advanced logging configuration](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs-loggroups.html) +which provides the ability to define a custom name for the module managed log group as well as specifying an existing log group to be used by the Lambda function instead. + +[CloudWatch Logs subscription filters](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_subscription_filter) +to stream logs to other Lambda functions (e.g. to forward logs to Amazon OpenSearch Service) can be declared inline +for the module managed log group or an existing log group. + +The module will create the required [IAM permissions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission) for CloudWatch logs automatically. Those permissions can be removed by setting `cloudwatch_logs_enabled = false`. -see [example](examples/with-cloudwatch-logs-subscription) for details +see [example](examples/cloudwatch-logs) for details ```hcl module "lambda" { // see above - // disable CloudWatch logs + // remove CloudWatch logs IAM permissions // cloudwatch_logs_enabled = false - cloudwatch_logs_retention_in_days = 14 + // configure module managed log group + cloudwatch_logs_log_group_class = "STANDARD" + cloudwatch_logs_retention_in_days = 7 + cloudwatch_logs_skip_destroy = false + + // advanced logging config including a custom CloudWatch log group managed by the module + logging_config = { + application_log_level = "INFO" + log_format = "JSON" + log_group = "/custom/my_function_name" + system_log_level = "WARN" + } + // register log subscription filters for the functions log group cloudwatch_log_subscription_filters = { - lambda_1 = { - //see https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_subscription_filter for available arguments - destination_arn = module.destination_1.arn + sub_1 = { + // see https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_subscription_filter for available arguments + destination_arn = module.sub_1.arn + filter_pattern = "%Lambda%" } + } +} - lambda_2 = { - destination_arn = module.destination_2.arn - } +resource "aws_cloudwatch_log_group" "existing" { + name = "/existing/${module.fixtures.output_function_name}" + retention_in_days = 1 +} + +module "sub_1" { + source = "../../" + + // other required arguments + + // disable creation of the module managed CloudWatch log group + create_cloudwatch_log_group = false + + // advanced logging config using an external CloudWatch log group + logging_config = { + log_format = "Text" + log_group = aws_cloudwatch_log_group.existing.name } } ``` @@ -288,7 +323,7 @@ module "lambda" { For `image` deployment packages, the Lambda Insights extension needs to be added to the [container image](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Lambda-Insights-Getting-Started-docker.html): ```dockerfile -FROM public.ecr.aws/lambda/nodejs:12 +FROM public.ecr.aws/lambda/nodejs:22 RUN curl -O https://lambda-insights-extension.s3-ap-northeast-1.amazonaws.com/amazon_linux/lambda-insights-extension.rpm && \ rpm -U lambda-insights-extension.rpm && \ @@ -312,7 +347,7 @@ see [examples](examples/deployment) for details. - [container-image](examples/container-image) - [deployment](examples/deployment) - [with-cloudwatch-event-rules](examples/with-cloudwatch-event-rules) -- [with-cloudwatch-logs-subscription](examples/with-cloudwatch-logs-subscription) +- [with-cloudwatch-logs-subscription](examples/cloudwatch-logs) - [with-event-source-mappings](examples/with-event-source-mappings) - [with-sns-subscriptions](examples/with-sns-subscriptions) - [with-vpc](examples/with-vpc) @@ -380,6 +415,7 @@ No modules. | [aws_lambda_permission.sns](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission) | resource | | [aws_sns_topic_subscription.subscription](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_cloudwatch_log_group.lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/cloudwatch_log_group) | data source | | [aws_iam_policy.lambda_insights](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy) | data source | | [aws_iam_policy.tracing](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy) | data source | | [aws_iam_policy.vpc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy) | data source | @@ -400,7 +436,10 @@ No modules. | [cloudwatch\_log\_subscription\_filters](#input\_cloudwatch\_log\_subscription\_filters) | CloudWatch Logs subscription filter resources. Currently supports only Lambda functions as destinations. | `map(any)` | `{}` | no | | [cloudwatch\_logs\_enabled](#input\_cloudwatch\_logs\_enabled) | Enables your Lambda function to send logs to CloudWatch. The IAM role of this Lambda function will be enhanced with required permissions. | `bool` | `true` | no | | [cloudwatch\_logs\_kms\_key\_id](#input\_cloudwatch\_logs\_kms\_key\_id) | The ARN of the KMS Key to use when encrypting log data. | `string` | `null` | no | +| [cloudwatch\_logs\_log\_group\_class](#input\_cloudwatch\_logs\_log\_group\_class) | Specifies the log class of the log group. Possible values are: `STANDARD`, `INFREQUENT_ACCESS`, or `DELIVERY`. | `string` | `null` | no | | [cloudwatch\_logs\_retention\_in\_days](#input\_cloudwatch\_logs\_retention\_in\_days) | Specifies the number of days you want to retain log events in the specified log group. Possible values are: 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 3653, and 0. If you select 0, the events in the log group are always retained and never expire. | `number` | `null` | no | +| [cloudwatch\_logs\_skip\_destroy](#input\_cloudwatch\_logs\_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 | +| [create\_cloudwatch\_log\_group](#input\_create\_cloudwatch\_log\_group) | Create and manage the CloudWatch Log Group for the Lambda function. Set to `false` to reuse an existing log group. | `bool` | `true` | no | | [description](#input\_description) | Description of what your Lambda Function does. | `string` | `""` | no | | [environment](#input\_environment) | Environment (e.g. env variables) configuration for the Lambda function enable you to dynamically pass settings to your function code and libraries |
object({
variables = map(string)
})
| `null` | no | | [ephemeral\_storage\_size](#input\_ephemeral\_storage\_size) | The size of your Lambda functions ephemeral storage (/tmp) represented in MB. Valid value between 512 MB to 10240 MB. | `number` | `512` | no | @@ -415,6 +454,7 @@ No modules. | [kms\_key\_arn](#input\_kms\_key\_arn) | Amazon Resource Name (ARN) of the AWS Key Management Service (KMS) key that is used to encrypt environment variables. If this configuration is not provided when environment variables are in use, AWS Lambda uses a default service key. If this configuration is provided when environment variables are not in use, the AWS Lambda API does not save this configuration and Terraform will show a perpetual difference of adding the key. To fix the perpetual difference, remove this configuration. | `string` | `""` | no | | [lambda\_at\_edge](#input\_lambda\_at\_edge) | Enable Lambda@Edge for your Node.js or Python functions. Required trust relationship and publishing of function versions will be configured. | `bool` | `false` | no | | [layers](#input\_layers) | List of Lambda Layer Version ARNs (maximum of 5) to attach to your Lambda Function. | `list(string)` | `[]` | no | +| [logging\_config](#input\_logging\_config) | Configuration block for advanced logging settings. |
object({
log_format = string
application_log_level = optional(string, null)
log_group = optional(string, null)
system_log_level = optional(string, null)
})
| `null` | no | | [memory\_size](#input\_memory\_size) | Amount of memory in MB your Lambda Function can use at runtime. | `number` | `128` | no | | [package\_type](#input\_package\_type) | The Lambda deployment package type. Valid values are Zip and Image. | `string` | `"Zip"` | no | | [publish](#input\_publish) | Whether to publish creation/change as new Lambda Function Version. | `bool` | `false` | no | diff --git a/cloudwatch_logs.tf b/cloudwatch_logs.tf index 0204325..14363f2 100644 --- a/cloudwatch_logs.tf +++ b/cloudwatch_logs.tf @@ -1,9 +1,26 @@ +locals { + log_group_name = coalesce(try(var.logging_config.log_group, null), "/aws/lambda/${var.lambda_at_edge ? "us-east-1." : ""}${var.function_name}") + log_group_arn = try(data.aws_cloudwatch_log_group.lambda[0].arn, aws_cloudwatch_log_group.lambda[0].arn, "") +} + +data "aws_cloudwatch_log_group" "lambda" { + count = var.create_cloudwatch_log_group ? 0 : 1 + + region = var.region + + name = local.log_group_name +} + resource "aws_cloudwatch_log_group" "lambda" { + count = var.create_cloudwatch_log_group ? 1 : 0 + region = var.region - name = "/aws/lambda/${var.lambda_at_edge ? "us-east-1." : ""}${var.function_name}" + name = local.log_group_name + log_group_class = var.cloudwatch_logs_log_group_class retention_in_days = var.cloudwatch_logs_retention_in_days kms_key_id = var.cloudwatch_logs_kms_key_id + skip_destroy = var.cloudwatch_logs_skip_destroy tags = var.tags } @@ -15,7 +32,7 @@ resource "aws_lambda_permission" "cloudwatch_logs" { action = "lambda:InvokeFunction" function_name = lookup(each.value, "destination_arn", null) principal = "logs.${data.aws_region.current.region}.amazonaws.com" - source_arn = "${aws_cloudwatch_log_group.lambda.arn}:*" + source_arn = "${local.log_group_arn}:*" } resource "aws_cloudwatch_log_subscription_filter" "cloudwatch_logs" { @@ -27,7 +44,7 @@ resource "aws_cloudwatch_log_subscription_filter" "cloudwatch_logs" { destination_arn = lookup(each.value, "destination_arn", null) distribution = lookup(each.value, "distribution", null) filter_pattern = lookup(each.value, "filter_pattern", "") - log_group_name = aws_cloudwatch_log_group.lambda.name + log_group_name = local.log_group_name name = each.key role_arn = lookup(each.value, "role_arn", null) } diff --git a/examples/cloudwatch-logs/README.md b/examples/cloudwatch-logs/README.md new file mode 100644 index 0000000..467b9ec --- /dev/null +++ b/examples/cloudwatch-logs/README.md @@ -0,0 +1,65 @@ +# Example with CloudWatch logs configuration + +Create AWS Lambda functions showcasing [advanced logging configuration](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs-loggroups.html) +and log [subscription filters](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/Subscriptions.html). + +## usage + +``` +terraform init +terraform plan +terraform apply +``` + +Note that this example may create resources which cost money. Run `terraform destroy` to destroy those resources. + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [archive](#requirement\_archive) | >= 2.2 | +| [aws](#requirement\_aws) | >= 6.0 | + +## Providers + +| Name | Version | +|------|---------| +| [archive](#provider\_archive) | >= 2.2 | +| [aws](#provider\_aws) | >= 6.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [fixtures](#module\_fixtures) | ../fixtures | n/a | +| [logs\_subscription](#module\_logs\_subscription) | ../../ | n/a | +| [sub\_1](#module\_sub\_1) | ../../ | n/a | +| [sub\_2](#module\_sub\_2) | ../../ | n/a | + +## Resources + +| Name | Type | +|------|------| +| [aws_cloudwatch_log_group.existing](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | +| [archive_file.subscription_handler](https://registry.terraform.io/providers/hashicorp/archive/latest/docs/data-sources/file) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [region](#input\_region) | n/a | `string` | `"eu-west-1"` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [arn](#output\_arn) | The Amazon Resource Name (ARN) identifying your Lambda Function. | +| [cloudwatch\_custom\_log\_group\_arn](#output\_cloudwatch\_custom\_log\_group\_arn) | The Amazon Resource Name (ARN) identifying the custom CloudWatch log group used by your Lambda function. | +| [cloudwatch\_custom\_log\_group\_name](#output\_cloudwatch\_custom\_log\_group\_name) | The name of the custom CloudWatch log group. | +| [cloudwatch\_existing\_log\_group\_arn](#output\_cloudwatch\_existing\_log\_group\_arn) | The Amazon Resource Name (ARN) identifying the existing CloudWatch log group used by your Lambda function. | +| [cloudwatch\_existing\_log\_group\_name](#output\_cloudwatch\_existing\_log\_group\_name) | The name of the existing CloudWatch log group. | +| [function\_name](#output\_function\_name) | The unique name of your Lambda Function. | +| [role\_name](#output\_role\_name) | The name of the IAM role attached to the Lambda Function. | + \ No newline at end of file diff --git a/examples/cloudwatch-logs/handler/index.js b/examples/cloudwatch-logs/handler/index.js new file mode 100644 index 0000000..928cc68 --- /dev/null +++ b/examples/cloudwatch-logs/handler/index.js @@ -0,0 +1,14 @@ +var zlib = require('zlib'); + +exports.handler = function(input, context) { + var payload = Buffer.from(input.awslogs.data, 'base64'); + zlib.gunzip(payload, function(e, result) { + if (e) { + context.fail(e); + } else { + result = JSON.parse(result.toString()); + console.log("Event Data:", JSON.stringify(result, null, 2)); + context.succeed(); + } + }); +}; \ No newline at end of file diff --git a/examples/cloudwatch-logs/main.tf b/examples/cloudwatch-logs/main.tf new file mode 100644 index 0000000..2dfb6b3 --- /dev/null +++ b/examples/cloudwatch-logs/main.tf @@ -0,0 +1,97 @@ +locals { + handler = "index.handler" + runtime = "nodejs22.x" +} + +module "fixtures" { + source = "../fixtures" +} + +module "logs_subscription" { + source = "../../" + + description = "Example usage for an AWS Lambda with CloudWatch logs subscription filters and advanced log configuration using a custom log group name." + filename = module.fixtures.output_path + function_name = module.fixtures.output_function_name + handler = local.handler + runtime = local.runtime + source_code_hash = module.fixtures.output_base64sha256 + + // configure module managed log group + cloudwatch_logs_log_group_class = "STANDARD" + cloudwatch_logs_retention_in_days = 7 + cloudwatch_logs_skip_destroy = false + + // advanced logging config including a custom CloudWatch log group managed by the module + logging_config = { + application_log_level = "INFO" + log_format = "JSON" + log_group = "/custom/${module.fixtures.output_function_name}" + system_log_level = "WARN" + } + + // register log subscription filters for the functions log group + cloudwatch_log_subscription_filters = { + sub_1 = { + destination_arn = module.sub_1.arn + filter_pattern = "%Lambda%" + } + + sub_2 = { + destination_arn = module.sub_2.arn + } + } +} + +data "archive_file" "subscription_handler" { + type = "zip" + source_file = "${path.module}/handler/index.js" + output_path = "${path.module}/handler.zip" + output_file_mode = "0666" +} + +resource "aws_cloudwatch_log_group" "existing" { + name = "/existing/${module.fixtures.output_function_name}" + retention_in_days = 1 +} + +module "sub_1" { + source = "../../" + + description = "Example usage of a log subscription Lambda function with advanced log configuration." + filename = data.archive_file.subscription_handler.output_path + function_name = "${module.fixtures.output_function_name}-sub-1" + handler = local.handler + runtime = local.runtime + source_code_hash = data.archive_file.subscription_handler.output_base64sha256 + + + cloudwatch_logs_retention_in_days = 1 + create_cloudwatch_log_group = false + + // advanced logging config using an external CloudWatch log group + logging_config = { + log_format = "Text" + log_group = aws_cloudwatch_log_group.existing.name + } +} + +module "sub_2" { + source = "../../" + + description = "Example usage of a log subscription Lambda function with advanced log configuration." + filename = data.archive_file.subscription_handler.output_path + function_name = "${module.fixtures.output_function_name}-sub-2" + handler = local.handler + runtime = local.runtime + source_code_hash = data.archive_file.subscription_handler.output_base64sha256 + + cloudwatch_logs_retention_in_days = 1 + create_cloudwatch_log_group = false + + // advanced logging config using an external CloudWatch log group + logging_config = { + log_format = "Text" + log_group = aws_cloudwatch_log_group.existing.name + } +} diff --git a/examples/cloudwatch-logs/outputs.tf b/examples/cloudwatch-logs/outputs.tf new file mode 100644 index 0000000..c735fc9 --- /dev/null +++ b/examples/cloudwatch-logs/outputs.tf @@ -0,0 +1,34 @@ +output "arn" { + description = "The Amazon Resource Name (ARN) identifying your Lambda Function." + value = module.logs_subscription.arn +} + +output "cloudwatch_custom_log_group_name" { + description = "The name of the custom CloudWatch log group." + value = module.logs_subscription.cloudwatch_log_group_name +} + +output "cloudwatch_custom_log_group_arn" { + description = "The Amazon Resource Name (ARN) identifying the custom CloudWatch log group used by your Lambda function." + value = module.logs_subscription.cloudwatch_log_group_arn +} + +output "cloudwatch_existing_log_group_name" { + description = "The name of the existing CloudWatch log group." + value = module.sub_1.cloudwatch_log_group_name +} + +output "cloudwatch_existing_log_group_arn" { + description = "The Amazon Resource Name (ARN) identifying the existing CloudWatch log group used by your Lambda function." + value = module.sub_1.cloudwatch_log_group_arn +} + +output "function_name" { + description = "The unique name of your Lambda Function." + value = module.logs_subscription.function_name +} + +output "role_name" { + description = "The name of the IAM role attached to the Lambda Function." + value = module.logs_subscription.role_name +} diff --git a/examples/with-cloudwatch-logs-subscription/provider.tf b/examples/cloudwatch-logs/provider.tf similarity index 100% rename from examples/with-cloudwatch-logs-subscription/provider.tf rename to examples/cloudwatch-logs/provider.tf diff --git a/examples/with-cloudwatch-logs-subscription/variables.tf b/examples/cloudwatch-logs/variables.tf similarity index 100% rename from examples/with-cloudwatch-logs-subscription/variables.tf rename to examples/cloudwatch-logs/variables.tf diff --git a/examples/with-cloudwatch-logs-subscription/versions.tf b/examples/cloudwatch-logs/versions.tf similarity index 64% rename from examples/with-cloudwatch-logs-subscription/versions.tf rename to examples/cloudwatch-logs/versions.tf index db13b0a..b8e9229 100644 --- a/examples/with-cloudwatch-logs-subscription/versions.tf +++ b/examples/cloudwatch-logs/versions.tf @@ -6,5 +6,9 @@ terraform { source = "hashicorp/aws" version = ">= 6.0" } + archive = { + source = "hashicorp/archive" + version = ">= 2.2" + } } } diff --git a/examples/complete/README.md b/examples/complete/README.md index c17b508..c441efe 100644 --- a/examples/complete/README.md +++ b/examples/complete/README.md @@ -46,6 +46,7 @@ No resources. | Name | Description | |------|-------------| | [arn](#output\_arn) | The Amazon Resource Name (ARN) identifying your Lambda Function. | +| [cloudwatch\_log\_group\_arn](#output\_cloudwatch\_log\_group\_arn) | The Amazon Resource Name (ARN) identifying the CloudWatch log group used by your Lambda function. | | [cloudwatch\_log\_group\_name](#output\_cloudwatch\_log\_group\_name) | The name of the CloudWatch log group used by your Lambda function. | | [function\_name](#output\_function\_name) | The unique name of your Lambda Function. | | [role\_name](#output\_role\_name) | The name of the IAM role attached to the Lambda Function. | diff --git a/examples/complete/main.tf b/examples/complete/main.tf index 1c95ff9..7667d25 100644 --- a/examples/complete/main.tf +++ b/examples/complete/main.tf @@ -23,13 +23,22 @@ module "lambda" { snap_start = false source_code_hash = module.fixtures.output_base64sha256 timeout = 3 - tracing_config_mode = "Active" - // logs and metrics + // logs, metrics and tracing cloudwatch_logs_enabled = true + cloudwatch_logs_log_group_class = "STANDARD" cloudwatch_logs_retention_in_days = 7 + cloudwatch_logs_skip_destroy = false cloudwatch_lambda_insights_enabled = true layers = ["arn:aws:lambda:${local.region}:580247275435:layer:LambdaInsightsExtension-Arm64:23"] + tracing_config_mode = "Active" + + // Advanced logging configuration + logging_config = { + application_log_level = "INFO" + log_format = "JSON" + system_log_level = "WARN" + } environment = { variables = { diff --git a/examples/complete/outputs.tf b/examples/complete/outputs.tf index da381f9..76acd0a 100644 --- a/examples/complete/outputs.tf +++ b/examples/complete/outputs.tf @@ -3,6 +3,11 @@ output "arn" { value = module.lambda.arn } +output "cloudwatch_log_group_arn" { + description = "The Amazon Resource Name (ARN) identifying the CloudWatch log group used by your Lambda function." + value = module.lambda.cloudwatch_log_group_arn +} + output "cloudwatch_log_group_name" { description = "The name of the CloudWatch log group used by your Lambda function." value = module.lambda.cloudwatch_log_group_name diff --git a/examples/complete/provider.tf b/examples/complete/provider.tf index aab71dc..5300107 100644 --- a/examples/complete/provider.tf +++ b/examples/complete/provider.tf @@ -4,5 +4,4 @@ provider "aws" { skip_credentials_validation = true skip_metadata_api_check = true skip_region_validation = true - } diff --git a/examples/fixtures/context/index.js b/examples/fixtures/context/index.js index d7836d5..61216ca 100644 --- a/examples/fixtures/context/index.js +++ b/examples/fixtures/context/index.js @@ -1,4 +1,8 @@ -exports.handler = async function(event, context) { - console.log("EVENT: \n" + JSON.stringify(event, null, 2)) - return context.logStreamName +exports.handler = async function (event, context) { + + console.debug({ event, context }) + console.info("Hello from Lambda!") + console.warn("This is a warning message!") + + return context.logStreamName } \ No newline at end of file diff --git a/examples/with-cloudwatch-logs-subscription/README.md b/examples/with-cloudwatch-logs-subscription/README.md deleted file mode 100644 index c5c7373..0000000 --- a/examples/with-cloudwatch-logs-subscription/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# Example with CloudWatch logs configuration - -Creates an AWS Lambda function with CloudWatch logs subscription filters and configured retention time. - -## usage - -``` -terraform init -terraform plan -terraform apply -``` - -Note that this example may create resources which cost money. Run `terraform destroy` to destroy those resources. - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.5.7 | -| [aws](#requirement\_aws) | >= 6.0 | - -## Providers - -No providers. - -## Modules - -| Name | Source | Version | -|------|--------|---------| -| [destination\_1](#module\_destination\_1) | ../../ | n/a | -| [destination\_2](#module\_destination\_2) | ../../ | n/a | -| [lambda](#module\_lambda) | ../../ | n/a | -| [source](#module\_source) | ../fixtures | n/a | - -## Resources - -No resources. - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [region](#input\_region) | n/a | `string` | `"eu-west-1"` | no | - -## Outputs - -| Name | Description | -|------|-------------| -| [arn](#output\_arn) | The Amazon Resource Name (ARN) identifying your Lambda Function. | -| [cloudwatch\_log\_group\_name](#output\_cloudwatch\_log\_group\_name) | The name of the CloudWatch log group used by your Lambda function. | -| [function\_name](#output\_function\_name) | The unique name of your Lambda Function. | -| [role\_name](#output\_role\_name) | The name of the IAM role attached to the Lambda Function. | - \ No newline at end of file diff --git a/examples/with-cloudwatch-logs-subscription/main.tf b/examples/with-cloudwatch-logs-subscription/main.tf deleted file mode 100644 index b225d77..0000000 --- a/examples/with-cloudwatch-logs-subscription/main.tf +++ /dev/null @@ -1,48 +0,0 @@ -module "source" { - source = "../fixtures" -} - -module "lambda" { - source = "../../" - - cloudwatch_logs_retention_in_days = 14 - description = "Example usage for an AWS Lambda with a CloudWatch logs subscription filter." - filename = module.source.output_path - function_name = "example-without-cloudwatch-logs-subscription" - handler = "index.handler" - runtime = "nodejs22.x" - source_code_hash = module.source.output_base64sha256 - - cloudwatch_log_subscription_filters = { - lambda_1 = { - //see https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_subscription_filter for available arguments - destination_arn = module.destination_1.arn // required - } - - lambda_2 = { - destination_arn = module.destination_2.arn // required - } - } -} - -module "destination_1" { - source = "../../" - - cloudwatch_logs_retention_in_days = 1 - filename = module.source.output_path - function_name = "cloudwatch-logs-subscription-destination-1" - handler = "index.handler" - runtime = "nodejs22.x" - source_code_hash = module.source.output_base64sha256 -} - -module "destination_2" { - source = "../../" - - cloudwatch_logs_retention_in_days = 1 - filename = module.source.output_path - function_name = "cloudwatch-logs-subscription-destination-2" - handler = "index.handler" - runtime = "nodejs22.x" - source_code_hash = module.source.output_base64sha256 -} diff --git a/examples/with-cloudwatch-logs-subscription/outputs.tf b/examples/with-cloudwatch-logs-subscription/outputs.tf deleted file mode 100644 index da381f9..0000000 --- a/examples/with-cloudwatch-logs-subscription/outputs.tf +++ /dev/null @@ -1,19 +0,0 @@ -output "arn" { - description = "The Amazon Resource Name (ARN) identifying your Lambda Function." - value = module.lambda.arn -} - -output "cloudwatch_log_group_name" { - description = "The name of the CloudWatch log group used by your Lambda function." - value = module.lambda.cloudwatch_log_group_name -} - -output "function_name" { - description = "The unique name of your Lambda Function." - value = module.lambda.function_name -} - -output "role_name" { - description = "The name of the IAM role attached to the Lambda Function." - value = module.lambda.role_name -} diff --git a/iam.tf b/iam.tf index e001dd7..01469f2 100644 --- a/iam.tf +++ b/iam.tf @@ -100,7 +100,7 @@ data "aws_iam_policy_document" "logs" { #trivy:ignore:AVD-AWS-0057 resources = [ - "${aws_cloudwatch_log_group.lambda.arn}:*" + "${local.log_group_arn}:*" ] } } diff --git a/main.tf b/main.tf index 0ea3e8c..c2ad039 100644 --- a/main.tf +++ b/main.tf @@ -77,8 +77,17 @@ resource "aws_lambda_function" "lambda" { } } - // create the CloudWatch log group first so it's no create automatically - // by AWS Lambda + dynamic "logging_config" { + for_each = var.logging_config == null ? [] : [var.logging_config] + content { + application_log_level = logging_config.value.application_log_level + log_format = logging_config.value.log_format + log_group = logging_config.value.log_group + system_log_level = logging_config.value.system_log_level + } + } + + // create the CloudWatch log group first so it's not automatically created by AWS Lambda depends_on = [aws_cloudwatch_log_group.lambda] } @@ -158,8 +167,17 @@ resource "aws_lambda_function" "lambda_external_lifecycle" { } } - // create the CloudWatch log group first so it's no create automatically - // by AWS Lambda + dynamic "logging_config" { + for_each = var.logging_config == null ? [] : [var.logging_config] + content { + application_log_level = logging_config.value.application_log_level + log_format = logging_config.value.log_format + log_group = logging_config.value.log_group + system_log_level = logging_config.value.system_log_level + } + } + + // create the CloudWatch log group first so it's not automatically created by AWS Lambda depends_on = [aws_cloudwatch_log_group.lambda] lifecycle { diff --git a/outputs.tf b/outputs.tf index f767d49..669b607 100644 --- a/outputs.tf +++ b/outputs.tf @@ -5,12 +5,12 @@ output "arn" { output "cloudwatch_log_group_name" { description = "The name of the CloudWatch log group used by your Lambda function." - value = aws_cloudwatch_log_group.lambda.name + value = local.log_group_name } output "cloudwatch_log_group_arn" { description = "The Amazon Resource Name (ARN) identifying the CloudWatch log group used by your Lambda function." - value = aws_cloudwatch_log_group.lambda.arn + value = local.log_group_arn } output "function_name" { diff --git a/variables.tf b/variables.tf index c0064a7..ad625c7 100644 --- a/variables.tf +++ b/variables.tf @@ -31,6 +31,7 @@ variable "cloudwatch_lambda_insights_enabled" { type = bool } +# FIXME: this variable should be renamed in the next major release to reflect that it attaches CloudWatch Logs permissions variable "cloudwatch_logs_enabled" { description = "Enables your Lambda function to send logs to CloudWatch. The IAM role of this Lambda function will be enhanced with required permissions." type = bool @@ -43,18 +44,37 @@ variable "cloudwatch_logs_kms_key_id" { default = null } + +variable "cloudwatch_logs_log_group_class" { + description = "Specifies the log class of the log group. Possible values are: `STANDARD`, `INFREQUENT_ACCESS`, or `DELIVERY`." + default = null + type = string +} + variable "cloudwatch_logs_retention_in_days" { description = "Specifies the number of days you want to retain log events in the specified log group. Possible values are: 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 3653, and 0. If you select 0, the events in the log group are always retained and never expire." default = null type = number } +variable "cloudwatch_logs_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 "cloudwatch_log_subscription_filters" { description = "CloudWatch Logs subscription filter resources. Currently supports only Lambda functions as destinations." default = {} type = map(any) } +variable "create_cloudwatch_log_group" { + description = "Create and manage the CloudWatch Log Group for the Lambda function. Set to `false` to reuse an existing log group." + default = true + type = bool +} + variable "description" { description = "Description of what your Lambda Function does." default = "" @@ -87,15 +107,21 @@ variable "filename" { type = string } +variable "handler" { + description = "The function entrypoint in your code." + default = "" + type = string +} + variable "ignore_external_function_updates" { description = "Ignore updates to your Lambda function executed externally to the Terraform lifecycle. Set this to `true` if you're using CodeDeploy, aws CLI or other external tools to update your Lambda function code." default = false type = bool } -variable "handler" { - description = "The function entrypoint in your code." - default = "" +variable "iam_role_name" { + description = "Override the name of the IAM role for the function. Otherwise the default will be your function name with the region as a suffix." + default = null type = string } @@ -135,6 +161,17 @@ variable "layers" { type = list(string) } +variable "logging_config" { + description = "Configuration block for advanced logging settings." + default = null + type = object({ + log_format = string + application_log_level = optional(string, null) + log_group = optional(string, null) + system_log_level = optional(string, null) + }) +} + variable "memory_size" { description = "Amount of memory in MB your Lambda Function can use at runtime." default = 128 @@ -249,12 +286,6 @@ variable "vpc_config" { }) } -variable "iam_role_name" { - description = "Override the name of the IAM role for the function. Otherwise the default will be your function name with the region as a suffix." - default = null - type = string -} - variable "snap_start" { description = "Enable snap start settings for low-latency startups. This feature is currently only supported for `java11` and `java17` runtimes and `x86_64` architectures." default = false