thinkwork-cli 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +3 -3
  2. package/dist/cli.js +251 -47
  3. package/dist/terraform/examples/greenfield/main.tf +190 -0
  4. package/dist/terraform/examples/greenfield/terraform.tfvars.example +28 -0
  5. package/dist/terraform/modules/_internal/workspace-guard/main.tf +29 -0
  6. package/dist/terraform/modules/app/agentcore-runtime/main.tf +217 -0
  7. package/dist/terraform/modules/app/appsync-subscriptions/main.tf +122 -0
  8. package/dist/terraform/modules/app/appsync-subscriptions/outputs.tf +20 -0
  9. package/dist/terraform/modules/app/appsync-subscriptions/variables.tf +31 -0
  10. package/dist/terraform/modules/app/crons/main.tf +55 -0
  11. package/dist/terraform/modules/app/hindsight-memory/README.md +66 -0
  12. package/dist/terraform/modules/app/hindsight-memory/main.tf +331 -0
  13. package/dist/terraform/modules/app/job-triggers/main.tf +70 -0
  14. package/dist/terraform/modules/app/lambda-api/.build/placeholder.zip +0 -0
  15. package/dist/terraform/modules/app/lambda-api/handlers.tf +311 -0
  16. package/dist/terraform/modules/app/lambda-api/main.tf +245 -0
  17. package/dist/terraform/modules/app/lambda-api/outputs.tf +24 -0
  18. package/dist/terraform/modules/app/lambda-api/variables.tf +153 -0
  19. package/dist/terraform/modules/app/ses-email/main.tf +51 -0
  20. package/dist/terraform/modules/app/static-site/main.tf +176 -0
  21. package/dist/terraform/modules/data/aurora-postgres/README.md +92 -0
  22. package/dist/terraform/modules/data/aurora-postgres/main.tf +185 -0
  23. package/dist/terraform/modules/data/aurora-postgres/outputs.tf +30 -0
  24. package/dist/terraform/modules/data/aurora-postgres/variables.tf +114 -0
  25. package/dist/terraform/modules/data/bedrock-knowledge-base/main.tf +102 -0
  26. package/dist/terraform/modules/data/s3-buckets/main.tf +91 -0
  27. package/dist/terraform/modules/foundation/cognito/main.tf +377 -0
  28. package/dist/terraform/modules/foundation/cognito/outputs.tf +29 -0
  29. package/dist/terraform/modules/foundation/cognito/variables.tf +124 -0
  30. package/dist/terraform/modules/foundation/dns/main.tf +49 -0
  31. package/dist/terraform/modules/foundation/kms/main.tf +49 -0
  32. package/dist/terraform/modules/foundation/vpc/main.tf +137 -0
  33. package/dist/terraform/modules/foundation/vpc/outputs.tf +14 -0
  34. package/dist/terraform/modules/foundation/vpc/variables.tf +40 -0
  35. package/dist/terraform/modules/thinkwork/main.tf +212 -0
  36. package/dist/terraform/modules/thinkwork/outputs.tf +87 -0
  37. package/dist/terraform/modules/thinkwork/variables.tf +241 -0
  38. package/dist/terraform/schema.graphql +199 -0
  39. package/package.json +2 -2
