rastack 0.0.41 → 0.0.42

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.
@@ -0,0 +1,635 @@
1
+ AWSTemplateFormatVersion: "2010-09-09"
2
+ Description: >-
3
+ React API Stack per-app backend. Runs the rastack-server Rust API as a Lambda
4
+ custom runtime over an S3 Iceberg warehouse with Cognito identity. Deployed
5
+ once per app; thereafter CI only runs aws lambda update-function-code. See
6
+ docs/serverless-deploy.md.
7
+
8
+ Parameters:
9
+ AppName:
10
+ Type: String
11
+ Default: airline
12
+ Description: >
13
+ Short slug identifying this app; namespaces buckets, roles, SSM. No hyphens:
14
+ it feeds the Cognito IdentityPoolName, which AWS validates against [\w ]+
15
+ (letters/digits/underscore/space only).
16
+ AllowedPattern: "^[a-z0-9]{1,20}$"
17
+
18
+ NamePrefix:
19
+ Type: String
20
+ Default: rastack
21
+ # No hyphens — same Cognito IdentityPoolName [\w ]+ constraint as AppName.
22
+ AllowedPattern: "^[a-z0-9]{1,20}$"
23
+
24
+ GitHubOwner:
25
+ Type: String
26
+ Default: theserverkid
27
+ Description: GitHub org/user of the consumer repo whose CI ships the Lambda code.
28
+
29
+ GitHubRepo:
30
+ Type: String
31
+ Default: reactapistack-demo
32
+ Description: Consumer repo whose workflow may assume the deploy role (code-only).
33
+
34
+ GitHubBranch:
35
+ Type: String
36
+ Default: master
37
+ Description: Branch of GitHubRepo allowed to deploy.
38
+
39
+ ArtifactsBucket:
40
+ Type: String
41
+ Description: >
42
+ Name of the bootstrap artifacts bucket that holds the Lambda placeholder
43
+ bundle (ArtifactsBucketName output of bootstrap.yaml).
44
+
45
+ PlaceholderKey:
46
+ Type: String
47
+ Default: placeholders/bootstrap.zip
48
+ Description: >
49
+ Key of the placeholder bundle the function is CREATED from. CI replaces the
50
+ real code out of band via update-function-code; this key never changes, so
51
+ stack updates never revert CI's code.
52
+
53
+ OIDCProviderArn:
54
+ Type: String
55
+ Default: ""
56
+ Description: >
57
+ GitHub OIDC provider ARN. Leave blank to derive the account's single
58
+ provider ARN automatically (bootstrap.yaml created it).
59
+
60
+ IcebergNamespace:
61
+ Type: String
62
+ Default: production
63
+
64
+ EnableDataEventTrail:
65
+ Type: String
66
+ Default: "true"
67
+ AllowedValues: ["true", "false"]
68
+ Description: >
69
+ Create a CloudTrail trail of S3 object-level data events on the warehouse.
70
+ Accounts are limited to 5 trails per region — set "false" to skip if you
71
+ are near the limit or centralize data events elsewhere.
72
+
73
+ AdvancedSecurity:
74
+ Type: String
75
+ Default: "ENFORCED"
76
+ AllowedValues: ["ENFORCED", "AUDIT", "OFF"]
77
+ Description: >
78
+ Cognito advanced security (threat protection) mode. Several regions do not
79
+ offer it — e.g. af-south-1, ap-east-1, ap-south-2, ap-southeast-3/4/5,
80
+ eu-central-2, eu-south-2, me-central-1, ca-west-1 — and there a User Pool
81
+ carrying a UserPoolAddOns block is undeployable (CloudFormation's early
82
+ validation rejects the change set). Set OFF in those regions to omit the
83
+ add-on entirely; `rastack init` sets this automatically from the target
84
+ region.
85
+
86
+ Conditions:
87
+ DeriveOIDCArn: !Equals [!Ref OIDCProviderArn, ""]
88
+ ShouldTrailDataEvents: !Equals [!Ref EnableDataEventTrail, "true"]
89
+ UseAdvancedSecurity: !Not [!Equals [!Ref AdvancedSecurity, "OFF"]]
90
+
91
+ Resources:
92
+
93
+ # -- Encryption key -----------------------------------------------------------
94
+ WarehouseKey:
95
+ Type: AWS::KMS::Key
96
+ DeletionPolicy: Retain
97
+ UpdateReplacePolicy: Retain
98
+ Properties:
99
+ Description: !Sub "React API Stack warehouse key (${AppName})"
100
+ EnableKeyRotation: true
101
+ KeyPolicy:
102
+ Version: "2012-10-17"
103
+ Statement:
104
+ - Sid: AccountAdministration
105
+ Effect: Allow
106
+ Principal:
107
+ AWS: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:root"
108
+ Action: "kms:*"
109
+ Resource: "*"
110
+
111
+ WarehouseKeyAlias:
112
+ Type: AWS::KMS::Alias
113
+ Properties:
114
+ AliasName: !Sub "alias/${NamePrefix}-${AppName}-warehouse"
115
+ TargetKeyId: !Ref WarehouseKey
116
+
117
+ # -- Storage — S3 IS the database (Iceberg warehouse) -------------------------
118
+ WarehouseBucket:
119
+ Type: AWS::S3::Bucket
120
+ DeletionPolicy: Retain
121
+ UpdateReplacePolicy: Retain
122
+ # Server-access logging needs the audit bucket's log-delivery grant in place
123
+ # first (ACLs are disabled, so the permission comes from its bucket policy).
124
+ DependsOn: AuditLogBucketPolicy
125
+ Properties:
126
+ BucketName: !Sub "${NamePrefix}-${AppName}-${AWS::AccountId}-warehouse"
127
+ VersioningConfiguration:
128
+ Status: Enabled
129
+ BucketEncryption:
130
+ ServerSideEncryptionConfiguration:
131
+ - ServerSideEncryptionByDefault:
132
+ SSEAlgorithm: aws:kms
133
+ KMSMasterKeyID: !GetAtt WarehouseKey.Arn
134
+ BucketKeyEnabled: true
135
+ PublicAccessBlockConfiguration:
136
+ BlockPublicAcls: true
137
+ BlockPublicPolicy: true
138
+ IgnorePublicAcls: true
139
+ RestrictPublicBuckets: true
140
+ OwnershipControls:
141
+ Rules:
142
+ - ObjectOwnership: BucketOwnerEnforced
143
+ LifecycleConfiguration:
144
+ Rules:
145
+ - Id: expire-noncurrent-versions
146
+ Status: Enabled
147
+ NoncurrentVersionExpiration:
148
+ NoncurrentDays: 7
149
+ AbortIncompleteMultipartUpload:
150
+ DaysAfterInitiation: 7
151
+ LoggingConfiguration:
152
+ DestinationBucketName: !Ref AuditLogBucket
153
+ LogFilePrefix: s3-access/warehouse/
154
+
155
+ WarehouseBucketPolicy:
156
+ Type: AWS::S3::BucketPolicy
157
+ Properties:
158
+ Bucket: !Ref WarehouseBucket
159
+ PolicyDocument:
160
+ Version: "2012-10-17"
161
+ Statement:
162
+ - Sid: DenyInsecureTransport
163
+ Effect: Deny
164
+ Principal: "*"
165
+ Action: "s3:*"
166
+ Resource:
167
+ - !GetAtt WarehouseBucket.Arn
168
+ - !Sub "${WarehouseBucket.Arn}/*"
169
+ Condition:
170
+ Bool:
171
+ aws:SecureTransport: "false"
172
+
173
+ # -- Audit trail --------------------------------------------------------------
174
+ AuditLogBucket:
175
+ Type: AWS::S3::Bucket
176
+ DeletionPolicy: Retain
177
+ UpdateReplacePolicy: Retain
178
+ Properties:
179
+ BucketName: !Sub "${NamePrefix}-${AppName}-${AWS::AccountId}-audit"
180
+ BucketEncryption:
181
+ ServerSideEncryptionConfiguration:
182
+ - ServerSideEncryptionByDefault:
183
+ SSEAlgorithm: AES256
184
+ PublicAccessBlockConfiguration:
185
+ BlockPublicAcls: true
186
+ BlockPublicPolicy: true
187
+ IgnorePublicAcls: true
188
+ RestrictPublicBuckets: true
189
+ OwnershipControls:
190
+ Rules:
191
+ - ObjectOwnership: BucketOwnerEnforced
192
+ LifecycleConfiguration:
193
+ Rules:
194
+ - Id: expire-audit-logs
195
+ Status: Enabled
196
+ ExpirationInDays: 400
197
+
198
+ AuditLogBucketPolicy:
199
+ Type: AWS::S3::BucketPolicy
200
+ Properties:
201
+ Bucket: !Ref AuditLogBucket
202
+ PolicyDocument:
203
+ Version: "2012-10-17"
204
+ Statement:
205
+ - Sid: S3ServerAccessLogsPolicy
206
+ Effect: Allow
207
+ Principal:
208
+ Service: logging.s3.amazonaws.com
209
+ Action: s3:PutObject
210
+ Resource: !Sub "${AuditLogBucket.Arn}/s3-access/*"
211
+ Condition:
212
+ StringEquals:
213
+ aws:SourceAccount: !Ref AWS::AccountId
214
+ - Sid: CloudTrailAclCheck
215
+ Effect: Allow
216
+ Principal:
217
+ Service: cloudtrail.amazonaws.com
218
+ Action: s3:GetBucketAcl
219
+ Resource: !GetAtt AuditLogBucket.Arn
220
+ - Sid: CloudTrailWrite
221
+ Effect: Allow
222
+ Principal:
223
+ Service: cloudtrail.amazonaws.com
224
+ Action: s3:PutObject
225
+ Resource: !Sub "${AuditLogBucket.Arn}/cloudtrail/AWSLogs/${AWS::AccountId}/*"
226
+ Condition:
227
+ StringEquals:
228
+ s3:x-amz-acl: bucket-owner-full-control
229
+ - Sid: DenyInsecureTransport
230
+ Effect: Deny
231
+ Principal: "*"
232
+ Action: "s3:*"
233
+ Resource:
234
+ - !GetAtt AuditLogBucket.Arn
235
+ - !Sub "${AuditLogBucket.Arn}/*"
236
+ Condition:
237
+ Bool:
238
+ aws:SecureTransport: "false"
239
+
240
+ WarehouseTrail:
241
+ Type: AWS::CloudTrail::Trail
242
+ Condition: ShouldTrailDataEvents
243
+ DependsOn: AuditLogBucketPolicy
244
+ Properties:
245
+ TrailName: !Sub "${NamePrefix}-${AppName}-warehouse-data-events"
246
+ S3BucketName: !Ref AuditLogBucket
247
+ S3KeyPrefix: cloudtrail
248
+ IsLogging: true
249
+ IncludeGlobalServiceEvents: false
250
+ EventSelectors:
251
+ - ReadWriteType: All
252
+ IncludeManagementEvents: false
253
+ DataResources:
254
+ - Type: AWS::S3::Object
255
+ Values:
256
+ - !Sub "${WarehouseBucket.Arn}/"
257
+
258
+ # -- Identity -----------------------------------------------------------------
259
+ UserPool:
260
+ Type: AWS::Cognito::UserPool
261
+ Properties:
262
+ UserPoolName: !Sub "${NamePrefix}-${AppName}-users"
263
+ AutoVerifiedAttributes: [email]
264
+ UsernameAttributes: [email]
265
+ MfaConfiguration: "ON"
266
+ EnabledMfas:
267
+ - SOFTWARE_TOKEN_MFA
268
+ # Omitted entirely (not just set OFF) where the region lacks threat
269
+ # protection — the property itself is unavailable there, so a null add-on
270
+ # would still fail early validation.
271
+ UserPoolAddOns: !If
272
+ - UseAdvancedSecurity
273
+ - AdvancedSecurityMode: !Ref AdvancedSecurity
274
+ - !Ref "AWS::NoValue"
275
+ Policies:
276
+ PasswordPolicy:
277
+ MinimumLength: 12
278
+ RequireLowercase: false
279
+ RequireUppercase: false
280
+ RequireNumbers: false
281
+ RequireSymbols: false
282
+
283
+ UserPoolClient:
284
+ Type: AWS::Cognito::UserPoolClient
285
+ Properties:
286
+ UserPoolId: !Ref UserPool
287
+ ClientName: !Sub "${NamePrefix}-${AppName}-web"
288
+ GenerateSecret: false
289
+ ExplicitAuthFlows:
290
+ - ALLOW_USER_SRP_AUTH
291
+ - ALLOW_REFRESH_TOKEN_AUTH
292
+ PreventUserExistenceErrors: ENABLED
293
+ TokenValidityUnits:
294
+ AccessToken: minutes
295
+ IdToken: minutes
296
+ RefreshToken: days
297
+ AccessTokenValidity: 30
298
+ IdTokenValidity: 30
299
+ RefreshTokenValidity: 30
300
+ EnableTokenRevocation: true
301
+
302
+ IdentityPool:
303
+ Type: AWS::Cognito::IdentityPool
304
+ Properties:
305
+ IdentityPoolName: !Sub "${NamePrefix}_${AppName}_identity"
306
+ AllowUnauthenticatedIdentities: false
307
+ CognitoIdentityProviders:
308
+ - ClientId: !Ref UserPoolClient
309
+ ProviderName: !GetAtt UserPool.ProviderName
310
+
311
+ # Temp S3 credentials fenced to warehouse/tenants/{own sub}/ via the
312
+ # ${cognito-identity.amazonaws.com:sub} IAM policy variable. One role, every user.
313
+ WarehouseTenantRole:
314
+ Type: AWS::IAM::Role
315
+ Properties:
316
+ RoleName: !Sub "${NamePrefix}-${AppName}-tenant"
317
+ AssumeRolePolicyDocument:
318
+ Version: "2012-10-17"
319
+ Statement:
320
+ - Effect: Allow
321
+ Principal:
322
+ Federated: cognito-identity.amazonaws.com
323
+ Action: sts:AssumeRoleWithWebIdentity
324
+ Condition:
325
+ StringEquals:
326
+ cognito-identity.amazonaws.com:aud: !Ref IdentityPool
327
+ ForAnyValue:StringLike:
328
+ cognito-identity.amazonaws.com:amr: authenticated
329
+ Policies:
330
+ - PolicyName: own-tenant-prefix-only
331
+ PolicyDocument:
332
+ Version: "2012-10-17"
333
+ Statement:
334
+ - Sid: OwnTenantObjects
335
+ Effect: Allow
336
+ Action:
337
+ - s3:GetObject
338
+ - s3:PutObject
339
+ - s3:DeleteObject
340
+ Resource: !Sub "${WarehouseBucket.Arn}/warehouse/tenants/${!cognito-identity.amazonaws.com:sub}/*"
341
+ - Sid: OwnTenantListing
342
+ Effect: Allow
343
+ Action: s3:ListBucket
344
+ Resource: !GetAtt WarehouseBucket.Arn
345
+ Condition:
346
+ StringLike:
347
+ s3:prefix: !Sub "warehouse/tenants/${!cognito-identity.amazonaws.com:sub}/*"
348
+ - Sid: WarehouseKeyViaS3Only
349
+ Effect: Allow
350
+ Action:
351
+ - kms:Decrypt
352
+ - kms:GenerateDataKey
353
+ Resource: !GetAtt WarehouseKey.Arn
354
+ Condition:
355
+ StringEquals:
356
+ kms:ViaService: !Sub "s3.${AWS::Region}.amazonaws.com"
357
+
358
+ IdentityPoolRoles:
359
+ Type: AWS::Cognito::IdentityPoolRoleAttachment
360
+ Properties:
361
+ IdentityPoolId: !Ref IdentityPool
362
+ Roles:
363
+ authenticated: !GetAtt WarehouseTenantRole.Arn
364
+
365
+ # -- API (API Gateway v2 HTTP API) --------------------------------------------
366
+ ApiAccessLogGroup:
367
+ Type: AWS::Logs::LogGroup
368
+ Properties:
369
+ LogGroupName: !Sub "/rastack/${AppName}/api-access"
370
+ RetentionInDays: 90
371
+
372
+ HttpApi:
373
+ Type: AWS::ApiGatewayV2::Api
374
+ Properties:
375
+ Name: !Sub "${NamePrefix}-${AppName}-api"
376
+ ProtocolType: HTTP
377
+
378
+ HttpApiIntegration:
379
+ Type: AWS::ApiGatewayV2::Integration
380
+ Properties:
381
+ ApiId: !Ref HttpApi
382
+ IntegrationType: AWS_PROXY
383
+ IntegrationUri: !GetAtt ApiFunction.Arn
384
+ IntegrationMethod: POST
385
+ PayloadFormatVersion: "2.0"
386
+
387
+ HttpApiRoute:
388
+ Type: AWS::ApiGatewayV2::Route
389
+ Properties:
390
+ ApiId: !Ref HttpApi
391
+ # $default catches every method + path (incl. /) so the Rust router owns
392
+ # all routing — /api/{app}/v1/{model}/ and /admin alike.
393
+ RouteKey: "$default"
394
+ Target: !Sub "integrations/${HttpApiIntegration}"
395
+
396
+ HttpApiStage:
397
+ Type: AWS::ApiGatewayV2::Stage
398
+ Properties:
399
+ ApiId: !Ref HttpApi
400
+ StageName: "$default"
401
+ AutoDeploy: true
402
+ AccessLogSettings:
403
+ DestinationArn: !GetAtt ApiAccessLogGroup.Arn
404
+ Format: '{"requestId":"$context.requestId","ip":"$context.identity.sourceIp","requestTime":"$context.requestTime","method":"$context.httpMethod","path":"$context.path","status":"$context.status","latencyMs":"$context.responseLatency","userAgent":"$context.identity.userAgent"}'
405
+ DefaultRouteSettings:
406
+ ThrottlingBurstLimit: 200
407
+ ThrottlingRateLimit: 100
408
+
409
+ # -- Compute — the Rust API as a Lambda custom runtime ------------------------
410
+ # Least-privilege execution role: the API touches only warehouse/* (plus its
411
+ # KMS key via S3) and writes its own logs — not the whole bucket, not others.
412
+ LambdaExecutionRole:
413
+ Type: AWS::IAM::Role
414
+ Properties:
415
+ RoleName: !Sub "${NamePrefix}-${AppName}-lambda-exec"
416
+ AssumeRolePolicyDocument:
417
+ Version: "2012-10-17"
418
+ Statement:
419
+ - Effect: Allow
420
+ Principal:
421
+ Service: lambda.amazonaws.com
422
+ Action: sts:AssumeRole
423
+ ManagedPolicyArns:
424
+ - !Sub "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
425
+ Policies:
426
+ - PolicyName: warehouse-access
427
+ PolicyDocument:
428
+ Version: "2012-10-17"
429
+ Statement:
430
+ - Sid: WarehouseObjects
431
+ Effect: Allow
432
+ Action:
433
+ - s3:GetObject
434
+ - s3:PutObject
435
+ - s3:DeleteObject
436
+ Resource: !Sub "${WarehouseBucket.Arn}/warehouse/*"
437
+ - Sid: WarehouseListing
438
+ Effect: Allow
439
+ Action: s3:ListBucket
440
+ Resource: !GetAtt WarehouseBucket.Arn
441
+ Condition:
442
+ StringLike:
443
+ s3:prefix: "warehouse/*"
444
+ - Sid: WarehouseKeyViaS3Only
445
+ Effect: Allow
446
+ Action:
447
+ - kms:Decrypt
448
+ - kms:GenerateDataKey
449
+ Resource: !GetAtt WarehouseKey.Arn
450
+ Condition:
451
+ StringEquals:
452
+ kms:ViaService: !Sub "s3.${AWS::Region}.amazonaws.com"
453
+
454
+ ApiFunctionLogGroup:
455
+ Type: AWS::Logs::LogGroup
456
+ Properties:
457
+ LogGroupName: !Sub "/aws/lambda/${NamePrefix}-${AppName}-api"
458
+ RetentionInDays: 90
459
+
460
+ # provided.al2023 arm64 (Graviton). Created from the placeholder bundle in the
461
+ # bootstrap artifacts bucket; CI replaces the code out of band. Code.S3Key is
462
+ # fixed, so stack updates never diff/overwrite what CI shipped.
463
+ ApiFunction:
464
+ Type: AWS::Lambda::Function
465
+ DependsOn: ApiFunctionLogGroup
466
+ Properties:
467
+ FunctionName: !Sub "${NamePrefix}-${AppName}-api"
468
+ Runtime: provided.al2023
469
+ Handler: bootstrap
470
+ Architectures:
471
+ - arm64
472
+ MemorySize: 512
473
+ Timeout: 30
474
+ Role: !GetAtt LambdaExecutionRole.Arn
475
+ Code:
476
+ S3Bucket: !Ref ArtifactsBucket
477
+ S3Key: !Ref PlaceholderKey
478
+ Environment:
479
+ Variables:
480
+ RASTACK_MANIFEST: /var/task/schema.rastack.json
481
+ RASTACK_OPENAPI: /var/task/openapi.json
482
+ RASTACK_WAREHOUSE: !Sub "s3://${WarehouseBucket}/warehouse"
483
+ RASTACK_NAMESPACE: !Ref IcebergNamespace
484
+ RASTACK_JWKS: /var/task/jwks.json
485
+ RASTACK_ISSUER: !Sub "https://cognito-idp.${AWS::Region}.amazonaws.com/${UserPool}"
486
+ RASTACK_AUDIENCE: !Ref UserPoolClient
487
+ RASTACK_TOKEN_USE: access
488
+
489
+ ApiInvokePermission:
490
+ Type: AWS::Lambda::Permission
491
+ Properties:
492
+ FunctionName: !Ref ApiFunction
493
+ Action: lambda:InvokeFunction
494
+ Principal: apigateway.amazonaws.com
495
+ SourceArn: !Sub "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${HttpApi}/*/*"
496
+
497
+ # -- Deploy role (GitHub OIDC) — code only ------------------------------------
498
+ # CI assumes this to ship compiled code. It can UpdateFunctionCode on THIS one
499
+ # function and read this app's SSM params — nothing else — with an explicit
500
+ # deny that stops it touching data, keys, identity, or its own trust.
501
+ DeployRole:
502
+ Type: AWS::IAM::Role
503
+ Properties:
504
+ RoleName: !Sub "${NamePrefix}-${AppName}-deploy"
505
+ Description: OIDC role CI assumes to run `aws lambda update-function-code`.
506
+ MaxSessionDuration: 3600
507
+ AssumeRolePolicyDocument:
508
+ Version: "2012-10-17"
509
+ Statement:
510
+ - Effect: Allow
511
+ Principal:
512
+ Federated: !If
513
+ - DeriveOIDCArn
514
+ - !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:oidc-provider/token.actions.githubusercontent.com"
515
+ - !Ref OIDCProviderArn
516
+ Action: sts:AssumeRoleWithWebIdentity
517
+ Condition:
518
+ StringEquals:
519
+ token.actions.githubusercontent.com:aud: sts.amazonaws.com
520
+ StringLike:
521
+ token.actions.githubusercontent.com:sub: !Sub "repo:${GitHubOwner}/${GitHubRepo}:ref:refs/heads/${GitHubBranch}"
522
+ Policies:
523
+ - PolicyName: update-function-code-only
524
+ PolicyDocument:
525
+ Version: "2012-10-17"
526
+ Statement:
527
+ - Sid: UpdateThisFunction
528
+ Effect: Allow
529
+ Action:
530
+ - lambda:UpdateFunctionCode
531
+ - lambda:GetFunction
532
+ - lambda:GetFunctionConfiguration
533
+ Resource: !GetAtt ApiFunction.Arn
534
+ - Sid: ReadAppParameters
535
+ Effect: Allow
536
+ Action:
537
+ - ssm:GetParameter
538
+ - ssm:GetParameters
539
+ - ssm:GetParametersByPath
540
+ Resource: !Sub "arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/rastack/${AppName}/*"
541
+ - PolicyName: deny-privilege-escalation
542
+ PolicyDocument:
543
+ Version: "2012-10-17"
544
+ Statement:
545
+ - Sid: DenyPrivilegeEscalation
546
+ Effect: Deny
547
+ Action:
548
+ - iam:*
549
+ - sts:AssumeRole
550
+ - organizations:*
551
+ - account:*
552
+ - kms:*
553
+ - s3:*
554
+ - cognito-idp:*
555
+ - cognito-identity:*
556
+ - lambda:AddPermission
557
+ - lambda:UpdateFunctionConfiguration
558
+ - lambda:CreateFunction
559
+ - lambda:DeleteFunction
560
+ Resource: "*"
561
+
562
+ # -- SSM — the non-secret interface CI and the frontend read ------------------
563
+ ParamApiUrl:
564
+ Type: AWS::SSM::Parameter
565
+ Properties:
566
+ Name: !Sub "/rastack/${AppName}/api-url"
567
+ Type: String
568
+ Value: !Sub "https://${HttpApi}.execute-api.${AWS::Region}.amazonaws.com"
569
+
570
+ ParamFunctionName:
571
+ Type: AWS::SSM::Parameter
572
+ Properties:
573
+ Name: !Sub "/rastack/${AppName}/function-name"
574
+ Type: String
575
+ Value: !Ref ApiFunction
576
+
577
+ ParamUserPoolId:
578
+ Type: AWS::SSM::Parameter
579
+ Properties:
580
+ Name: !Sub "/rastack/${AppName}/user-pool-id"
581
+ Type: String
582
+ Value: !Ref UserPool
583
+
584
+ ParamUserPoolClientId:
585
+ Type: AWS::SSM::Parameter
586
+ Properties:
587
+ Name: !Sub "/rastack/${AppName}/user-pool-client-id"
588
+ Type: String
589
+ Value: !Ref UserPoolClient
590
+
591
+ ParamIdentityPoolId:
592
+ Type: AWS::SSM::Parameter
593
+ Properties:
594
+ Name: !Sub "/rastack/${AppName}/identity-pool-id"
595
+ Type: String
596
+ Value: !Ref IdentityPool
597
+
598
+ ParamJwksUrl:
599
+ Type: AWS::SSM::Parameter
600
+ Properties:
601
+ Name: !Sub "/rastack/${AppName}/jwks-url"
602
+ Type: String
603
+ Value: !Sub "https://cognito-idp.${AWS::Region}.amazonaws.com/${UserPool}/.well-known/jwks.json"
604
+
605
+ Outputs:
606
+ ApiUrl:
607
+ Description: Base URL of the deployed API (set as the frontend's remote base).
608
+ Value: !Sub "https://${HttpApi}.execute-api.${AWS::Region}.amazonaws.com"
609
+ FunctionName:
610
+ Description: Lambda function name — CI targets this with update-function-code.
611
+ Value: !Ref ApiFunction
612
+ DeployRoleArn:
613
+ Description: Wire as the AWS_DEPLOY_ROLE_ARN repo Variable in the consumer repo.
614
+ Value: !GetAtt DeployRole.Arn
615
+ Region:
616
+ Description: Deployment region — wire as the AWS_REGION repo Variable.
617
+ Value: !Ref AWS::Region
618
+ UserPoolId:
619
+ Description: Cognito User Pool (identity / JWTs).
620
+ Value: !Ref UserPool
621
+ UserPoolClientId:
622
+ Description: App client id — the token audience rastack-server pins.
623
+ Value: !Ref UserPoolClient
624
+ IdentityPoolId:
625
+ Description: Cognito Identity Pool (temp S3 credentials, sub-scoped).
626
+ Value: !Ref IdentityPool
627
+ JwksUrl:
628
+ Description: JWKS to bake into the Lambda bundle at build time.
629
+ Value: !Sub "https://cognito-idp.${AWS::Region}.amazonaws.com/${UserPool}/.well-known/jwks.json"
630
+ WarehouseBucketName:
631
+ Description: S3 Iceberg warehouse — the system of record.
632
+ Value: !Ref WarehouseBucket
633
+ AuditLogBucketName:
634
+ Description: S3 access logs + CloudTrail data events for the warehouse.
635
+ Value: !Ref AuditLogBucket