diff --git a/main.tf b/main.tf index fc231abe..9c8b1f36 100644 --- a/main.tf +++ b/main.tf @@ -19,11 +19,158 @@ locals { s3_key = var.s3_existing_package != null ? try(var.s3_existing_package.key, null) : (var.store_on_s3 ? var.s3_prefix != null ? format("%s%s", var.s3_prefix, replace(local.archive_filename_string, "/^.*//", "")) : replace(local.archive_filename_string, "/^\\.//", "") : null) s3_object_version = var.s3_existing_package != null ? try(var.s3_existing_package.version_id, null) : (var.store_on_s3 ? try(aws_s3_object.lambda_package[0].version_id, null) : null) + lambda_used = var.ignore_image_uri ? aws_lambda_function.image_function[0] : aws_lambda_function.this[0] + } resource "aws_lambda_function" "this" { - count = local.create && var.create_function && !var.create_layer ? 1 : 0 + count = local.create && var.create_function && !var.create_layer && !var.ignore_image_uri ? 1 : 0 + + function_name = var.function_name + description = var.description + role = var.create_role ? aws_iam_role.lambda[0].arn : var.lambda_role + handler = var.package_type != "Zip" ? null : var.handler + memory_size = var.memory_size + reserved_concurrent_executions = var.reserved_concurrent_executions + runtime = var.package_type != "Zip" ? null : var.runtime + layers = var.layers + timeout = var.lambda_at_edge ? min(var.timeout, 30) : var.timeout + publish = (var.lambda_at_edge || var.snap_start) ? true : var.publish + kms_key_arn = var.kms_key_arn + image_uri = var.image_uri + package_type = var.package_type + architectures = var.architectures + code_signing_config_arn = var.code_signing_config_arn + replace_security_groups_on_destroy = var.replace_security_groups_on_destroy + replacement_security_group_ids = var.replacement_security_group_ids + skip_destroy = var.skip_destroy + + /* ephemeral_storage is not supported in gov-cloud region, so it should be set to `null` */ + dynamic "ephemeral_storage" { + for_each = var.ephemeral_storage_size == null ? [] : [true] + + content { + size = var.ephemeral_storage_size + } + } + + filename = local.filename + source_code_hash = var.ignore_source_code_hash ? null : (local.filename == null ? false : fileexists(local.filename)) && !local.was_missing ? filebase64sha256(local.filename) : null + + s3_bucket = local.s3_bucket + s3_key = local.s3_key + s3_object_version = local.s3_object_version + + dynamic "image_config" { + for_each = length(var.image_config_entry_point) > 0 || length(var.image_config_command) > 0 || var.image_config_working_directory != null ? [true] : [] + content { + entry_point = var.image_config_entry_point + command = var.image_config_command + working_directory = var.image_config_working_directory + } + } + + dynamic "environment" { + for_each = length(keys(var.environment_variables)) == 0 ? [] : [true] + content { + variables = var.environment_variables + } + } + + dynamic "dead_letter_config" { + for_each = var.dead_letter_target_arn == null ? [] : [true] + content { + target_arn = var.dead_letter_target_arn + } + } + + dynamic "tracing_config" { + for_each = var.tracing_mode == null ? [] : [true] + content { + mode = var.tracing_mode + } + } + + dynamic "vpc_config" { + for_each = var.vpc_subnet_ids != null && var.vpc_security_group_ids != null ? [true] : [] + content { + security_group_ids = var.vpc_security_group_ids + subnet_ids = var.vpc_subnet_ids + } + } + + dynamic "file_system_config" { + for_each = var.file_system_arn != null && var.file_system_local_mount_path != null ? [true] : [] + content { + local_mount_path = var.file_system_local_mount_path + arn = var.file_system_arn + } + } + dynamic "snap_start" { + for_each = var.snap_start ? [true] : [] + + content { + apply_on = "PublishedVersions" + } + } + + dynamic "logging_config" { + # Dont create logging config on gov cloud as it is not avaible. + # See https://github.com/hashicorp/terraform-provider-aws/issues/34810 + for_each = data.aws_partition.current.partition == "aws" ? [true] : [] + + content { + log_group = var.logging_log_group + log_format = var.logging_log_format + application_log_level = var.logging_log_format == "Text" ? null : var.logging_application_log_level + system_log_level = var.logging_log_format == "Text" ? null : var.logging_system_log_level + } + } + + dynamic "timeouts" { + for_each = length(var.timeouts) > 0 ? [true] : [] + + content { + create = try(var.timeouts.create, null) + update = try(var.timeouts.update, null) + delete = try(var.timeouts.delete, null) + } + } + + tags = merge( + { terraform-aws-modules = "lambda" }, + var.tags, + var.function_tags + ) + + depends_on = [ + null_resource.archive, + aws_s3_object.lambda_package, + + # Depending on the log group is necessary to allow Terraform to create the log group before AWS can. + # When a lambda function is invoked, AWS creates the log group automatically if it doesn't exist yet. + # Without the dependency, this can result in a race condition if the lambda function is invoked before + # Terraform can create the log group. + aws_cloudwatch_log_group.lambda, + + # Before the lambda is created the execution role with all its policies should be ready + aws_iam_role_policy_attachment.additional_inline, + aws_iam_role_policy_attachment.additional_json, + aws_iam_role_policy_attachment.additional_jsons, + aws_iam_role_policy_attachment.additional_many, + aws_iam_role_policy_attachment.additional_one, + aws_iam_role_policy_attachment.async, + aws_iam_role_policy_attachment.logs, + aws_iam_role_policy_attachment.dead_letter, + aws_iam_role_policy_attachment.vpc, + aws_iam_role_policy_attachment.tracing, + ] +} + +resource "aws_lambda_function" "image_function" { + count = local.create && var.create_function && !var.create_layer && var.ignore_image_uri ? 1 : 0 + function_name = var.function_name description = var.description role = var.create_role ? aws_iam_role.lambda[0].arn : var.lambda_role @@ -164,6 +311,10 @@ resource "aws_lambda_function" "this" { aws_iam_role_policy_attachment.vpc, aws_iam_role_policy_attachment.tracing, ] + lifecycle { + ignore_changes = [image_uri] + } + } resource "aws_lambda_layer_version" "this" { @@ -235,8 +386,8 @@ resource "aws_cloudwatch_log_group" "lambda" { resource "aws_lambda_provisioned_concurrency_config" "current_version" { count = local.create && var.create_function && !var.create_layer && var.provisioned_concurrent_executions > -1 ? 1 : 0 - function_name = aws_lambda_function.this[0].function_name - qualifier = aws_lambda_function.this[0].version + function_name = local.lambda_used + qualifier = local.lambda_used.version provisioned_concurrent_executions = var.provisioned_concurrent_executions } @@ -248,8 +399,8 @@ locals { resource "aws_lambda_function_event_invoke_config" "this" { for_each = { for k, v in local.qualifiers : k => v if v != null && local.create && var.create_function && !var.create_layer && var.create_async_event_config } - function_name = aws_lambda_function.this[0].function_name - qualifier = each.key == "current_version" ? aws_lambda_function.this[0].version : null + function_name = local.lambda_used.function_name + qualifier = each.key == "current_version" ? local.lambda_used.version : null maximum_event_age_in_seconds = var.maximum_event_age_in_seconds maximum_retry_attempts = var.maximum_retry_attempts @@ -277,8 +428,8 @@ resource "aws_lambda_function_event_invoke_config" "this" { resource "aws_lambda_permission" "current_version_triggers" { for_each = { for k, v in var.allowed_triggers : k => v if local.create && var.create_function && !var.create_layer && var.create_current_version_allowed_triggers } - function_name = aws_lambda_function.this[0].function_name - qualifier = aws_lambda_function.this[0].version + function_name = local.lambda_used.function_name + qualifier = local.lambda_used.version statement_id_prefix = try(each.value.statement_id, each.key) action = try(each.value.action, "lambda:InvokeFunction") @@ -297,7 +448,7 @@ resource "aws_lambda_permission" "current_version_triggers" { resource "aws_lambda_permission" "unqualified_alias_triggers" { for_each = { for k, v in var.allowed_triggers : k => v if local.create && var.create_function && !var.create_layer && var.create_unqualified_alias_allowed_triggers } - function_name = aws_lambda_function.this[0].function_name + function_name = local.lambda_used.function_name statement_id_prefix = try(each.value.statement_id, each.key) action = try(each.value.action, "lambda:InvokeFunction") @@ -315,7 +466,7 @@ resource "aws_lambda_permission" "unqualified_alias_triggers" { resource "aws_lambda_event_source_mapping" "this" { for_each = { for k, v in var.event_source_mapping : k => v if local.create && var.create_function && !var.create_layer && var.create_unqualified_alias_allowed_triggers } - function_name = aws_lambda_function.this[0].arn + function_name = local.lambda_used.arn event_source_arn = try(each.value.event_source_arn, null) @@ -395,10 +546,10 @@ resource "aws_lambda_event_source_mapping" "this" { resource "aws_lambda_function_url" "this" { count = local.create && var.create_function && !var.create_layer && var.create_lambda_function_url ? 1 : 0 - function_name = aws_lambda_function.this[0].function_name + function_name = local.lambda_used.function_name # Error: error creating Lambda Function URL: ValidationException - qualifier = var.create_unqualified_alias_lambda_function_url ? null : aws_lambda_function.this[0].version + qualifier = var.create_unqualified_alias_lambda_function_url ? null : local.lambda_used.version authorization_type = var.authorization_type invoke_mode = var.invoke_mode @@ -425,7 +576,7 @@ resource "null_resource" "sam_metadata_aws_lambda_function" { triggers = { # This is a way to let SAM CLI correlates between the Lambda function resource, and this metadata # resource - resource_name = "aws_lambda_function.this[0]" + resource_name = "local.lambda_used" resource_type = "ZIP_LAMBDA_FUNCTION" # The Lambda function source code. diff --git a/outputs.tf b/outputs.tf index 6d53a66c..d2e4f84a 100644 --- a/outputs.tf +++ b/outputs.tf @@ -1,7 +1,7 @@ # Lambda Function output "lambda_function_arn" { description = "The ARN of the Lambda Function" - value = try(aws_lambda_function.this[0].arn, "") + value = try(local.lambda_used.arn, "") } output "lambda_function_arn_static" { @@ -11,57 +11,57 @@ output "lambda_function_arn_static" { output "lambda_function_invoke_arn" { description = "The Invoke ARN of the Lambda Function" - value = try(aws_lambda_function.this[0].invoke_arn, "") + value = try(local.lambda_used.invoke_arn, "") } output "lambda_function_name" { description = "The name of the Lambda Function" - value = try(aws_lambda_function.this[0].function_name, "") + value = try(local.lambda_used.function_name, "") } output "lambda_function_qualified_arn" { description = "The ARN identifying your Lambda Function Version" - value = try(aws_lambda_function.this[0].qualified_arn, "") + value = try(local.lambda_used.qualified_arn, "") } output "lambda_function_qualified_invoke_arn" { description = "The Invoke ARN identifying your Lambda Function Version" - value = try(aws_lambda_function.this[0].qualified_invoke_arn, "") + value = try(local.lambda_used.qualified_invoke_arn, "") } output "lambda_function_version" { description = "Latest published version of Lambda Function" - value = try(aws_lambda_function.this[0].version, "") + value = try(local.lambda_used.version, "") } output "lambda_function_last_modified" { description = "The date Lambda Function resource was last modified" - value = try(aws_lambda_function.this[0].last_modified, "") + value = try(local.lambda_used.last_modified, "") } output "lambda_function_kms_key_arn" { description = "The ARN for the KMS encryption key of Lambda Function" - value = try(aws_lambda_function.this[0].kms_key_arn, "") + value = try(local.lambda_used.kms_key_arn, "") } output "lambda_function_source_code_hash" { description = "Base64-encoded representation of raw SHA-256 sum of the zip file" - value = try(aws_lambda_function.this[0].source_code_hash, "") + value = try(local.lambda_used.source_code_hash, "") } output "lambda_function_source_code_size" { description = "The size in bytes of the function .zip file" - value = try(aws_lambda_function.this[0].source_code_size, "") + value = try(local.lambda_used.source_code_size, "") } output "lambda_function_signing_job_arn" { description = "ARN of the signing job" - value = try(aws_lambda_function.this[0].signing_job_arn, "") + value = try(local.lambda_used.signing_job_arn, "") } output "lambda_function_signing_profile_version_arn" { description = "ARN of the signing profile version" - value = try(aws_lambda_function.this[0].signing_profile_version_arn, "") + value = try(local.lambda_used.signing_profile_version_arn, "") } # Lambda Function URL diff --git a/variables.tf b/variables.tf index 829019c7..a59a7781 100644 --- a/variables.tf +++ b/variables.tf @@ -206,6 +206,12 @@ variable "package_type" { default = "Zip" } +variable "ignore_image_uri" { + description = "Ignores changes to the image_uri" + type = bool + default = false +} + variable "image_uri" { description = "The ECR image URI containing the function's deployment package." type = string