1+ provider "aws" {
2+ region = " us-east-1"
3+
4+ default_tags {
5+ tags = {
6+ o11y-workshop = " lambda-tracing"
7+ }
8+ }
9+ }
10+
11+
12+ # Get IAM Role
13+ data "aws_caller_identity" "current" {}
14+ resource "aws_iam_role" "lambda_kinesis" {
15+ name = " lambda_kinesis"
16+ assume_role_policy = jsonencode ({
17+ Version = " 2012-10-17"
18+ Statement = [
19+ {
20+ Action = " sts:AssumeRole"
21+ Effect = " Allow"
22+ Principal = {
23+ Service = " lambda.amazonaws.com"
24+ }
25+ }
26+ ]
27+ })
28+ }
29+ resource "aws_iam_role_policy_attachment" "lambda_kinesis_execution" {
30+ role = aws_iam_role. lambda_kinesis . name
31+ policy_arn = " arn:aws:iam::aws:policy/AmazonKinesisFullAccess"
32+ }
33+
34+ # Create S3 Bucket, Ownership, ACL
35+ resource "aws_s3_bucket" "lambda_bucket" {
36+ bucket = " ${ var . prefix } -lambda-code"
37+ }
38+
39+ resource "aws_s3_bucket_ownership_controls" "lambda_bucket" {
40+ bucket = aws_s3_bucket. lambda_bucket . id
41+ rule {
42+ object_ownership = " BucketOwnerPreferred"
43+ }
44+ }
45+
46+ resource "aws_s3_bucket_acl" "lambda_bucket" {
47+ depends_on = [aws_s3_bucket_ownership_controls . lambda_bucket ]
48+
49+ bucket = aws_s3_bucket. lambda_bucket . id
50+ acl = " private"
51+ }
52+
53+
54+ # Package Producer and Consumer Apps, Upload to S3 Bucket
55+ data "archive_file" "producer_app" {
56+ type = " zip"
57+
58+ source_file = " ${ path . module } /handler/producer.mjs"
59+ output_path = " ${ path . module } /lambda_producer.zip"
60+ }
61+
62+ resource "aws_s3_object" "producer_app" {
63+ bucket = aws_s3_bucket. lambda_bucket . id
64+
65+ key = " lambda_producer.zip"
66+ source = data. archive_file . producer_app . output_path
67+
68+ etag = filemd5 (data. archive_file . producer_app . output_path )
69+ }
70+
71+ data "archive_file" "consumer_app" {
72+ type = " zip"
73+
74+ source_file = " ${ path . module } /handler/consumer.mjs"
75+ output_path = " ${ path . module } /lambda_consumer.zip"
76+ }
77+
78+ resource "aws_s3_object" "consumer_app" {
79+ bucket = aws_s3_bucket. lambda_bucket . id
80+
81+ key = " lambda_consumer.zip"
82+ source = data. archive_file . consumer_app . output_path
83+
84+ etag = filemd5 (data. archive_file . consumer_app . output_path )
85+ }
86+
87+
88+ # Create Lambda Functions
89+ resource "aws_lambda_function" "lambda_producer" {
90+ function_name = " ${ var . prefix } -producer"
91+
92+ s3_bucket = aws_s3_bucket. lambda_bucket . id
93+ s3_key = aws_s3_object. producer_app . key
94+
95+ runtime = " nodejs20.x"
96+ handler = " producer.producer"
97+
98+ source_code_hash = data. archive_file . producer_app . output_base64sha256
99+
100+ role = aws_iam_role. lambda_kinesis . arn
101+
102+ environment {
103+ variables = {
104+ SPLUNK_ACCESS_TOKEN = var.o11y_access_token
105+ SPLUNK_REALM = var.o11y_realm
106+ OTEL_SERVICE_NAME = " ${ var . prefix } -producer-lambda"
107+ OTEL_RESOURCE_ATTRIBUTES = " deployment.environment=${ var . prefix } -lambda-shop"
108+ AWS_LAMBDA_EXEC_WRAPPER = " /opt/nodejs-otel-handler"
109+ KINESIS_STREAM = aws_kinesis_stream.lambda_stream.name
110+ }
111+ }
112+
113+ layers = var. otel_lambda_layer
114+
115+ timeout = 60
116+ }
117+
118+ resource "aws_lambda_function" "lambda_consumer" {
119+ function_name = " ${ var . prefix } -consumer"
120+
121+ s3_bucket = aws_s3_bucket. lambda_bucket . id
122+ s3_key = aws_s3_object. consumer_app . key
123+
124+ runtime = " nodejs20.x"
125+ handler = " consumer.consumer"
126+
127+ source_code_hash = data. archive_file . consumer_app . output_base64sha256
128+
129+ role = aws_iam_role. lambda_kinesis . arn
130+
131+ environment {
132+ variables = {
133+ SPLUNK_ACCESS_TOKEN = var.o11y_access_token
134+ SPLUNK_REALM = var.o11y_realm
135+ OTEL_SERVICE_NAME = " ${ var . prefix } -consumer-lambda"
136+ OTEL_RESOURCE_ATTRIBUTES = " deployment.environment=${ var . prefix } -lambda-shop"
137+ AWS_LAMBDA_EXEC_WRAPPER = " /opt/nodejs-otel-handler"
138+ }
139+ }
140+
141+ layers = var. otel_lambda_layer
142+
143+ timeout = 60
144+ }
145+
146+ # Add API Gateway API, Stage, Integration, Route and Permission Resources
147+ resource "aws_apigatewayv2_api" "lambda" {
148+ name = " ${ var . prefix } -gateway"
149+ protocol_type = " HTTP"
150+ }
151+
152+ resource "aws_apigatewayv2_stage" "lambda" {
153+ api_id = aws_apigatewayv2_api. lambda . id
154+
155+ name = " ${ var . prefix } -gw-stage"
156+ auto_deploy = true
157+
158+ access_log_settings {
159+ destination_arn = aws_cloudwatch_log_group. api_gw . arn
160+
161+ format = jsonencode ({
162+ requestId = " $context.requestId"
163+ sourceIp = " $context.identity.sourceIp"
164+ requestTime = " $context.requestTime"
165+ protocol = " $context.protocol"
166+ httpMethod = " $context.httpMethod"
167+ resourcePath = " $context.resourcePath"
168+ routeKey = " $context.routeKey"
169+ status = " $context.status"
170+ responseLength = " $context.responseLength"
171+ integrationErrorMessage = " $context.integrationErrorMessage"
172+ }
173+ )
174+ }
175+ }
176+
177+ resource "aws_apigatewayv2_integration" "lambda" {
178+ api_id = aws_apigatewayv2_api. lambda . id
179+
180+ integration_uri = aws_lambda_function. lambda_producer . invoke_arn
181+ integration_type = " AWS_PROXY"
182+ integration_method = " POST"
183+ }
184+
185+ resource "aws_apigatewayv2_route" "lambda" {
186+ api_id = aws_apigatewayv2_api. lambda . id
187+
188+ route_key = " POST /producer"
189+ target = " integrations/${ aws_apigatewayv2_integration . lambda . id } "
190+ }
191+
192+ # Lambda Permission for API Gateway
193+ resource "aws_lambda_permission" "api_gw" {
194+ statement_id = " AllowExecutionFromAPIGateway"
195+ action = " lambda:InvokeFunction"
196+ function_name = aws_lambda_function. lambda_producer . function_name
197+ principal = " apigateway.amazonaws.com"
198+
199+ source_arn = " ${ aws_apigatewayv2_api . lambda . execution_arn } /*/*"
200+ }
201+
202+ # CloudWatch Log Groups for Lambda and API Gateway
203+ resource "aws_cloudwatch_log_group" "lambda_producer" {
204+ name = " /aws/lambda/${ aws_lambda_function . lambda_producer . function_name } "
205+
206+ retention_in_days = 30
207+ }
208+
209+ resource "aws_cloudwatch_log_group" "lambda_consumer" {
210+ name = " /aws/lambda/${ aws_lambda_function . lambda_consumer . function_name } "
211+
212+ retention_in_days = 30
213+ }
214+
215+ resource "aws_cloudwatch_log_group" "api_gw" {
216+ name = " /aws/api_gw/${ aws_apigatewayv2_api . lambda . name } "
217+
218+ retention_in_days = 30
219+ }
220+
221+ # Kinesis Data Stream
222+ resource "aws_kinesis_stream" "lambda_stream" {
223+ name = " ${ var . prefix } -lambda_stream"
224+ shard_count = 1
225+ retention_period = 24
226+ shard_level_metrics = [
227+ " IncomingBytes" ,
228+ " OutgoingBytes"
229+ ]
230+ }
231+
232+ # Source Mapping Lambda Consumer to Kinesis Stream
233+ resource "aws_lambda_event_source_mapping" "kinesis_lambda_event_mapping" {
234+ batch_size = 100
235+ event_source_arn = aws_kinesis_stream. lambda_stream . arn
236+ enabled = true
237+ function_name = aws_lambda_function. lambda_consumer . arn
238+ starting_position = " LATEST"
239+ }
0 commit comments