diff --git a/services/search-index-lambda/.gitignore b/services/search-index-lambda/.gitignore new file mode 100644 diff --git a/services/terraform/dev/main.tf b/services/terraform/dev/main.tf --- a/services/terraform/dev/main.tf +++ b/services/terraform/dev/main.tf @@ -45,8 +45,8 @@ module "shared" { source = "../modules/shared" is_dev = true - - vpc_id = null + + vpc_id = null cidr_block = null subnet_ids = [] } diff --git a/services/terraform/modules/shared/dynamodb.tf b/services/terraform/modules/shared/dynamodb.tf --- a/services/terraform/modules/shared/dynamodb.tf +++ b/services/terraform/modules/shared/dynamodb.tf @@ -83,7 +83,7 @@ name = "tunnelbroker-undelivered-messages" hash_key = "deviceID" range_key = "messageID" - billing_mode = "PAY_PER_REQUEST" + billing_mode = "PAY_PER_REQUEST" attribute { name = "deviceID" @@ -97,9 +97,9 @@ } resource "aws_dynamodb_table" "identity-users" { - name = "identity-users" - hash_key = "userID" - billing_mode = "PAY_PER_REQUEST" + name = "identity-users" + hash_key = "userID" + billing_mode = "PAY_PER_REQUEST" stream_enabled = true stream_view_type = "NEW_AND_OLD_IMAGES" diff --git a/services/terraform/modules/shared/opensearch.tf b/services/terraform/modules/shared/opensearch.tf --- a/services/terraform/modules/shared/opensearch.tf +++ b/services/terraform/modules/shared/opensearch.tf @@ -3,7 +3,7 @@ } resource "aws_security_group" "identity-search" { - count = var.is_dev ? 0 : 1 + count = var.is_dev ? 0 : 1 name = "${var.vpc_id}-opensearch-${var.domain}" description = "Managed by Terraform" vpc_id = var.is_dev ? null : var.vpc_id @@ -29,7 +29,7 @@ engine_version = "OpenSearch_1.0" cluster_config { - instance_type = "t3.medium.search" + instance_type = "t3.medium.search" } vpc_options { diff --git a/services/terraform/modules/shared/outputs.tf b/services/terraform/modules/shared/outputs.tf --- a/services/terraform/modules/shared/outputs.tf +++ b/services/terraform/modules/shared/outputs.tf @@ -4,6 +4,7 @@ aws_dynamodb_table.backup-service-backup, aws_dynamodb_table.reports-service-reports, aws_dynamodb_table.tunnelbroker-undelivered-messages, + aws_dynamodb_table.identity-users, ] } diff --git a/services/terraform/modules/shared/search_index_lambda.tf b/services/terraform/modules/shared/search_index_lambda.tf new file mode 100644 --- /dev/null +++ b/services/terraform/modules/shared/search_index_lambda.tf @@ -0,0 +1,62 @@ +variable "search_index_lambda_iam_role_arn" { + default = "arn:aws:iam::000000000000:role/lambda-role" +} + + +variable "lambda_zip_dir" { + type = string + default = "../../search-index-lambda/target/lambda/search-index-lambda" +} + +resource "aws_lambda_function" "search_index_lambda" { + function_name = "search-index-lambda-function" + filename = "${var.lambda_zip_dir}/bootstrap.zip" + source_code_hash = filebase64sha256("${var.lambda_zip_dir}/bootstrap.zip") + handler = "bootstrap" + role = var.search_index_lambda_iam_role_arn + runtime = "provided.al2" + architectures = ["arm64"] + timeout = 300 + + vpc_config { + subnet_ids = var.subnet_ids + security_group_ids = [aws_security_group.search_index_lambda.id] + } + + environment { + variables = { + RUST_BACKTRACE = "1" + OPENSEARCH_ENDPOINT = aws_opensearch_domain.identity-search.endpoint + } + } + + tracing_config { + mode = "Active" + } +} + +resource "aws_lambda_event_source_mapping" "trigger" { + count = var.is_dev ? 0 : 1 + event_source_arn = aws_dynamodb_table.identity-users.stream_arn + function_name = aws_lambda_function.search_index_lambda.arn + starting_position = "LATEST" +} + +resource "aws_security_group" "search_index_lambda" { + name = "search_index_lambda_sg" + vpc_id = var.vpc_id + + egress { + from_port = 443 + to_port = 443 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } +} + + +resource "aws_lambda_function_event_invoke_config" "example" { + function_name = aws_lambda_function.search_index_lambda.function_name + maximum_event_age_in_seconds = 60 + maximum_retry_attempts = 2 +} diff --git a/services/terraform/remote/aws_iam.tf b/services/terraform/remote/aws_iam.tf --- a/services/terraform/remote/aws_iam.tf +++ b/services/terraform/remote/aws_iam.tf @@ -195,10 +195,90 @@ ] } + +data "aws_iam_policy_document" "assume_identity_search_role" { + statement { + effect = "Allow" + + principals { + type = "Service" + identifiers = ["lambda.amazonaws.com"] + } + + actions = ["sts:AssumeRole"] + } +} + +resource "aws_iam_role" "search_index_lambda" { + name = "search_index_lambda" + assume_role_policy = data.aws_iam_policy_document.assume_identity_search_role.json + + managed_policy_arns = [ + aws_iam_policy.manage_cloudwatch_logs.arn, + aws_iam_policy.read_identity_users_stream.arn, + ] +} + +data "aws_iam_policy_document" "read_identity_users_stream" { + statement { + effect = "Allow" + + actions = [ + "dynamodb:GetRecords", + "dynamodb:GetShardIterator", + "dynamodb:DescribeStream", + "dynamodb:ListStreams", + ] + resources = [ + module.shared.dynamodb_tables["identity-users"].arn, + module.shared.dynamodb_tables["identity-users"].stream_arn, + "${module.shared.dynamodb_tables["identity-users"].arn}/stream/*", + ] + } +} + +resource "aws_iam_policy" "read_identity_users_stream" { + name = "read-identity-users-stream" + path = "/" + description = "IAM policy for managing identity-users stream" + policy = data.aws_iam_policy_document.read_identity_users_stream.json +} + +data "aws_iam_policy_document" "manage_cloudwatch_logs" { + statement { + effect = "Allow" + + actions = [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents", + ] + + resources = ["arn:aws:logs:*:*:*"] + } +} + +resource "aws_iam_policy" "manage_cloudwatch_logs" { + name = "manage-cloudwatch-logs" + path = "/" + description = "IAM policy for managing cloudwatch logs" + policy = data.aws_iam_policy_document.manage_cloudwatch_logs.json +} + +resource "aws_iam_role_policy_attachment" "AWSLambdaVPCAccessExecutionRole" { + role = aws_iam_role.search_index_lambda.name + policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" +} + data "aws_iam_policy_document" "opensearch_domain_access" { statement { effect = "Allow" + principals { + type = "*" + identifiers = ["${aws_iam_role.search_index_lambda.arn}"] + } + actions = [ "es:ESHttpHead", "es:ESHttpPost", @@ -206,13 +286,13 @@ "es:ESHttpDelete", "es:ESHttpPut", ] + resources = ["${module.shared.opensearch_domain_identity.arn}/*"] } } -resource "aws_iam_policy" "opensearch_domain_access" { - name = "opensearch-domain-access" - path = "/" - description = "IAM policy for accessing opensearch domain" - policy = data.aws_iam_policy_document.opensearch_domain_access.json +resource "aws_opensearch_domain_policy" "opensearch_domain_access" { + domain_name = module.shared.opensearch_domain_identity.domain_name + access_policies = data.aws_iam_policy_document.opensearch_domain_access.json } + diff --git a/services/terraform/remote/main.tf b/services/terraform/remote/main.tf --- a/services/terraform/remote/main.tf +++ b/services/terraform/remote/main.tf @@ -52,8 +52,9 @@ source = "../modules/shared" bucket_name_suffix = local.s3_bucket_name_suffix - vpc_id = aws_vpc.default.id - cidr_block = aws_vpc.default.cidr_block + vpc_id = aws_vpc.default.id + search_index_lambda_iam_role_arn = aws_iam_role.search_index_lambda.arn + cidr_block = aws_vpc.default.cidr_block subnet_ids = [ aws_subnet.public_a.id, ]