From de3cb9a9427b70ea6ad4bac04f8ed76f9dd2822e Mon Sep 17 00:00:00 2001 From: John Ajera <37360952+jajera@users.noreply.github.com> Date: Mon, 8 Sep 2025 13:18:25 +0000 Subject: [PATCH] docs: add lambda vpc example adds example that demonstrate lambda in vpc --- examples/vpc/.terraform.lock.hcl | 101 ++++++++ examples/vpc/README.md | 41 +++ .../external/lambda/vpc-connectivity-test.js | 120 +++++++++ .../vpc/external/vpc-connectivity-test.zip | Bin 0 -> 1333 bytes examples/vpc/main.tf | 238 ++++++++++++++++++ main.tf | 43 +++- outputs.tf | 10 + variables.tf | 30 +++ 8 files changed, 582 insertions(+), 1 deletion(-) create mode 100644 examples/vpc/.terraform.lock.hcl create mode 100644 examples/vpc/README.md create mode 100644 examples/vpc/external/lambda/vpc-connectivity-test.js create mode 100644 examples/vpc/external/vpc-connectivity-test.zip create mode 100644 examples/vpc/main.tf diff --git a/examples/vpc/.terraform.lock.hcl b/examples/vpc/.terraform.lock.hcl new file mode 100644 index 0000000..cc6230d --- /dev/null +++ b/examples/vpc/.terraform.lock.hcl @@ -0,0 +1,101 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/archive" { + version = "2.7.1" + hashes = [ + "h1:62VrkalDPMKB9zerCBS4iKTbvxejwnAWn/XXYZZQWD4=", + "zh:19881bb356a4a656a865f48aee70c0b8a03c35951b7799b6113883f67f196e8e", + "zh:2fcfbf6318dd514863268b09bbe19bfc958339c636bcbcc3664b45f2b8bf5cc6", + "zh:3323ab9a504ce0a115c28e64d0739369fe85151291a2ce480d51ccbb0c381ac5", + "zh:362674746fb3da3ab9bd4e70c75a3cdd9801a6cf258991102e2c46669cf68e19", + "zh:7140a46d748fdd12212161445c46bbbf30a3f4586c6ac97dd497f0c2565fe949", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:875e6ce78b10f73b1efc849bfcc7af3a28c83a52f878f503bb22776f71d79521", + "zh:b872c6ed24e38428d817ebfb214da69ea7eefc2c38e5a774db2ccd58e54d3a22", + "zh:cd6a44f731c1633ae5d37662af86e7b01ae4c96eb8b04144255824c3f350392d", + "zh:e0600f5e8da12710b0c52d6df0ba147a5486427c1a2cc78f31eea37a47ee1b07", + "zh:f21b2e2563bbb1e44e73557bcd6cdbc1ceb369d471049c40eb56cb84b6317a60", + "zh:f752829eba1cc04a479cf7ae7271526b402e206d5bcf1fcce9f535de5ff9e4e6", + ] +} + +provider "registry.terraform.io/hashicorp/aws" { + version = "6.12.0" + constraints = ">= 6.0.0" + hashes = [ + "h1:8u90EMle+I3Auh4f/LPP6fEfRsAF6xCFnUZF4b7ngEs=", + "zh:054bcbf13c6ac9ddd2247876f82f9b56493e2f71d8c88baeec142386a395165d", + "zh:195489f16ad5621db2cec80be997d33060462a3b8d442c890bef3eceba34fa4d", + "zh:3461ef14904ab7de246296e44d24c042f3190e6bead3d7ce1d9fda63dcb0f047", + "zh:44517a0035996431e4127f45db5a84f53ce80730eae35629eda3101709df1e5c", + "zh:4b0374abaa6b9a9debed563380cc944873e4f30771dd1da7b9e812a49bf485e3", + "zh:531468b99465bd98a89a4ce2f1a30168dfadf6edb57f7836df8a977a2c4f9804", + "zh:6a95ed7b4852174aa748d3412bff3d45e4d7420d12659f981c3d9f4a1a59a35f", + "zh:88c2d21af1e64eed4a13dbb85590c66a519f3ecc54b72875d4bb6326f3ef84e7", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:a8b648470bb5df098e56b1ec5c6a39e0bbb7b496b23a19ea9f494bf48d4a122a", + "zh:b23fb13efdb527677db546bc92aeb2bdf64ff3f480188841f2bfdfa7d3d907c1", + "zh:be5858a1951ae5f5a9c388949c3e3c66a3375f684fb79b06b1d1db7a9703b18e", + "zh:c368e03a7c922493daf4c7348faafc45f455225815ef218b5491c46cea5f76b7", + "zh:e31e75d5d19b8ac08aa01be7e78207966e1faa3b82ed9fe3acfdc2d806be924c", + "zh:ea84182343b5fd9252a6fae41e844eed4fdc3311473a753b09f06e49ec0e7853", + ] +} + +provider "registry.terraform.io/hashicorp/http" { + version = "3.5.0" + hashes = [ + "h1:8bUoPwS4hahOvzCBj6b04ObLVFXCEmEN8T/5eOHmWOM=", + "zh:047c5b4920751b13425efe0d011b3a23a3be97d02d9c0e3c60985521c9c456b7", + "zh:157866f700470207561f6d032d344916b82268ecd0cf8174fb11c0674c8d0736", + "zh:1973eb9383b0d83dd4fd5e662f0f16de837d072b64a6b7cd703410d730499476", + "zh:212f833a4e6d020840672f6f88273d62a564f44acb0c857b5961cdb3bbc14c90", + "zh:2c8034bc039fffaa1d4965ca02a8c6d57301e5fa9fff4773e684b46e3f78e76a", + "zh:5df353fc5b2dd31577def9cc1a4ebf0c9a9c2699d223c6b02087a3089c74a1c6", + "zh:672083810d4185076c81b16ad13d1224b9e6ea7f4850951d2ab8d30fa6e41f08", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:7b4200f18abdbe39904b03537e1a78f21ebafe60f1c861a44387d314fda69da6", + "zh:843feacacd86baed820f81a6c9f7bd32cf302db3d7a0f39e87976ebc7a7cc2ee", + "zh:a9ea5096ab91aab260b22e4251c05f08dad2ed77e43e5e4fadcdfd87f2c78926", + "zh:d02b288922811739059e90184c7f76d45d07d3a77cc48d0b15fd3db14e928623", + ] +} + +provider "registry.terraform.io/hashicorp/null" { + version = "3.2.4" + hashes = [ + "h1:hkf5w5B6q8e2A42ND2CjAvgvSN3puAosDmOJb3zCVQM=", + "zh:59f6b52ab4ff35739647f9509ee6d93d7c032985d9f8c6237d1f8a59471bbbe2", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:795c897119ff082133150121d39ff26cb5f89a730a2c8c26f3a9c1abf81a9c43", + "zh:7b9c7b16f118fbc2b05a983817b8ce2f86df125857966ad356353baf4bff5c0a", + "zh:85e33ab43e0e1726e5f97a874b8e24820b6565ff8076523cc2922ba671492991", + "zh:9d32ac3619cfc93eb3c4f423492a8e0f79db05fec58e449dee9b2d5873d5f69f", + "zh:9e15c3c9dd8e0d1e3731841d44c34571b6c97f5b95e8296a45318b94e5287a6e", + "zh:b4c2ab35d1b7696c30b64bf2c0f3a62329107bd1a9121ce70683dec58af19615", + "zh:c43723e8cc65bcdf5e0c92581dcbbdcbdcf18b8d2037406a5f2033b1e22de442", + "zh:ceb5495d9c31bfb299d246ab333f08c7fb0d67a4f82681fbf47f2a21c3e11ab5", + "zh:e171026b3659305c558d9804062762d168f50ba02b88b231d20ec99578a6233f", + "zh:ed0fe2acdb61330b01841fa790be00ec6beaac91d41f311fb8254f74eb6a711f", + ] +} + +provider "registry.terraform.io/hashicorp/random" { + version = "3.7.2" + hashes = [ + "h1:356j/3XnXEKr9nyicLUufzoF4Yr6hRy481KIxRVpK0c=", + "zh:14829603a32e4bc4d05062f059e545a91e27ff033756b48afbae6b3c835f508f", + "zh:1527fb07d9fea400d70e9e6eb4a2b918d5060d604749b6f1c361518e7da546dc", + "zh:1e86bcd7ebec85ba336b423ba1db046aeaa3c0e5f921039b3f1a6fc2f978feab", + "zh:24536dec8bde66753f4b4030b8f3ef43c196d69cccbea1c382d01b222478c7a3", + "zh:29f1786486759fad9b0ce4fdfbbfece9343ad47cd50119045075e05afe49d212", + "zh:4d701e978c2dd8604ba1ce962b047607701e65c078cb22e97171513e9e57491f", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:7b8434212eef0f8c83f5a90c6d76feaf850f6502b61b53c329e85b3b281cba34", + "zh:ac8a23c212258b7976e1621275e3af7099e7e4a3d4478cf8d5d2a27f3bc3e967", + "zh:b516ca74431f3df4c6cf90ddcdb4042c626e026317a33c53f0b445a3d93b720d", + "zh:dc76e4326aec2490c1600d6871a95e78f9050f9ce427c71707ea412a2f2f1a62", + "zh:eac7b63e86c749c7d48f527671c7aee5b4e26c10be6ad7232d6860167f99dbb0", + ] +} diff --git a/examples/vpc/README.md b/examples/vpc/README.md new file mode 100644 index 0000000..543232f --- /dev/null +++ b/examples/vpc/README.md @@ -0,0 +1,41 @@ +# VPC Lambda Example + +Example demonstrating Lambda deployment within a VPC using the `terraform-aws-lambda-versioned` module. + +## What it creates + +- VPC with public and private subnets across 3 AZs +- NAT Gateway for outbound internet access +- Lambda function deployed in private subnets +- Function URL for HTTP access +- Jumphost for VPC connectivity testing + +## Usage + +```bash +terraform init +terraform plan +terraform apply +``` + +## Testing + +```bash +# Get Lambda Function URL +terraform output lambda + +# Test via Function URL +curl $(terraform output -raw lambda_function_url) +``` + +## VPC Access + +- **Function URL**: Internet-accessible (public) +- **Lambda in VPC**: Private subnets, internet via NAT Gateway +- **VPC-only access**: Set `create_function_url = false` + +## Cleanup + +```bash +terraform destroy +``` diff --git a/examples/vpc/external/lambda/vpc-connectivity-test.js b/examples/vpc/external/lambda/vpc-connectivity-test.js new file mode 100644 index 0000000..a8c8312 --- /dev/null +++ b/examples/vpc/external/lambda/vpc-connectivity-test.js @@ -0,0 +1,120 @@ +const https = require('https'); + +/** + * VPC Connectivity Test Lambda Function + * + * This function demonstrates that a Lambda function deployed in a VPC can still + * access external APIs and services over the internet. It queries an external + * API to verify VPC configuration, NAT Gateway setup, and security group rules. + */ +module.exports.handler = async (event) => { + const version = process.env.AWS_LAMBDA_FUNCTION_VERSION; + const startTime = Date.now(); + + console.log('VPC Connectivity Test - Lambda version:', version); + console.log('Event:', JSON.stringify(event, null, 2)); + + try { + // Query an external API to demonstrate internet connectivity from VPC + const externalData = await queryExternalAPI(); + const executionTime = Date.now() - startTime; + + const response = { + statusCode: 200, + headers: { + 'Content-Type': 'application/json', + 'X-Lambda-Version': version, + 'X-Execution-Time': `${executionTime}ms` + }, + body: JSON.stringify({ + message: 'VPC Connectivity Test: Lambda successfully accessed external API from VPC', + test: 'VPC Internet Connectivity', + status: 'PASSED', + lambdaVersion: version, + executionTime: `${executionTime}ms`, + externalAPI: { + status: 'success', + data: externalData + }, + timestamp: new Date().toISOString(), + environment: { + region: process.env.AWS_REGION, + functionName: process.env.AWS_LAMBDA_FUNCTION_NAME, + memorySize: process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE + } + }) + }; + + console.log('Response:', JSON.stringify(response, null, 2)); + return response; + + } catch (error) { + console.error('Error:', error); + + return { + statusCode: 500, + headers: { + 'Content-Type': 'application/json', + 'X-Lambda-Version': version + }, + body: JSON.stringify({ + message: 'VPC Connectivity Test: Failed to access external API', + test: 'VPC Internet Connectivity', + status: 'FAILED', + lambdaVersion: version, + error: error.message, + timestamp: new Date().toISOString() + }) + }; + } +}; + +// Helper function to query external API +function queryExternalAPI() { + return new Promise((resolve, reject) => { + const options = { + hostname: 'httpbin.org', + port: 443, + path: '/json', + method: 'GET', + timeout: 5000 + }; + + const req = https.request(options, (res) => { + let data = ''; + + res.on('data', (chunk) => { + data += chunk; + }); + + res.on('end', () => { + try { + const jsonData = JSON.parse(data); + resolve({ + url: `https://${options.hostname}${options.path}`, + statusCode: res.statusCode, + response: jsonData + }); + } catch (parseError) { + resolve({ + url: `https://${options.hostname}${options.path}`, + statusCode: res.statusCode, + response: data, + parseError: parseError.message + }); + } + }); + }); + + req.on('error', (error) => { + reject(new Error(`External API request failed: ${error.message}`)); + }); + + req.on('timeout', () => { + req.destroy(); + reject(new Error('External API request timed out')); + }); + + req.end(); + }); +} diff --git a/examples/vpc/external/vpc-connectivity-test.zip b/examples/vpc/external/vpc-connectivity-test.zip new file mode 100644 index 0000000000000000000000000000000000000000..7aa8955edc32202f2f4d5a9fa5f22abc09e2c238 GIT binary patch literal 1333 zcmWIWW@Zs#-~d8&AORF8D@fK&&d>hLlt3|hsKvEb!XRi8J^71`%4jFUIz9jRY`?uQxQnp>f(S5+RIX*vD< z?{i1_Si_~tqSCpJDSWi-SKZZTS@27I@4Xjq*0864*s!C;`=Di;)xG1p8bq~tx9ik) zNyI$2xw7$rt--d{v1<~7=Q_4%KAqsRm}zgaRK(l#n<rpZS`7#|C2O!;l$!kW}C^o39HSxWP*=9O*FWm0=mZv6<_aWaW3>VRD8 z9?erZP5F;<^&}Tc9?03$B)#lotLTD&=QkIe5_>jjNB+_i#uF-=PQ5;ObH(&74gU*= z{bx>ys2f;@B$zbG7f)%` ztzEnOs(|lKjZE%hWmm6K!-Za_N+)P9lYIH>(4$TJTm$a7J)!u>&`QM*E&wlkIUt~+r&tBPvV$B_H>)KZIT_1V*<_5Zd6 z-E$&SlcK*oEUEudTD{^<&s{bDNsFW$dv;x2!<}k)S*?ZBc5Qd|^9?@UnY9IClb;&x z*pw&KykOF^79&|szwRVybKa!sr%%NjZe?I_@iE?ix$NxIgGtiMSD1Jk%UkT5Ay)i* zp4jE5HJMAwe~St1>tFgn(>H{@(1TM`1v~KQ`#?| z8_X>}?bZc>UW&h`#+7Onzp!Hb8?fxsibKJz)}{yV{}C7dlYjoPX}54sM(Ml?!GDJ@ z?w;q9zdz?o&y%P9;g0_oRa(4Ke5IYW^diT;<0{TK>%=nx-~Hfw99LYs=fAJv>R-YQ ztskBtK_%!d00`9!l4jNuTdm>%8Z5-av2vNt<6v&P(rd-Dl|NY?V`R z%$+9MbFgrg#gfKPEakGh3fUgC?Vj`h-bd|lnHp2J;_{1Eo@%gt>TufgF)#kx)A-K0 zH^Sd%T>cAen) zD@0e#&H3tV8K!r3j_1>L%)gx%W+mU45&G=#?(656EZ6*=e>#%)bRhGk+7>VGz#Y8p z$|C1HMMR95x~hzHKds>LT;6_fJ|hFe|NjBr>>O(s7jsT#VPH_^Wnc*KW@Hj!Koo7r da-gCO72qo90=!w-Kq?u5&<#l61e(sk002ycX%GMa literal 0 HcmV?d00001 diff --git a/examples/vpc/main.tf b/examples/vpc/main.tf new file mode 100644 index 0000000..4a19fbb --- /dev/null +++ b/examples/vpc/main.tf @@ -0,0 +1,238 @@ +############################################ +# Terraform & Provider Configuration +############################################ + +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 6.0.0" + } + random = { + source = "hashicorp/random" + } + } +} + +provider "aws" { + region = "ap-southeast-2" +} + +############################################ +# Data Sources +############################################ + +data "aws_region" "current" {} +data "aws_caller_identity" "current" {} + +############################################ +# Random Suffix for Resource Names +############################################ + +resource "random_string" "suffix" { + length = 4 + special = false + upper = false +} + +############################################ +# Local Variables +############################################ + +locals { + # VPC 1 Configuration + name = "cltest1" + base_name = "${local.name}-${random_string.suffix.result}" + + # Common tags + suffix = random_string.suffix.result + + tags = { + Environment = "dev" + } +} + +# Get current user's public IP +data "http" "my_public_ip" { + url = "https://checkip.amazonaws.com/" +} + +############################################ +# VPC Configuration +############################################ + +module "vpc" { + source = "cloudbuildlab/vpc/aws" + + vpc_name = local.base_name + vpc_cidr = "10.0.0.0/16" + availability_zones = ["ap-southeast-2a", "ap-southeast-2b", "ap-southeast-2c"] + + public_subnet_cidrs = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"] + private_subnet_cidrs = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"] + + # Enable Internet Gateway & NAT Gateway + create_igw = true + nat_gateway_type = "single" + + tags = local.tags +} + +############################################ +# Security Groups +############################################ + +resource "aws_security_group" "jumphost_sg" { + name = "${local.base_name}-jumphost-sg" + description = "Security group for jumphost" + vpc_id = module.vpc.vpc_id + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + tags = merge(local.tags, { + Name = "${local.base_name}-jumphost-sg" + }) +} + +resource "aws_security_group" "lambda_sg" { + name = "${local.base_name}-lambda-sg" + description = "Security group for lambda" + vpc_id = module.vpc.vpc_id + + ingress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = module.vpc.private_subnet_cidrs + } + + egress { + description = "HTTPS to AWS services" + from_port = 443 + to_port = 443 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + egress { + description = "HTTP to AWS services" + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + egress { + description = "DNS resolution" + from_port = 53 + to_port = 53 + protocol = "udp" + cidr_blocks = ["0.0.0.0/0"] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + tags = merge(local.tags, { + Name = "${local.base_name}-jumphost-sg" + }) +} + +############################################ +# Jumphosts +############################################ + +module "jumphost" { + source = "tfstack/jumphost/aws" + + name = "${local.base_name}-jumphost" + subnet_id = module.vpc.private_subnet_ids[0] + vpc_id = module.vpc.vpc_id + + create_security_group = false + vpc_security_group_ids = [aws_security_group.jumphost_sg.id] + allowed_cidr_blocks = ["${trimspace(data.http.my_public_ip.response_body)}/32"] + assign_eip = false + + user_data_extra = <<-EOT + hostname ${local.base_name}-jumphost + yum install -y mtr nc + EOT + + tags = local.tags +} + +############################################ +# Lambda Function +############################################ + +# Convert code to zip file +data "archive_file" "vpc_test" { + type = "zip" + output_path = "${path.module}/external/vpc-connectivity-test.zip" + + source { + content = file("${path.module}/external/lambda/vpc-connectivity-test.js") + filename = "vpc-connectivity-test.js" + } +} + +module "s3_bucket" { + source = "tfstack/s3/aws" + + bucket_name = "lambda-zips" + bucket_suffix = random_string.suffix.result + enable_versioning = true + + tags = local.tags +} + +resource "aws_s3_object" "vpc_test" { + bucket = module.s3_bucket.bucket_id + key = "vpc-connectivity-test.zip" + source = data.archive_file.vpc_test.output_path + etag = data.archive_file.vpc_test.output_md5 +} + +module "lambda" { + source = "../../" + + function_name = "vpc-connectivity-test-${random_string.suffix.result}" + handler = "vpc-connectivity-test.handler" + runtime = "nodejs20.x" + + s3_bucket = module.s3_bucket.bucket_id + s3_key = aws_s3_object.vpc_test.key + s3_object_version = aws_s3_object.vpc_test.version_id + + policy_arns = [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + + environment_variables = { + ENVIRONMENT = local.tags.Environment + } + + vpc_config = { + vpc_id = module.vpc.vpc_id + subnet_ids = module.vpc.private_subnet_ids + security_group_ids = [aws_security_group.lambda_sg.id] + } + + # Enable Function URL for HTTP access + create_function_url = true + + tags = local.tags +} +output "lambda" { + value = module.lambda +} diff --git a/main.tf b/main.tf index 54b23d1..b3952c7 100644 --- a/main.tf +++ b/main.tf @@ -1,3 +1,6 @@ +data "aws_region" "current" {} +data "aws_caller_identity" "current" {} + locals { # Validate package type specific requirements validate_zip_package = var.package_type == "Zip" ? ( @@ -11,6 +14,12 @@ locals { validate_image_package = var.package_type == "Image" ? ( var.image_uri != null ) : true + + # Generate VPC policy IDs when VPC config is provided (EC2 condition keys expect raw IDs, not ARNs) + vpc_policy_subnet_ids = var.vpc_config != null ? var.vpc_config.subnet_ids : [] + vpc_policy_security_group_ids = var.vpc_config != null ? var.vpc_config.security_group_ids : [] + + vpc_arn = var.vpc_config != null ? "arn:aws:ec2:${data.aws_region.current.id}:${data.aws_caller_identity.current.account_id}:vpc/${var.vpc_config.vpc_id}" : null } resource "aws_iam_role" "lambda" { @@ -39,6 +48,13 @@ resource "aws_iam_role_policy_attachment" "lambda_policies" { policy_arn = each.value } +resource "aws_iam_role_policy_attachment" "lambda_vpc_access" { + count = var.vpc_config != null ? 1 : 0 + + role = aws_iam_role.lambda.name + policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaENIManagementAccess" +} + resource "aws_lambda_function" "this" { function_name = var.function_name role = aws_iam_role.lambda.arn @@ -92,8 +108,13 @@ resource "aws_lambda_function" "this" { tags = var.tags - # Validate package type specific requirements + # Ensure proper cleanup of VPC resources + depends_on = [ + aws_iam_role_policy_attachment.lambda_vpc_access + ] + lifecycle { + # Validate package type specific requirements precondition { condition = local.validate_zip_package error_message = "For Zip package type, handler, runtime, s3_bucket, s3_key, and s3_object_version are required." @@ -104,3 +125,23 @@ resource "aws_lambda_function" "this" { } } } + +# Function URL for HTTP access +resource "aws_lambda_function_url" "this" { + count = var.create_function_url ? 1 : 0 + + function_name = aws_lambda_function.this.function_name + authorization_type = var.function_url_authorization_type + + dynamic "cors" { + for_each = var.function_url_cors != null ? [var.function_url_cors] : [] + content { + allow_credentials = cors.value.allow_credentials + allow_origins = cors.value.allow_origins + allow_methods = cors.value.allow_methods + allow_headers = cors.value.allow_headers + expose_headers = cors.value.expose_headers + max_age = cors.value.max_age + } + } +} diff --git a/outputs.tf b/outputs.tf index 3260ad8..13fb3c9 100644 --- a/outputs.tf +++ b/outputs.tf @@ -17,3 +17,13 @@ output "lambda_role_arn" { description = "ARN of the Lambda IAM role" value = aws_iam_role.lambda.arn } + +output "function_url" { + description = "Function URL for HTTP access" + value = var.create_function_url ? aws_lambda_function_url.this[0].function_url : null +} + +output "function_invoke_arn" { + description = "Invoke ARN of the Lambda function" + value = aws_lambda_function.this.invoke_arn +} diff --git a/variables.tf b/variables.tf index f2f0258..d309e38 100644 --- a/variables.tf +++ b/variables.tf @@ -100,6 +100,7 @@ variable "kms_key_arn" { variable "vpc_config" { description = "VPC configuration for the Lambda function" type = object({ + vpc_id = string subnet_ids = list(string) security_group_ids = list(string) }) @@ -151,6 +152,35 @@ variable "policy_arns" { } } +variable "create_function_url" { + description = "Whether to create a Function URL for HTTP access" + type = bool + default = false +} + +variable "function_url_authorization_type" { + description = "Authorization type for Function URL" + type = string + default = "NONE" + validation { + condition = contains(["NONE", "AWS_IAM"], var.function_url_authorization_type) + error_message = "Authorization type must be either NONE or AWS_IAM." + } +} + +variable "function_url_cors" { + description = "CORS configuration for Function URL" + type = object({ + allow_credentials = optional(bool, false) + allow_origins = optional(list(string), ["*"]) + allow_methods = optional(list(string), ["*"]) + allow_headers = optional(list(string), ["date", "keep-alive"]) + expose_headers = optional(list(string), ["date", "keep-alive"]) + max_age = optional(number, 86400) + }) + default = null +} + variable "tags" { description = "Tags to apply to the Lambda function" type = map(string)