From c0c08b15840014b0ba772e3848446c5a2b2012014ba45ea2e617b2c7ba7aba80 Mon Sep 17 00:00:00 2001 From: xpk Date: Sun, 15 Mar 2026 13:02:59 +0800 Subject: [PATCH] feat: example of apigateway rest api --- RestApi/README.md | 53 ++++++++++++++++++++++ RestApi/function.py | 45 +++++++++++++++++++ RestApi/function.zip | Bin 0 -> 666 bytes RestApi/main.tf | 90 +++++++++++++++++++++++++++++++++++++ RestApi/outputs.tf | 3 ++ RestApi/provider.tf | 27 +++++++++++ RestApi/restapi-oas30.json | 57 +++++++++++++++++++++++ RestApi/variables.tf | 7 +++ 8 files changed, 282 insertions(+) create mode 100644 RestApi/README.md create mode 100644 RestApi/function.py create mode 100644 RestApi/function.zip create mode 100644 RestApi/main.tf create mode 100644 RestApi/outputs.tf create mode 100644 RestApi/provider.tf create mode 100644 RestApi/restapi-oas30.json create mode 100644 RestApi/variables.tf diff --git a/RestApi/README.md b/RestApi/README.md new file mode 100644 index 0000000..254ceef --- /dev/null +++ b/RestApi/README.md @@ -0,0 +1,53 @@ + +## Requirements + +| Name | Version | +|------|---------| +| terraform | ~> 1.13.0 | +| aws | >= 5.0 | + +## Providers + +| Name | Version | +|------|---------| +| archive | 2.7.1 | +| aws | 6.36.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| LambdaRole | ../modules/security_identity_compliance/iam-role-v2 | n/a | + +## Resources + +| Name | Type | +|------|------| +| [aws_api_gateway_deployment.HashWebApp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_deployment) | resource | +| [aws_api_gateway_rest_api.HashWebApp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_rest_api) | resource | +| [aws_api_gateway_stage.test](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_stage) | resource | +| [aws_lambda_function.HashWebApp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function) | resource | +| [aws_lambda_permission.HashWebApp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission) | resource | +| [archive_file.HashWebApp](https://registry.terraform.io/providers/hashicorp/archive/latest/docs/data-sources/file) | data source | +| [aws_caller_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| application | n/a | `any` | n/a | yes | +| aws-region | n/a | `any` | n/a | yes | +| environment | n/a | `any` | n/a | yes | +| owner | n/a | `any` | n/a | yes | +| project | n/a | `any` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| TestUrl | n/a | +| last-updated | n/a | + +--- +## Authorship +This module was developed by xpk. \ No newline at end of file diff --git a/RestApi/function.py b/RestApi/function.py new file mode 100644 index 0000000..2269aae --- /dev/null +++ b/RestApi/function.py @@ -0,0 +1,45 @@ +# this function takes in "input_string" and return the sha256 hash of that string + +import json +import hashlib + +def lambda_handler(event, context): + # Get input_string from query params, body, or path + input_string = None + + # Try query parameters first + if 'queryStringParameters' in event and event['queryStringParameters']: + input_string = event['queryStringParameters'].get('input_string') + + # Try request body + if not input_string and 'body' in event: + try: + body = json.loads(event['body']) if isinstance(event['body'], str) else event['body'] + input_string = body.get('input_string') + except: + input_string = event['body'] + + # Try path parameter as fallback + if not input_string and 'pathParameters' in event: + input_string = event['pathParameters'].get('input_string', event['pathParameters'].get('proxy')) + + if not input_string: + return { + 'statusCode': 400, + 'headers': {'Content-Type': 'application/json'}, + 'body': json.dumps({'error': 'input_string required'}) + } + + # Calculate SHA256 + sha256_hash = hashlib.sha256(input_string.encode('utf-8')).hexdigest() + + return { + 'statusCode': 200, + 'headers': { + 'Content-Type': 'application/json' + }, + 'body': json.dumps({ + 'input_string': input_string, + 'sha256': sha256_hash + }) + } diff --git a/RestApi/function.zip b/RestApi/function.zip new file mode 100644 index 0000000000000000000000000000000000000000000000000000000000000000..d963343ed1bef219dc554477a36720d783ea9e68491609e8e9d8df34cabb309e GIT binary patch literal 666 zcmWIWW@Zs#-~d7f2E{HQ0SDYbR$6IZa!F=>o?by^OUS{z+g1X5UTce;Y!q7XgY}?y z0LO=g9~Rn*vQCN?l*lej-8j*+HAYm7=YO^1sdX;lr8Sd|&b)hbrf7a+_R|HgN=}~2 zNT^X}_d48rDff8x@vxU~c^6De=(Jwln9UUa`0!)lD=)GPL<@R(m`yydtTUbVaJliX zKW_?qZ>e-H;gY(OtFkWp$|l9xm-H{qan3ZkJa4lSBg2kX{UZib8K#`K+xw4Nj~UmNV3nd%EH6ar=MI8#nN1 z>Pj_VTB_c;@KE-%XpQAtlCP*9ZQps`iaAThu|%MBN5KK*wC6UDGvk9!AKfZdxVh7@ zj`3Iwqio0x6KS3M>ux`-EDo%n@|ZPW@4z3cZClgscfS$&+;brC(97M{F6TIopO_mh z>A9%k|NHl!f9z13vM-jAedDM5)1K{~zVB;7WA5!0LVwx+&yQy)+@rwW+?{BdvOPPt zN+4zJ#(A=qkIaK3Cq1%YtF%$yd}!Glsg$PY>*veM%|G<|;j4)D)yb9nB*hNjE{Upk z++{K;=we~%ch8iYi!Kxhf1y@3WYFaQAc#30H5 literal 0 HcmV?d00001 diff --git a/RestApi/main.tf b/RestApi/main.tf new file mode 100644 index 0000000..30a83df --- /dev/null +++ b/RestApi/main.tf @@ -0,0 +1,90 @@ +# Lambda function and role + +module "LambdaRole" { + source = "../modules/security_identity_compliance/iam-role-v2" + role-name = "AWSLambdaBasicExecutionRole-HashWebApp" + description = "Lambda execution role for HashWebApp" + create-instance-profile = false + trusted-entity = "lambda.amazonaws.com" + policies = { + AWSLambdaBasicExecutionRole = { + description = "AWSLambdaBasicExecutionRole for HashWebApp" + policy = jsonencode( + { + "Version" : "2012-10-17", + "Statement" : [ + { + "Effect" : "Allow", + "Action" : "logs:CreateLogGroup", + "Resource" : "arn:aws:logs:${var.aws-region}:${data.aws_caller_identity.this.account_id}:*" + }, + { + "Effect" : "Allow", + "Action" : [ + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Resource" : [ + "arn:aws:logs:${var.aws-region}:${data.aws_caller_identity.this.account_id}:log-group:/aws/lambda/HashWebApp:*" + ] + } + ] + } + ) + } + } +} + +data "archive_file" "HashWebApp" { + type = "zip" + source_file = "${path.module}/function.py" + output_path = "${path.module}/function.zip" +} + +resource "aws_lambda_function" "HashWebApp" { + filename = data.archive_file.HashWebApp.output_path + function_name = "HashWebApp" + role = module.LambdaRole.role-arn + handler = "function.lambda_handler" + code_sha256 = data.archive_file.HashWebApp.output_base64sha256 + architectures = ["arm64"] + + runtime = "python3.14" +} + +resource "aws_lambda_permission" "HashWebApp" { + statement_id = "AllowExecutionFromApiGateway" + action = "lambda:InvokeFunction" + function_name = aws_lambda_function.HashWebApp.function_name + principal = "apigateway.amazonaws.com" + source_arn = "${aws_api_gateway_rest_api.HashWebApp.execution_arn}/*/*/*" +} + +# rest api +resource "aws_api_gateway_rest_api" "HashWebApp" { + body = file("${path.module}/restapi-oas30.json") + + name = "HashWebApp" + + endpoint_configuration { + types = ["REGIONAL"] + } +} + +resource "aws_api_gateway_deployment" "HashWebApp" { + rest_api_id = aws_api_gateway_rest_api.HashWebApp.id + + triggers = { + redeployment = sha1(jsonencode(aws_api_gateway_rest_api.HashWebApp.body)) + } + + lifecycle { + create_before_destroy = true + } +} + +resource "aws_api_gateway_stage" "test" { + deployment_id = aws_api_gateway_deployment.HashWebApp.id + rest_api_id = aws_api_gateway_rest_api.HashWebApp.id + stage_name = "test" +} \ No newline at end of file diff --git a/RestApi/outputs.tf b/RestApi/outputs.tf new file mode 100644 index 0000000..33d8df9 --- /dev/null +++ b/RestApi/outputs.tf @@ -0,0 +1,3 @@ +output "TestUrl" { + value = "${aws_api_gateway_stage.test.invoke_url}/?input_string=TestString" +} \ No newline at end of file diff --git a/RestApi/provider.tf b/RestApi/provider.tf new file mode 100644 index 0000000..144b487 --- /dev/null +++ b/RestApi/provider.tf @@ -0,0 +1,27 @@ +provider "aws" { + region = var.aws-region + + default_tags { + tags = { + Environment = var.environment + Project = var.project + Application = var.application + Owner = var.owner + TerraformDir = "${reverse(split("/", path.cwd))[1]}/${reverse(split("/", path.cwd))[0]}" + } + } +} + +output "last-updated" { + value = timestamp() +} + +terraform { + required_version = "~> 1.13.0" + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.0" + } + } +} \ No newline at end of file diff --git a/RestApi/restapi-oas30.json b/RestApi/restapi-oas30.json new file mode 100644 index 0000000..5b79043 --- /dev/null +++ b/RestApi/restapi-oas30.json @@ -0,0 +1,57 @@ +{ + "openapi" : "3.0.1", + "info" : { + "title" : "HelloWorld", + "version" : "2026-03-15T04:22:58Z" + }, + "servers" : [ { + "url" : "https://8cti482053.execute-api.ap-east-1.amazonaws.com/{basePath}", + "variables" : { + "basePath" : { + "default" : "test" + } + } + } ], + "paths" : { + "/" : { + "x-amazon-apigateway-any-method" : { + "responses" : { + "200" : { + "description" : "200 response", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Empty" + } + } + } + } + }, + "x-amazon-apigateway-integration" : { + "type" : "aws_proxy", + "uri" : "arn:aws:apigateway:ap-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-east-1:040216112220:function:HashWebApp/invocations", + "httpMethod" : "POST", + "responses" : { + "default" : { + "statusCode" : "200" + } + }, + "passthroughBehavior" : "when_no_match", + "timeoutInMillis" : 10000, + "responseTransferMode" : "BUFFERED", + "contentHandling" : "CONVERT_TO_TEXT" + } + } + } + }, + "components" : { + "schemas" : { + "Empty" : { + "title" : "Empty Schema", + "type" : "object" + } + } + }, + "x-amazon-apigateway-security-policy" : "SecurityPolicy_TLS13_1_3_2025_09", + "x-amazon-apigateway-endpoint-access-mode" : "BASIC" +} \ No newline at end of file diff --git a/RestApi/variables.tf b/RestApi/variables.tf new file mode 100644 index 0000000..9489f57 --- /dev/null +++ b/RestApi/variables.tf @@ -0,0 +1,7 @@ +variable "aws-region" {} +variable "environment" {} +variable "project" {} +variable "application" {} +variable "owner" {} + +data "aws_caller_identity" "this" {} \ No newline at end of file