-
Notifications
You must be signed in to change notification settings - Fork 1k
Expand file tree
/
Copy pathsfn-invoicing-bedrock-chargeback-stack.ts
More file actions
121 lines (101 loc) · 4.16 KB
/
Copy pathsfn-invoicing-bedrock-chargeback-stack.ts
File metadata and controls
121 lines (101 loc) · 4.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// Copyright 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as sfn from 'aws-cdk-lib/aws-stepfunctions';
import * as tasks from 'aws-cdk-lib/aws-stepfunctions-tasks';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as path from 'path';
export class SfnInvoicingBedrockChargebackStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const modelId = this.node.tryGetContext('modelId') ||
'us.anthropic.claude-sonnet-4-20250514-v1:0';
// DynamoDB table for chargeback ledger
const chargebackTable = new dynamodb.Table(this, 'ChargebackLedger', {
partitionKey: { name: 'invoiceUnitId', type: dynamodb.AttributeType.STRING },
sortKey: { name: 'period', type: dynamodb.AttributeType.STRING },
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
// Lambda: List Invoice Units
const listUnitsFn = new lambda.Function(this, 'ListInvoiceUnits', {
runtime: lambda.Runtime.PYTHON_3_12,
handler: 'list_units.handler',
code: lambda.Code.fromAsset(path.join(__dirname, '..', '..', 'src')),
timeout: cdk.Duration.seconds(30),
memorySize: 256,
});
listUnitsFn.addToRolePolicy(new iam.PolicyStatement({
actions: ['invoicing:ListInvoiceUnits'],
resources: ['*'], // Invoicing API does not support resource-level permissions
}));
// Lambda: Fetch Invoices per Unit
const fetchInvoicesFn = new lambda.Function(this, 'FetchInvoices', {
runtime: lambda.Runtime.PYTHON_3_12,
handler: 'fetch_invoices.handler',
code: lambda.Code.fromAsset(path.join(__dirname, '..', '..', 'src')),
timeout: cdk.Duration.seconds(60),
memorySize: 256,
});
fetchInvoicesFn.addToRolePolicy(new iam.PolicyStatement({
actions: ['invoicing:ListInvoiceSummaries', 'sts:GetCallerIdentity'],
resources: ['*'], // Invoicing API does not support resource-level permissions
}));
// Lambda: Bedrock Analysis + DynamoDB write
const analysisFn = new lambda.Function(this, 'BedrockAnalysis', {
runtime: lambda.Runtime.PYTHON_3_12,
handler: 'analysis.handler',
code: lambda.Code.fromAsset(path.join(__dirname, '..', '..', 'src')),
timeout: cdk.Duration.seconds(120),
memorySize: 512,
environment: {
MODEL_ID: modelId,
TABLE_NAME: chargebackTable.tableName,
},
});
analysisFn.addToRolePolicy(new iam.PolicyStatement({
actions: ['bedrock:InvokeModel'],
resources: [
`arn:aws:bedrock:${this.region}:${this.account}:inference-profile/${modelId}`,
'arn:aws:bedrock:*::foundation-model/*',
],
}));
chargebackTable.grantWriteData(analysisFn);
// Step Functions definition
const listUnitsTask = new tasks.LambdaInvoke(this, 'ListUnitsTask', {
lambdaFunction: listUnitsFn,
outputPath: '$.Payload',
});
const fetchInvoicesTask = new tasks.LambdaInvoke(this, 'FetchInvoicesTask', {
lambdaFunction: fetchInvoicesFn,
outputPath: '$.Payload',
});
const analysisTask = new tasks.LambdaInvoke(this, 'AnalysisTask', {
lambdaFunction: analysisFn,
outputPath: '$.Payload',
});
// Map state: iterate over each invoice unit in parallel
const mapState = new sfn.Map(this, 'ProcessEachUnit', {
itemsPath: '$.units',
maxConcurrency: 5,
});
mapState.itemProcessor(
fetchInvoicesTask.next(analysisTask)
);
const definition = listUnitsTask.next(mapState);
const stateMachine = new sfn.StateMachine(this, 'InvoiceReconciliation', {
definitionBody: sfn.DefinitionBody.fromChainable(definition),
timeout: cdk.Duration.minutes(15),
});
// Outputs
new cdk.CfnOutput(this, 'StateMachineArn', {
value: stateMachine.stateMachineArn,
});
new cdk.CfnOutput(this, 'ChargebackTableName', {
value: chargebackTable.tableName,
});
}
}