diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml
index cc105ab..a99cffa 100644
--- a/.github/workflows/checks.yml
+++ b/.github/workflows/checks.yml
@@ -10,7 +10,7 @@ jobs:
- uses: actions/checkout@master
- uses: hashicorp/setup-terraform@v1
with:
- terraform_version: 1.3.0
+ terraform_version: 1.7.4
- id: Init
run: terraform init -no-color
- id: Fmt
diff --git a/.tool-versions b/.tool-versions
new file mode 100644
index 0000000..a41ff70
--- /dev/null
+++ b/.tool-versions
@@ -0,0 +1 @@
+terraform 1.7.4
diff --git a/README.md b/README.md
index a1df14c..52ed7ee 100644
--- a/README.md
+++ b/README.md
@@ -32,52 +32,48 @@ No requirements.
## Modules
-No modules.
+| Name | Source | Version |
+|------|--------|---------|
+| [lambda\_function](#module\_lambda\_function) | terraform-aws-modules/lambda/aws | 7.2.6 |
## Resources
| Name | Type |
|------|------|
-| [aws_cloudwatch_log_group.lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |
-| [aws_cloudwatch_metric_alarm.lambda_errors](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_metric_alarm) | resource |
-| [aws_iam_role_policy_attachment.lambda_basic_execution](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
-| [aws_iam_role_policy_attachment.lambda_insights](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
-| [aws_iam_role_policy_attachment.lambda_networking](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
-| [aws_iam_role_policy_attachment.lambda_xray](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
-| [aws_lambda_function.lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function) | resource |
-| [aws_s3_bucket_object.lambda_deploy_object](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_object) | resource |
-| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
+| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
+| [aws_sns_topic.notifications](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/sns_topic) | data source |
+| [aws_ssm_parameter.account_name](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
| [aws_ssm_parameter.deployment_bucket_id](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
+| [aws_ssm_parameter.global_account_id](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
+| [aws_ssm_parameter.kms_key_arn](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
-| [deployment\_bucket\_id](#input\_deployment\_bucket\_id) | ID of S3 bucket that should store our deployment artifacts. Will use the /account/DEPLOYMENT\_BUCKET\_ID value from SSM unless specified otherwise. | `string` | `null` | no |
-| [description](#input\_description) | Description of the Lambda Function | `string` | `null` | no |
-| [environment](#input\_environment) | Environment variables to be passed to the function | `map(string)` | `{}` | no |
-| [error\_rate\_alarm\_threshold](#input\_error\_rate\_alarm\_threshold) | Error rate (in percent, 1-100) at which to trigger an alarm notification | `number` | `25` | no |
-| [git\_sha](#input\_git\_sha) | Hash generated by `git hash-object` in source repo and used to determine whether a lambda needs to be updated | `string` | `null` | no |
-| [handler](#input\_handler) | Name of the handler function inside the artifact (https://docs.aws.amazon.com/lambda/latest/dg/configuration-console.html) | `string` | n/a | yes |
-| [layer\_arns](#input\_layer\_arns) | List of ARNs for layers to use with the function | `list(string)` | `[]` | no |
-| [log\_retention\_in\_days](#input\_log\_retention\_in\_days) | Number of days to keep function logs in Cloudwatch | `number` | `365` | no |
-| [memory\_size](#input\_memory\_size) | Amount of memory (in MB) to allocate to the function | `number` | `128` | no |
-| [name](#input\_name) | Name for the function | `string` | n/a | yes |
-| [notifications\_topic\_arn](#input\_notifications\_topic\_arn) | SNS topic to send error notifications | `string` | n/a | yes |
-| [path](#input\_path) | Local path to a zipped artifact containing the function code | `string` | n/a | yes |
-| [reserved\_concurrent\_executions](#input\_reserved\_concurrent\_executions) | Reserved concurrent executions (none by default) | `number` | `null` | no |
-| [role\_name](#input\_role\_name) | Name of the execution role for the function. It does not need to include logging/networking permissions - those policies will be added automatically. | `string` | n/a | yes |
-| [runtime](#input\_runtime) | Language runtime for the function (https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html) | `string` | n/a | yes |
-| [security\_group\_ids](#input\_security\_group\_ids) | Security groups for the function (if run in a VPC) | `list(string)` | `[]` | no |
-| [subnet\_ids](#input\_subnet\_ids) | Subnets for the function (if run in a VPC) | `list(string)` | `[]` | no |
-| [tags](#input\_tags) | Tags to apply to created resources | `map(any)` | `{}` | no |
-| [timeout](#input\_timeout) | Function timeout in seconds | `number` | `15` | no |
+| [allowed\_triggers](#input\_allowed\_triggers) | n/a | `map(any)` | `{}` | no |
+| [description](#input\_description) | n/a | `string` | `null` | no |
+| [ecr\_address](#input\_ecr\_address) | n/a | `string` | n/a | yes |
+| [ecr\_arn](#input\_ecr\_arn) | n/a | `string` | n/a | yes |
+| [engine\_name](#input\_engine\_name) | n/a | `string` | n/a | yes |
+| [environment\_variables](#input\_environment\_variables) | n/a | `map(any)` | `{}` | no |
+| [event\_source\_mapping](#input\_event\_source\_mapping) | Map of event source mappings | `map(any)` | `{}` | no |
+| [function\_name](#input\_function\_name) | n/a | `string` | n/a | yes |
+| [handler](#input\_handler) | n/a | `string` | `null` | no |
+| [image\_tag](#input\_image\_tag) | n/a | `string` | `"latest"` | no |
+| [memory\_size](#input\_memory\_size) | n/a | `number` | `1024` | no |
+| [policy\_statements](#input\_policy\_statements) | n/a | `map(any)` | `{}` | no |
+| [security\_group\_ids](#input\_security\_group\_ids) | n/a | `list(string)` | `[]` | no |
+| [subnet\_ids](#input\_subnet\_ids) | n/a | `list(string)` | `[]` | no |
+| [timeout](#input\_timeout) | n/a | `string` | `"60"` | no |
## Outputs
| Name | Description |
|------|-------------|
-| [function\_arn](#output\_function\_arn) | ARN of the created/updated Lambda function |
-| [function\_invoke\_arn](#output\_function\_invoke\_arn) | ARN for invoking the created Lambda function |
-| [function\_name](#output\_function\_name) | Name of the created Lambda function |
-| [function\_version](#output\_function\_version) | Version of the created/updated Lambda function |
+| [function\_arn](#output\_function\_arn) | n/a |
+| [function\_name](#output\_function\_name) | n/a |
+| [function\_version](#output\_function\_version) | n/a |
+| [lambda\_role\_arn](#output\_lambda\_role\_arn) | n/a |
+| [lambda\_role\_name](#output\_lambda\_role\_name) | n/a |
+| [qualified\_function\_arn](#output\_qualified\_function\_arn) | n/a |
diff --git a/data.tf b/data.tf
index 4c2b538..5178b52 100644
--- a/data.tf
+++ b/data.tf
@@ -1,5 +1,21 @@
-data "aws_caller_identity" "current" {}
+data "aws_region" "current" {}
+
+data "aws_ssm_parameter" "account_name" {
+ name = "/account/name"
+}
+
+data "aws_sns_topic" "notifications" {
+ name = "account-notifications"
+}
data "aws_ssm_parameter" "deployment_bucket_id" {
name = "/account/DEPLOYMENT_BUCKET_ID"
}
+
+data "aws_ssm_parameter" "kms_key_arn" {
+ name = "/account/KMS_KEY_ARN"
+}
+
+data "aws_ssm_parameter" "global_account_id" {
+ name = "/global/account_id"
+}
diff --git a/main.tf b/main.tf
index fc0cc6a..1eda524 100644
--- a/main.tf
+++ b/main.tf
@@ -23,166 +23,73 @@
*/
locals {
- deploy_artifact_key = "deploy.zip"
- deployment_bucket_id = coalesce(var.deployment_bucket_id, data.aws_ssm_parameter.deployment_bucket_id.value)
- source_hash = coalesce(var.git_sha, try(filebase64sha256(var.path), null))
- role_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/${var.role_name}"
+ description = coalesce(var.description, "Highwing Engine - ${title(var.engine_name)}")
+ handler = coalesce(var.handler, "engines/${var.engine_name}/lambda.handler")
+
+ environment = data.aws_ssm_parameter.account_name.value
+ environment_variables = (
+ startswith(local.environment, "production")
+ ? var.environment_variables
+ : merge(var.environment_variables, {
+ DEV_MODE_ENABLED = true
+ })
+ )
}
-# Configure default role permissions
-resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
- role = var.role_name
- policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
-}
-
-resource "aws_iam_role_policy_attachment" "lambda_networking" {
- role = var.role_name
- policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
-}
-
-resource "aws_iam_role_policy_attachment" "lambda_xray" {
- role = var.role_name
- policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
-}
-
-resource "aws_iam_role_policy_attachment" "lambda_insights" {
- role = var.role_name
- policy_arn = "arn:aws:iam::aws:policy/CloudWatchLambdaInsightsExecutionRolePolicy"
-}
-
-# S3 object to hold the deployed artifact
-resource "aws_s3_bucket_object" "lambda_deploy_object" {
- count = var.image_uri == null ? 1 : 0
- bucket = local.deployment_bucket_id
- key = "${var.name}/${local.deploy_artifact_key}"
- source = var.path
- source_hash = md5(local.source_hash)
- tags = merge(var.tags, {
- GitSHA = var.git_sha
- })
-}
-
-# The Lambda function itself
-resource "aws_lambda_function" "lambda" {
- function_name = var.name
- description = var.description
- handler = var.handler
- layers = var.image_uri == null ? concat(
- var.layer_arns,
- ["arn:aws:lambda:us-east-1:580247275435:layer:LambdaInsightsExtension:14"] # ARN for us-east-1
- ) : null
- memory_size = var.memory_size
- publish = true
- reserved_concurrent_executions = var.reserved_concurrent_executions
- role = local.role_arn
- runtime = var.runtime
- package_type = var.image_uri == null ? "Zip" : "Image"
- s3_bucket = var.image_uri == null ? local.deployment_bucket_id : null
- s3_key = var.image_uri == null ? aws_s3_bucket_object.lambda_deploy_object[0].key : null
- s3_object_version = var.image_uri == null ? aws_s3_bucket_object.lambda_deploy_object[0].version_id : null
- image_uri = var.image_uri
- tags = var.tags
- timeout = var.timeout
-
- dynamic "vpc_config" {
- for_each = length(var.subnet_ids) > 0 ? [1] : []
- content {
- subnet_ids = var.subnet_ids
- security_group_ids = var.security_group_ids
- }
- }
-
- dynamic "environment" {
- for_each = length(var.environment) > 0 ? [1] : []
- content {
- variables = var.environment
- }
- }
-
- tracing_config {
- mode = "Active"
- }
-
- lifecycle {
- precondition {
- condition = (var.image_uri != null && var.path == null) || (var.image_uri == null && var.path != null)
- error_message = "Cannot specify image_uri AND path"
+module "lambda_function" {
+ source = "terraform-aws-modules/lambda/aws"
+ version = "7.2.6"
+
+ function_name = var.function_name
+ description = local.description
+ handler = local.handler
+ memory_size = var.memory_size
+ publish = true
+ timeout = var.timeout
+
+ vpc_subnet_ids = var.subnet_ids
+ vpc_security_group_ids = var.security_group_ids
+
+ attach_cloudwatch_logs_policy = true
+ attach_dead_letter_policy = true
+ attach_network_policy = true
+ attach_tracing_policy = true
+
+ allowed_triggers = var.allowed_triggers
+
+ number_of_policies = 2
+ policies = [
+ "arn:aws:iam::aws:policy/CloudWatchLambdaInsightsExecutionRolePolicy",
+ "arn:aws:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole"
+ ]
+ attach_policies = true
+
+ attach_policy_statements = length(var.policy_statements) > 0 ? true : false
+ policy_statements = merge(var.policy_statements, {
+ ecr = {
+ effect = "Allow"
+ actions = [
+ "ecr:BatchGetImage",
+ "ecr:GetDownloadUrlForLayer"
+ ]
+ resources = [var.ecr_arn]
}
- precondition {
- condition = (var.image_uri != null && var.handler == null) || (var.image_uri == null && var.handler != null)
- error_message = "Cannot specify image_uri AND handler"
- }
- precondition {
- condition = (var.image_uri != null && length(var.layer_arns) == 0) || (var.image_uri == null)
- error_message = "Cannot specify image_uri AND layer_arns"
- }
- precondition {
- condition = (var.image_uri != null && var.path == null) || (var.image_uri == null && var.path != null)
- error_message = "Cannot specify image_uri AND path"
- }
- precondition {
- condition = (var.image_uri != null && var.runtime == null) || (var.image_uri == null && var.path != null)
- error_message = "Cannot specify image_uri AND runtime"
- }
- }
-
-}
-
-# An alarm to notify of function errors
-resource "aws_cloudwatch_metric_alarm" "lambda_errors" {
- alarm_actions = [var.notifications_topic_arn]
- alarm_description = "This metric monitors the error rate on the ${var.name} lambda"
- alarm_name = "${var.name} - High Error Rate"
- comparison_operator = "GreaterThanOrEqualToThreshold"
- evaluation_periods = "2"
- threshold = var.error_rate_alarm_threshold
- treat_missing_data = "notBreaching"
-
- metric_query {
- id = "error_rate"
- expression = "errors/invocations*100"
- label = "Error Rate"
- return_data = "true"
- }
-
- metric_query {
- id = "errors"
-
- metric {
- metric_name = "Errors"
- namespace = "AWS/Lambda"
- period = "600"
- stat = "Sum"
- unit = "Count"
-
- dimensions = {
- FunctionName = aws_lambda_function.lambda.function_name
- }
- }
- }
+ })
- metric_query {
- id = "invocations"
+ cloudwatch_logs_retention_in_days = 1096
+ dead_letter_target_arn = data.aws_sns_topic.notifications.arn
+ kms_key_arn = data.aws_ssm_parameter.kms_key_arn.value
+ tracing_mode = "Active"
- metric {
- metric_name = "Invocations"
- namespace = "AWS/Lambda"
- period = "600"
- stat = "Sum"
- unit = "Count"
+ create_package = false
+ image_uri = "${var.ecr_address}:lambda_${var.image_tag}"
+ package_type = "Image"
+ architectures = ["x86_64"]
- dimensions = {
- FunctionName = aws_lambda_function.lambda.function_name
- }
- }
- }
+ event_source_mapping = var.event_source_mapping
- tags = var.tags
+ environment_variables = merge(local.environment_variables, {
+ DD_SERVICE = replace(var.function_name, "-", "_")
+ })
}
-# Configure logging in Cloudwatch
-resource "aws_cloudwatch_log_group" "lambda" {
- name = "/aws/lambda/${var.name}"
- retention_in_days = var.log_retention_in_days
- tags = var.tags
-}
diff --git a/outputs.tf b/outputs.tf
index 95b0fd5..8cde8f4 100644
--- a/outputs.tf
+++ b/outputs.tf
@@ -1,19 +1,23 @@
-output "function_name" {
- description = "Name of the created Lambda function"
- value = aws_lambda_function.lambda.function_name
+output "function_arn" {
+ value = module.lambda_function.lambda_function_arn
}
-output "function_arn" {
- description = "ARN of the created/updated Lambda function"
- value = aws_lambda_function.lambda.arn
+output "qualified_function_arn" {
+ value = module.lambda_function.lambda_function_qualified_arn
+}
+
+output "function_name" {
+ value = module.lambda_function.lambda_function_name
}
output "function_version" {
- description = "Version of the created/updated Lambda function"
- value = aws_lambda_function.lambda.version
+ value = module.lambda_function.lambda_function_version
+}
+
+output "lambda_role_arn" {
+ value = module.lambda_function.lambda_role_arn
}
-output "function_invoke_arn" {
- description = "ARN for invoking the created Lambda function"
- value = aws_lambda_function.lambda.invoke_arn
+output "lambda_role_name" {
+ value = module.lambda_function.lambda_role_name
}
diff --git a/variables.tf b/variables.tf
new file mode 100644
index 0000000..f6d46b3
--- /dev/null
+++ b/variables.tf
@@ -0,0 +1,71 @@
+variable "allowed_triggers" {
+ type = map(any)
+ default = {}
+}
+
+variable "engine_name" {
+ type = string
+}
+
+variable "function_name" {
+ type = string
+}
+
+variable "description" {
+ type = string
+ default = null
+}
+
+variable "ecr_address" {
+ type = string
+}
+
+variable "ecr_arn" {
+ type = string
+}
+
+variable "environment_variables" {
+ type = map(any)
+ default = {}
+}
+
+variable "event_source_mapping" {
+ description = "Map of event source mappings"
+ type = map(any)
+ default = {}
+}
+
+variable "handler" {
+ type = string
+ default = null
+}
+
+variable "image_tag" {
+ type = string
+ default = "latest"
+}
+
+variable "memory_size" {
+ type = number
+ default = 1024
+}
+
+variable "policy_statements" {
+ type = map(any)
+ default = {}
+}
+
+variable "subnet_ids" {
+ type = list(string)
+ default = []
+}
+
+variable "security_group_ids" {
+ type = list(string)
+ default = []
+}
+
+variable "timeout" {
+ type = string
+ default = "60"
+}
diff --git a/vars.tf b/vars.tf
deleted file mode 100644
index 2494ad9..0000000
--- a/vars.tf
+++ /dev/null
@@ -1,116 +0,0 @@
-variable "description" {
- default = null
- description = "Description of the Lambda Function"
- type = string
-}
-
-variable "image_uri" {
- default = null
- description = "Image URI of the ECR container image for the Lambda Function. Overrides the use of path variable & s3 deployment object"
- type = string
-}
-
-variable "deployment_bucket_id" {
- default = null
- description = "ID of S3 bucket that should store our deployment artifacts. Will use the /account/DEPLOYMENT_BUCKET_ID value from SSM unless specified otherwise."
- type = string
-}
-
-variable "environment" {
- default = {}
- description = "Environment variables to be passed to the function"
- type = map(string)
-}
-
-variable "error_rate_alarm_threshold" {
- default = 25
- description = "Error rate (in percent, 1-100) at which to trigger an alarm notification"
- type = number
-}
-
-variable "git_sha" {
- default = null
- description = "Hash generated by `git hash-object` in source repo and used to determine whether a lambda needs to be updated"
- type = string
-}
-
-variable "handler" {
- default = null
- description = "Name of the handler function inside the artifact (https://docs.aws.amazon.com/lambda/latest/dg/configuration-console.html)"
- type = string
-}
-
-variable "layer_arns" {
- default = []
- description = "List of ARNs for layers to use with the function"
- type = list(string)
-}
-
-variable "log_retention_in_days" {
- description = "Number of days to keep function logs in Cloudwatch"
- type = number
- default = 1096
-}
-
-variable "memory_size" {
- description = "Amount of memory (in MB) to allocate to the function"
- type = number
- default = 128
-}
-
-variable "name" {
- description = "Name for the function"
- type = string
-}
-
-variable "notifications_topic_arn" {
- description = "SNS topic to send error notifications"
- type = string
-}
-
-variable "path" {
- default = null
- description = "Local path to a zipped artifact containing the function code"
- type = string
-}
-
-variable "reserved_concurrent_executions" {
- type = number
- description = "Reserved concurrent executions (none by default)"
- default = null
-}
-
-variable "role_name" {
- description = "Name of the execution role for the function. It does not need to include logging/networking permissions - those policies will be added automatically."
- type = string
-}
-
-variable "runtime" {
- default = null
- type = string
- description = "Language runtime for the function (https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html)"
-}
-
-variable "security_group_ids" {
- type = list(string)
- description = "Security groups for the function (if run in a VPC)"
- default = []
-}
-
-variable "subnet_ids" {
- type = list(string)
- description = "Subnets for the function (if run in a VPC)"
- default = []
-}
-
-variable "tags" {
- type = map(any)
- description = "Tags to apply to created resources"
- default = {}
-}
-
-variable "timeout" {
- type = number
- description = "Function timeout in seconds"
- default = 15
-}