22
33import com .unicorn .constructs .EksCluster ;
44import software .amazon .awscdk .*;
5+ import software .amazon .awscdk .services .apigateway .*;
56import software .amazon .awscdk .services .ec2 .*;
67import software .amazon .awscdk .services .iam .*;
78import software .amazon .awscdk .services .lambda .*;
89import software .amazon .awscdk .services .logs .LogGroup ;
910import software .amazon .awscdk .services .logs .RetentionDays ;
1011import software .amazon .awscdk .services .s3 .Bucket ;
11- import software .amazon .awscdk .services .sns .Topic ;
12- import software .amazon .awscdk .services .sns .TopicPolicy ;
13- import software .amazon .awscdk .services .sns .subscriptions .LambdaSubscription ;
14- import software .amazon .awscdk .services .eks .CfnCluster ;
1512import software .constructs .Construct ;
1613
1714import java .util .List ;
@@ -23,11 +20,8 @@ public class InfrastructureMonitoringJVM extends Construct {
2320 public InfrastructureMonitoringJVM (Construct scope , String id , Bucket s3Bucket , EksCluster eksCluster , IVpc vpc ) {
2421 super (scope , id );
2522
26- // Create Lambda function first (from InfrastructureLambdaBedrock)
27- Function threadDumpFunction = createThreadDumpLambda (s3Bucket , eksCluster , vpc );
28-
29- // Create monitoring infrastructure (from MonitoringConstruct)
30- createMonitoringInfrastructure (vpc , eksCluster .getCluster (), threadDumpFunction );
23+ // Create Lambda function
24+ createThreadDumpLambda (s3Bucket , eksCluster , vpc );
3125 }
3226
3327 private Function createThreadDumpLambda (Bucket s3Bucket , EksCluster eksCluster , IVpc vpc ) {
@@ -62,17 +56,20 @@ private Function createThreadDumpLambda(Bucket s3Bucket, EksCluster eksCluster,
6256 "Allow Lambda access to Kubernetes API"
6357 );
6458
65- // IAM Role for Bedrock
66- Role bedrockRole = Role .Builder .create (this , "BedrockAccessRole" )
67- .assumedBy (new ServicePrincipal ("bedrock.amazonaws.com" ))
68- .description ("Role for Bedrock Claude 3.7 access" )
59+ // Create separate security group for VPC endpoint
60+ SecurityGroup vpcEndpointSg = SecurityGroup .Builder .create (this , "VpcEndpointSecurityGroup" )
61+ .vpc (vpc )
62+ .securityGroupName ("unicornstore-apigw-vpce-sg" )
63+ .description ("Security group for API Gateway VPC endpoint" )
64+ .allowAllOutbound (true )
6965 .build ();
7066
71- bedrockRole .addToPolicy (PolicyStatement .Builder .create ()
72- .effect (Effect .ALLOW )
73- .actions (List .of ("bedrock:InvokeModel" , "bedrock:ListFoundationModels" ))
74- .resources (List .of ("arn:aws:bedrock:*:*:inference-profile/us.anthropic.claude-3-7-sonnet-20250219-v1:0" ))
75- .build ());
67+ // Allow VPC traffic to access API Gateway via VPC endpoint
68+ vpcEndpointSg .addIngressRule (
69+ Peer .ipv4 (vpc .getVpcCidrBlock ()),
70+ Port .tcp (443 ),
71+ "Allow VPC traffic to API Gateway VPC endpoint"
72+ );
7673
7774 // IAM Role for Lambda
7875 Role lambdaRole = Role .Builder .create (this , "LambdaBedrockRole" )
@@ -90,12 +87,12 @@ private Function createThreadDumpLambda(Bucket s3Bucket, EksCluster eksCluster,
9087 .effect (Effect .ALLOW )
9188 .actions (List .of (
9289 "logs:CreateLogGroup" ,
93- "logs:CreateLogStream" ,
90+ "logs:CreateLogStream" ,
9491 "logs:PutLogEvents"
9592 ))
9693 .resources (List .of (
97- String .format ("arn:aws:logs:%s:%s:log-group:/aws/lambda/unicornstore-thread-dump-lambda*" ,
98- Stack .of (this ).getRegion (),
94+ String .format ("arn:aws:logs:%s:%s:log-group:/aws/lambda/unicornstore-thread-dump-lambda*" ,
95+ Stack .of (this ).getRegion (),
9996 Stack .of (this ).getAccount ())
10097 ))
10198 .build ());
@@ -108,7 +105,9 @@ private Function createThreadDumpLambda(Bucket s3Bucket, EksCluster eksCluster,
108105 "bedrock:InvokeModelWithResponseStream"
109106 ))
110107 .resources (List .of (
111- "arn:aws:bedrock:*:*:inference-profile/us.anthropic.claude-3-7-sonnet-20250219-v1:0"
108+ "arn:aws:bedrock:*:*:inference-profile/us.anthropic.claude-3-7-sonnet-20250219-v1:0" ,
109+ "arn:aws:bedrock:*:*:foundation-model/anthropic.claude-3-7-sonnet-20250219-v1:0" ,
110+ "arn:aws:bedrock:*:*:foundation-model/openai.gpt-oss-120b-1:0"
112111 ))
113112 .build ());
114113
@@ -204,44 +203,50 @@ private Function createThreadDumpLambda(Bucket s3Bucket, EksCluster eksCluster,
204203 .removalPolicy (RemovalPolicy .DESTROY )
205204 .build ();
206205
207- FunctionUrl .Builder .create (this , "ThreadDumpFunctionUrl" )
208- .function (threadDumpFunction )
209- .authType (FunctionUrlAuthType .NONE )
206+ // Create VPC endpoint for API Gateway
207+ InterfaceVpcEndpoint apiGatewayVpcEndpoint = InterfaceVpcEndpoint .Builder .create (this , "ApiGatewayVpcEndpoint" )
208+ .vpc (vpc )
209+ .service (InterfaceVpcEndpointAwsService .APIGATEWAY )
210+ .subnets (SubnetSelection .builder ()
211+ .subnetType (SubnetType .PRIVATE_WITH_EGRESS )
212+ .build ())
213+ .privateDnsEnabled (true )
214+ .securityGroups (List .of (lambdaSg ))
215+ .build ();
216+
217+ // Create private API Gateway
218+ RestApi api = RestApi .Builder .create (this , "ThreadDumpApi" )
219+ .restApiName ("unicornstore-thread-dump-api" )
220+ .endpointConfiguration (EndpointConfiguration .builder ()
221+ .types (List .of (EndpointType .PRIVATE ))
222+ .vpcEndpoints (List .of (apiGatewayVpcEndpoint ))
223+ .build ())
224+ .policy (PolicyDocument .Builder .create ()
225+ .statements (List .of (
226+ PolicyStatement .Builder .create ()
227+ .effect (Effect .ALLOW )
228+ .principals (List .of (new AnyPrincipal ()))
229+ .actions (List .of ("execute-api:Invoke" ))
230+ .resources (List .of ("*" ))
231+ .conditions (Map .of (
232+ "StringEquals" , Map .of (
233+ "aws:SourceVpce" , apiGatewayVpcEndpoint .getVpcEndpointId ()
234+ )
235+ ))
236+ .build ()
237+ ))
238+ .build ())
210239 .build ();
211240
212- threadDumpFunction .addPermission ("AllowPublicInvocation" ,
213- software .amazon .awscdk .services .lambda .Permission .builder ()
214- .principal (new AnyPrincipal ())
215- .action ("lambda:InvokeFunctionUrl" )
216- .functionUrlAuthType (FunctionUrlAuthType .NONE )
217- .build ());
241+ // Add Lambda integration
242+ LambdaIntegration integration = new LambdaIntegration (threadDumpFunction );
243+ api .getRoot ().addMethod ("POST" , integration );
244+ api .getRoot ().addProxy ();
218245
219246 if (eksCluster != null ) {
220247 eksCluster .createAccessEntry (lambdaRole .getRoleArn (), eksCluster .getCluster ().getName (), "lambda-eks-access-role" );
221248 }
222249
223250 return threadDumpFunction ;
224251 }
225-
226- private void createMonitoringInfrastructure (IVpc vpc , CfnCluster eksCluster , Function alertHandlerLambda ) {
227- Topic alarmTopic = Topic .Builder .create (this , "AlarmTopic" )
228- .topicName ("UnicornStoreAlarms" )
229- .displayName ("Unicorn Store Alarms" )
230- .build ();
231-
232- TopicPolicy .Builder .create (this , "AlarmTopicPolicy" )
233- .topics (List .of (alarmTopic ))
234- .build ()
235- .getDocument ()
236- .addStatements (PolicyStatement .Builder .create ()
237- .effect (Effect .DENY )
238- .actions (List .of ("sns:Publish" ))
239- .principals (List .of (new AnyPrincipal ()))
240- .resources (List .of (alarmTopic .getTopicArn ()))
241- .conditions (Map .of ("Bool" , Map .of ("aws:SecureTransport" , "false" )))
242- .build ());
243-
244- alarmTopic .addSubscription (new LambdaSubscription (alertHandlerLambda ));
245- }
246-
247252}
0 commit comments