# TODO # add sns / sqs, AWS IoT actions terraform { backend "s3" { bucket = "sondehub-terraform" key = "sondehub-main" region = "us-east-1" profile = "sondes" } } provider "aws" { region = "us-east-1" profile = "sondes" } locals { domain_name = "v2.sondehub.org" } data "aws_caller_identity" "current" {} data "aws_iot_endpoint" "endpoint" { endpoint_type = "iot:Data-ATS" } resource "aws_iam_role" "IAMRole" { path = "/" name = "Cognito_sondesAuth_Role" assume_role_policy = < { 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 = 60 type = each.value.type zone_id = aws_route53_zone.Route53HostedZone.zone_id } resource "aws_acm_certificate_validation" "CertificateManagerCertificate" { certificate_arn = aws_acm_certificate.CertificateManagerCertificate.arn validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn] } # resource "aws_route53_record" "Route53RecordSet5" { # name = "api" # type = "CNAME" # ttl = 60 # records = [ # "${aws_apigatewayv2_domain_name.ApiGatewayV2DomainName.domain_name_configuration.0.target_domain_name}." # ] # zone_id = aws_route53_zone.Route53HostedZone.zone_id # } resource "aws_cognito_user_pool_domain" "main" { domain = "auth.${local.domain_name}" user_pool_id = aws_cognito_user_pool.CognitoUserPool.id certificate_arn = aws_acm_certificate_validation.CertificateManagerCertificate.certificate_arn } resource "aws_route53_record" "Route53RecordSet6" { name = "auth" type = "A" alias { name = "${aws_cognito_user_pool_domain.main.cloudfront_distribution_arn}." zone_id = "Z2FDTNDATAQYW2" evaluate_target_health = false } zone_id = aws_route53_zone.Route53HostedZone.zone_id } resource "aws_route53_record" "Route53RecordSet7" { name = "es" type = "CNAME" ttl = 300 records = [ aws_elasticsearch_domain.ElasticsearchDomain.endpoint ] zone_id = aws_route53_zone.Route53HostedZone.zone_id } data "archive_file" "api_to_iot" { type = "zip" source_file = "sonde-api-to-iot-core/lambda_function.py" output_path = "${path.module}/build/sonde-api-to-iot-core.zip" } data "archive_file" "query" { type = "zip" source_file = "query/lambda_function.py" output_path = "${path.module}/build/query.zip" } data "archive_file" "history" { type = "zip" source_file = "history/lambda_function.py" output_path = "${path.module}/build/history.zip" } data "archive_file" "sign_socket" { type = "zip" source_file = "sign-websocket/lambda_function.py" output_path = "${path.module}/build/sign_socket.zip" } data "archive_file" "predictions" { type = "zip" source_file = "predict/lambda_function.py" output_path = "${path.module}/build/predictions.zip" } resource "aws_lambda_function" "LambdaFunction" { function_name = "sonde-api-to-iot-core" handler = "lambda_function.lambda_handler" filename = "${path.module}/build/sonde-api-to-iot-core.zip" source_code_hash = data.archive_file.api_to_iot.output_base64sha256 publish = true memory_size = 256 role = aws_iam_role.IAMRole5.arn runtime = "python3.7" timeout = 10 tracing_config { mode = "Active" } environment { variables = { "IOT_ENDPOINT" = data.aws_iot_endpoint.endpoint.endpoint_address } } layers = [ "arn:aws:lambda:us-east-1:${data.aws_caller_identity.current.account_id}:layer:xray-python:1", "arn:aws:lambda:us-east-1:${data.aws_caller_identity.current.account_id}:layer:iot:3" ] } resource "aws_lambda_function" "get_sondes" { function_name = "query" handler = "lambda_function.get_sondes" filename = "${path.module}/build/query.zip" source_code_hash = data.archive_file.query.output_base64sha256 publish = true memory_size = 256 role = aws_iam_role.IAMRole5.arn runtime = "python3.7" timeout = 10 tracing_config { mode = "Active" } environment { variables = { "ES" = "es.${local.domain_name}" } } layers = [ "arn:aws:lambda:us-east-1:${data.aws_caller_identity.current.account_id}:layer:xray-python:1", "arn:aws:lambda:us-east-1:${data.aws_caller_identity.current.account_id}:layer:iot:3" ] } resource "aws_lambda_function" "listeners" { function_name = "listeners" handler = "lambda_function.get_listeners" filename = "${path.module}/build/query.zip" source_code_hash = data.archive_file.query.output_base64sha256 publish = true memory_size = 256 role = aws_iam_role.IAMRole5.arn runtime = "python3.7" timeout = 10 tracing_config { mode = "Active" } environment { variables = { "ES" = "es.${local.domain_name}" } } layers = [ "arn:aws:lambda:us-east-1:${data.aws_caller_identity.current.account_id}:layer:xray-python:1", "arn:aws:lambda:us-east-1:${data.aws_caller_identity.current.account_id}:layer:iot:3" ] } resource "aws_lambda_function" "datanew" { function_name = "datanew" handler = "lambda_function.datanew" filename = "${path.module}/build/query.zip" source_code_hash = data.archive_file.query.output_base64sha256 publish = true memory_size = 256 role = aws_iam_role.IAMRole5.arn runtime = "python3.7" timeout = 10 tracing_config { mode = "Active" } environment { variables = { "ES" = "es.${local.domain_name}" } } layers = [ "arn:aws:lambda:us-east-1:${data.aws_caller_identity.current.account_id}:layer:xray-python:1", "arn:aws:lambda:us-east-1:${data.aws_caller_identity.current.account_id}:layer:iot:3" ] } resource "aws_lambda_function" "predictions" { function_name = "predictions" handler = "lambda_function.predict" filename = "${path.module}/build/predictions.zip" source_code_hash = data.archive_file.predictions.output_base64sha256 publish = true memory_size = 256 role = aws_iam_role.IAMRole5.arn runtime = "python3.7" timeout = 10 tracing_config { mode = "Active" } environment { variables = { "ES" = "es.${local.domain_name}" } } layers = [ "arn:aws:lambda:us-east-1:${data.aws_caller_identity.current.account_id}:layer:xray-python:1", "arn:aws:lambda:us-east-1:${data.aws_caller_identity.current.account_id}:layer:iot:3" ] } resource "aws_lambda_function" "get_telem" { function_name = "get_telem" handler = "lambda_function.get_telem" filename = "${path.module}/build/query.zip" source_code_hash = data.archive_file.query.output_base64sha256 publish = true memory_size = 256 role = aws_iam_role.IAMRole5.arn runtime = "python3.7" timeout = 30 tracing_config { mode = "Active" } environment { variables = { "ES" = "es.${local.domain_name}" } } layers = [ "arn:aws:lambda:us-east-1:${data.aws_caller_identity.current.account_id}:layer:xray-python:1", "arn:aws:lambda:us-east-1:${data.aws_caller_identity.current.account_id}:layer:iot:3" ] } resource "aws_lambda_function" "sign_socket" { function_name = "sign-websocket" handler = "lambda_function.lambda_handler" filename = "${path.module}/build/sign_socket.zip" source_code_hash = data.archive_file.sign_socket.output_base64sha256 publish = true memory_size = 128 role = aws_iam_role.sign_socket.arn runtime = "python3.7" timeout = 10 tracing_config { mode = "Active" } environment { variables = { "IOT_ENDPOINT" = data.aws_iot_endpoint.endpoint.endpoint_address } } layers = [ "arn:aws:lambda:us-east-1:${data.aws_caller_identity.current.account_id}:layer:xray-python:1", "arn:aws:lambda:us-east-1:${data.aws_caller_identity.current.account_id}:layer:iot:3" ] } resource "aws_lambda_function" "history" { function_name = "history" handler = "lambda_function.history" filename = "${path.module}/build/history.zip" source_code_hash = data.archive_file.history.output_base64sha256 publish = true memory_size = 1024 role = aws_iam_role.history.arn runtime = "python3.7" timeout = 30 tracing_config { mode = "Active" } layers = [ "arn:aws:lambda:us-east-1:${data.aws_caller_identity.current.account_id}:layer:xray-python:1", "arn:aws:lambda:us-east-1:${data.aws_caller_identity.current.account_id}:layer:iot:3" ] } resource "aws_lambda_permission" "sign_socket" { action = "lambda:InvokeFunction" function_name = aws_lambda_function.sign_socket.arn principal = "apigateway.amazonaws.com" source_arn = "arn:aws:execute-api:us-east-1:${data.aws_caller_identity.current.account_id}:${aws_apigatewayv2_api.ApiGatewayV2Api.id}/*/*/sondes/websocket" } resource "aws_lambda_permission" "history" { action = "lambda:InvokeFunction" function_name = aws_lambda_function.history.arn principal = "apigateway.amazonaws.com" source_arn = "arn:aws:execute-api:us-east-1:${data.aws_caller_identity.current.account_id}:${aws_apigatewayv2_api.ApiGatewayV2Api.id}/*/*/sonde/{serial}" } resource "aws_lambda_permission" "get_sondes" { action = "lambda:InvokeFunction" function_name = aws_lambda_function.get_sondes.arn principal = "apigateway.amazonaws.com" source_arn = "arn:aws:execute-api:us-east-1:${data.aws_caller_identity.current.account_id}:${aws_apigatewayv2_api.ApiGatewayV2Api.id}/*/*/sondes" } resource "aws_lambda_permission" "listeners" { action = "lambda:InvokeFunction" function_name = aws_lambda_function.listeners.arn principal = "apigateway.amazonaws.com" source_arn = "arn:aws:execute-api:us-east-1:${data.aws_caller_identity.current.account_id}:${aws_apigatewayv2_api.ApiGatewayV2Api.id}/*/*/listeners" } resource "aws_lambda_permission" "datanew" { action = "lambda:InvokeFunction" function_name = aws_lambda_function.datanew.arn principal = "apigateway.amazonaws.com" source_arn = "arn:aws:execute-api:us-east-1:${data.aws_caller_identity.current.account_id}:${aws_apigatewayv2_api.ApiGatewayV2Api.id}/*/*/datanew" } resource "aws_lambda_permission" "predictions" { action = "lambda:InvokeFunction" function_name = aws_lambda_function.predictions.arn principal = "apigateway.amazonaws.com" source_arn = "arn:aws:execute-api:us-east-1:${data.aws_caller_identity.current.account_id}:${aws_apigatewayv2_api.ApiGatewayV2Api.id}/*/*/predictions" } resource "aws_lambda_permission" "get_telem" { action = "lambda:InvokeFunction" function_name = aws_lambda_function.get_telem.arn principal = "apigateway.amazonaws.com" source_arn = "arn:aws:execute-api:us-east-1:${data.aws_caller_identity.current.account_id}:${aws_apigatewayv2_api.ApiGatewayV2Api.id}/*/*/sondes/telemetry" } resource "aws_lambda_permission" "LambdaPermission2" { action = "lambda:InvokeFunction" function_name = aws_lambda_function.LambdaFunction.arn principal = "apigateway.amazonaws.com" source_arn = "arn:aws:execute-api:us-east-1:${data.aws_caller_identity.current.account_id}:${aws_apigatewayv2_api.ApiGatewayV2Api.id}/*/*/sondes/telemetry" } resource "aws_lambda_layer_version" "LambdaLayerVersion2" { compatible_runtimes = [ "python3.8" ] layer_name = "iot" s3_bucket = "sondehub-lambda-layers" s3_key = "iot.zip" source_code_hash = "sHyE9vXk+BzFphPe8evfiL79fcxsSEYVfpbTVi2IwH0=" } resource "aws_lambda_layer_version" "LambdaLayerVersion4" { compatible_runtimes = [ "python3.8" ] layer_name = "xray-python" s3_bucket = "sondehub-lambda-layers" s3_key = "xray-python.zip" source_code_hash = "ta4o2brS2ZRAeWhZjqrm6MhOc3RlYNgkOuD4dxSonEc=" } resource "aws_s3_bucket" "S3Bucket" { bucket = "sondehub-lambda-layers" } resource "aws_cloudwatch_log_group" "LogsLogGroup" { name = "/aws/lambda/sonde-api-to-iot-core" } resource "aws_apigatewayv2_api" "ApiGatewayV2Api" { name = "sondehub-v2" disable_execute_api_endpoint = true api_key_selection_expression = "$request.header.x-api-key" protocol_type = "HTTP" route_selection_expression = "$request.method $request.path" cors_configuration { allow_credentials = false allow_headers = [ "*", ] allow_methods = [ "*", ] allow_origins = [ "*", ] expose_headers = [] max_age = 0 } } resource "aws_apigatewayv2_stage" "ApiGatewayV2Stage" { name = "$default" api_id = aws_apigatewayv2_api.ApiGatewayV2Api.id default_route_settings { detailed_metrics_enabled = false } auto_deploy = true lifecycle { ignore_changes = [deployment_id] } } resource "aws_apigatewayv2_stage" "ApiGatewayV2Stage2" { name = "prod" api_id = aws_apigatewayv2_api.ApiGatewayV2Api.id deployment_id = aws_apigatewayv2_deployment.ApiGatewayV2Deployment4.id default_route_settings { detailed_metrics_enabled = false } } resource "aws_apigatewayv2_deployment" "ApiGatewayV2Deployment3" { api_id = aws_apigatewayv2_api.ApiGatewayV2Api.id } resource "aws_apigatewayv2_deployment" "ApiGatewayV2Deployment4" { api_id = aws_apigatewayv2_api.ApiGatewayV2Api.id } resource "aws_apigatewayv2_route" "ApiGatewayV2Route" { api_id = aws_apigatewayv2_api.ApiGatewayV2Api.id api_key_required = false authorization_type = "NONE" route_key = "PUT /sondes/telemetry" target = "integrations/${aws_apigatewayv2_integration.ApiGatewayV2Integration.id}" } resource "aws_apigatewayv2_route" "sign_socket" { api_id = aws_apigatewayv2_api.ApiGatewayV2Api.id api_key_required = false authorization_type = "NONE" route_key = "GET /sondes/websocket" target = "integrations/${aws_apigatewayv2_integration.sign_socket.id}" } resource "aws_apigatewayv2_route" "history" { api_id = aws_apigatewayv2_api.ApiGatewayV2Api.id api_key_required = false authorization_type = "NONE" route_key = "GET /sonde/{serial}" target = "integrations/${aws_apigatewayv2_integration.history.id}" } resource "aws_apigatewayv2_route" "get_sondes" { api_id = aws_apigatewayv2_api.ApiGatewayV2Api.id api_key_required = false authorization_type = "NONE" route_key = "GET /sondes" target = "integrations/${aws_apigatewayv2_integration.get_sondes.id}" } resource "aws_apigatewayv2_route" "listeners" { api_id = aws_apigatewayv2_api.ApiGatewayV2Api.id api_key_required = false authorization_type = "NONE" route_key = "GET /listeners" target = "integrations/${aws_apigatewayv2_integration.listeners.id}" } resource "aws_apigatewayv2_route" "datanew" { api_id = aws_apigatewayv2_api.ApiGatewayV2Api.id api_key_required = false authorization_type = "NONE" route_key = "GET /datanew" target = "integrations/${aws_apigatewayv2_integration.datanew.id}" } resource "aws_apigatewayv2_route" "predictions" { api_id = aws_apigatewayv2_api.ApiGatewayV2Api.id api_key_required = false authorization_type = "NONE" route_key = "GET /predictions" target = "integrations/${aws_apigatewayv2_integration.predictions.id}" } resource "aws_apigatewayv2_route" "get_telem" { api_id = aws_apigatewayv2_api.ApiGatewayV2Api.id api_key_required = false authorization_type = "NONE" route_key = "GET /sondes/telemetry" target = "integrations/${aws_apigatewayv2_integration.get_telem.id}" } resource "aws_apigatewayv2_integration" "sign_socket" { api_id = aws_apigatewayv2_api.ApiGatewayV2Api.id connection_type = "INTERNET" integration_method = "POST" integration_type = "AWS_PROXY" integration_uri = aws_lambda_function.sign_socket.arn timeout_milliseconds = 30000 payload_format_version = "2.0" } resource "aws_apigatewayv2_integration" "history" { api_id = aws_apigatewayv2_api.ApiGatewayV2Api.id connection_type = "INTERNET" integration_method = "POST" integration_type = "AWS_PROXY" integration_uri = aws_lambda_function.history.arn timeout_milliseconds = 30000 payload_format_version = "2.0" } resource "aws_apigatewayv2_integration" "get_sondes" { api_id = aws_apigatewayv2_api.ApiGatewayV2Api.id connection_type = "INTERNET" integration_method = "POST" integration_type = "AWS_PROXY" integration_uri = aws_lambda_function.get_sondes.arn timeout_milliseconds = 30000 payload_format_version = "2.0" } resource "aws_apigatewayv2_integration" "listeners" { api_id = aws_apigatewayv2_api.ApiGatewayV2Api.id connection_type = "INTERNET" integration_method = "POST" integration_type = "AWS_PROXY" integration_uri = aws_lambda_function.listeners.arn timeout_milliseconds = 30000 payload_format_version = "2.0" } resource "aws_apigatewayv2_integration" "datanew" { api_id = aws_apigatewayv2_api.ApiGatewayV2Api.id connection_type = "INTERNET" integration_method = "POST" integration_type = "AWS_PROXY" integration_uri = aws_lambda_function.datanew.arn timeout_milliseconds = 30000 payload_format_version = "2.0" } resource "aws_apigatewayv2_integration" "predictions" { api_id = aws_apigatewayv2_api.ApiGatewayV2Api.id connection_type = "INTERNET" integration_method = "POST" integration_type = "AWS_PROXY" integration_uri = aws_lambda_function.predictions.arn timeout_milliseconds = 30000 payload_format_version = "2.0" } resource "aws_apigatewayv2_integration" "get_telem" { api_id = aws_apigatewayv2_api.ApiGatewayV2Api.id connection_type = "INTERNET" integration_method = "POST" integration_type = "AWS_PROXY" integration_uri = aws_lambda_function.get_telem.arn timeout_milliseconds = 30000 payload_format_version = "2.0" } resource "aws_apigatewayv2_integration" "ApiGatewayV2Integration" { api_id = aws_apigatewayv2_api.ApiGatewayV2Api.id connection_type = "INTERNET" integration_method = "POST" integration_type = "AWS_PROXY" integration_uri = aws_lambda_function.LambdaFunction.arn timeout_milliseconds = 30000 payload_format_version = "2.0" } resource "aws_apigatewayv2_api_mapping" "ApiGatewayV2ApiMapping" { api_id = aws_apigatewayv2_api.ApiGatewayV2Api.id domain_name = aws_apigatewayv2_domain_name.ApiGatewayV2DomainName.id stage = "$default" api_mapping_key = "" } resource "aws_apigatewayv2_domain_name" "ApiGatewayV2DomainName" { domain_name = "api-raw.${local.domain_name}" domain_name_configuration { certificate_arn = aws_acm_certificate_validation.CertificateManagerCertificate.certificate_arn endpoint_type = "REGIONAL" security_policy = "TLS_1_2" } } resource "aws_acm_certificate" "CertificateManagerCertificate" { domain_name = local.domain_name subject_alternative_names = [ "*.${local.domain_name}" ] validation_method = "DNS" } resource "aws_elasticsearch_domain" "ElasticsearchDomain" { domain_name = "sondes-v2" elasticsearch_version = "7.9" cluster_config { dedicated_master_count = 3 dedicated_master_enabled = false dedicated_master_type = "t3.small.elasticsearch" instance_count = 1 instance_type = "r5.large.elasticsearch" zone_awareness_enabled = false } cognito_options { enabled = true identity_pool_id = aws_cognito_identity_pool.CognitoIdentityPool.id role_arn = aws_iam_role.IAMRole3.arn user_pool_id = aws_cognito_user_pool.CognitoUserPool.id } access_policies = <