From c773ce73aa354eab5c3586cb5e7e601e6f08c99c Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Wed, 27 Mar 2024 14:44:21 +0000 Subject: [PATCH] Custom Lambdas * Allows deploying custom Lambda functions * This creates an S3 bucket where zipped lambda functions can be placed, and referenced to deploy as a Lambda function. * Policies can also be placed in the S3 bucket and referenced, which will then be created and attached to the Lambda role. --- README.md | 24 +++++- data.tf | 18 ++++ elasticache-infrastructure-security-group.tf | 23 ++++- lambda-custom-functions-s3-store.tf | 67 +++++++++++++++ lambda-custom-functions-security-group.tf | 9 ++ lambda-custom-functions.tf | 90 ++++++++++++++++++++ locals.tf | 3 + rds-infrastructure-security-group.tf | 23 ++++- variables.tf | 35 ++++++++ 9 files changed, 288 insertions(+), 4 deletions(-) create mode 100644 lambda-custom-functions-s3-store.tf create mode 100644 lambda-custom-functions-security-group.tf create mode 100644 lambda-custom-functions.tf diff --git a/README.md b/README.md index f091805..4dc43f6 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ This project creates and manages resources within an AWS account for infrastruct | [aws_cloudwatch_log_group.infrastructure_ecs_cluster_service](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | | [aws_cloudwatch_log_group.infrastructure_rds_exports](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | | [aws_cloudwatch_log_group.infrastructure_vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | +| [aws_cloudwatch_log_group.lambda_custom_functions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | | [aws_codebuild_project.infrastructure_ecs_cluster_service_build](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codebuild_project) | resource | | [aws_codedeploy_app.infrastructure_ecs_cluster_service_blue_green](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codedeploy_app) | resource | | [aws_codedeploy_deployment_config.infrastructure_ecs_cluster_service_blue_green](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codedeploy_deployment_config) | resource | @@ -123,6 +124,8 @@ This project creates and manages resources within an AWS account for infrastruct | [aws_iam_policy.infrastructure_ecs_cluster_service_task_execution_s3_read_envfiles](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_policy.infrastructure_ecs_cluster_ssm_service_setting_rw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_policy.infrastructure_rds_monitoring](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.lambda_custom_functions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.lambda_custom_functions_additional](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_role.ecs_cluster_infrastructure_draining_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role.infrastructure_ecs_cluster](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role.infrastructure_ecs_cluster_autoscaling_lifecycle_termination](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | @@ -133,6 +136,7 @@ This project creates and manages resources within an AWS account for infrastruct | [aws_iam_role.infrastructure_ecs_cluster_service_task_execution](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role.infrastructure_rds_monitoring](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role.infrastructure_vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role.lambda_custom_functions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role_policy.infrastructure_vpc_flow_logs_allow_cloudwatch_rw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | | [aws_iam_role_policy_attachment.ecs_cluster_infrastructure_draining_ecs_container_instance_state_update_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_iam_role_policy_attachment.ecs_cluster_infrastructure_draining_kms_encrypt](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | @@ -158,12 +162,15 @@ This project creates and manages resources within an AWS account for infrastruct | [aws_iam_role_policy_attachment.infrastructure_ecs_cluster_service_task_execution_s3_read_envfiles](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_iam_role_policy_attachment.infrastructure_ecs_cluster_ssm_service_setting_rw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_iam_role_policy_attachment.infrastructure_rds_monitoring](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.lambda_custom_functions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.lambda_custom_functions_additional](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_iam_user.infrastructure_ecs_cluster_service_build_pipeline_buildspec_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user) | resource | | [aws_internet_gateway.infrastructure_public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/internet_gateway) | resource | | [aws_kms_alias.custom_s3_buckets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | resource | | [aws_kms_alias.infrastructure](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | resource | | [aws_kms_key.custom_s3_buckets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource | | [aws_kms_key.infrastructure](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource | +| [aws_lambda_function.custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function) | resource | | [aws_lambda_function.ecs_cluster_infrastructure_draining](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function) | resource | | [aws_lambda_permission.ecs_cluster_infrastructure_draining_allow_sns_execution](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission) | resource | | [aws_launch_template.infrastructure_ecs_cluster](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/launch_template) | resource | @@ -210,6 +217,7 @@ This project creates and manages resources within an AWS account for infrastruct | [aws_s3_bucket.infrastructure_ecs_cluster_service_build_pipeline_buildspec_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | | [aws_s3_bucket.infrastructure_ecs_cluster_service_environment_files](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | | [aws_s3_bucket.infrastructure_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | +| [aws_s3_bucket.lambda_custom_functions_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | | [aws_s3_bucket_acl.infrastructure_logs_log_delivery_write](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_acl) | resource | | [aws_s3_bucket_lifecycle_configuration.custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration) | resource | | [aws_s3_bucket_lifecycle_configuration.infrastructure_ecs_cluster_service_alb_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration) | resource | @@ -220,6 +228,7 @@ This project creates and manages resources within an AWS account for infrastruct | [aws_s3_bucket_logging.infrastructure_ecs_cluster_service_build_pipeline_artifact_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_logging) | resource | | [aws_s3_bucket_logging.infrastructure_ecs_cluster_service_build_pipeline_buildspec_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_logging) | resource | | [aws_s3_bucket_logging.infrastructure_ecs_cluster_service_environment_files](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_logging) | resource | +| [aws_s3_bucket_logging.lambda_custom_functions_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_logging) | resource | | [aws_s3_bucket_ownership_controls.custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_ownership_controls) | resource | | [aws_s3_bucket_ownership_controls.infrastructure_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_ownership_controls) | resource | | [aws_s3_bucket_policy.cloudformation_custom_stack_template_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource | @@ -229,6 +238,7 @@ This project creates and manages resources within an AWS account for infrastruct | [aws_s3_bucket_policy.infrastructure_ecs_cluster_service_build_pipeline_buildspec_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource | | [aws_s3_bucket_policy.infrastructure_ecs_cluster_service_environment_files](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource | | [aws_s3_bucket_policy.infrastructure_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource | +| [aws_s3_bucket_policy.lambda_custom_functions_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource | | [aws_s3_bucket_public_access_block.cloudformation_custom_stack_template_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | | [aws_s3_bucket_public_access_block.custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | | [aws_s3_bucket_public_access_block.infrastructure_ecs_cluster_service_alb_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | @@ -236,6 +246,7 @@ This project creates and manages resources within an AWS account for infrastruct | [aws_s3_bucket_public_access_block.infrastructure_ecs_cluster_service_build_pipeline_buildspec_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | | [aws_s3_bucket_public_access_block.infrastructure_ecs_cluster_service_environment_files](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | | [aws_s3_bucket_public_access_block.infrastructure_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | +| [aws_s3_bucket_public_access_block.lambda_custom_functions_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | | [aws_s3_bucket_server_side_encryption_configuration.cloudformation_custom_stack_template_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | | [aws_s3_bucket_server_side_encryption_configuration.custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | | [aws_s3_bucket_server_side_encryption_configuration.infrastructure_ecs_cluster_service_alb_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | @@ -243,6 +254,7 @@ This project creates and manages resources within an AWS account for infrastruct | [aws_s3_bucket_server_side_encryption_configuration.infrastructure_ecs_cluster_service_build_pipeline_buildspec_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | | [aws_s3_bucket_server_side_encryption_configuration.infrastructure_ecs_cluster_service_environment_files](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | | [aws_s3_bucket_server_side_encryption_configuration.infrastructure_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | +| [aws_s3_bucket_server_side_encryption_configuration.lambda_custom_functions_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | | [aws_s3_bucket_versioning.cloudformation_custom_stack_template_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | | [aws_s3_bucket_versioning.custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | | [aws_s3_bucket_versioning.infrastructure_ecs_cluster_service_alb_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | @@ -250,9 +262,11 @@ This project creates and manages resources within an AWS account for infrastruct | [aws_s3_bucket_versioning.infrastructure_ecs_cluster_service_build_pipeline_buildspec_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | | [aws_s3_bucket_versioning.infrastructure_ecs_cluster_service_environment_files](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | | [aws_s3_bucket_versioning.infrastructure_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | +| [aws_s3_bucket_versioning.lambda_custom_functions_store](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | | [aws_s3_object.infrastructure_ecs_cluster_service_build_pipeline_buildspec_store_files](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource | | [aws_secretsmanager_secret.infrastructure_rds_root_password](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret) | resource | | [aws_secretsmanager_secret_version.infrastructure_rds_root_password](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret_version) | resource | +| [aws_security_group.custom_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | | [aws_security_group.infrastructure_ecs_cluster_container_instances](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | | [aws_security_group.infrastructure_ecs_cluster_efs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | | [aws_security_group.infrastructure_ecs_cluster_service_alb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | @@ -270,8 +284,10 @@ This project creates and manages resources within an AWS account for infrastruct | [aws_security_group_rule.infrastructure_ecs_cluster_service_alb_container_instance_egress_udp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | | [aws_security_group_rule.infrastructure_ecs_cluster_service_alb_http](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | | [aws_security_group_rule.infrastructure_ecs_cluster_service_alb_https](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | -| [aws_security_group_rule.infrastructure_elasticache_ingress_tcp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | -| [aws_security_group_rule.infrastructure_rds_ingress_tcp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | +| [aws_security_group_rule.infrastructure_elasticache_ingress_tcp_custom_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | +| [aws_security_group_rule.infrastructure_elasticache_ingress_tcp_ecs_instances](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | +| [aws_security_group_rule.infrastructure_rds_ingress_tcp_custom_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | +| [aws_security_group_rule.infrastructure_rds_ingress_tcp_ecs_instances](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | | [aws_sns_topic.infrastructure_ecs_cluster_autoscaling_lifecycle_termination](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource | | [aws_sns_topic_subscription.ecs_cluster_infrastructure_draining_autoscaling_lifecycle_termination](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource | | [aws_subnet.infrastructure_private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource | @@ -289,6 +305,8 @@ This project creates and manages resources within an AWS account for infrastruct | [aws_elb_service_account.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/elb_service_account) | data source | | [aws_route53_zone.root](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/route53_zone) | data source | | [aws_s3_object.ecs_cluster_service_buildspec](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/s3_object) | data source | +| [aws_s3_object.lambda_custom_functions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/s3_object) | data source | +| [aws_s3_object.lambda_custom_functions_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/s3_object) | data source | | [external_external.s3_presigned_url](https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/external) | data source | | [external_external.ssm_dhmc_setting](https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/external) | data source | @@ -299,6 +317,7 @@ This project creates and manages resources within an AWS account for infrastruct | [aws\_profile\_name\_route53\_root](#input\_aws\_profile\_name\_route53\_root) | AWS Profile name which is configured for the account in which the root Route53 Hosted Zone exists. | `string` | n/a | yes | | [aws\_region](#input\_aws\_region) | AWS region in which to launch resources | `string` | n/a | yes | | [custom\_cloudformation\_stacks](#input\_custom\_cloudformation\_stacks) | Map of CloudFormation stacks to deploy
{
stack-name = {
s3\_template\_store\_key: The filename of a CloudFormation template that is stored within the S3 bucket, created by the `enable_cloudformatian_s3_template_store`
template\_body: (Optional - use of s3\_template\_store\_key is preferred) The CloudFormation template body
parameters: The CloudFormation template parameters ({ parameter-name = parameter-value, ... })
on\_failure: What to do on failure, either 'DO\_NOTHING', 'ROLLBACK' or 'DELETE'
capabilities: A list of capabilities. Valid values: `CAPABILITY_NAMED_IAM`, `CAPABILITY_IAM`, `CAPABILITY_AUTO_EXPAND`
}
} |
map(object({
s3_template_store_key = optional(string, null)
template_body = optional(string, null)
parameters = optional(map(string), null)
on_failure = optional(string, null)
capabilities = optional(list(string), null)
}))
| n/a | yes | +| [custom\_lambda\_functions](#input\_custom\_lambda\_functions) | Map of Lambda functions to deploy
{
function-name = {
s3\_function\_store\_zip\_key: The key of a Zipped Lambda function that is stored within the S3 bucket, created by the `enable_lambda_functions_s3_store`. If a file with the same name, with the `.json` extension is found, this will be used as a policy for the function (eg. `my-function.zip` will use the `my-function.json` as a policy).
s3\_function\_store\_policy\_key: The key of a policy (json) stored within the S3 bucket, created by the `enable_lambda_functions_s3_store`. This policy will be attached to the Lambda role.
handler: The function entrypoint in the code
runtime: The function runtime
memory: Amount of memory in MB your Lambda Function can use at runtime.
timeout: Amount of time your Lambda Function has to run in seconds
environment\_variables: Map of environment variables that are accessible from the function code during execution.
log\_retention: Days to retain logs
launch\_in\_infrastructure\_vpc: Conditionally launch within the infrastructure VPC. This will give access to resources launched within the VPC.
}
} |
map(object({
s3_function_store_zip_key = optional(string, null)
s3_function_store_policy_key = optional(string, null)
handler = optional(string, null)
runtime = optional(string, null)
memory = optional(number, null)
timeout = optional(number, null)
environment_variables = optional(map(string), null)
log_retention = optional(number, null)
launch_in_infrastructure_vpc = optional(bool, null)
}))
| n/a | yes | | [custom\_route53\_hosted\_zones](#input\_custom\_route53\_hosted\_zones) | Map of Route53 Hosted Zone configurations to create
{
example.com = {
ns\_records: Map of NS records to create ({ "domain.example.com" = { values = ["ns1.example.com", "ns2.example.com"], ttl = 300 })
a\_records: Map of A records to create ({ "domain.example.com" = { values = ["1.2.3.4", "5.6.7.8"], ttl = 300 })
alias\_records: Map of ALIAS records to create ({ "domain.example.com" = { value = "example.cloudfront.com", zone\_id = "Z2FDTNDATAQYW2" })
cname\_records: Map of CNAME records to create ({ "domain.example.com" = { values = ["external1.example.com", "external2.example.com"], ttl = 60 })
mx\_records: Map of MX records to create ({ "example.com" = { values = ["1 mail.example.com", "5 mail2.example.com"], ttl = 60 })
txt\_records: Map of TXT records to create ({ "example.com" = { values = ["v=spf1 include:spf.example.com -all"], ttl = 60 })
}
} |
map(object({
ns_records = optional(map(object({
values = list(string)
ttl = optional(number, 300)
})), null)
a_records = optional(map(object({
values = list(string)
ttl = optional(number, 300)
})), null)
alias_records = optional(map(object({
value = string
zone_id = string
})), null)
cname_records = optional(map(object({
values = list(string)
ttl = optional(number, 300)
})), null)
mx_records = optional(map(object({
values = list(string)
ttl = optional(number, 300)
})), null)
txt_records = optional(map(object({
values = list(string)
ttl = optional(number, 300)
})), null)
}))
| n/a | yes | | [custom\_s3\_buckets](#input\_custom\_s3\_buckets) | Map of S3 buckets to create, and conditionally serve via CloudFront. The S3 configuration will follow AWS best practices (eg. Private, ACLS disabled, SSE, Versioning, Logging). The bucket must be emptied before attempting deletion/destruction."
{
bucket-name = {
create\_dedicated\_kms\_key: Conditionally create a KMS key specifically for this bucket's server side encryption (rather than using the Infrastructure's KMS key). It's recommended to use this if the S3 bucket will be accessed from external AWS accounts.
transition\_to\_ia\_days: Conditionally transition objects to 'Standard Infrequent Access' storage in N days
transition\_to\_glacier\_days: Conditionally transition objects to 'Glacier' storage in N days
cloudfront\_dedicated\_distribution: Conditionally create a CloudFront distribution to serve objects from the S3 bucket.
cloudfront\_s3\_root: Sets the S3 document root when being served from CloudFront. By default this will be '/'. If `cloudfront_infrastructure_ecs_cluster_service_path` has been set, this helps by modifying the request from `/sub-directory-path` to `/` by use of a CloudFront function.
cloudfront\_infrastructure\_ecs\_cluster\_service: Conditionally create an Origin on a CloudFront distribution that is serving the given Infrastructure ECS Cluster Service name
cloudfront\_infrastructure\_ecs\_cluster\_service\_path: If `cloudfront_infrastructure_ecs_cluster_service`, set this to the path that objects will be served from.
}
} |
map(object({
create_dedicated_kms_key = optional(bool, null)
transition_to_ia_days = optional(number, null)
transition_to_glacier_days = optional(number, null)
cloudfront_dedicated_distribution = optional(bool, null)
cloudfront_s3_root = optional(string, null)
cloudfront_infrastructure_ecs_cluster_service = optional(string, null)
cloudfront_infrastructure_ecs_cluster_service_path = optional(string, null)
}))
| n/a | yes | | [ecs\_cluster\_efs\_directories](#input\_ecs\_cluster\_efs\_directories) | ECS cluster EFS directories to create | `list(string)` | n/a | yes | @@ -310,6 +329,7 @@ This project creates and manages resources within an AWS account for infrastruct | [enable\_infrastructure\_ecs\_cluster\_efs](#input\_enable\_infrastructure\_ecs\_cluster\_efs) | Conditionally create and mount EFS to the ECS cluster instances | `bool` | n/a | yes | | [enable\_infrastructure\_ecs\_cluster\_services\_alb\_logs](#input\_enable\_infrastructure\_ecs\_cluster\_services\_alb\_logs) | Enable Infrastructure ECS cluster services ALB logs | `bool` | n/a | yes | | [enable\_infrastructure\_route53\_hosted\_zone](#input\_enable\_infrastructure\_route53\_hosted\_zone) | Creates a Route53 hosted zone, where DNS records will be created for resources launched within this module. | `bool` | n/a | yes | +| [enable\_lambda\_functions\_s3\_store](#input\_enable\_lambda\_functions\_s3\_store) | Creates an S3 bucket to store custom Lambda functions, which can then be referenced in `custom_lambdas`. A user with RW access to the bucket is also created. | `bool` | n/a | yes | | [environment](#input\_environment) | The environment name to be used as part of the resource prefix | `string` | n/a | yes | | [infrastructure\_dockerhub\_email](#input\_infrastructure\_dockerhub\_email) | Dockerhub email | `string` | n/a | yes | | [infrastructure\_dockerhub\_token](#input\_infrastructure\_dockerhub\_token) | Dockerhub token which has permissions to pull images | `string` | n/a | yes | diff --git a/data.tf b/data.tf index 4ba7d8d..8907de1 100644 --- a/data.tf +++ b/data.tf @@ -66,6 +66,24 @@ data "aws_cloudfront_response_headers_policy" "managed_policy" { name = "Managed-${each.value}" } +data "aws_s3_object" "lambda_custom_functions" { + for_each = { + for k, custom_lambda in local.custom_lambda_functions : k => custom_lambda if custom_lambda["s3_function_store_zip_key"] != null + } + + bucket = aws_s3_bucket.lambda_custom_functions_store[0].id + key = each.value["s3_function_store_zip_key"] +} + +data "aws_s3_object" "lambda_custom_functions_policy" { + for_each = { + for k, custom_lambda in local.custom_lambda_functions : k => custom_lambda if custom_lambda["s3_function_store_policy_key"] != null + } + + bucket = aws_s3_bucket.lambda_custom_functions_store[0].id + key = each.value["s3_function_store_policy_key"] +} + # aws_ssm_service_setting doesn't yet have a data source, so we need to use # a script to retrieve SSM service settings # https://github.com/hashicorp/terraform-provider-aws/issues/25170 diff --git a/elasticache-infrastructure-security-group.tf b/elasticache-infrastructure-security-group.tf index 50ad680..e59c819 100644 --- a/elasticache-infrastructure-security-group.tf +++ b/elasticache-infrastructure-security-group.tf @@ -6,7 +6,7 @@ resource "aws_security_group" "infrastructure_elasticache" { vpc_id = aws_vpc.infrastructure[0].id } -resource "aws_security_group_rule" "infrastructure_elasticache_ingress_tcp" { +resource "aws_security_group_rule" "infrastructure_elasticache_ingress_tcp_ecs_instances" { for_each = local.infrastructure_vpc_network_enable_public || local.infrastructure_vpc_network_enable_private ? local.infrastructure_elasticache : {} description = "Allow ElastiCache port tcp ingress from ECS instances if launched, otherwise from the subnet" @@ -18,3 +18,24 @@ resource "aws_security_group_rule" "infrastructure_elasticache_ingress_tcp" { source_security_group_id = local.enable_infrastructure_ecs_cluster ? aws_security_group.infrastructure_ecs_cluster_container_instances[0].id : null security_group_id = aws_security_group.infrastructure_elasticache[each.key].id } + +resource "aws_security_group_rule" "infrastructure_elasticache_ingress_tcp_custom_lambda" { + for_each = local.infrastructure_vpc_network_enable_public || local.infrastructure_vpc_network_enable_private ? { + for k, v in merge(flatten([for elasticache_k, elasticache_v in local.infrastructure_elasticache : + flatten([ + for lambda_k, lambda_v in local.custom_lambda_functions : { "${elasticache_k}-${lambda_k}" = merge(elasticache_k, { + lambda_source_security_group = lambda_v["launch_in_infrastructure_vpc"] == true ? aws_security_group.custom_lambda[lambda_k].id : null + }) } + ]) + ])...) : k => v if v["launch_in_infrastructure_vpc"] != null + } : {} + + description = "Allow ElastiCache port tcp ingress from Custom Lambdas if launched" + type = "ingress" + from_port = local.elasticache_ports[each.value["engine"]] + to_port = local.elasticache_ports[each.value["engine"]] + protocol = "tcp" + cidr_blocks = local.infrastructure_vpc_network_enable_private ? [for subnet in aws_subnet.infrastructure_private : subnet.cidr_block] : local.infrastructure_vpc_network_enable_public ? [for subnet in aws_subnet.infrastructure_public : subnet.cidr_block] : null + source_security_group_id = v["launch_in_infrastructure_vpc"] + security_group_id = aws_security_group.infrastructure_elasticache[each.key].id +} diff --git a/lambda-custom-functions-s3-store.tf b/lambda-custom-functions-s3-store.tf new file mode 100644 index 0000000..f83dd34 --- /dev/null +++ b/lambda-custom-functions-s3-store.tf @@ -0,0 +1,67 @@ +resource "aws_s3_bucket" "lambda_custom_functions_store" { + count = local.enable_lambda_functions_s3_store ? 1 : 0 + + bucket = "${local.resource_prefix_hash}-lambda-custom-functions" +} + +resource "aws_s3_bucket_policy" "lambda_custom_functions_store" { + count = local.enable_lambda_functions_s3_store ? 1 : 0 + + bucket = aws_s3_bucket.lambda_custom_functions_store[0].id + policy = templatefile( + "${path.module}/policies/s3-bucket-policy.json.tpl", + { + statement = < custom_lambda if custom_lambda["launch_in_infrastructure_vpc"] == true + } : {} + + name = "${local.resource_prefix}-custom-lambda-${each.key}" + description = "${local.resource_prefix} custom lambda ${each.key}" + vpc_id = aws_vpc.infrastructure[0].id +} diff --git a/lambda-custom-functions.tf b/lambda-custom-functions.tf new file mode 100644 index 0000000..50aade1 --- /dev/null +++ b/lambda-custom-functions.tf @@ -0,0 +1,90 @@ +resource "aws_cloudwatch_log_group" "lambda_custom_functions" { + for_each = local.custom_lambda_functions + + name = "/aws/lambda/${local.project_name}-${each.key}-custom-lambda" + kms_key_id = local.infrastructure_kms_encryption ? aws_kms_key.infrastructure[0].arn : null + retention_in_days = each.value["log_retention"] +} + +resource "aws_iam_role" "lambda_custom_functions" { + for_each = local.custom_lambda_functions + + name = "${local.resource_prefix_hash}-${substr(sha512("${each.key}-custom-lambda"), 0, 6)}" + description = "${local.resource_prefix}-${each.key}-custom-lambda" + assume_role_policy = templatefile( + "${path.root}/policies/assume-roles/service-principle-standard.json.tpl", + { services = jsonencode(["lambda.amazonaws.com"]) } + ) +} + +resource "aws_iam_policy" "lambda_custom_functions" { + for_each = local.custom_lambda_functions + + name = "${local.resource_prefix}-${each.key}-custom-lambda" + policy = templatefile( + "${path.root}/policies/lambda-default.json.tpl", + { + region = local.aws_region + account_id = local.aws_account_id + function_name = "${local.project_name}-${each.key}-custom-lambda" + } + ) +} + +resource "aws_iam_role_policy_attachment" "lambda_custom_functions" { + for_each = local.custom_lambda_functions + + role = aws_iam_role.lambda_custom_functions[0].name + policy_arn = aws_iam_policy.lambda_custom_functions[0].arn +} + +resource "aws_iam_policy" "lambda_custom_functions_additional" { + for_each = { + for k, custom_lambda in local.custom_lambda_functions : k => custom_lambda if custom_lambda["s3_function_store_policy_key"] != null + } + + name = "${local.resource_prefix}-${each.key}-custom-lambda-additional" + policy = data.aws_s3_object.lambda_custom_functions_policy[each.key].body +} + +resource "aws_iam_role_policy_attachment" "lambda_custom_functions_additional" { + for_each = { + for k, custom_lambda in local.custom_lambda_functions : k => custom_lambda if custom_lambda["s3_function_store_policy_key"] != null + } + + role = aws_iam_role.lambda_custom_functions[0].name + policy_arn = aws_iam_policy.lambda_custom_functions_additional[0].arn +} + +resource "aws_lambda_function" "custom" { + for_each = local.custom_lambda_functions + + s3_bucket = data.aws_s3_object.lambda_custom_functions[each.key].bucket + s3_key = data.aws_s3_object.lambda_custom_functions[each.key].key + s3_object_version = data.aws_s3_object.lambda_custom_functions[each.key].version_id + + function_name = "${local.resource_prefix}-custom-${each.key}" + description = "${local.resource_prefix} Custom ${each.key}" + handler = each.value["handler"] + runtime = each.value["runtime"] + role = aws_iam_role.lambda_custom_functions[0].name + memory_size = each.value["memory"] + package_type = "Zip" + timeout = each.value["timeout"] + + environment { + variables = each.value["environment_variables"] + } + + dynamic "vpc_config" { + for_each = each.value["launch_in_infrastructure_vpc"] == true ? [1] : [] + content { + security_group_ids = [aws_security_group.custom_lambda[each.key].id] + subnet_ids = local.infrastructure_vpc_network_enable_private ? [for subnet in aws_subnet.infrastructure_private : subnet.id] : local.infrastructure_vpc_network_enable_public ? [for subnet in aws_subnet.infrastructure_public : subnet.id] : null + } + } + + tracing_config { + mode = "Active" + } +} diff --git a/locals.tf b/locals.tf index b37f01c..20df415 100644 --- a/locals.tf +++ b/locals.tf @@ -198,6 +198,9 @@ locals { enable_cloudformatian_s3_template_store = var.enable_cloudformatian_s3_template_store != null ? var.enable_cloudformatian_s3_template_store : false custom_cloudformation_stacks = var.custom_cloudformation_stacks + enable_lambda_functions_s3_store = var.enable_lambda_functions_s3_store != null ? var.enable_lambda_functions_s3_store : false + custom_lambda_functions = var.custom_lambda_functions != null ? var.custom_lambda_functions : {} + s3_object_presign = local.enable_cloudformatian_s3_template_store ? toset([ for k, v in local.custom_cloudformation_stacks : "${aws_s3_bucket.cloudformation_custom_stack_template_store[0].id}/${v["s3_template_store_key"]}" if v["s3_template_store_key"] != null ]) : [] diff --git a/rds-infrastructure-security-group.tf b/rds-infrastructure-security-group.tf index 9a97d31..7f594ab 100644 --- a/rds-infrastructure-security-group.tf +++ b/rds-infrastructure-security-group.tf @@ -6,7 +6,7 @@ resource "aws_security_group" "infrastructure_rds" { vpc_id = aws_vpc.infrastructure[0].id } -resource "aws_security_group_rule" "infrastructure_rds_ingress_tcp" { +resource "aws_security_group_rule" "infrastructure_rds_ingress_tcp_ecs_instances" { for_each = local.infrastructure_vpc_network_enable_public || local.infrastructure_vpc_network_enable_private ? local.infrastructure_rds : {} description = "Allow RDS port tcp ingress from ECS instances if launched, otherwise from the subnet" @@ -18,3 +18,24 @@ resource "aws_security_group_rule" "infrastructure_rds_ingress_tcp" { source_security_group_id = local.enable_infrastructure_ecs_cluster ? aws_security_group.infrastructure_ecs_cluster_container_instances[0].id : null security_group_id = aws_security_group.infrastructure_rds[each.key].id } + +resource "aws_security_group_rule" "infrastructure_rds_ingress_tcp_custom_lambda" { + for_each = local.infrastructure_vpc_network_enable_public || local.infrastructure_vpc_network_enable_private ? { + for k, v in merge(flatten([for rds_k, rds_v in local.infrastructure_rds : + flatten([ + for lambda_k, lambda_v in local.custom_lambda_functions : { "${rds_k}-${lambda_k}" = merge(rds_k, { + lambda_source_security_group = lambda_v["launch_in_infrastructure_vpc"] == true ? aws_security_group.custom_lambda[lambda_k].id : null + }) } + ]) + ])...) : k => v if v["launch_in_infrastructure_vpc"] != null + } : {} + + description = "Allow RDS port tcp ingress from Custom Lambdas if launched" + type = "ingress" + from_port = local.rds_ports[each.value["engine"]] + to_port = local.rds_ports[each.value["engine"]] + protocol = "tcp" + cidr_blocks = local.infrastructure_vpc_network_enable_private ? [for subnet in aws_subnet.infrastructure_private : subnet.cidr_block] : local.infrastructure_vpc_network_enable_public ? [for subnet in aws_subnet.infrastructure_public : subnet.cidr_block] : null + source_security_group_id = v["launch_in_infrastructure_vpc"] + security_group_id = aws_security_group.infrastructure_rds[each.key].id +} diff --git a/variables.tf b/variables.tf index f595e13..3fd5a71 100644 --- a/variables.tf +++ b/variables.tf @@ -642,3 +642,38 @@ variable "custom_cloudformation_stacks" { capabilities = optional(list(string), null) })) } + +variable "enable_lambda_functions_s3_store" { + description = "Creates an S3 bucket to store custom Lambda functions, which can then be referenced in `custom_lambdas`. A user with RW access to the bucket is also created." + type = bool +} + +variable "custom_lambda_functions" { + description = <