Skip to content

Instantly share code, notes, and snippets.

@gunzip
Created May 8, 2023 17:32
Show Gist options
  • Save gunzip/4f322c577151f7ea40e9d13efa5a1260 to your computer and use it in GitHub Desktop.
Save gunzip/4f322c577151f7ea40e9d13efa5a1260 to your computer and use it in GitHub Desktop.
pdv tokenizer
terraform {
required_version = ">= 1.0.0"
backend "s3" {}
required_providers {
aws = {
source = "hashicorp/aws"
version = "= 4.63.0"
configuration_aliases = [aws.alternate]
}
}
}
provider "aws" {
region = var.aws_region
default_tags {
tags = var.tags
}
}
provider "aws" {
alias = "eu-central-1"
region = "eu-central-1"
default_tags {
tags = var.tags
}
}
locals {
project = format("%s-%s", var.app_name, var.env_short)
}
data "aws_caller_identity" "current" {}locals {
task_tokenizer_name = format("%s-task-tokenizer", local.project)
service_tokenizer_name = format("%s-service-tokenizer", local.project)
api_key_list = { for k in var.tokenizer_plans : k.key_name => k }
additional_keys = flatten([for k in var.tokenizer_plans :
[for a in k.additional_keys :
{
"key" : a
"plan" : k.key_name
}]
])
tokenizer_api_name = format("%s-tokenizer-api", local.project)
tokenizer_stage_name = "v1"
list_tokenizer_key_to_name = concat(
[for n in var.tokenizer_plans : "'${aws_api_gateway_api_key.main[n.key_name].id}':'${n.key_name}'"],
[for n in local.additional_keys : "'${aws_api_gateway_api_key.additional[n.key].id}':'${n.plan}'"]
)
tokenizer_log_group_name = "API-Gateway-Execution-Logs_${aws_api_gateway_rest_api.tokenizer.id}/${local.tokenizer_stage_name}"
tokenizer_api_plan_ids = merge(
{ for k in keys(local.api_key_list) : k => aws_api_gateway_usage_plan.tokenizer[k].id },
{ for k in local.additional_keys : k.key => aws_api_gateway_usage_plan.tokenizer[k.plan].id },
)
}module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "3.14.0"
name = format("%s-vpc", local.project)
cidr = var.vpc_cidr
azs = var.azs
private_subnets = var.vpc_private_subnets_cidr
private_subnet_suffix = "private"
public_subnets = var.vpc_public_subnets_cidr
public_subnet_suffix = "public"
intra_subnets = var.vpc_internal_subnets_cidr
enable_nat_gateway = var.enable_nat_gateway
enable_dns_hostnames = true
enable_dns_support = true
}
data "aws_iam_policy_document" "generic_endpoint_policy" {
statement {
effect = "Deny"
actions = ["*"]
resources = ["*"]
principals {
type = "*"
identifiers = ["*"]
}
condition {
test = "StringNotEquals"
variable = "aws:SourceVpc"
values = [module.vpc.vpc_id]
}
}
}
data "aws_iam_policy_document" "dynamodb_endpoint_policy" {
statement {
effect = "Deny"
actions = ["dynamodb:*"]
resources = ["*"]
principals {
type = "*"
identifiers = ["*"]
}
condition {
test = "StringNotEquals"
variable = "aws:sourceVpce"
values = [module.vpc.vpc_id]
}
}
}
data "aws_security_group" "default" {
name = "default"
vpc_id = module.vpc.vpc_id
}
resource "aws_security_group" "vpc_tls" {
name_prefix = format("%s_vpc_tls_sg", local.project)
description = "Allow TLS inbound traffic"
vpc_id = module.vpc.vpc_id
ingress {
description = "TLS from VPC"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = [module.vpc.vpc_cidr_block]
}
tags = { Name = format("%s_vpc_tls_sg", local.project) }
}
module "vpc_endpoints" {
source = "terraform-aws-modules/vpc/aws//modules/vpc-endpoints"
vpc_id = module.vpc.vpc_id
security_group_ids = [data.aws_security_group.default.id]
endpoints = {
s3 = {
service = "s3"
service_type = "Gateway"
route_table_ids = flatten([module.vpc.intra_route_table_ids, module.vpc.private_route_table_ids, module.vpc.public_route_table_ids])
tags = { Name = "s3-vpc-endpoint" }
},
logs = {
service = "logs"
private_dns_enabled = true
subnet_ids = module.vpc.private_subnets
#policy = data.aws_iam_policy_document.generic_endpoint_policy.json
security_group_ids = [aws_security_group.vpc_tls.id]
tags = { Name = "logs-endpoint" }
},
ecr_api = {
service = "ecr.api"
private_dns_enabled = true
subnet_ids = module.vpc.private_subnets
#policy = data.aws_iam_policy_document.generic_endpoint_policy.json
security_group_ids = [aws_security_group.vpc_tls.id]
tags = { Name = "ecr.api-endpoint" }
},
ecr_dkr = {
service = "ecr.dkr"
private_dns_enabled = true
subnet_ids = module.vpc.private_subnets
#policy = data.aws_iam_policy_document.generic_endpoint_policy.json
security_group_ids = [aws_security_group.vpc_tls.id]
tags = { Name = "ecr.dkr-endpoint" }
},
dynamodb = {
service = "dynamodb"
service_type = "Gateway"
route_table_ids = flatten([module.vpc.intra_route_table_ids, module.vpc.private_route_table_ids, module.vpc.public_route_table_ids])
# policy = data.aws_iam_policy_document.dynamodb_endpoint_policy.json
tags = { Name = "dynamodb-vpc-endpoint" }
},
/*
ecs = {
service = "ecs"
private_dns_enabled = true
subnet_ids = module.vpc.private_subnets
tags = merge({ Name = "ecs-endpoint" }, var.tags)
},
ecs_telemetry = {
service = "ecs-telemetry"
private_dns_enabled = true
subnet_ids = module.vpc.private_subnets
tags = merge({ Name = "ecs-telemetry-endpoint" }, var.tags)
},
kms = {
service = "kms"
private_dns_enabled = true
subnet_ids = module.vpc.private_subnets
security_group_ids = [aws_security_group.vpc_tls.id]
tags = merge({ Name = "kms-endpoint" }, var.tags)
},
*/
}
}
data "aws_route_tables" "accepter" {
vpc_id = module.vpc.vpc_id
}
resource "aws_route" "accepter" {
count = var.vpc_peering != null ? length(data.aws_route_tables.accepter.ids) : 0
route_table_id = data.aws_route_tables.accepter.ids[count.index]
destination_cidr_block = var.vpc_peering.owner_cidr_block
vpc_peering_connection_id = var.vpc_peering.owner_connection_id
}
locals {
repositories = [
format("%s-ecr", local.project, ), # todo rename into tokenizer
]
}
resource "aws_ecr_repository" "main" {
count = length(local.repositories)
name = local.repositories[count.index]
image_scanning_configuration {
scan_on_push = false
}
tags = { "Name" : local.repositories[count.index] }
}
resource "aws_ecr_lifecycle_policy" "main" {
count = length(local.repositories)
repository = aws_ecr_repository.main[count.index].name
policy = jsonencode({
rules = [{
rulePriority = 1
description = format("Keeps last %s images", var.ecr_keep_nr_images)
action = {
type = "expire"
}
selection = {
tagStatus = "any"
countType = "imageCountMoreThan"
countNumber = var.ecr_keep_nr_images
}
}]
})
}data "aws_iam_policy_document" "ecs_tasks_assume_role_policy" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["ecs-tasks.amazonaws.com"]
}
}
}
resource "aws_iam_role" "ecs_execution_task" {
name = format("%s-execution-task-role", local.project)
assume_role_policy = data.aws_iam_policy_document.ecs_tasks_assume_role_policy.json
tags = { Name = format("%s-execution-task-role", local.project) }
}
resource "aws_iam_role_policy_attachment" "ecsTaskExecutionRole_policy" {
role = aws_iam_role.ecs_execution_task.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"
}
# policy to allow execute command.
resource "aws_iam_policy" "execute_command_policy" {
count = var.ecs_enable_execute_command ? 1 : 0
name = "PagoPaECSExecuteCommand"
description = "Policy to allow ecs execute command."
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"ssmmessages:CreateControlChannel",
"ssmmessages:CreateDataChannel",
"ssmmessages:OpenControlChannel",
"ssmmessages:OpenDataChannel"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "ecs_execute_command_policy" {
count = var.ecs_enable_execute_command ? 1 : 0
role = aws_iam_role.ecs_execution_task.name
policy_arn = aws_iam_policy.execute_command_policy[0].arn
}
## policy to allow ecs to read and write in dynamodb
resource "aws_iam_policy" "dynamodb_rw" {
name = "PagoPaECSReadWriteDynamoDB"
description = "Policy to allow ecs tasks to read and write in dynamodb table"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"dynamodb:PutItem",
"dynamodb:DeleteItem",
"dynamodb:GetItem",
"dynamodb:Query",
"dynamodb:UpdateItem",
"dynamodb:GetRecords"
],
"Effect": "Allow",
"Resource": [
"${module.dynamodb_table_token.dynamodb_table_arn}",
"${module.dynamodb_table_token.dynamodb_table_arn}/index/${local.dynamo_gsi_token_name}"
]
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "ecs_dynamodb_rw" {
role = aws_iam_role.ecs_execution_task.name
policy_arn = aws_iam_policy.dynamodb_rw.arn
}
## Allow ecs tasks to encrypt and decrypt KMS key
resource "aws_iam_policy" "ecs_allow_kms" {
name = "PagoPaAllowECSKMS"
description = "Policy to allow ECS tasks to encrypt and decrypt data at rest in DynamoDB."
policy = templatefile(
"./iam_policies/allow-kms-encrypt-decrypt.json.tpl",
{
account_id = data.aws_caller_identity.current.account_id
}
)
}
resource "aws_iam_role_policy_attachment" "ecs_allow_kms" {
role = aws_iam_role.ecs_execution_task.name
policy_arn = aws_iam_policy.ecs_allow_kms.arn
}
## Allow fargate task to query cloud hsm
## This action does not support resource-level permissions. Policies granting access must specify "*" ## in the resource element.
resource "aws_iam_policy" "ecs_allow_hsm" {
count = var.create_cloudhsm ? 1 : 0
name = "PagoPaAllowECSHSM"
description = "Policy to allow ECS tasks query cloud hsm."
policy = templatefile(
"./iam_policies/allow-hsm.tpl.json", {}
)
}
resource "aws_iam_role_policy_attachment" "ecs_allow_hsm" {
count = var.create_cloudhsm ? 1 : 0
role = aws_iam_role.ecs_execution_task.name
policy_arn = aws_iam_policy.ecs_allow_hsm[0].arn
}
## IAM Group Developer
resource "aws_iam_group" "developers" {
name = "Developers"
}
data "aws_iam_policy" "power_user" {
name = "PowerUserAccess"
}
### Deny access to secret devops
data "aws_secretsmanager_secret" "devops" {
name = "devops"
}
resource "aws_iam_policy" "deny_secrets_devops" {
name = "PagoPaDenyAccessSecretsDevops"
description = "Deny access to devops secrets."
policy = templatefile(
"./iam_policies/deny-access-secret-devops.json.tpl",
{
secret_arn = data.aws_secretsmanager_secret.devops.arn
}
)
}
resource "aws_iam_group_policy_attachment" "power_user" {
count = var.env_short == "u" ? 1 : 0
group = aws_iam_group.developers.name
policy_arn = data.aws_iam_policy.power_user.arn
}
resource "aws_iam_group_policy_attachment" "deny_secrets_devops" {
count = var.env_short == "u" ? 1 : 0
group = aws_iam_group.developers.name
policy_arn = aws_iam_policy.deny_secrets_devops.arn
}
resource "aws_iam_policy" "deploy_ecs" {
name = "PagoPaECSDeploy"
description = "Policy to allow deploy on ECS."
policy = templatefile(
"./iam_policies/deploy-ecs.json.tpl",
{
account_id = data.aws_caller_identity.current.account_id
execute_task_role_arn = aws_iam_role.ecs_execution_task.arn
}
)
}
data "aws_iam_policy" "ec2_ecr_full_access" {
name = "AmazonEC2ContainerRegistryFullAccess"
}
## Deploy with github action
resource "aws_iam_role" "githubecsdeploy" {
name = "GitHubDeployECS"
description = "Role to assume to create the infrastructure."
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow",
Principal = {
"Federated" : "arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/token.actions.githubusercontent.com"
},
Action = "sts:AssumeRoleWithWebIdentity",
Condition = {
StringLike = {
"token.actions.githubusercontent.com:sub" : [
"repo:${var.github_tokenizer_repo}:*"
]
},
"ForAllValues:StringEquals" = {
"token.actions.githubusercontent.com:iss" : "https://token.actions.githubusercontent.com",
"token.actions.githubusercontent.com:aud" : "sts.amazonaws.com"
}
}
}
]
})
}
resource "aws_iam_role_policy_attachment" "deploy_ecs" {
role = aws_iam_role.githubecsdeploy.name
policy_arn = aws_iam_policy.deploy_ecs.arn
}
resource "aws_iam_role_policy_attachment" "deploy_ec2_ecr_full_access" {
role = aws_iam_role.githubecsdeploy.name
policy_arn = data.aws_iam_policy.ec2_ecr_full_access.arn
}# Cluster
resource "aws_ecs_cluster" "ecs_cluster" {
name = format("%s-ecs-cluster", local.project)
tags = { Name = format("%s-ecs", local.project) }
setting {
name = "containerInsights"
value = var.enable_container_insights ? "enabled" : "diabled"
}
}
locals {
service_ids = [
join("/", ["service", aws_ecs_cluster.ecs_cluster.name, aws_ecs_service.tokenizer.name, ]),
]
}
## Autoscaling
resource "aws_appautoscaling_target" "ecs_target" {
count = length(local.service_ids)
max_capacity = var.ecs_autoscaling.max_capacity
min_capacity = var.ecs_autoscaling.min_capacity
resource_id = local.service_ids[count.index]
scalable_dimension = "ecs:service:DesiredCount"
service_namespace = "ecs"
}
resource "aws_appautoscaling_policy" "ecs_policy_memory" {
count = length(local.service_ids)
name = format("%s-memory-autoscaling", element(split("/", local.service_ids[count.index]), 2))
policy_type = "TargetTrackingScaling"
resource_id = aws_appautoscaling_target.ecs_target[count.index].resource_id
scalable_dimension = aws_appautoscaling_target.ecs_target[count.index].scalable_dimension
service_namespace = aws_appautoscaling_target.ecs_target[count.index].service_namespace
target_tracking_scaling_policy_configuration {
scale_in_cooldown = var.ecs_autoscaling.scale_in_cooldown
scale_out_cooldown = var.ecs_autoscaling.scale_out_cooldown
predefined_metric_specification {
predefined_metric_type = "ECSServiceAverageMemoryUtilization"
}
target_value = 80
}
}
resource "aws_appautoscaling_policy" "ecs_policy_cpu" {
count = length(local.service_ids)
name = format("%s-cpu-autoscaling", element(split("/", local.service_ids[count.index]), 2))
policy_type = "TargetTrackingScaling"
resource_id = aws_appautoscaling_target.ecs_target[count.index].resource_id
scalable_dimension = aws_appautoscaling_target.ecs_target[count.index].scalable_dimension
service_namespace = aws_appautoscaling_target.ecs_target[count.index].service_namespace
target_tracking_scaling_policy_configuration {
scale_in_cooldown = var.ecs_autoscaling.scale_in_cooldown
scale_out_cooldown = var.ecs_autoscaling.scale_out_cooldown
predefined_metric_specification {
predefined_metric_type = "ECSServiceAverageCPUUtilization"
}
target_value = 80
}
}resource "aws_cloudwatch_log_group" "ecs_tokenizer" {
name = format("ecs/%s", local.task_tokenizer_name)
retention_in_days = var.ecs_logs_retention_days
tags = {
Name = local.task_tokenizer_name
}
}
resource "aws_ecs_task_definition" "tokenizer" {
family = local.task_tokenizer_name
container_definitions = <<DEFINITION
[
{
"name": "${local.project}-container",
"image": "${aws_ecr_repository.main[0].repository_url}:latest",
"entryPoint": [],
"essential": true,
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "${aws_cloudwatch_log_group.ecs_tokenizer.id}",
"awslogs-region": "${var.aws_region}",
"awslogs-stream-prefix": "${local.project}"
}
},
"portMappings": [
{
"containerPort": ${var.container_port_tokenizer},
"hostPort": ${var.container_port_tokenizer}
}
],
"environment": [
{
"name": "AWS_REGION",
"value": "${var.aws_region}"
},
{
"name": "APP_SERVER_PORT",
"value": "${var.container_port_tokenizer}"
},
{
"name": "APP_LOG_LEVEL",
"value": "${var.ms_tokenizer_log_level}"
},
{
"name": "REST_CLIENT_LOGGER_LEVEL",
"value": "${var.ms_tokenizer_rest_client_log_level}"
},
{
"name": "ENABLE_CONFIDENTIAL_FILTER",
"value": "${var.ms_tokenizer_enable_confidential_filter}"
},
{
"name": "ENABLE_SINGLE_LINE_STACK_TRACE_LOGGING",
"value": "${var.ms_tokenizer_enable_single_line_stack_trace_logging}"
}
],
"cpu": 256,
"memory": 512,
"networkMode": "awsvpc"
}
]
DEFINITION
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
memory = "512"
cpu = "256"
execution_role_arn = aws_iam_role.ecs_execution_task.arn
task_role_arn = aws_iam_role.ecs_execution_task.arn
tags = { Name = format("%s-ecs-td", local.project) }
}
data "aws_ecs_task_definition" "tokenizer" {
task_definition = aws_ecs_task_definition.tokenizer.family
}
# Service
resource "aws_ecs_service" "tokenizer" {
name = local.service_tokenizer_name
cluster = aws_ecs_cluster.ecs_cluster.id
task_definition = format("%s:%s",
aws_ecs_task_definition.tokenizer.family,
max(aws_ecs_task_definition.tokenizer.revision, data.aws_ecs_task_definition.tokenizer.revision)
)
launch_type = "FARGATE"
scheduling_strategy = "REPLICA"
desired_count = var.replica_count
force_new_deployment = true
enable_execute_command = var.ecs_enable_execute_command
network_configuration {
subnets = module.vpc.private_subnets
assign_public_ip = false
security_groups = [
# NLB
aws_security_group.nsg_task.id
]
}
load_balancer {
target_group_arn = module.nlb.target_group_arns[0]
container_name = format("%s-container", local.project)
container_port = var.container_port_tokenizer
}
depends_on = [module.nlb]
tags = { Name : local.service_tokenizer_name }
}
resource "aws_cloudwatch_metric_alarm" "cpu_utilization_high" {
count = length(local.service_ids)
alarm_name = format("%s-CPU-Utilization-High-%s", split("/", local.service_ids[count.index])[2],
var.ecs_as_threshold.cpu_max)
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = "1"
metric_name = "CPUUtilization"
namespace = "AWS/ECS"
period = "60"
statistic = "Average"
threshold = var.ecs_as_threshold.cpu_max
dimensions = {
ClusterName = aws_ecs_cluster.ecs_cluster.name
ServiceName = split("/", local.service_ids[count.index])[2]
}
alarm_actions = [
aws_appautoscaling_policy.app_up[count.index].arn,
aws_sns_topic.alarms.arn,
]
}
resource "aws_cloudwatch_metric_alarm" "cpu_utilization_low" {
count = length(local.service_ids)
alarm_name = format("%s-CPU-Utilization-Low-%s", split("/", local.service_ids[count.index])[2],
var.ecs_as_threshold.cpu_min)
comparison_operator = "LessThanThreshold"
evaluation_periods = "1"
metric_name = "CPUUtilization"
namespace = "AWS/ECS"
period = "60"
statistic = "Average"
threshold = var.ecs_as_threshold.cpu_min
dimensions = {
ClusterName = aws_ecs_cluster.ecs_cluster.name
ServiceName = split("/", local.service_ids[count.index])[2]
}
alarm_actions = [
aws_appautoscaling_policy.app_down[count.index].arn,
]
}
# Alarm
resource "aws_cloudwatch_metric_alarm" "mem_utilization_high" {
count = length(local.service_ids)
alarm_name = format("%s-Mem-Utilization-High-%s", split("/", local.service_ids[count.index])[2],
var.ecs_as_threshold.mem_max)
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = "1"
metric_name = "MemoryUtilization"
namespace = "AWS/ECS"
period = "60"
statistic = "Average"
threshold = var.ecs_as_threshold.mem_max
dimensions = {
ClusterName = aws_ecs_cluster.ecs_cluster.name
ServiceName = split("/", local.service_ids[count.index])[2]
}
alarm_actions = [
#aws_appautoscaling_policy.app_up[count.index].arn,
aws_sns_topic.alarms.arn,
]
}
resource "aws_appautoscaling_policy" "app_up" {
count = length(local.service_ids)
name = format("%s-scale-up", aws_ecs_cluster.ecs_cluster.name)
service_namespace = aws_appautoscaling_target.ecs_target[count.index].service_namespace
resource_id = aws_appautoscaling_target.ecs_target[count.index].resource_id
scalable_dimension = aws_appautoscaling_target.ecs_target[count.index].scalable_dimension
step_scaling_policy_configuration {
adjustment_type = "ChangeInCapacity"
cooldown = 60
metric_aggregation_type = "Average"
step_adjustment {
metric_interval_lower_bound = 0
scaling_adjustment = 1
}
}
}
resource "aws_appautoscaling_policy" "app_down" {
count = length(local.service_ids)
name = format("%s-scale-down", aws_ecs_cluster.ecs_cluster.name)
service_namespace = aws_appautoscaling_target.ecs_target[count.index].service_namespace
resource_id = aws_appautoscaling_target.ecs_target[count.index].resource_id
scalable_dimension = aws_appautoscaling_target.ecs_target[count.index].scalable_dimension
step_scaling_policy_configuration {
adjustment_type = "ChangeInCapacity"
cooldown = 300
metric_aggregation_type = "Average"
step_adjustment {
metric_interval_upper_bound = 0
scaling_adjustment = -1
}
}
}
resource "aws_kms_key" "dynamo_db" {
description = "Kms dynamo db encryption key."
customer_master_key_spec = "SYMMETRIC_DEFAULT"
tags = { Name = format("%s-dynamo-table-key", local.project) }
}
resource "aws_kms_alias" "dynamo_db" {
name = format("alias/%s-dynamo-table", local.project)
target_key_id = aws_kms_key.dynamo_db.id
}
resource "aws_kms_key" "dynamo_db_replica" {
provider = aws.eu-central-1
count = var.env_short == "p" ? 1 : 0
description = "Kms dynamo db encryption key."
customer_master_key_spec = "SYMMETRIC_DEFAULT"
tags = { Name = format("%s-dynamo-table-key", local.project) }
}
resource "aws_kms_alias" "dynamo_db_replica" {
provider = aws.eu-central-1
count = var.env_short == "p" ? 1 : 0
name = format("alias/%s-dynamo-table", local.project)
target_key_id = aws_kms_key.dynamo_db_replica[0].id
}locals {
apigw_custom_domain = join(".", ["api", keys(var.public_dns_zones)[0]])
}
resource "aws_acm_certificate" "main" {
count = var.apigw_custom_domain_create ? 1 : 0
domain_name = local.apigw_custom_domain
validation_method = "DNS"
lifecycle {
create_before_destroy = true
}
}
resource "aws_route53_record" "cert_validation" {
for_each = var.apigw_custom_domain_create ? {
for dvo in aws_acm_certificate.main[0].domain_validation_options : dvo.domain_name => {
name = dvo.resource_record_name
record = dvo.resource_record_value
type = dvo.resource_record_type
}
} : {}
allow_overwrite = true
name = each.value.name
records = [each.value.record]
ttl = 3600 # 1h
type = each.value.type
zone_id = module.dn_zone.route53_zone_zone_id[keys(var.public_dns_zones)[0]]
}
locals {
# Token
dynamodb_table_token = "Token"
dynamo_gsi_token_name = keys(var.table_token_autoscling_indexes)[0]
}
# Table Token
module "dynamodb_table_token" {
source = "terraform-aws-modules/dynamodb-table/aws"
name = local.dynamodb_table_token
hash_key = "PK"
range_key = "SK"
stream_enabled = var.table_token_stream_enabled
stream_view_type = var.table_token_stream_enabled ? "NEW_IMAGE" : null
point_in_time_recovery_enabled = var.dynamodb_point_in_time_recovery_enabled
billing_mode = "PROVISIONED"
autoscaling_enabled = true
read_capacity = var.table_token_read_capacity
write_capacity = var.table_token_write_capacity
attributes = [
{
name = "PK"
type = "S"
},
{
name = "SK"
type = "S"
},
{
name = "token"
type = "S"
},
]
global_secondary_indexes = [
{
name = local.dynamo_gsi_token_name
hash_key = "token"
projection_type = "ALL"
write_capacity = var.table_token_autoscling_indexes[local.dynamo_gsi_token_name]["write_min_capacity"]
read_capacity = var.table_token_autoscling_indexes[local.dynamo_gsi_token_name]["read_min_capacity"]
}
]
server_side_encryption_enabled = true
server_side_encryption_kms_key_arn = aws_kms_alias.dynamo_db.target_key_arn
autoscaling_read = var.table_token_autoscaling_read
autoscaling_write = var.table_token_autoscaling_write
autoscaling_indexes = var.table_token_autoscling_indexes
replica_regions = var.dynamodb_region_replication_enable ? [{
region_name = "eu-central-1"
kms_key_arn = aws_kms_alias.dynamo_db_replica[0].target_key_arn
}] : []
tags = { Name = local.dynamodb_table_token }
}resource "aws_iam_role" "pipe" {
count = var.create_event_bridge_pipe ? 1 : 0
name = "pipe-tokens-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = {
Effect = "Allow"
Action = "sts:AssumeRole"
Principal = {
Service = "pipes.amazonaws.com"
}
Condition = {
StringEquals = {
"aws:SourceAccount" = data.aws_caller_identity.current.account_id
}
}
}
})
}
resource "aws_iam_role_policy" "source" {
count = var.create_event_bridge_pipe ? 1 : 0
name = "AllowPipeConsumeStream"
role = aws_iam_role.pipe[0].id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"dynamodb:GetRecords",
"dynamodb:GetShardIterator",
"dynamodb:DescribeStream",
"dynamodb:ListStreams"
],
Resource = [
module.dynamodb_table_token.dynamodb_table_stream_arn
]
},
{
Effect = "Allow"
Action = [
"kms:Decrypt",
"kms:Encrypt",
],
Resource = [
aws_kms_alias.dynamo_db.target_key_arn
]
},
]
})
}
resource "aws_sqs_queue" "target" {
count = var.create_event_bridge_pipe ? 1 : 0
name = format("queue-%s-tokens", var.env_short)
}
resource "aws_iam_role_policy" "target" {
count = var.create_event_bridge_pipe ? 1 : 0
name = "AllowPipeWriteSQS"
role = aws_iam_role.pipe[0].id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"sqs:SendMessage",
],
Resource = [
aws_sqs_queue.target[0].arn,
]
},
]
})
}
resource "aws_pipes_pipe" "token" {
count = var.create_event_bridge_pipe ? 1 : 0
depends_on = [aws_iam_role_policy.source, aws_iam_role_policy.target]
name = format("pipe-%s-tokens", var.env_short)
role_arn = aws_iam_role.pipe[0].arn
source = module.dynamodb_table_token.dynamodb_table_stream_arn
target = aws_sqs_queue.target[0].arn
source_parameters {
filter_criteria {
filter {
pattern = jsonencode({
dynamodb = {
Keys = {
SK = {
S = [
{
anything-but = "GLOBAL"
},
]
}
}
}
})
}
}
}
target_parameters {}
}
# https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/aws-services-cloudwatch-metrics.html
module "metric_alarms" {
count = length(var.dynamodb_alarms)
source = "terraform-aws-modules/cloudwatch/aws//modules/metric-alarm"
version = "~> 3.0"
actions_enabled = var.dynamodb_alarms[count.index].actions_enabled
alarm_name = var.dynamodb_alarms[count.index].alarm_name
alarm_description = var.dynamodb_alarms[count.index].alarm_description
comparison_operator = var.dynamodb_alarms[count.index].comparison_operator
evaluation_periods = var.dynamodb_alarms[count.index].evaluation_periods
threshold = var.dynamodb_alarms[count.index].threshold
period = var.dynamodb_alarms[count.index].period
unit = var.dynamodb_alarms[count.index].unit
datapoints_to_alarm = var.dynamodb_alarms[count.index].datapoints_to_alarm
namespace = var.dynamodb_alarms[count.index].namespace
metric_name = var.dynamodb_alarms[count.index].metric_name
statistic = var.dynamodb_alarms[count.index].statistic
alarm_actions = [aws_sns_topic.alarms.arn]
}
module "dynamo_successful_request_latency" {
source = "terraform-aws-modules/cloudwatch/aws//modules/metric-alarm"
version = "~> 3.0"
alarm_name = "dynamodb-successful-request-latency"
alarm_description = "Dynamodb successful request latency"
comparison_operator = "LessThanLowerOrGreaterThanUpperThreshold"
evaluation_periods = 2
threshold_metric_id = "ad1"
metric_query = [
{
id = "m1"
return_data = true
metric = [{
dimensions = null
metric_name = "SuccessfulRequestLatency"
namespace = "AWS/DynamoDB"
period = 300
stat = "Maximum"
unit = "Percent"
},
]
}, {
expression = "ANOMALY_DETECTION_BAND(m1, 2)"
id = "ad1"
label = "SuccessfulRequestLatency"
return_data = true
},
]
}
## Read capacity units
module "dynamodb_read_capacity_units_limit_alarm" {
source = "terraform-aws-modules/cloudwatch/aws//modules/metric-alarms-by-multiple-dimensions"
version = "~> 3.0"
alarm_name = "dynamodb-read-capacity-units-"
actions_enabled = var.env_short == "p" ? true : false
alarm_description = "Alarm when read capacity reaches 80% of provisioned read capacity"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = 1
threshold = var.table_token_read_capacity - (var.table_token_read_capacity * 0.2)
period = 60
namespace = "AWS/DynamoDB"
metric_name = "ConsumedReadCapacityUnits"
statistic = "Average"
dimensions = {
"token" = {
TableName = local.dynamodb_table_token
},
}
alarm_actions = [aws_sns_topic.alarms.arn]
}
## Write capacity units
module "dynamodb_write_capacity_units_limit_alarm" {
source = "terraform-aws-modules/cloudwatch/aws//modules/metric-alarms-by-multiple-dimensions"
version = "~> 3.0"
alarm_name = "dynamodb-write-capacity-units-"
actions_enabled = var.env_short == "p" ? true : false
alarm_description = "Alarm when write capacity reaches 80% of provisioned read capacity"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = 1
threshold = var.table_token_write_capacity - (var.table_token_write_capacity * 0.2)
period = 60
namespace = "AWS/DynamoDB"
metric_name = "ConsumedWriteCapacityUnits"
statistic = "Average"
dimensions = {
"token" = {
TableName = local.dynamodb_table_token
},
}
alarm_actions = [aws_sns_topic.alarms.arn]
}
### Global secondary index read capacity.
module "gsi_index_read_capacity_units_limit_alarm" {
source = "terraform-aws-modules/cloudwatch/aws//modules/metric-alarms-by-multiple-dimensions"
version = "~> 3.0"
alarm_name = "read-capacity-units-"
actions_enabled = var.env_short == "p" ? true : false
alarm_description = "Alarm when read capacity reaches 80% of provisioned read capacity"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = 1
threshold = var.table_token_autoscling_indexes[local.dynamo_gsi_token_name]["read_max_capacity"] - (var.table_token_autoscling_indexes[local.dynamo_gsi_token_name]["read_max_capacity"] * 0.2)
period = 60
namespace = "AWS/DynamoDB"
metric_name = "ConsumedReadCapacityUnits"
statistic = "Average"
dimensions = {
"gsi_index" = {
GlobalSecondaryIndexName = local.dynamo_gsi_token_name
},
}
alarm_actions = [aws_sns_topic.alarms.arn]
}
### Global secondary write read capacity.
module "gsi_index_write_capacity_units_limit_alarm" {
source = "terraform-aws-modules/cloudwatch/aws//modules/metric-alarms-by-multiple-dimensions"
version = "~> 3.0"
alarm_name = "write-capacity-units-"
actions_enabled = var.env_short == "p" ? true : false
alarm_description = "Alarm when read capacity reaches 80% of provisioned read capacity"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = 1
threshold = var.table_token_autoscling_indexes[local.dynamo_gsi_token_name]["write_max_capacity"] - (var.table_token_autoscling_indexes[local.dynamo_gsi_token_name]["write_max_capacity"] * 0.2)
period = 60
namespace = "AWS/DynamoDB"
metric_name = "ConsumedWriteCapacityUnits"
statistic = "Average"
dimensions = {
"gsi_index" = {
GlobalSecondaryIndexName = local.dynamo_gsi_token_name
},
}
alarm_actions = [aws_sns_topic.alarms.arn]
}
#ExceedingThroughput
module "dynamodb_request_exceeding_throughput_alarm" {
source = "terraform-aws-modules/cloudwatch/aws//modules/metric-alarms-by-multiple-dimensions"
version = "~> 3.0"
alarm_name = "dynamodb-exceeding-throughput-"
actions_enabled = var.env_short == "p" ? true : false
alarm_description = "Alarm when my requests are exceeding provisioned throughput quotas of a table."
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 1
threshold = 0
period = 300
unit = "Count"
namespace = "AWS/DynamoDB"
metric_name = "ThrottledRequests"
statistic = "Sum"
dimensions = {
"token-getitem" = {
TableName = local.dynamodb_table_token
Operation = "GetItem"
},
"token-query" = {
TableName = local.dynamodb_table_token
Operation = "Query"
},
"token-putitem" = {
TableName = local.dynamodb_table_token
Operation = "PutItem"
},
}
alarm_actions = [aws_sns_topic.alarms.arn]
}resource "aws_cloudhsm_v2_cluster" "main" {
count = var.create_cloudhsm ? 1 : 0
hsm_type = "hsm1.medium"
subnet_ids = module.vpc.private_subnets
tags = { "Name" : format("%s-cloudhsm", local.project) }
}
resource "aws_cloudhsm_v2_hsm" "hsm1" {
count = var.create_cloudhsm ? 1 : 0
subnet_id = module.vpc.private_subnets[0]
cluster_id = aws_cloudhsm_v2_cluster.main[0].id
}
resource "aws_security_group_rule" "task_hsm" {
count = var.create_cloudhsm ? 1 : 0
description = "Only allow connections from fargate tasks nsg ${count.index}"
type = "ingress"
from_port = 2223
to_port = 2225
protocol = "tcp"
# security group assigned to fargate tasks.
source_security_group_id = aws_security_group.nsg_task.id
security_group_id = aws_cloudhsm_v2_cluster.main[0].security_group_id
}
data "aws_network_interface" "hsm" {
count = var.create_cloudhsm ? 1 : 0
id = aws_cloudhsm_v2_hsm.hsm1[0].hsm_eni_id
}module "dn_zone" {
source = "terraform-aws-modules/route53/aws//modules/zones"
version = "~> 2.0"
zones = var.public_dns_zones
}
resource "aws_route53_record" "uat" {
count = var.env_short == "p" ? 1 : 0
allow_overwrite = true
name = "uat"
ttl = var.dns_record_ttl
type = "NS"
zone_id = module.dn_zone.route53_zone_zone_id[keys(var.public_dns_zones)[0]]
records = [
"ns-1471.awsdns-55.org",
"ns-90.awsdns-11.com",
"ns-962.awsdns-56.net",
"ns-1970.awsdns-54.co.uk",
]
}
resource "aws_api_gateway_domain_name" "main" {
count = var.apigw_custom_domain_create ? 1 : 0
domain_name = local.apigw_custom_domain
regional_certificate_arn = aws_acm_certificate.main[0].arn
endpoint_configuration {
types = ["REGIONAL"]
}
security_policy = "TLS_1_2"
}
resource "aws_route53_record" "main" {
count = var.apigw_custom_domain_create ? 1 : 0
zone_id = module.dn_zone.route53_zone_zone_id[keys(var.public_dns_zones)[0]]
name = aws_api_gateway_domain_name.main[0].domain_name
type = "A"
# ttl = var.dns_record_ttl
alias {
name = aws_api_gateway_domain_name.main[0].regional_domain_name
zone_id = aws_api_gateway_domain_name.main[0].regional_zone_id
evaluate_target_health = true
}
}locals {
apigw_name = format("%s-apigw-vpc-lik", local.project)
webacl_name = format("%s-webacl", local.project)
}
resource "aws_api_gateway_vpc_link" "apigw" {
name = local.apigw_name
description = "allows public API Gateway for ${local.apigw_name} to talk to private NLB"
target_arns = [module.nlb.lb_arn]
tags = { Name = local.apigw_name }
}
## API Gateway cloud watch logs
resource "aws_api_gateway_account" "main" {
cloudwatch_role_arn = aws_iam_role.apigw.arn
}
resource "aws_iam_role" "apigw" {
name = "api_gateway_cloudwatch_global"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "apigateway.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_role_policy" "cloudwatch" {
name = "default"
role = aws_iam_role.apigw.id
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams",
"logs:PutLogEvents",
"logs:GetLogEvents",
"logs:FilterLogEvents"
],
"Resource": "*"
}
]
}
EOF
}
# Create API Gateway Role
resource "aws_iam_role" "s3_api_gateyway_role" {
name = "s3-api-gateyway-role"
# Create Trust Policy for API Gateway
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "apigateway.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
data "aws_iam_policy" "s3_full_access" {
name = "AmazonS3ReadOnlyAccess"
}
resource "aws_iam_role_policy_attachment" "s3_policy_attach" {
role = aws_iam_role.s3_api_gateyway_role.name
policy_arn = data.aws_iam_policy.s3_full_access.arn
}resource "aws_api_gateway_api_key" "main" {
for_each = local.api_key_list
name = each.key
}
# Temporary change to allow interop to share selfcare namespace.
resource "aws_api_gateway_api_key" "additional" {
for_each = { for k in local.additional_keys : k.key => k }
name = each.key
}resource "aws_api_gateway_rest_api" "tokenizer" {
name = local.tokenizer_api_name
api_key_source = "HEADER"
body = templatefile("./api/ms_tokenizer/api-docs.json.tftpl",
{
uri = format("http://%s", module.nlb.lb_dns_name),
connection_id = aws_api_gateway_vpc_link.apigw.id
request_template = chomp(templatefile("./api/request_template.tftpl",
{
list_key_to_name = join(",", local.list_tokenizer_key_to_name)
}))
responses = file("./api/ms_tokenizer/status_code_mapping.tpl.json")
responses_only_token = file("./api/ms_tokenizer/status_code_mapping_only_token.tpl.json")
}
)
endpoint_configuration {
types = ["REGIONAL"]
}
tags = { Name = local.tokenizer_api_name }
}
resource "aws_api_gateway_deployment" "tokenizer" {
rest_api_id = aws_api_gateway_rest_api.tokenizer.id
# stage_name = local.tokenizer_stage_name
triggers = {
redeployment = sha1(jsonencode(aws_api_gateway_rest_api.tokenizer.body))
}
lifecycle {
create_before_destroy = true
}
}
resource "aws_cloudwatch_log_group" "tokenizer" {
name = local.tokenizer_log_group_name
retention_in_days = var.apigw_execution_logs_retention
tags = { Name = local.tokenizer_api_name }
}
resource "aws_api_gateway_stage" "tokenizer" {
deployment_id = aws_api_gateway_deployment.tokenizer.id
rest_api_id = aws_api_gateway_rest_api.tokenizer.id
stage_name = local.tokenizer_stage_name
cache_cluster_size = 0.5 #why is this needed ?
documentation_version = aws_api_gateway_documentation_version.main.version
dynamic "access_log_settings" {
for_each = var.apigw_access_logs_enable ? ["dymmy"] : []
content {
destination_arn = aws_cloudwatch_log_group.tokenizer.arn
#todo: find a better way to represent this log format.
format = "{ \"requestId\":\"$context.requestId\", \"extendedRequestId\":\"$context.extendedRequestId\", \"ip\": \"$context.identity.sourceIp\", \"caller\":\"$context.identity.caller\", \"user\":\"$context.identity.user\", \"requestTime\":\"$context.requestTime\", \"httpMethod\":\"$context.httpMethod\", \"resourcePath\":\"$context.resourcePath\", \"status\":\"$context.status\", \"protocol\":\"$context.protocol\", \"responseLength\":\"$context.responseLength\"}"
}
}
}
resource "aws_api_gateway_method_settings" "tokenizer" {
rest_api_id = aws_api_gateway_rest_api.tokenizer.id
stage_name = local.tokenizer_stage_name
method_path = "*/*"
settings {
# Enable CloudWatch logging and metrics
metrics_enabled = true
data_trace_enabled = var.apigw_data_trace_enabled
logging_level = "ERROR"
#todo.
# Limit the rate of calls to prevent abuse and unwanted charges
#throttling_rate_limit = 100
#throttling_burst_limit = 50
}
}
resource "aws_api_gateway_usage_plan" "tokenizer" {
for_each = local.api_key_list
name = format("%s-api-plan-%s", local.project, lower(each.key))
description = "Usage plan for tokenizer apis"
api_stages {
api_id = aws_api_gateway_rest_api.tokenizer.id
stage = aws_api_gateway_stage.tokenizer.stage_name
dynamic "throttle" {
for_each = each.value.method_throttle
content {
path = throttle.value.path
burst_limit = throttle.value.burst_limit
rate_limit = throttle.value.rate_limit
}
}
}
/*
quota_settings {
limit = 20
offset = 2
period = "WEEK"
}
*/
#TODO: tune this settings
throttle_settings {
burst_limit = each.value.burst_limit
rate_limit = each.value.rate_limit
}
}
resource "aws_api_gateway_usage_plan_key" "tokenizer" {
for_each = local.api_key_list
key_id = aws_api_gateway_api_key.main[each.key].id
key_type = "API_KEY"
usage_plan_id = aws_api_gateway_usage_plan.tokenizer[each.key].id
}
resource "aws_api_gateway_usage_plan_key" "tokenizer_additional" {
for_each = { for k in local.additional_keys : k.key => k }
key_id = aws_api_gateway_api_key.additional[each.key].id
key_type = "API_KEY"
usage_plan_id = aws_api_gateway_usage_plan.tokenizer[each.value.plan].id
depends_on = [
aws_api_gateway_api_key.additional
]
}
## Mapping api tokenizer with apigw custom domain.
resource "aws_apigatewayv2_api_mapping" "tokenizer" {
count = var.apigw_custom_domain_create ? 1 : 0
api_id = aws_api_gateway_rest_api.tokenizer.id
stage = aws_api_gateway_stage.tokenizer.stage_name
domain_name = aws_api_gateway_domain_name.main[0].domain_name
api_mapping_key = format("tokenizer/%s", aws_api_gateway_stage.tokenizer.stage_name)
}
## WAF association
resource "aws_wafv2_web_acl_association" "tokenizer" {
web_acl_arn = aws_wafv2_web_acl.main.arn
resource_arn = "arn:aws:apigateway:${var.aws_region}::/restapis/${aws_api_gateway_rest_api.tokenizer.id}/stages/${aws_api_gateway_stage.tokenizer.stage_name}"
}
/*
//The API Gateway endpoint
output "api_gateway_endpoint" {
value = format("https://", aws_api_gateway_domain_name.main.domain_name)
}
*/
output "tokenizerinvoke_url" {
value = aws_api_gateway_deployment.tokenizer.invoke_url
}
## Alarms
### 4xx
module "api_tokenizer_4xx_error_alarm" {
source = "terraform-aws-modules/cloudwatch/aws//modules/metric-alarms-by-multiple-dimensions"
version = "~> 3.0"
actions_enabled = var.env_short == "p" ? true : false
alarm_name = "high-4xx-rate-"
alarm_description = "Api tokenizer error rate has exceeded 5%"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = 2
threshold = 200
period = 300
unit = "Count"
datapoints_to_alarm = 1
namespace = "AWS/ApiGateway"
metric_name = "4XXError"
statistic = "Sum"
dimensions = {
"${local.tokenizer_api_name}" = {
ApiName = local.tokenizer_api_name
Stage = local.tokenizer_stage_name
},
}
alarm_actions = [aws_sns_topic.alarms.arn]
}
### 5xx
module "api_tokenizer_5xx_error_alarm" {
source = "terraform-aws-modules/cloudwatch/aws//modules/metric-alarms-by-multiple-dimensions"
version = "~> 3.0"
actions_enabled = var.env_short == "p" ? true : false
alarm_name = "high-5xx-rate-"
alarm_description = "Api tokenizer error rate has exceeded 5%"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = 20
threshold = 5
period = 300
unit = "Count"
datapoints_to_alarm = 2
namespace = "AWS/ApiGateway"
metric_name = "5XXError"
statistic = "Sum"
dimensions = {
"${local.tokenizer_api_name}" = {
ApiName = local.tokenizer_api_name
Stage = local.tokenizer_stage_name
},
}
alarm_actions = [aws_sns_topic.alarms.arn]
}
### throttling (exceeded throttle limit)
module "log_filter_throttle_limit_tokenizer" {
source = "terraform-aws-modules/cloudwatch/aws//modules/log-metric-filter"
version = "~> 3.0"
name = format("%s-metric-throttle-rate-limit", local.tokenizer_api_name)
log_group_name = local.tokenizer_log_group_name
pattern = "exceeded throttle limit"
metric_transformation_namespace = "ErrorCount"
metric_transformation_name = format("%s-namespace", local.tokenizer_api_name)
depends_on = [
aws_cloudwatch_log_group.tokenizer
]
}
module "api_tokenizer_throttle_limit_alarm" {
source = "terraform-aws-modules/cloudwatch/aws//modules/metric-alarm"
version = "~> 3.0"
actions_enabled = var.env_short == "p" ? true : false
alarm_name = format("high-rate-limit-throttle-%s", local.tokenizer_api_name)
alarm_description = "Throttle rate limit too high."
comparison_operator = "GreaterThanThreshold"
datapoints_to_alarm = 1
evaluation_periods = 2
threshold = 20
period = 300
#unit = "Count"
namespace = "ErrorCount"
metric_name = format("%s-namespace", local.tokenizer_api_name)
statistic = "Sum"
alarm_actions = [aws_sns_topic.alarms.arn]
depends_on = [
module.log_filter_throttle_limit_tokenizer
]
}
locals {
latency_threshold = 2000
}
module "api_tokenizer_low_latency_alarm" {
source = "terraform-aws-modules/cloudwatch/aws//modules/metric-alarms-by-multiple-dimensions"
version = "~> 3.0"
actions_enabled = var.env_short == "p" ? true : false
alarm_name = "low-latency-"
alarm_description = format("The Api responds in more than %s ms.", local.latency_threshold)
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = 1
threshold = local.latency_threshold
period = 300
unit = "Count"
datapoints_to_alarm = 1
namespace = "AWS/ApiGateway"
metric_name = "Latency"
statistic = "Maximum"
dimensions = {
"${local.tokenizer_api_name}" = {
ApiName = local.tokenizer_api_name
Stage = local.tokenizer_stage_name
},
}
alarm_actions = [aws_sns_topic.alarms.arn]
}resource "aws_api_gateway_documentation_version" "main" {
# assign the deployment creation date as a version.
version = formatdate("YYYYMMDDHHmmss", aws_api_gateway_deployment.tokenizer.created_date)
rest_api_id = aws_api_gateway_rest_api.tokenizer.id
description = format("Documentation api %s", aws_api_gateway_rest_api.tokenizer.id)
lifecycle {
create_before_destroy = true
}
}
# export api
## open api
data "aws_api_gateway_export" "tokenizer" {
rest_api_id = aws_api_gateway_stage.tokenizer.rest_api_id
stage_name = aws_api_gateway_stage.tokenizer.stage_name
export_type = "oas30"
}
resource "aws_s3_object" "openapi_tokenizer" {
key = "openapi.json"
bucket = aws_s3_bucket.openapidocs.bucket
content = data.aws_api_gateway_export.tokenizer.body
content_encoding = "utf-8"
content_type = "application/json"
}
resource "aws_api_gateway_rest_api" "openapi_tokenizer" {
name = format("%s-openapi", local.project)
description = "Openapi tokenizer documentation."
}
resource "aws_api_gateway_resource" "folder" {
rest_api_id = aws_api_gateway_rest_api.openapi_tokenizer.id
parent_id = aws_api_gateway_rest_api.openapi_tokenizer.root_resource_id
path_part = "{folder}"
}
resource "aws_api_gateway_resource" "item" {
rest_api_id = aws_api_gateway_rest_api.openapi_tokenizer.id
parent_id = aws_api_gateway_resource.folder.id
path_part = "{item}"
}
resource "aws_api_gateway_method" "get_item" {
rest_api_id = aws_api_gateway_rest_api.openapi_tokenizer.id
resource_id = aws_api_gateway_resource.item.id
http_method = "GET"
authorization = "NONE"
request_parameters = {
"method.request.path.folder" = true
"method.request.path.item" = true
}
}
resource "aws_api_gateway_integration" "openapi_tokenizer_integration" {
rest_api_id = aws_api_gateway_rest_api.openapi_tokenizer.id
resource_id = aws_api_gateway_resource.item.id
http_method = aws_api_gateway_method.get_item.http_method
integration_http_method = "GET"
type = "AWS"
uri = format("arn:aws:apigateway:%s:s3:path/{bucket}/{object}", var.aws_region)
credentials = aws_iam_role.s3_api_gateyway_role.arn
request_parameters = {
"integration.request.path.bucket" = "method.request.path.folder"
"integration.request.path.object" = "method.request.path.item"
}
}
resource "aws_api_gateway_method_response" "openapi_tokenizer_response_200" {
rest_api_id = aws_api_gateway_rest_api.openapi_tokenizer.id
resource_id = aws_api_gateway_resource.item.id
http_method = aws_api_gateway_method.get_item.http_method
status_code = "200"
response_models = {
"application/json" = "Empty"
}
}
resource "aws_api_gateway_integration_response" "openapi_tokenizer_integration_200" {
depends_on = [aws_api_gateway_integration.openapi_tokenizer_integration]
rest_api_id = aws_api_gateway_rest_api.openapi_tokenizer.id
resource_id = aws_api_gateway_resource.item.id
http_method = aws_api_gateway_method.get_item.http_method
status_code = aws_api_gateway_method_response.openapi_tokenizer_response_200.status_code
}
resource "aws_api_gateway_deployment" "openapi_tokenizer" {
depends_on = [aws_api_gateway_integration.openapi_tokenizer_integration]
rest_api_id = aws_api_gateway_rest_api.openapi_tokenizer.id
}
resource "aws_api_gateway_stage" "openapi_tokenizer" {
deployment_id = aws_api_gateway_deployment.openapi_tokenizer.id
rest_api_id = aws_api_gateway_rest_api.openapi_tokenizer.id
stage_name = "v1"
cache_cluster_size = 0.5
}
resource "aws_api_gateway_method_settings" "openapi_tokenizer" {
rest_api_id = aws_api_gateway_rest_api.openapi_tokenizer.id
stage_name = aws_api_gateway_stage.openapi_tokenizer.stage_name
method_path = "*/*"
settings {
caching_enabled = true
throttling_rate_limit = 10
throttling_burst_limit = 2
}
}
## Mapping openapi with custom domain .
resource "aws_apigatewayv2_api_mapping" "openapi_tokrnizer" {
count = var.apigw_custom_domain_create ? 1 : 0
api_id = aws_api_gateway_rest_api.openapi_tokenizer.id
stage = aws_api_gateway_stage.openapi_tokenizer.stage_name
domain_name = aws_api_gateway_domain_name.main[0].domain_name
api_mapping_key = "docs"
}## Firewall regional web acl
resource "aws_wafv2_web_acl" "main" {
name = format("%s-webacl", local.project)
description = "Api gateway WAF."
scope = "REGIONAL"
visibility_config {
cloudwatch_metrics_enabled = var.web_acl_visibility_config.cloudwatch_metrics_enabled
metric_name = local.webacl_name
sampled_requests_enabled = var.web_acl_visibility_config.sampled_requests_enabled
}
default_action {
allow {}
}
rule {
name = "IpReputationList"
priority = 1
override_action {
count {}
}
statement {
managed_rule_group_statement {
name = "AWSManagedRulesAmazonIpReputationList"
vendor_name = "AWS"
}
}
visibility_config {
cloudwatch_metrics_enabled = var.web_acl_visibility_config.cloudwatch_metrics_enabled
metric_name = "IpReputationList"
sampled_requests_enabled = var.web_acl_visibility_config.sampled_requests_enabled
}
}
rule {
name = "CommonRuleSet"
priority = 2
override_action {
count {}
}
statement {
managed_rule_group_statement {
name = "AWSManagedRulesCommonRuleSet"
vendor_name = "AWS"
}
}
visibility_config {
cloudwatch_metrics_enabled = var.web_acl_visibility_config.cloudwatch_metrics_enabled
metric_name = "CommonRuleSet"
sampled_requests_enabled = var.web_acl_visibility_config.sampled_requests_enabled
}
}
rule {
name = "KnownBadInputsRuleSet"
priority = 3
override_action {
count {}
}
statement {
managed_rule_group_statement {
name = "AWSManagedRulesKnownBadInputsRuleSet"
vendor_name = "AWS"
}
}
visibility_config {
cloudwatch_metrics_enabled = var.web_acl_visibility_config.cloudwatch_metrics_enabled
metric_name = "KnownBadInputsRuleSet"
sampled_requests_enabled = var.web_acl_visibility_config.sampled_requests_enabled
}
}
rule {
name = "SQLiRuleSet"
priority = 4
override_action {
count {}
}
statement {
managed_rule_group_statement {
name = "AWSManagedRulesSQLiRuleSet"
vendor_name = "AWS"
}
}
visibility_config {
cloudwatch_metrics_enabled = var.web_acl_visibility_config.cloudwatch_metrics_enabled
metric_name = "SQLiRuleSet"
sampled_requests_enabled = var.web_acl_visibility_config.sampled_requests_enabled
}
}
tags = { Name = format("%s-webacl", local.project) }
}
## Alarm
module "webacl_count_alarm" {
source = "terraform-aws-modules/cloudwatch/aws//modules/metric-alarms-by-multiple-dimensions"
version = "~> 3.0"
count = var.web_acl_visibility_config.cloudwatch_metrics_enabled ? 1 : 0
alarm_name = "waf-"
actions_enabled = var.env_short == "p" ? true : false
alarm_description = "Alarm when webacl count greater than 10"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = 1
threshold = 10
period = 60
unit = "Count"
namespace = "AWS/WAFV2"
metric_name = "CountedRequests"
statistic = "Sum"
dimensions = {
"webacl" = {
WebACL = aws_wafv2_web_acl.main.name
Ragion = var.aws_region
Rule = aws_wafv2_web_acl.main.name
},
}
alarm_actions = [aws_sns_topic.alarms.arn]
}resource "aws_security_group" "nsg_task" {
name = format("%s-task-nlb", local.project)
description = "Limit connections from internal resources while allowing ${local.project}-task to connect to all external resources"
vpc_id = module.vpc.vpc_id
tags = { Name = format("%s-task-nlb", local.project) }
}
# Rules for the TASK (Targets the LB's IPs)
locals {
# list of container port in use.
container_ports = [
var.container_port_tokenizer,
]
}
resource "aws_security_group_rule" "nsg_task_ingress_rule" {
count = length(local.container_ports)
description = "Only allow connections from the NLB ${count.index}"
type = "ingress"
from_port = local.container_ports[count.index]
to_port = local.container_ports[count.index]
protocol = "tcp"
cidr_blocks = formatlist(
"%s/32",
flatten(data.aws_network_interface.nlb.*.private_ips),
)
security_group_id = aws_security_group.nsg_task.id
}
resource "aws_security_group_rule" "nsg_task_egress_rule" {
description = "Allows task to establish connections to all resources"
type = "egress"
from_port = "0"
to_port = "0"
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.nsg_task.id
}
# lookup the ENIs associated with the NLB
data "aws_network_interface" "nlb" {
count = length(module.vpc.private_subnets)
filter {
name = "description"
values = ["ELB ${module.nlb.lb_arn_suffix}"]
}
filter {
name = "subnet-id"
values = [element(module.vpc.private_subnets, count.index)]
}
}
module "nlb" {
source = "terraform-aws-modules/alb/aws"
name = format("%s-nlb", local.project)
load_balancer_type = "network"
vpc_id = module.vpc.vpc_id
subnets = module.vpc.private_subnets
enable_cross_zone_load_balancing = "true"
internal = true
http_tcp_listeners = [
{
port = 80
protocol = "TCP"
target_group_index = 0
},
]
target_groups = [
{
# service tokenizer
name = format("%s-tokenizer", local.project)
backend_protocol = "TCP"
backend_port = 80
#port = 80
target_type = "ip"
#preserve_client_ip = true
deregistration_delay = 30
vpc_id = module.vpc.vpc_id
health_check = {
enabled = true
healthy_threshold = 3
interval = 10
timeout = 6
unhealthy_threshold = 3
matcher = "200-399"
path = "/actuator/health"
}
},
]
tags = { Name : format("%s-nlb", local.project) }
}module "nlb_unhealthy_unhealthy_targets_alarm" {
source = "terraform-aws-modules/cloudwatch/aws//modules/metric-alarms-by-multiple-dimensions"
version = "~> 3.0"
count = length(module.nlb.target_group_names)
alarm_name = "nlb-unhealthy-unhealthy-"
actions_enabled = var.env_short == "p" ? true : false
alarm_description = "Alarm when an unhealthy count is greater than one in the target"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = 1
threshold = 1
period = 60
unit = "Count"
namespace = "AWS/NetworkELB"
metric_name = "UnHealthyHostCount"
statistic = "Sum"
dimensions = {
"${module.nlb.target_group_names[count.index]}" = {
TargetGroup = module.nlb.target_group_names[count.index]
LoadBalacer = module.nlb.lb_arn_suffix
},
}
alarm_actions = [aws_sns_topic.alarms.arn]
}# ApiGw throttling
resource "aws_cloudwatch_query_definition" "apigw_429" {
name = "ApiGateway/429"
log_group_names = [
local.tokenizer_log_group_name
]
query_string = file("./cloudwatch-query/apigw-429.sql")
}
resource "aws_cloudwatch_query_definition" "apigw_count_rate_limit" {
name = "ApiGateway/Count Rate Limit"
log_group_names = [
local.tokenizer_log_group_name
]
query_string = file("./cloudwatch-query/count-api-rate-limit.sql")
}
## ECS
resource "aws_cloudwatch_query_definition" "ecs_log_level_error" {
name = "ECS/Errors"
log_group_names = [
aws_cloudwatch_log_group.ecs_tokenizer.name,
]
query_string = file("./cloudwatch-query/log-level-error.sql")
}
resource "aws_cloudwatch_query_definition" "ecs_exception" {
name = "ECS/Exceptions"
log_group_names = [
aws_cloudwatch_log_group.ecs_tokenizer.name,
]
query_string = file("./cloudwatch-query/log-with-exception.sql")
}
resource "aws_cloudwatch_query_definition" "ecs_provisioned_throughput_exception" {
name = "ECS/Count Provisioned Throughput"
log_group_names = [
aws_cloudwatch_log_group.ecs_tokenizer.name,
]
query_string = file("./cloudwatch-query/count-provisioned-throughput-exceeded.sql")
}
resource "aws_cloudwatch_query_definition" "ecs_log_by_traceid" {
name = "ECS/Log by traceid"
log_group_names = [
aws_cloudwatch_log_group.ecs_tokenizer.name,
]
query_string = file("./cloudwatch-query/log-by-traceid.sql")
}resource "aws_cloudwatch_dashboard" "main" {
dashboard_name = "MS-Tokenizer"
dashboard_body = templatefile("${path.module}/dashboards/main.tpl.json",
{
aws_region = var.aws_region
tokenizer_api_name = aws_api_gateway_rest_api.tokenizer.name
nlb_arn_suffix = module.nlb.lb_arn_suffix
nlb_target_arn_suffix = module.nlb.target_group_arn_suffixes[0]
ecs_tokenizer_service = aws_ecs_service.tokenizer.name
ecs_cluster_name = aws_ecs_cluster.ecs_cluster.name
waf_web_acl = aws_wafv2_web_acl.main.name
tokenizer_api_plan_ids = local.tokenizer_api_plan_ids
tokenizer_api_id = aws_api_gateway_rest_api.tokenizer.id
tokenizer_api_state_name = aws_api_gateway_stage.tokenizer.stage_name
}
)
}module "sentinel" {
count = var.enable_sentinel_logs ? 1 : 0
source = "git::https://github.com/pagopa/terraform-aws-sentinel.git?ref=v1.0.1"
account_id = data.aws_caller_identity.current.account_id
queue_name = format("%s-sentinel-queue", local.project)
trail_name = format("%s-sentinel-trail", local.project)
sentinel_bucket_name = format("%s-sentinel-logs", local.project)
expiration_days = 3
sentinel_workspace_id = var.sentinel_workspace_id
}locals {
openapidocs_bucket_name = format("%s%sapis", var.app_name, var.env_short)
}
# S3 bucket for website.
resource "aws_s3_bucket" "openapidocs" {
bucket = local.openapidocs_bucket_name
force_destroy = true
}
resource "aws_s3_bucket_acl" "openapidocs" {
bucket = aws_s3_bucket.openapidocs.id
acl = "private"
}
resource "aws_s3_bucket_policy" "openapidocs" {
bucket = aws_s3_bucket.openapidocs.id
policy = templatefile("./templates/s3_policy.tpl.json", {
bucket_name = local.openapidocs_bucket_name
account_id = data.aws_caller_identity.current.id
})
}data "aws_secretsmanager_secret" "email_operation" {
name = "operation/alerts"
}
data "aws_secretsmanager_secret_version" "email_operation_lt" {
secret_id = data.aws_secretsmanager_secret.email_operation.id
}
resource "aws_sns_topic" "alarms" {
name = format("%s-alarms", local.project)
display_name = "Alarms"
}
resource "aws_sns_topic_subscription" "alarms_email" {
endpoint = jsondecode(data.aws_secretsmanager_secret_version.email_operation_lt.secret_string)["email"]
endpoint_auto_confirms = true
protocol = "email"
topic_arn = aws_sns_topic.alarms.arn
}moved {
from = aws_api_gateway_api_key.main["INTEROP-DEV"]
to = aws_api_gateway_api_key.additional["INTEROP-DEV"]
}
moved {
from = aws_api_gateway_api_key.main["INTEROP-UAT"]
to = aws_api_gateway_api_key.additional["INTEROP-UAT"]
}
moved {
from = aws_api_gateway_api_key.main["INTEROP"]
to = aws_api_gateway_api_key.additional["INTEROP"]
}variable "aws_region" {
type = string
description = "AWS region to create resources. Default Milan"
default = "eu-south-1"
}
variable "app_name" {
type = string
default = "tokenizer"
description = "App name. Tokenizer"
}
variable "environment" {
type = string
default = "dev"
description = "Environment"
}
variable "env_short" {
type = string
default = "d"
description = "Evnironment short."
}
variable "vpc_cidr" {
type = string
default = "10.1.0.0/16"
description = "VPC cidr."
}
variable "azs" {
type = list(string)
description = "Availability zones"
default = ["eu-south-1a", "eu-south-1b", "eu-south-1c"]
}
variable "vpc_private_subnets_cidr" {
type = list(string)
description = "Private subnets list of cidr."
default = ["10.1.1.0/24", "10.1.2.0/24", "10.1.3.0/24"]
}
variable "vpc_public_subnets_cidr" {
type = list(string)
description = "Private subnets list of cidr."
default = ["10.1.101.0/24", "10.1.102.0/24", "10.1.103.0/24"]
}
variable "vpc_internal_subnets_cidr" {
type = list(string)
description = "Internal subnets list of cidr. Mainly for private endpoints"
default = ["10.1.201.0/24", "10.1.202.0/24", "10.1.203.0/24"]
}
variable "enable_nat_gateway" {
type = bool
description = "Enable/Create nat gateway"
default = false
}
### VPC Peering
variable "vpc_peering" {
type = object({
owner_connection_id = string
owner_cidr_block = string
})
default = null
description = "Vpc peering configuration"
}
## Public Dns zones
variable "public_dns_zones" {
type = map(any)
description = "Route53 Hosted Zone"
}
variable "dns_record_ttl" {
type = number
description = "Dns record ttl (in sec)"
default = 86400 # 24 hours
}
## Api Gateway
variable "apigw_custom_domain_create" {
type = bool
description = "Create apigw Custom Domain with its tls certificate"
default = false
}
variable "apigw_access_logs_enable" {
type = bool
description = "Enable api gateway access logs"
default = false
}
variable "apigw_data_trace_enabled" {
type = bool
description = "Specifies whether data trace logging is enabled. It effects the log entries pushed to Amazon CloudWatch Logs."
default = false
}
variable "apigw_execution_logs_retention" {
type = number
default = 7
description = "Api gateway exection logs retention (days)"
}
## Web acl config
variable "web_acl_visibility_config" {
type = object({
cloudwatch_metrics_enabled = bool
sampled_requests_enabled = bool
})
default = {
cloudwatch_metrics_enabled = false
sampled_requests_enabled = false
}
description = "Cloudwatch metric eneble for web acl rules."
}
## ECR
variable "ecr_keep_nr_images" {
type = number
description = "Number of images to keep."
default = 10
}
## ECS
variable "ecs_logs_retention_days" {
type = number
description = "Specifies the number of days you want to retain log events in the specified log group."
default = 7
}
variable "ecs_enable_execute_command" {
type = bool
description = "Specifies whether to enable Amazon ECS Exec for the tasks within the service."
default = false
}
variable "container_port_tokenizer" {
type = number
description = "Container port tokenizer"
default = 8080
}
variable "replica_count" {
type = number
description = "Number of task replica"
default = 1
}
variable "ecs_autoscaling" {
type = object({
max_capacity = number
min_capacity = number
scale_in_cooldown = number
scale_out_cooldown = number
})
default = {
max_capacity = 3
min_capacity = 1
scale_in_cooldown = 180
scale_out_cooldown = 40
}
description = "ECS Service autoscaling."
}
variable "ecs_as_threshold" {
type = object({
cpu_min = number
cpu_max = number
mem_min = number
mem_max = number
})
default = {
cpu_max = 80
cpu_min = 20
mem_max = 80
mem_min = 60
}
description = "ECS Tasks autoscaling settings."
}
variable "ms_tokenizer_log_level" {
type = string
default = "DEBUG"
description = "Log level micro service tokenizer"
}
variable "ms_tokenizer_rest_client_log_level" {
type = string
default = "FULL"
description = "Rest client log level micro service tokenizer"
}
variable "ms_tokenizer_enable_confidential_filter" {
type = bool
default = false
description = "Enable a filter to avoid logging confidential data"
}
variable "ms_tokenizer_enable_single_line_stack_trace_logging" {
type = bool
default = false
description = "Enable logging stack trace in a single line"
}
variable "enable_container_insights" {
type = bool
description = "Enable container insight in ECS cluster"
default = false
}
# Dynamodb
variable "dynamodb_region_replication_enable" {
type = bool
description = "Enable dyamodb deplicaton in a secondary region."
default = false
}
variable "dynamodb_point_in_time_recovery_enabled" {
type = bool
description = "Enable dynamodb point in time recovery"
default = false
}
## Table Token
variable "table_token_read_capacity" {
type = number
description = "Table token read capacity."
}
variable "table_token_write_capacity" {
type = number
description = "Table token read capacity."
}
variable "table_token_autoscaling_read" {
type = object({
scale_in_cooldown = number
scale_out_cooldown = number
target_value = number
max_capacity = number
})
description = "Read autoscaling settings table token."
}
variable "table_token_autoscaling_write" {
type = object({
scale_in_cooldown = number
scale_out_cooldown = number
target_value = number
max_capacity = number
})
description = "Write autoscaling settings table token."
}
variable "table_token_stream_enabled" {
type = bool
description = "Enable Streams"
default = false
}
# Event bridge
variable "create_event_bridge_pipe" {
type = bool
description = "Create event bridge pipe."
default = false
}
// We assume every plan has its own api key
variable "tokenizer_plans" {
type = list(object({
key_name = string
burst_limit = number
rate_limit = number
additional_keys = list(string)
method_throttle = list(object({
path = string
burst_limit = number
rate_limit = number
}))
}))
description = "Usage plan with its api key and rate limit."
}
variable "table_token_autoscling_indexes" {
type = any
description = "Autoscaling gsi configurations"
}
variable "create_cloudhsm" {
type = bool
description = "Create cloudhsm cluster to enctypt dynamodb tables"
default = false
}
## Alarms
variable "dynamodb_alarms" {
type = list(
object({
actions_enabled = bool
alarm_name = string
alarm_description = string
comparison_operator = string
evaluation_periods = number
datapoints_to_alarm = number
threshold = number
period = number
unit = string
namespace = string
metric_name = string
statistic = string
}))
}
# Sentinel integration
variable "enable_sentinel_logs" {
type = bool
default = false
description = "Create all resources required to sento logs to azure sentinel."
}
variable "sentinel_servcie_account_id" {
type = string
description = "Microsoft Sentinel's service account ID for AWS."
default = "197857026523"
}
variable "sentinel_workspace_id" {
type = string
description = "Sentinel workspece id"
default = null
}
variable "github_tokenizer_repo" {
type = string
description = "Github repository allowed to run action for ECS deploy."
default = "pagopa/pdv-ms-tokenizer"
}
variable "tags" {
type = map(any)
default = {
CreatedBy = "Terraform"
}
}
output "vpc_cidr" {
value = module.vpc.vpc_cidr_block
}
## ecs
output "ecs_definition_task_arn" {
value = data.aws_ecs_task_definition.tokenizer.arn
}
output "ecs_definition_task_revision" {
value = data.aws_ecs_task_definition.tokenizer.revision
}
output "ecs_task_definition_tokenizer_arn" {
value = data.aws_ecs_task_definition.tokenizer.arn
}
output "ecs_task_definition_tokenizer_revision" {
value = data.aws_ecs_task_definition.tokenizer.revision
}
## dynamodb
output "dynamodb_table_token_arn" {
value = module.dynamodb_table_token.dynamodb_table_arn
}
output "dynamodb_table_token_id" {
value = module.dynamodb_table_token.dynamodb_table_id
}
output "dynamodb_table_token_stream_arn" {
value = var.table_token_stream_enabled ? module.dynamodb_table_token.dynamodb_table_stream_arn : null
}
# Dns
output "public_dns_zone_name" {
value = module.dn_zone.route53_zone_name
}
output "public_dns_servers" {
value = module.dn_zone.route53_zone_name_servers
}
# NLB
output "nlb_hostname" {
value = module.nlb.lb_dns_name
}
# API Gateway
output "api_gateway_endpoint" {
value = var.apigw_custom_domain_create ? format("https://%s", aws_api_gateway_domain_name.main[0].domain_name) : ""
}
output "openapi_endpoint" {
value = var.apigw_custom_domain_create ? format("https://%s/docs/%s/%s",
aws_api_gateway_domain_name.main[0].domain_name,
aws_s3_bucket.openapidocs.id,
aws_s3_object.openapi_tokenizer.key, ) : ""
}
output "tokenizer_api_keys" {
value = { for k in keys(local.api_key_list) : k => aws_api_gateway_usage_plan_key.tokenizer[k].value }
sensitive = true
}
output "tokenizer_api_ids" {
value = local.tokenizer_api_plan_ids
}
# cloud hsm
output "cloudhsm_cluster_id" {
value = try(aws_cloudhsm_v2_cluster.main[0].id, null)
}
output "cloudhsm_cluster_certificates" {
value = try(aws_cloudhsm_v2_cluster.main[0].cluster_certificates, null)
sensitive = true
}
output "cloudhsm_hsm_id" {
value = try(aws_cloudhsm_v2_hsm.hsm1[0].hsm_id, null)
}
output "clouthsm_hsm_eni_ip" {
value = try(data.aws_network_interface.hsm[0].private_ip, null)
}
# sentinel
output "sentinel_role_arn" {
value = try(module.sentinel[0].sentinel_role_arn, null)
}
output "sentinel_queue_url" {
value = try(module.sentinel[0].sentinel_queue_url, null)
}
# github action role.
output "github_ecs_deploy_role_arn" {
value = aws_iam_role.githubecsdeploy.arn
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment