diff --git a/README.md b/README.md
index 10cdbe39..9c2636b8 100644
--- a/README.md
+++ b/README.md
@@ -711,6 +711,7 @@ No modules.
| [aws_lambda_permission.unqualified_alias_triggers](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission) | resource |
| [aws_lambda_provisioned_concurrency_config.current_version](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_provisioned_concurrency_config) | resource |
| [aws_s3_object.lambda_package](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource |
+| [aws_signer_signing_job.lambda_code_signing](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/signer_signing_job) | resource |
| [local_file.archive_plan](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource |
| [null_resource.archive](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource |
| [null_resource.sam_metadata_aws_lambda_function](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource |
@@ -783,6 +784,7 @@ No modules.
| [docker\_image](#input\_docker\_image) | Docker image to use for the build | `string` | `""` | no |
| [docker\_pip\_cache](#input\_docker\_pip\_cache) | Whether to mount a shared pip cache folder into docker environment or not | `any` | `null` | no |
| [docker\_with\_ssh\_agent](#input\_docker\_with\_ssh\_agent) | Whether to pass SSH\_AUTH\_SOCK into docker environment or not | `bool` | `false` | no |
+| [enable\_code\_signing](#input\_enable\_code\_signing) | Must be used with a lambda storing code on s3. Set this to true for triggering a signing job creating a signed copy of the lambda zip. https://docs.aws.amazon.com/lambda/latest/dg/configuration-codesigning.html | `bool` | `false` | no |
| [environment\_variables](#input\_environment\_variables) | A map that defines environment variables for the Lambda Function. | `map(string)` | `{}` | no |
| [ephemeral\_storage\_size](#input\_ephemeral\_storage\_size) | Amount of ephemeral storage (/tmp) in MB your Lambda Function can use at runtime. Valid value between 512 MB to 10,240 MB (10 GB). | `number` | `512` | no |
| [event\_source\_mapping](#input\_event\_source\_mapping) | Map of event source mapping | `any` | `{}` | no |
@@ -792,6 +794,7 @@ No modules.
| [function\_tags](#input\_function\_tags) | A map of tags to assign only to the lambda function | `map(string)` | `{}` | no |
| [handler](#input\_handler) | Lambda Function entrypoint in your code | `string` | `""` | no |
| [hash\_extra](#input\_hash\_extra) | The string to add into hashing function. Useful when building same source path for different functions. | `string` | `""` | no |
+| [ignore\_signing\_job\_failure](#input\_ignore\_signing\_job\_failure) | Set this argument to true to ignore signing job failures and retrieve failed status and reason | `bool` | `false` | no |
| [ignore\_source\_code\_hash](#input\_ignore\_source\_code\_hash) | Whether to ignore changes to the function's source code hash. Set to true if you manage infrastructure and code deployments separately. | `bool` | `false` | no |
| [image\_config\_command](#input\_image\_config\_command) | The CMD for the docker image | `list(string)` | `[]` | no |
| [image\_config\_entry\_point](#input\_image\_config\_entry\_point) | The ENTRYPOINT for the docker image | `list(string)` | `[]` | no |
@@ -803,6 +806,7 @@ No modules.
| [kms\_key\_arn](#input\_kms\_key\_arn) | The ARN of KMS key to use by your Lambda Function | `string` | `null` | no |
| [lambda\_at\_edge](#input\_lambda\_at\_edge) | Set this to true if using Lambda@Edge, to enable publishing, limit the timeout, and allow edgelambda.amazonaws.com to invoke the function | `bool` | `false` | no |
| [lambda\_at\_edge\_logs\_all\_regions](#input\_lambda\_at\_edge\_logs\_all\_regions) | Whether to specify a wildcard in IAM policy used by Lambda@Edge to allow logging in all regions | `bool` | `true` | no |
+| [lambda\_code\_signing\_profile\_name](#input\_lambda\_code\_signing\_profile\_name) | Lambda code signing profile name https://console.aws.amazon.com/lambda/home#/code-signing-configurations | `string` | `null` | no |
| [lambda\_role](#input\_lambda\_role) | IAM role ARN attached to the Lambda Function. This governs both who / what can invoke your Lambda Function, as well as what resources our Lambda Function has access to. See Lambda Permission Model for more details. | `string` | `""` | no |
| [layer\_name](#input\_layer\_name) | Name of Lambda Layer to create | `string` | `""` | no |
| [layer\_skip\_destroy](#input\_layer\_skip\_destroy) | Whether to retain the old version of a previously deployed Lambda Layer. | `bool` | `false` | no |
@@ -852,6 +856,8 @@ No modules.
| [s3\_object\_tags\_only](#input\_s3\_object\_tags\_only) | Set to true to not merge tags with s3\_object\_tags. Useful to avoid breaching S3 Object 10 tag limit. | `bool` | `false` | no |
| [s3\_prefix](#input\_s3\_prefix) | Directory name where artifacts should be stored in the S3 bucket. If unset, the path from `artifacts_dir` is used | `string` | `null` | no |
| [s3\_server\_side\_encryption](#input\_s3\_server\_side\_encryption) | Specifies server-side encryption of the object in S3. Valid values are "AES256" and "aws:kms". | `string` | `null` | no |
+| [s3\_signing\_bucket](#input\_s3\_signing\_bucket) | Bucket where to upload the signed s3 file. If omitted default to var.s3\_bucket | `string` | `null` | no |
+| [s3\_signing\_prefix](#input\_s3\_signing\_prefix) | Prefix for the generated signed object. If omitted default to var.s3\_prefix | `string` | `null` | no |
| [skip\_destroy](#input\_skip\_destroy) | Set to true if you do not wish the function to be deleted at destroy time, and instead just remove the function from the Terraform state. Useful for Lambda@Edge functions attached to CloudFront distributions. | `bool` | `null` | no |
| [snap\_start](#input\_snap\_start) | (Optional) Snap start settings for low-latency startups | `bool` | `false` | no |
| [source\_path](#input\_source\_path) | The absolute path to a local file or directory containing your Lambda source code | `any` | `null` | no |
diff --git a/examples/code-signing/README.md b/examples/code-signing/README.md
index 6d4317d8..c691ca4b 100644
--- a/examples/code-signing/README.md
+++ b/examples/code-signing/README.md
@@ -43,7 +43,6 @@ Note that this example may create resources which cost money. Run `terraform des
|------|------|
| [aws_lambda_code_signing_config.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_code_signing_config) | resource |
| [aws_s3_object.unsigned](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource |
-| [aws_signer_signing_job.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/signer_signing_job) | resource |
| [aws_signer_signing_profile.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/signer_signing_profile) | resource |
| [random_pet.this](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/pet) | resource |
diff --git a/examples/code-signing/main.tf b/examples/code-signing/main.tf
index b899a401..c6d515ad 100644
--- a/examples/code-signing/main.tf
+++ b/examples/code-signing/main.tf
@@ -1,3 +1,6 @@
+locals {
+ lambda_code_signing_profile_name = replace(random_pet.this.id, "-", "")
+}
provider "aws" {
region = "eu-west-1"
@@ -14,21 +17,23 @@ provider "aws" {
module "lambda" {
source = "../../"
- function_name = random_pet.this.id
- handler = "index.lambda_handler"
- runtime = "python3.12"
- code_signing_config_arn = aws_lambda_code_signing_config.this.arn
- create_package = false
+ function_name = random_pet.this.id
+ handler = "index.lambda_handler"
+ runtime = "python3.12"
+ create_package = false
+ enable_code_signing = true
+ code_signing_config_arn = aws_lambda_code_signing_config.this.arn
+ lambda_code_signing_profile_name = local.lambda_code_signing_profile_name
+ s3_signing_prefix = "signed/"
+ store_on_s3 = true
s3_existing_package = {
- bucket = aws_signer_signing_job.this.signed_object[0].s3[0].bucket
- key = aws_signer_signing_job.this.signed_object[0].s3[0].key
+ bucket = module.s3_bucket.s3_bucket_id
+ key = aws_s3_object.unsigned.key
+ version_id = aws_s3_object.unsigned.version_id
}
-}
-################################################################################
-# Lambda Code Signing
-################################################################################
+}
resource "aws_s3_object" "unsigned" {
bucket = module.s3_bucket.s3_bucket_id
@@ -41,10 +46,14 @@ resource "aws_s3_object" "unsigned" {
]
}
+# ################################################################################
+# # Lambda Code Signing
+# ################################################################################
+
resource "aws_signer_signing_profile" "this" {
platform_id = "AWSLambda-SHA384-ECDSA"
# invalid value for name (must be alphanumeric with max length of 64 characters)
- name = replace(random_pet.this.id, "-", "")
+ name = local.lambda_code_signing_profile_name
signature_validity_period {
value = 3
@@ -52,27 +61,6 @@ resource "aws_signer_signing_profile" "this" {
}
}
-resource "aws_signer_signing_job" "this" {
- profile_name = aws_signer_signing_profile.this.name
-
- source {
- s3 {
- bucket = module.s3_bucket.s3_bucket_id
- key = aws_s3_object.unsigned.id
- version = aws_s3_object.unsigned.version_id
- }
- }
-
- destination {
- s3 {
- bucket = module.s3_bucket.s3_bucket_id
- prefix = "signed/"
- }
- }
-
- ignore_signing_job_failure = true
-}
-
resource "aws_lambda_code_signing_config" "this" {
allowed_publishers {
signing_profile_version_arns = [aws_signer_signing_profile.this.version_arn]
diff --git a/main.tf b/main.tf
index c67f1bbb..755d1dd8 100644
--- a/main.tf
+++ b/main.tf
@@ -19,6 +19,14 @@ 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)
+ # s3_signing
+ s3_signing_enabled = local.s3_key != null && local.s3_bucket != null && var.enable_code_signing && var.lambda_code_signing_profile_name != null
+ s3_signing_bucket = var.s3_signing_bucket != null && local.s3_signing_enabled ? var.s3_signing_bucket : local.s3_bucket
+ s3_signing_prefix = var.s3_signing_prefix != null && local.s3_signing_enabled ? var.s3_signing_prefix : (var.s3_prefix != null ? var.s3_prefix : "")
+
+ lambda_s3_bucket = local.s3_signing_enabled ? aws_signer_signing_job.lambda_code_signing[0].signed_object[0].s3[0].bucket : local.s3_bucket
+ lambda_s3_key = local.s3_signing_enabled ? aws_signer_signing_job.lambda_code_signing[0].signed_object[0].s3[0].key : local.s3_key
+ lambda_s3_version = local.s3_signing_enabled ? null : local.s3_object_version # aws_signer_signing_job does not return a version id
}
resource "aws_lambda_function" "this" {
@@ -55,9 +63,9 @@ resource "aws_lambda_function" "this" {
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
+ s3_bucket = local.lambda_s3_bucket
+ s3_key = local.lambda_s3_key
+ s3_object_version = local.lambda_s3_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] : []
@@ -181,9 +189,9 @@ resource "aws_lambda_layer_version" "this" {
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
+ s3_bucket = local.lambda_s3_bucket
+ s3_key = local.lambda_s3_key
+ s3_object_version = local.lambda_s3_version
depends_on = [null_resource.archive, aws_s3_object.lambda_package]
}
@@ -215,6 +223,27 @@ resource "aws_s3_object" "lambda_package" {
depends_on = [null_resource.archive]
}
+resource "aws_signer_signing_job" "lambda_code_signing" {
+ count = local.s3_signing_enabled ? 1 : 0
+ profile_name = var.lambda_code_signing_profile_name
+
+ source {
+ s3 {
+ bucket = local.s3_bucket
+ key = local.s3_key
+ version = local.s3_object_version
+ }
+ }
+
+ destination {
+ s3 {
+ bucket = local.s3_signing_bucket
+ prefix = local.s3_signing_prefix
+ }
+ }
+ ignore_signing_job_failure = var.ignore_signing_job_failure
+}
+
data "aws_cloudwatch_log_group" "lambda" {
count = local.create && var.create_function && !var.create_layer && var.use_existing_cloudwatch_log_group ? 1 : 0
diff --git a/variables.tf b/variables.tf
index c71f68ae..31793865 100644
--- a/variables.tf
+++ b/variables.tf
@@ -843,3 +843,37 @@ variable "recursive_loop" {
type = string
default = null
}
+
+###############
+# Code Signing
+###############
+
+variable "enable_code_signing" {
+ description = "Must be used with a lambda storing code on s3. Set this to true for triggering a signing job creating a signed copy of the lambda zip. https://docs.aws.amazon.com/lambda/latest/dg/configuration-codesigning.html"
+ type = bool
+ default = false
+}
+
+variable "lambda_code_signing_profile_name" {
+ description = "Lambda code signing profile name https://console.aws.amazon.com/lambda/home#/code-signing-configurations"
+ type = string
+ default = null
+}
+
+variable "s3_signing_bucket" {
+ description = "Bucket where to upload the signed s3 file. If omitted default to var.s3_bucket"
+ type = string
+ default = null
+}
+
+variable "s3_signing_prefix" {
+ description = "Prefix for the generated signed object. If omitted default to var.s3_prefix"
+ type = string
+ default = null
+}
+
+variable "ignore_signing_job_failure" {
+ description = "Set this argument to true to ignore signing job failures and retrieve failed status and reason"
+ type = bool
+ default = false
+}
diff --git a/wrappers/main.tf b/wrappers/main.tf
index 1092b4d3..1266ca19 100644
--- a/wrappers/main.tf
+++ b/wrappers/main.tf
@@ -53,6 +53,7 @@ module "wrapper" {
docker_image = try(each.value.docker_image, var.defaults.docker_image, "")
docker_pip_cache = try(each.value.docker_pip_cache, var.defaults.docker_pip_cache, null)
docker_with_ssh_agent = try(each.value.docker_with_ssh_agent, var.defaults.docker_with_ssh_agent, false)
+ enable_code_signing = try(each.value.enable_code_signing, var.defaults.enable_code_signing, false)
environment_variables = try(each.value.environment_variables, var.defaults.environment_variables, {})
ephemeral_storage_size = try(each.value.ephemeral_storage_size, var.defaults.ephemeral_storage_size, 512)
event_source_mapping = try(each.value.event_source_mapping, var.defaults.event_source_mapping, {})
@@ -62,6 +63,7 @@ module "wrapper" {
function_tags = try(each.value.function_tags, var.defaults.function_tags, {})
handler = try(each.value.handler, var.defaults.handler, "")
hash_extra = try(each.value.hash_extra, var.defaults.hash_extra, "")
+ ignore_signing_job_failure = try(each.value.ignore_signing_job_failure, var.defaults.ignore_signing_job_failure, false)
ignore_source_code_hash = try(each.value.ignore_source_code_hash, var.defaults.ignore_source_code_hash, false)
image_config_command = try(each.value.image_config_command, var.defaults.image_config_command, [])
image_config_entry_point = try(each.value.image_config_entry_point, var.defaults.image_config_entry_point, [])
@@ -73,6 +75,7 @@ module "wrapper" {
kms_key_arn = try(each.value.kms_key_arn, var.defaults.kms_key_arn, null)
lambda_at_edge = try(each.value.lambda_at_edge, var.defaults.lambda_at_edge, false)
lambda_at_edge_logs_all_regions = try(each.value.lambda_at_edge_logs_all_regions, var.defaults.lambda_at_edge_logs_all_regions, true)
+ lambda_code_signing_profile_name = try(each.value.lambda_code_signing_profile_name, var.defaults.lambda_code_signing_profile_name, null)
lambda_role = try(each.value.lambda_role, var.defaults.lambda_role, "")
layer_name = try(each.value.layer_name, var.defaults.layer_name, "")
layer_skip_destroy = try(each.value.layer_skip_destroy, var.defaults.layer_skip_destroy, false)
@@ -122,6 +125,8 @@ module "wrapper" {
s3_object_tags_only = try(each.value.s3_object_tags_only, var.defaults.s3_object_tags_only, false)
s3_prefix = try(each.value.s3_prefix, var.defaults.s3_prefix, null)
s3_server_side_encryption = try(each.value.s3_server_side_encryption, var.defaults.s3_server_side_encryption, null)
+ s3_signing_bucket = try(each.value.s3_signing_bucket, var.defaults.s3_signing_bucket, null)
+ s3_signing_prefix = try(each.value.s3_signing_prefix, var.defaults.s3_signing_prefix, null)
skip_destroy = try(each.value.skip_destroy, var.defaults.skip_destroy, null)
snap_start = try(each.value.snap_start, var.defaults.snap_start, false)
source_path = try(each.value.source_path, var.defaults.source_path, null)