@@ -0,0 +1,311 @@
1
+ ################################################################################
2
+ # Real Lambda Handlers
3
+ #
4
+ # Each handler is bundled by scripts/build-lambdas.sh into dist/lambdas/<name>.zip.
5
+ # Terraform references them via var.lambda_zips_dir (local path) for dev deploys,
6
+ # or via S3 (lambda_artifact_bucket) for production.
7
+ ################################################################################
8
+
9
+ locals {
10
+ use_local_zips = var.lambda_zips_dir != ""
11
+ runtime = "nodejs20.x"
12
+
13
+ # Common environment variables shared by all API handlers
14
+ common_env = {
15
+ STAGE = var.stage
16
+ DATABASE_URL = "postgresql://${var.db_username}:${urlencode(var.db_password)}@${var.db_cluster_endpoint}:5432/${var.database_name}?sslmode=no-verify"
17
+ DATABASE_SECRET_ARN = var.graphql_db_secret_arn
18
+ DATABASE_HOST = var.db_cluster_endpoint
19
+ DATABASE_NAME = var.database_name
20
+ BUCKET_NAME = var.bucket_name
21
+ USER_POOL_ID = var.user_pool_id
22
+ COGNITO_USER_POOL_ID = var.user_pool_id
23
+ ADMIN_CLIENT_ID = var.admin_client_id
24
+ MOBILE_CLIENT_ID = var.mobile_client_id
25
+ COGNITO_APP_CLIENT_IDS = "${var.admin_client_id},${var.mobile_client_id}"
26
+ APPSYNC_ENDPOINT = var.appsync_api_url
27
+ APPSYNC_API_KEY = var.appsync_api_key
28
+ GRAPHQL_API_KEY = var.appsync_api_key
29
+ API_AUTH_SECRET = var.api_auth_secret
30
+ THINKWORK_API_SECRET = var.api_auth_secret
31
+ MANIFLOW_API_SECRET = var.api_auth_secret
32
+ AGENTCORE_INVOKE_URL = var.agentcore_invoke_url
33
+ AGENTCORE_FUNCTION_NAME = var.agentcore_function_name
34
+ WORKSPACE_BUCKET = var.bucket_name
35
+ HINDSIGHT_ENDPOINT = var.hindsight_endpoint
36
+ NODE_OPTIONS = "--enable-source-maps"
37
+ }
38
+ }
39
+
40
+ # ---------------------------------------------------------------------------
41
+ # Helper: creates a Lambda function from a local zip
42
+ # ---------------------------------------------------------------------------
43
+
44
+ resource "aws_lambda_function" "handler" {
45
+ for_each = local.use_local_zips ? toset([
46
+ "graphql-http",
47
+ "chat-agent-invoke",
48
+ "wakeup-processor",
49
+ "agents",
50
+ "agent-actions",
51
+ "messages",
52
+ "connections",
53
+ "oauth-authorize",
54
+ "oauth-callback",
55
+ "teams",
56
+ "team-members",
57
+ "tenants",
58
+ "users",
59
+ "invites",
60
+ "skills",
61
+ "activity",
62
+ "routines",
63
+ "budgets",
64
+ "guardrails",
65
+ "scheduled-jobs",
66
+ "job-schedule-manager",
67
+ "webhooks",
68
+ "webhooks-admin",
69
+ "workspace-files",
70
+ "knowledge-base-manager",
71
+ "knowledge-base-files",
72
+ "email-send",
73
+ "email-inbound",
74
+ "github-app",
75
+ "github-repos",
76
+ "memory",
77
+ "artifact-deliver",
78
+ "recipe-refresh",
79
+ "connector-installs",
80
+ "connector-secrets",
81
+ "agent-skills-list",
82
+ "bootstrap-workspaces",
83
+ "code-factory",
84
+ ]) : toset([])
85
+
86
+ function_name = "thinkwork-${var.stage}-api-${each.key}"
87
+ role = aws_iam_role.lambda.arn
88
+ handler = "index.handler"
89
+ runtime = local.runtime
90
+ timeout = each.key == "wakeup-processor" ? 300 : each.key == "chat-agent-invoke" ? 300 : 30
91
+ memory_size = each.key == "graphql-http" ? 512 : each.key == "wakeup-processor" ? 512 : 256
92
+
93
+ filename = "${var.lambda_zips_dir}/${each.key}.zip"
94
+ source_code_hash = filebase64sha256("${var.lambda_zips_dir}/${each.key}.zip")
95
+
96
+ environment {
97
+ variables = merge(local.common_env, {
98
+ FUNCTION_NAME = each.key
99
+ })
100
+ }
101
+
102
+ tags = {
103
+ Name = "thinkwork-${var.stage}-api-${each.key}"
104
+ Handler = each.key
105
+ }
106
+ }
107
+
108
+ # ---------------------------------------------------------------------------
109
+ # API Gateway routes → Lambda integrations
110
+ # ---------------------------------------------------------------------------
111
+
112
+ locals {
113
+ # Map of route_key → handler name for API Gateway
114
+ api_routes = local.use_local_zips ? {
115
+ # GraphQL — the main API entry point
116
+ "POST /graphql" = "graphql-http"
117
+ "GET /graphql" = "graphql-http"
118
+
119
+ # Health check (keep placeholder alive too)
120
+ # "GET /health" is handled by placeholder
121
+
122
+ # Agents
123
+ "ANY /api/agents/{proxy+}" = "agents"
124
+ "ANY /api/agents" = "agents"
125
+
126
+ # Agent actions (start/stop/heartbeat/budget)
127
+ "ANY /api/agent-actions/{proxy+}" = "agent-actions"
128
+
129
+ # Messages
130
+ "ANY /api/messages/{proxy+}" = "messages"
131
+ "ANY /api/messages" = "messages"
132
+
133
+ # Teams
134
+ "ANY /api/teams/{proxy+}" = "teams"
135
+ "ANY /api/teams" = "teams"
136
+ "ANY /api/team-members/{proxy+}" = "team-members"
137
+
138
+ # Tenants
139
+ "ANY /api/tenants/{proxy+}" = "tenants"
140
+ "ANY /api/tenants" = "tenants"
141
+
142
+ # Users
143
+ "ANY /api/users/{proxy+}" = "users"
144
+ "ANY /api/users" = "users"
145
+
146
+ # Invites
147
+ "ANY /api/invites/{proxy+}" = "invites"
148
+ "ANY /api/invites" = "invites"
149
+
150
+ # Skills
151
+ "ANY /api/skills/{proxy+}" = "skills"
152
+ "ANY /api/skills" = "skills"
153
+
154
+ # Activity
155
+ "ANY /api/activity/{proxy+}" = "activity"
156
+ "ANY /api/activity" = "activity"
157
+
158
+ # Connections + OAuth
159
+ "ANY /api/connections/{proxy+}" = "connections"
160
+ "ANY /api/connections" = "connections"
161
+ "GET /api/oauth/authorize" = "oauth-authorize"
162
+ "GET /api/oauth/callback" = "oauth-callback"
163
+
164
+ # Routines
165
+ "ANY /api/routines/{proxy+}" = "routines"
166
+ "ANY /api/routines" = "routines"
167
+
168
+ # Budgets
169
+ "ANY /api/budgets/{proxy+}" = "budgets"
170
+ "ANY /api/budgets" = "budgets"
171
+
172
+ # Guardrails
173
+ "ANY /api/guardrails/{proxy+}" = "guardrails"
174
+ "ANY /api/guardrails" = "guardrails"
175
+
176
+ # Scheduled Jobs
177
+ "ANY /api/scheduled-jobs/{proxy+}" = "scheduled-jobs"
178
+ "ANY /api/scheduled-jobs" = "scheduled-jobs"
179
+ "ANY /api/thread-turns/{proxy+}" = "scheduled-jobs"
180
+ "ANY /api/thread-turns" = "scheduled-jobs"
181
+
182
+ # Job Schedule Manager (EventBridge CRUD)
183
+ "ANY /api/job-schedules/{proxy+}" = "job-schedule-manager"
184
+ "ANY /api/job-schedules" = "job-schedule-manager"
185
+
186
+ # Webhooks (public trigger)
187
+ "POST /webhooks/{proxy+}" = "webhooks"
188
+
189
+ # Webhooks admin
190
+ "ANY /api/webhooks/{proxy+}" = "webhooks-admin"
191
+ "ANY /api/webhooks" = "webhooks-admin"
192
+
193
+ # Workspace files
194
+ "ANY /api/workspaces/{proxy+}" = "workspace-files"
195
+
196
+ # Knowledge bases
197
+ "ANY /api/knowledge-bases/{proxy+}" = "knowledge-base-files"
198
+
199
+ # Email
200
+ "POST /api/email/send" = "email-send"
201
+
202
+ # Memory
203
+ "ANY /api/memory/{proxy+}" = "memory"
204
+
205
+ # Artifacts
206
+ "POST /api/artifacts/{proxy+}" = "artifact-deliver"
207
+
208
+ # Recipes
209
+ "POST /api/recipe-refresh" = "recipe-refresh"
210
+
211
+ # GitHub App
212
+ "ANY /api/github-app/{proxy+}" = "github-app"
213
+ "POST /api/github/webhook" = "github-app"
214
+
215
+ # Connectors
216
+ "ANY /api/connector-installs/{proxy+}" = "connector-installs"
217
+ "ANY /api/connector-secrets/{proxy+}" = "connector-secrets"
218
+ } : {}
219
+ }
220
+
221
+ resource "aws_apigatewayv2_integration" "handler" {
222
+ for_each = local.api_routes
223
+
224
+ api_id = aws_apigatewayv2_api.main.id
225
+ integration_type = "AWS_PROXY"
226
+ integration_uri = aws_lambda_function.handler[each.value].invoke_arn
227
+ payload_format_version = "2.0"
228
+ }
229
+
230
+ resource "aws_apigatewayv2_route" "handler" {
231
+ for_each = local.api_routes
232
+
233
+ api_id = aws_apigatewayv2_api.main.id
234
+ route_key = each.key
235
+ target = "integrations/${aws_apigatewayv2_integration.handler[each.key].id}"
236
+ }
237
+
238
+ resource "aws_lambda_permission" "handler_apigw" {
239
+ for_each = local.use_local_zips ? toset(distinct(values(local.api_routes))) : toset([])
240
+
241
+ statement_id = "AllowAPIGateway"
242
+ action = "lambda:InvokeFunction"
243
+ function_name = aws_lambda_function.handler[each.key].function_name
244
+ principal = "apigateway.amazonaws.com"
245
+ source_arn = "${aws_apigatewayv2_api.main.execution_arn}/*/*"
246
+ }
247
+
248
+ # ---------------------------------------------------------------------------
249
+ # Wakeup Processor — EventBridge schedule (every 1 min)
250
+ # ---------------------------------------------------------------------------
251
+
252
+ resource "aws_scheduler_schedule" "wakeup_processor" {
253
+ count = local.use_local_zips ? 1 : 0
254
+
255
+ name = "thinkwork-${var.stage}-wakeup-processor"
256
+ group_name = "default"
257
+ schedule_expression = "rate(1 minutes)"
258
+ state = "ENABLED"
259
+
260
+ flexible_time_window {
261
+ mode = "OFF"
262
+ }
263
+
264
+ target {
265
+ arn = aws_lambda_function.handler["wakeup-processor"].arn
266
+ role_arn = aws_iam_role.scheduler.arn
267
+ }
268
+ }
269
+
270
+ resource "aws_iam_role" "scheduler" {
271
+ name = "thinkwork-${var.stage}-scheduler-role"
272
+
273
+ assume_role_policy = jsonencode({
274
+ Version = "2012-10-17"
275
+ Statement = [{
276
+ Effect = "Allow"
277
+ Principal = { Service = "scheduler.amazonaws.com" }
278
+ Action = "sts:AssumeRole"
279
+ }]
280
+ })
281
+ }
282
+
283
+ resource "aws_iam_role_policy" "scheduler_invoke" {
284
+ name = "invoke-lambda"
285
+ role = aws_iam_role.scheduler.id
286
+
287
+ policy = jsonencode({
288
+ Version = "2012-10-17"
289
+ Statement = [{
290
+ Effect = "Allow"
291
+ Action = ["lambda:InvokeFunction"]
292
+ Resource = local.use_local_zips ? [for k, v in aws_lambda_function.handler : v.arn] : []
293
+ }]
294
+ })
295
+ }
296
+
297
+ # ---------------------------------------------------------------------------
298
+ # SSM Parameters — Lambda ARNs for cross-function invocation
299
+ # ---------------------------------------------------------------------------
300
+
301
+ resource "aws_ssm_parameter" "lambda_arns" {
302
+ for_each = local.use_local_zips ? {
303
+ "chat-agent-invoke-fn-arn" = aws_lambda_function.handler["chat-agent-invoke"].arn
304
+ "kb-manager-fn-arn" = aws_lambda_function.handler["knowledge-base-manager"].arn
305
+ "job-schedule-manager-fn-arn" = aws_lambda_function.handler["job-schedule-manager"].arn
306
+ } : {}
307
+
308
+ name = "/thinkwork/${var.stage}/${each.key}"
309
+ type = "String"
310
+ value = each.value
311
+ }
@@ -0,0 +1,245 @@
1
+ ################################################################################
2
+ # Lambda API — App Module
3
+ #
4
+ # Creates the API Gateway V2 HTTP API and a shared Lambda execution role.
5
+ # Individual Lambda functions are added in migration Phases 2-4 as their
6
+ # code is ported. For Phase 1, a hello-world placeholder Lambda proves
7
+ # the infrastructure works end-to-end.
8
+ #
9
+ # In production this module will contain 30+ Lambda functions covering:
10
+ # - GraphQL HTTP handler (the main API entry point)
11
+ # - Agent invoke / chat
12
+ # - Thread, agent, template, connector CRUD
13
+ # - Skills, KB, memory handlers
14
+ # - Connectors (Slack, GitHub, Google)
15
+ # - Email inbound/outbound
16
+ # - OAuth callbacks
17
+ ################################################################################
18
+
19
+ data "aws_caller_identity" "current" {}
20
+
21
+ ################################################################################
22
+ # API Gateway V2 — HTTP API
23
+ ################################################################################
24
+
25
+ resource "aws_apigatewayv2_api" "main" {
26
+ name = "thinkwork-${var.stage}-api"
27
+ protocol_type = "HTTP"
28
+
29
+ cors_configuration {
30
+ allow_headers = ["*"]
31
+ allow_methods = ["*"]
32
+ allow_origins = ["*"]
33
+ max_age = 3600
34
+ }
35
+
36
+ tags = {
37
+ Name = "thinkwork-${var.stage}-api"
38
+ }
39
+ }
40
+
41
+ resource "aws_apigatewayv2_stage" "default" {
42
+ api_id = aws_apigatewayv2_api.main.id
43
+ name = "$default"
44
+ auto_deploy = true
45
+
46
+ tags = {
47
+ Name = "thinkwork-${var.stage}-api-default"
48
+ }
49
+ }
50
+
51
+ ################################################################################
52
+ # Custom Domain (optional)
53
+ ################################################################################
54
+
55
+ resource "aws_apigatewayv2_domain_name" "main" {
56
+ count = var.custom_domain != "" ? 1 : 0
57
+ domain_name = var.custom_domain
58
+
59
+ domain_name_configuration {
60
+ certificate_arn = var.certificate_arn
61
+ endpoint_type = "REGIONAL"
62
+ security_policy = "TLS_1_2"
63
+ }
64
+ }
65
+
66
+ resource "aws_apigatewayv2_api_mapping" "main" {
67
+ count = var.custom_domain != "" ? 1 : 0
68
+ api_id = aws_apigatewayv2_api.main.id
69
+ domain_name = aws_apigatewayv2_domain_name.main[0].id
70
+ stage = aws_apigatewayv2_stage.default.id
71
+ }
72
+
73
+ ################################################################################
74
+ # Shared Lambda Execution Role
75
+ ################################################################################
76
+
77
+ resource "aws_iam_role" "lambda" {
78
+ name = "thinkwork-${var.stage}-api-lambda-role"
79
+
80
+ assume_role_policy = jsonencode({
81
+ Version = "2012-10-17"
82
+ Statement = [{
83
+ Effect = "Allow"
84
+ Principal = { Service = "lambda.amazonaws.com" }
85
+ Action = "sts:AssumeRole"
86
+ }]
87
+ })
88
+ }
89
+
90
+ resource "aws_iam_role_policy_attachment" "lambda_basic" {
91
+ role = aws_iam_role.lambda.name
92
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
93
+ }
94
+
95
+ resource "aws_iam_role_policy" "lambda_rds" {
96
+ name = "rds-data-api"
97
+ role = aws_iam_role.lambda.id
98
+
99
+ policy = jsonencode({
100
+ Version = "2012-10-17"
101
+ Statement = [{
102
+ Effect = "Allow"
103
+ Action = [
104
+ "rds-data:ExecuteStatement",
105
+ "rds-data:BatchExecuteStatement",
106
+ "rds-data:BeginTransaction",
107
+ "rds-data:CommitTransaction",
108
+ "rds-data:RollbackTransaction",
109
+ ]
110
+ Resource = var.db_cluster_arn
111
+ }]
112
+ })
113
+ }
114
+
115
+ resource "aws_iam_role_policy" "lambda_secrets" {
116
+ name = "secrets-manager"
117
+ role = aws_iam_role.lambda.id
118
+
119
+ policy = jsonencode({
120
+ Version = "2012-10-17"
121
+ Statement = [{
122
+ Effect = "Allow"
123
+ Action = ["secretsmanager:GetSecretValue"]
124
+ Resource = var.graphql_db_secret_arn
125
+ }]
126
+ })
127
+ }
128
+
129
+ resource "aws_iam_role_policy" "lambda_s3" {
130
+ name = "s3-access"
131
+ role = aws_iam_role.lambda.id
132
+
133
+ policy = jsonencode({
134
+ Version = "2012-10-17"
135
+ Statement = [{
136
+ Effect = "Allow"
137
+ Action = [
138
+ "s3:GetObject",
139
+ "s3:PutObject",
140
+ "s3:DeleteObject",
141
+ "s3:ListBucket",
142
+ ]
143
+ Resource = [
144
+ var.bucket_arn,
145
+ "${var.bucket_arn}/*",
146
+ ]
147
+ }]
148
+ })
149
+ }
150
+
151
+ resource "aws_iam_role_policy" "lambda_cognito" {
152
+ name = "cognito-access"
153
+ role = aws_iam_role.lambda.id
154
+
155
+ policy = jsonencode({
156
+ Version = "2012-10-17"
157
+ Statement = [{
158
+ Effect = "Allow"
159
+ Action = [
160
+ "cognito-idp:AdminGetUser",
161
+ "cognito-idp:AdminUpdateUserAttributes",
162
+ "cognito-idp:ListUsers",
163
+ ]
164
+ Resource = var.user_pool_arn
165
+ }]
166
+ })
167
+ }
168
+
169
+ resource "aws_iam_role_policy" "lambda_bedrock" {
170
+ name = "bedrock-invoke"
171
+ role = aws_iam_role.lambda.id
172
+
173
+ policy = jsonencode({
174
+ Version = "2012-10-17"
175
+ Statement = [{
176
+ Effect = "Allow"
177
+ Action = ["bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream"]
178
+ Resource = "*"
179
+ }]
180
+ })
181
+ }
182
+
183
+ ################################################################################
184
+ # Placeholder Lambda — proves the infrastructure works
185
+ #
186
+ # This will be replaced by real handlers in Phases 2-4.
187
+ ################################################################################
188
+
189
+ data "archive_file" "placeholder" {
190
+ type = "zip"
191
+ output_path = "${path.module}/.build/placeholder.zip"
192
+
193
+ source {
194
+ content = <<-JS
195
+ exports.handler = async (event) => ({
196
+ statusCode: 200,
197
+ headers: { "Content-Type": "application/json" },
198
+ body: JSON.stringify({ status: "ok", stage: process.env.STAGE }),
199
+ });
200
+ JS
201
+ filename = "index.js"
202
+ }
203
+ }
204
+
205
+ resource "aws_lambda_function" "placeholder" {
206
+ function_name = "thinkwork-${var.stage}-api-placeholder"
207
+ role = aws_iam_role.lambda.arn
208
+ handler = "index.handler"
209
+ runtime = "nodejs20.x"
210
+ timeout = 10
211
+
212
+ filename = data.archive_file.placeholder.output_path
213
+ source_code_hash = data.archive_file.placeholder.output_base64sha256
214
+
215
+ environment {
216
+ variables = {
217
+ STAGE = var.stage
218
+ }
219
+ }
220
+
221
+ tags = {
222
+ Name = "thinkwork-${var.stage}-api-placeholder"
223
+ }
224
+ }
225
+
226
+ resource "aws_apigatewayv2_integration" "placeholder" {
227
+ api_id = aws_apigatewayv2_api.main.id
228
+ integration_type = "AWS_PROXY"
229
+ integration_uri = aws_lambda_function.placeholder.invoke_arn
230
+ payload_format_version = "2.0"
231
+ }
232
+
233
+ resource "aws_apigatewayv2_route" "placeholder" {
234
+ api_id = aws_apigatewayv2_api.main.id
235
+ route_key = "GET /health"
236
+ target = "integrations/${aws_apigatewayv2_integration.placeholder.id}"
237
+ }
238
+
239
+ resource "aws_lambda_permission" "placeholder_apigw" {
240
+ statement_id = "AllowAPIGateway"
241
+ action = "lambda:InvokeFunction"
242
+ function_name = aws_lambda_function.placeholder.function_name
243
+ principal = "apigateway.amazonaws.com"
244
+ source_arn = "${aws_apigatewayv2_api.main.execution_arn}/*/*"
245
+ }
@@ -0,0 +1,24 @@
1
+ output "api_id" {
2
+ description = "API Gateway V2 API ID"
3
+ value = aws_apigatewayv2_api.main.id
4
+ }
5
+
6
+ output "api_endpoint" {
7
+ description = "API Gateway V2 endpoint URL"
8
+ value = aws_apigatewayv2_stage.default.invoke_url
9
+ }
10
+
11
+ output "api_execution_arn" {
12
+ description = "API Gateway V2 execution ARN (for Lambda permissions)"
13
+ value = aws_apigatewayv2_api.main.execution_arn
14
+ }
15
+
16
+ output "lambda_role_arn" {
17
+ description = "Shared Lambda execution role ARN (for other modules that add routes)"
18
+ value = aws_iam_role.lambda.arn
19
+ }
20
+
21
+ output "lambda_role_name" {
22
+ description = "Shared Lambda execution role name"
23
+ value = aws_iam_role.lambda.name
24
+ }