diff --git a/build/terraform/aws/README.md b/build/terraform/aws/README.md index 70f48384..334eb28a 100644 --- a/build/terraform/aws/README.md +++ b/build/terraform/aws/README.md @@ -51,16 +51,6 @@ This module is used to create Event Bridge rules that trigger a Lambda. Read more about Event Bridge [here](https://aws.amazon.com/eventbridge/). -### IAM - -This module is used to provide default Identity and Access Management (IAM) policies for the most commonly used permissions. We use this naming convention: [AWS service]\_[read|write|modify]\_policy. For example, the `kinesis_read_policy` grants all the permissions required to read from a provided Kinesis stream. - -Read more about IAM policies [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html). - -### IAM Attachment - -This module is used to attach policies from the IAM module to resources used in a pipeline (such as Kinesis streams, KMS keys, DynamoDB tables, etc.). Separating policies and policy attachment allows for granular permission control. We recommend [granting least privilege](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege) whenever possible. - ### Kinesis This module is used to create new Kinesis Data Streams (KDS) and accompanying CloudWatch alarms. The streams created by this module are intended to be used with Substation's autoscaling application -- this feature provides stream autoscaling at a significantly reduced cost compared to Kinesis Firehose. @@ -77,14 +67,9 @@ Read more about the Key Management Service [here](https://aws.amazon.com/kms/). ### Lambda -This module is used to create and manage Lambda, which is the recommended service for data processing. At release, the Lambda Substation app ( `cmd/aws/lambda/substation` ) supports these Lambda triggers: - -* API Gateway -* Kinesis Data Streams -* SNS via S3 -* S3 +This module is used to create and manage Lambda, which is the recommended service for data processing. -This module is flexible enough to deploy supporting apps (such as `cmd/aws/lambda/kinesis_autoscaling` ) and custom apps (such as apps that provide data enrichment functionality). When new Lambda are created with this module, an accompanying AppConfig configuration profile is created under the `substation` application. +This module is flexible enough to deploy supporting apps (such as `cmd/aws/lambda/kinesis_autoscaling`) and custom apps (such as apps that provide data enrichment functionality). When new Lambda are created with this module, an accompanying AppConfig configuration profile is created under the `substation` application. Read more about AWS Lambda [here](https://aws.amazon.com/lambda/). @@ -114,4 +99,4 @@ Read more about SQS [here](https://aws.amazon.com/sqs/). This module can be used to create a custom VPC with outbound connectivity via a NAT gateway to a public subnet that contains an IGW. This allows for connectivity with VPC only services. -Read more about VPCs [here](https://aws.amazon.com/vpc/). \ No newline at end of file +Read more about VPCs [here](https://aws.amazon.com/vpc/). diff --git a/build/terraform/aws/api_gateway/kinesis_data_stream/_variables.tf b/build/terraform/aws/api_gateway/kinesis_data_stream/_variables.tf index 9508989f..d74c9020 100644 --- a/build/terraform/aws/api_gateway/kinesis_data_stream/_variables.tf +++ b/build/terraform/aws/api_gateway/kinesis_data_stream/_variables.tf @@ -1,12 +1,14 @@ variable "config" { type = object({ - name = string - stream = string + name = string + stream = string timeout = optional(number, 1000) - }) + }) + description = "Configuration for the API Gateway Kinesis Data Stream integration." } variable "tags" { - type = map(any) - default = {} + type = map(any) + default = {} + description = "Tags to apply to all resources." } diff --git a/build/terraform/aws/api_gateway/lambda/_variables.tf b/build/terraform/aws/api_gateway/lambda/_variables.tf index c7e84071..0145a8c5 100644 --- a/build/terraform/aws/api_gateway/lambda/_variables.tf +++ b/build/terraform/aws/api_gateway/lambda/_variables.tf @@ -5,9 +5,11 @@ variable "config" { arn = string }) }) + description = "Configuration for the API Gateway Lambda integration." } variable "tags" { - type = map(any) - default = {} + type = map(any) + default = {} + description = "Tags to apply to all resources." } diff --git a/build/terraform/aws/dynamodb/_variables.tf b/build/terraform/aws/dynamodb/_variables.tf index 0dc29bf7..84b38734 100644 --- a/build/terraform/aws/dynamodb/_variables.tf +++ b/build/terraform/aws/dynamodb/_variables.tf @@ -1,13 +1,14 @@ variable "kms" { type = object({ - arn = string - id = string + arn = string + id = string }) + description = "KMS key used to encrypt the table." } variable "config" { type = object({ - name = string + name = string hash_key = string attributes = list(object({ name = string @@ -30,15 +31,18 @@ variable "config" { # https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.html stream_view_type = optional(string, "NEW_AND_OLD_IMAGES") }) + + description = "Configuration for the DynamoDB table." } variable "tags" { - type = map(any) - default = {} + type = map(any) + default = {} + description = "Tags to apply to all resources." } variable "access" { - type = list(string) - default = [] + type = list(string) + default = [] description = "List of IAM ARNs that are granted access to the resource." } diff --git a/build/terraform/aws/dynamodb/main.tf b/build/terraform/aws/dynamodb/main.tf index dfe30f03..296f5da3 100644 --- a/build/terraform/aws/dynamodb/main.tf +++ b/build/terraform/aws/dynamodb/main.tf @@ -42,14 +42,14 @@ resource "aws_dynamodb_table" "table" { # Applies the policy to each role in the access list. resource "aws_iam_role_policy_attachment" "access" { - for_each = toset(var.access) - role = each.value + for_each = toset(var.access) + role = each.value policy_arn = aws_iam_policy.access.arn } resource "aws_iam_policy" "access" { - name = var.config.name - description = "Policy for the ${var.config.name} DynamoDB table" + name = "${var.config.name}-access" + description = "Policy for the ${var.config.name} DynamoDB table." policy = data.aws_iam_policy_document.access.json } diff --git a/build/terraform/aws/ecr/_variables.tf b/build/terraform/aws/ecr/_variables.tf index 7913d087..60d9d312 100644 --- a/build/terraform/aws/ecr/_variables.tf +++ b/build/terraform/aws/ecr/_variables.tf @@ -1,17 +1,20 @@ -variable "config" { +variable "kms" { type = object({ - name = string - }) + arn = string + id = string + }) + description = "KMS key used to encrypt the repository." } -variable "kms" { +variable "config" { type = object({ - arn = string - id = string + name = string }) + description = "Configuration for the ECR repository." } variable "tags" { - type = map(any) - default = {} + type = map(any) + default = {} + description = "Tags to apply to all resources." } diff --git a/build/terraform/aws/event_bridge/lambda/_variables.tf b/build/terraform/aws/event_bridge/lambda/_variables.tf index 361b124d..6623c9d5 100644 --- a/build/terraform/aws/event_bridge/lambda/_variables.tf +++ b/build/terraform/aws/event_bridge/lambda/_variables.tf @@ -1,16 +1,18 @@ variable "config" { type = object({ - name = string + name = string description = string - schedule = string + schedule = string function = object({ - arn = string + arn = string name = string }) - }) + }) + description = "Configuration for the EventBridge Lambda rule." } variable "tags" { - type = map(any) - default = {} + type = map(any) + default = {} + description = "Tags to apply to all resources." } diff --git a/build/terraform/aws/kinesis_data_stream/_variables.tf b/build/terraform/aws/kinesis_data_stream/_variables.tf index c0f1efdf..0340c841 100644 --- a/build/terraform/aws/kinesis_data_stream/_variables.tf +++ b/build/terraform/aws/kinesis_data_stream/_variables.tf @@ -1,26 +1,29 @@ -variable "config" { +variable "kms" { type = object({ - name = string - autoscaling_topic = string - shards = optional(number, 2) - retention = optional(number, 24) + arn = string + id = string }) + description = "KMS key used to encrypt the stream." } -variable kms { +variable "config" { type = object({ - arn = string - id = string + name = string + autoscaling_topic = string + shards = optional(number, 2) + retention = optional(number, 24) }) + description = "Configuration for the Kinesis stream." } variable "tags" { - type = map(any) - default = {} + type = map(any) + default = {} + description = "Tags to apply to all resources." } variable "access" { - type = list(string) - default = [] + type = list(string) + default = [] description = "List of IAM ARNs that are granted access to the resource." } diff --git a/build/terraform/aws/kinesis_data_stream/main.tf b/build/terraform/aws/kinesis_data_stream/main.tf index 7571cc2d..9a528109 100644 --- a/build/terraform/aws/kinesis_data_stream/main.tf +++ b/build/terraform/aws/kinesis_data_stream/main.tf @@ -13,14 +13,14 @@ resource "aws_kinesis_stream" "stream" { # Applies the policy to each role in the access list. resource "aws_iam_role_policy_attachment" "access" { - for_each = toset(var.access) - role = each.value + for_each = toset(var.access) + role = each.value policy_arn = aws_iam_policy.access.arn } resource "aws_iam_policy" "access" { - name = var.config.name - description = "Policy for the ${var.config.name} Kinesis Data Stream" + name = "${var.config.name}-access" + description = "Policy for the ${var.config.name} Kinesis Data Stream." policy = data.aws_iam_policy_document.access.json } diff --git a/build/terraform/aws/kms/_variables.tf b/build/terraform/aws/kms/_variables.tf index 13230cde..a85114bc 100644 --- a/build/terraform/aws/kms/_variables.tf +++ b/build/terraform/aws/kms/_variables.tf @@ -1,11 +1,13 @@ -variable config { +variable "config" { type = object({ - name = string - policy = optional(string, "") + name = string + policy = optional(string, null) }) + description = "Configuration for the KMS key." } variable "tags" { - type = map(any) - default = {} + type = map(any) + default = {} + description = "Tags to apply to all resources." } diff --git a/build/terraform/aws/lambda/_variables.tf b/build/terraform/aws/lambda/_variables.tf index cf85f695..2b27da79 100644 --- a/build/terraform/aws/lambda/_variables.tf +++ b/build/terraform/aws/lambda/_variables.tf @@ -1,46 +1,53 @@ variable "appconfig" { type = object({ arn = string - id = string + id = string }) + description = "AppConfig application used for the Lambda configuration." } variable "kms" { type = object({ arn = string - id = string + id = string }) + description = "KMS key used to encrypt the Lambda." } variable "config" { type = object({ - name = string + name = string description = string - image_uri = string - architectures = optional(list(string), ["x86_64"]) - timeout = optional(number, 300) - memory = optional(number, 1024) - env = optional(map(any), null) - secret = optional(bool, false) + image_uri = string + image_arm = bool + timeout = optional(number, 300) + memory = optional(number, 1024) + env = optional(map(any), null) + secret = optional(bool, false) vpc_config = optional(object({ - subnet_ids = list(string) + subnet_ids = list(string) security_group_ids = list(string) - }), null) + }), { + subnet_ids = [] + security_group_ids = [] + }) iam_statements = optional(list(object({ - sid = string - actions = list(string) + sid = string + actions = list(string) resources = list(string) })), []) }) + description = "Configuration for the Lambda function." } variable "tags" { - type = map(any) - default = {} + type = map(any) + default = {} + description = "Tags to apply to all resources." } variable "access" { - type = list(string) - default = [] + type = list(string) + default = [] description = "List of IAM ARNs that are granted access to the resource." } diff --git a/build/terraform/aws/lambda/main.tf b/build/terraform/aws/lambda/main.tf index 2ba9f765..62198a3b 100644 --- a/build/terraform/aws/lambda/main.tf +++ b/build/terraform/aws/lambda/main.tf @@ -1,4 +1,4 @@ -# var.map[*] is a convenience function for handling empty maps +# var.map[*] is a convenience function for handling empty maps. locals { env = var.config.env[*] } @@ -6,23 +6,32 @@ locals { resource "aws_lambda_function" "lambda_function" { function_name = var.config.name description = var.config.description + + # Runtime settings. + role = aws_iam_role.role.arn + kms_key_arn = var.kms.arn + timeout = var.config.timeout + memory_size = var.config.memory + + # Architecture settings. + package_type = "Image" # Only container images are supported. image_uri = var.config.image_uri - package_type = "Image" - architectures = var.config.architectures - role = aws_iam_role.role.arn - timeout = var.config.timeout - memory_size = var.config.memory + architectures = var.config.image_arm ? ["arm64"] : ["x86_64"] + + # Network settings. vpc_config { subnet_ids = var.config.vpc_config.subnet_ids security_group_ids = var.config.vpc_config.security_group_ids } + # Tracing settings. tracing_config { mode = "Active" } - # required for avoiding errors due to missing environment variables + # Environment settings. + # Required for avoiding errors due to missing environment variables. dynamic "environment" { for_each = local.env content { @@ -30,8 +39,7 @@ resource "aws_lambda_function" "lambda_function" { } } - kms_key_arn = var.kms.arn - tags = var.tags + tags = var.tags } resource "aws_iam_role" "role" { @@ -64,7 +72,7 @@ resource "aws_appconfig_configuration_profile" "config" { tags = var.tags } -# optional secret creation +# Optional secrets creation. resource "aws_secretsmanager_secret" "secret" { count = var.config.secret ? 1 : 0 name = var.config.name @@ -103,7 +111,7 @@ resource "aws_iam_role_policy_attachment" "custom_policy_attachment" { resource "aws_iam_policy" "custom_policy" { name = var.config.name - description = "Policy for the ${var.config.name} Lambda" + description = "Policy for the ${var.config.name} Lambda." policy = data.aws_iam_policy_document.custom_policy_document.json } @@ -117,7 +125,7 @@ data "aws_iam_policy_document" "custom_policy_document" { "appconfig:GetLatestConfiguration", "appconfig:StartConfigurationSession", ] - + resources = [ "${var.appconfig.arn}/*" ] @@ -150,16 +158,18 @@ data "aws_iam_policy_document" "custom_policy_document" { } ################################################ -# Applies the policy to each role in the access list. +# Access Policies +################################################ + resource "aws_iam_role_policy_attachment" "access" { - for_each = toset(var.access) - role = each.value + for_each = toset(var.access) + role = each.value policy_arn = aws_iam_policy.access.arn } resource "aws_iam_policy" "access" { - name = var.config.name - description = "Policy for the ${var.config.name} Kinesis Data Stream" + name = "${var.config.name}-access" + description = "Policy for the ${var.config.name} Lambda." policy = data.aws_iam_policy_document.access.json } @@ -185,7 +195,6 @@ data "aws_iam_policy_document" "access" { actions = [ "lambda:InvokeAsync", "lambda:InvokeFunction", - ] resources = [ diff --git a/build/terraform/aws/networking/vpc/_variables.tf b/build/terraform/aws/networking/vpc/_variables.tf index 8667c111..781d5fae 100644 --- a/build/terraform/aws/networking/vpc/_variables.tf +++ b/build/terraform/aws/networking/vpc/_variables.tf @@ -5,11 +5,12 @@ variable "config" { "10.0.0.0/18" = "us-east-1a" }) private_subnets = optional(map(string), { - "10.0.64.0/18" = "us-east-1a" - "10.0.128.0/18" = "us-east-1b" - "10.0.192.0/18" = "us-east-1c" + "10.0.64.0/18" = "us-east-1a" + "10.0.128.0/18" = "us-east-1b" + "10.0.192.0/18" = "us-east-1c" }) }) + description = "Configuration for the VPC." validation { condition = length(keys(var.config.public_subnet)) == 1 @@ -23,7 +24,7 @@ variable "config" { } variable "tags" { - description = "Tags to apply to the VPC." type = map(any) default = {} + description = "Tags to apply to all resources." } diff --git a/build/terraform/aws/s3/_variables.tf b/build/terraform/aws/s3/_variables.tf index c7ff9b80..0df91191 100644 --- a/build/terraform/aws/s3/_variables.tf +++ b/build/terraform/aws/s3/_variables.tf @@ -1,24 +1,27 @@ variable "kms" { type = object({ - arn = string - id = string + arn = string + id = string }) + description = "KMS key used to encrypt the bucket." } variable "config" { type = object({ - name = string + name = string force_destroy = optional(bool, true) }) + description = "Configuration for the S3 bucket." } variable "tags" { - type = map(any) - default = {} + type = map(any) + default = {} + description = "Tags to apply to all resources." } variable "access" { - type = list(string) - default = [] + type = list(string) + default = [] description = "List of IAM ARNs that are granted access to the resource." } diff --git a/build/terraform/aws/s3/main.tf b/build/terraform/aws/s3/main.tf index 52f4f7a2..d68bf911 100644 --- a/build/terraform/aws/s3/main.tf +++ b/build/terraform/aws/s3/main.tf @@ -37,14 +37,14 @@ resource "aws_s3_bucket_server_side_encryption_configuration" "encryption" { # Applies the policy to each role in the access list. resource "aws_iam_role_policy_attachment" "access" { - for_each = toset(var.access) - role = each.value + for_each = toset(var.access) + role = each.value policy_arn = aws_iam_policy.access.arn } resource "aws_iam_policy" "access" { - name = var.config.name - description = "Policy for the ${var.config.name} S3 bucket" + name = "${var.config.name}-access" + description = "Policy for the ${var.config.name} S3 bucket." policy = data.aws_iam_policy_document.access.json } diff --git a/build/terraform/aws/s3/worm/_variables.tf b/build/terraform/aws/s3/worm/_variables.tf index 53c94021..ad4be463 100644 --- a/build/terraform/aws/s3/worm/_variables.tf +++ b/build/terraform/aws/s3/worm/_variables.tf @@ -1,25 +1,28 @@ variable "kms" { type = object({ - arn = string - id = string + arn = string + id = string }) + description = "KMS key used to encrypt the bucket." } variable "config" { type = object({ - name = string - retention = number + name = string + retention = number force_destroy = optional(bool, false) }) + description = "Configuration for the S3 bucket." } variable "tags" { - type = map(any) - default = {} + type = map(any) + default = {} + description = "Tags to apply to all resources." } variable "access" { - type = list(string) - default = [] + type = list(string) + default = [] description = "List of IAM ARNs that are granted access to the resource." } diff --git a/build/terraform/aws/s3/worm/main.tf b/build/terraform/aws/s3/worm/main.tf index e34ed59b..4c66e3a4 100644 --- a/build/terraform/aws/s3/worm/main.tf +++ b/build/terraform/aws/s3/worm/main.tf @@ -46,14 +46,14 @@ resource "aws_s3_bucket_object_lock_configuration" "object_lock" { # Applies the policy to each role in the access list. resource "aws_iam_role_policy_attachment" "access" { - for_each = toset(var.access) - role = each.value + for_each = toset(var.access) + role = each.value policy_arn = aws_iam_policy.access.arn } resource "aws_iam_policy" "access" { - name = var.config.name - description = "Policy for the ${var.config.name} SNS topic." + name = "${var.config.name}-access" + description = "Policy for the ${var.config.name} S3 bucket." policy = data.aws_iam_policy_document.access.json } diff --git a/build/terraform/aws/sns/_variables.tf b/build/terraform/aws/sns/_variables.tf index 49099a2b..b0812b0b 100644 --- a/build/terraform/aws/sns/_variables.tf +++ b/build/terraform/aws/sns/_variables.tf @@ -1,23 +1,26 @@ variable "kms" { type = object({ - arn = string - id = string + arn = string + id = string }) + description = "KMS key used to encrypt the topic." } variable "config" { type = object({ name = string }) + description = "Configuration for the SNS topic." } variable "tags" { - type = map(any) - default = {} + type = map(any) + default = {} + description = "Tags to apply to all resources." } variable "access" { - type = list(string) - default = [] + type = list(string) + default = [] description = "List of IAM ARNs that are granted access to the resource." } diff --git a/build/terraform/aws/sns/main.tf b/build/terraform/aws/sns/main.tf index a515cf17..3d14d67a 100644 --- a/build/terraform/aws/sns/main.tf +++ b/build/terraform/aws/sns/main.tf @@ -9,14 +9,14 @@ resource "aws_sns_topic" "topic" { # Applies the policy to each role in the access list. resource "aws_iam_role_policy_attachment" "access" { - for_each = toset(var.access) - role = each.value + for_each = toset(var.access) + role = each.value policy_arn = aws_iam_policy.access.arn } resource "aws_iam_policy" "access" { - name = var.config.name - description = "Policy for the ${var.config.name} SNS topic" + name = "${var.config.name}-access" + description = "Policy for the ${var.config.name} SNS topic." policy = data.aws_iam_policy_document.access.json } diff --git a/build/terraform/aws/sqs/_variables.tf b/build/terraform/aws/sqs/_variables.tf index ddc4cfd3..b6884954 100644 --- a/build/terraform/aws/sqs/_variables.tf +++ b/build/terraform/aws/sqs/_variables.tf @@ -1,25 +1,38 @@ variable "kms" { type = object({ - arn = string - id = string + arn = string + id = string }) + description = "KMS key used to encrypt the queue." } variable "config" { type = object({ - name = string - delay = optional(number, 0) - timeout = optional(number, 300) + name = string + delay = optional(number, 0) + timeout = optional(number, 30) }) + description = "Configuration for the SQS queue." + + validation { + condition = var.config.delay > 900 + error_message = "Delay must be less than 15 minutes." + } + + validation { + condition = var.config.timeout > 43200 + error_message = "Timeout must be less than 12 hours." + } } variable "tags" { - type = map(any) - default = {} + type = map(any) + default = {} + description = "Tags to apply to all resources." } variable "access" { - type = list(string) - default = [] + type = list(string) + default = [] description = "List of IAM ARNs that are granted access to the resource." } diff --git a/build/terraform/aws/sqs/main.tf b/build/terraform/aws/sqs/main.tf index 9749cf84..632a2e14 100644 --- a/build/terraform/aws/sqs/main.tf +++ b/build/terraform/aws/sqs/main.tf @@ -24,14 +24,14 @@ resource "aws_sqs_queue" "queue" { # Applies the policy to each role in the access list. resource "aws_iam_role_policy_attachment" "access" { - for_each = toset(var.access) - role = each.value + for_each = toset(var.access) + role = each.value policy_arn = aws_iam_policy.access.arn } resource "aws_iam_policy" "access" { - name = var.config.name - description = "Policy for the ${var.config.name} SQS queue" + name = "${var.config.name}-access" + description = "Policy for the ${var.config.name} SQS queue." policy = data.aws_iam_policy_document.access.json }