konokenj.cdk-api-mcp-server 0.31.0__py3-none-any.whl → 0.33.0__py3-none-any.whl
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.
Potentially problematic release.
This version of konokenj.cdk-api-mcp-server might be problematic. Click here for more details.
- cdk_api_mcp_server/__about__.py +1 -1
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigatewayv2/integ.api-dualstack.ts +3 -4
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigatewayv2/integ.api.ts +2 -4
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigatewayv2/integ.stage.ts +7 -20
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigatewayv2-authorizers/integ.iam.ts +34 -38
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigatewayv2-integrations/integ.sqs.ts +58 -71
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-cloudfront/README.md +292 -1
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-cloudwatch/README.md +71 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-cloudwatch/integ.alarm-and-dashboard.ts +12 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-cloudwatch/integ.dashboard-with-metric-id-and-visible.ts +70 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-codebuild/README.md +67 -3
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-codebuild/integ.project-s3-cache.ts +71 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-ecr-assets/integ.assets-docker.ts +6 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-ecs/integ.availability-zone-rebalancing.ts +14 -4
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-ecs/integ.enable-execute-command.ts +35 -29
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-ecs/integ.exec-command.ts +16 -22
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-ecs/integ.lb-awsvpc-nw.ts +26 -16
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-ecs/integ.pseudo-terminal.ts +18 -8
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-events/integ.api-destination.ts +42 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-events-targets/README.md +7 -2
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-iam/integ.custom-permissions-boundary-aspect.ts +50 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-kinesisfirehose/README.md +3 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-kinesisfirehose/integ.s3-bucket.lit.ts +1 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-lambda/integ.lambda-policy-with-token-resolution.ts +46 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-rds/README.md +19 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-rds/integ.cluster-lookup.ts +100 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-route53-targets/integ.elastic-beanstalk-hostedzoneid.ts +1 -1
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-stepfunctions-tasks/integ.invoke-jsonata.ts +87 -80
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-stepfunctions-tasks/integ.invoke.ts +87 -69
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-stepfunctions-tasks/integ.start-job-run.ts +96 -43
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-synthetics/README.md +40 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-synthetics/integ.canary-resources-to-replicate-tags.ts +36 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-synthetics/integ.canary-retry.ts +32 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/cx-api/FEATURE_FLAGS.md +29 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/pipelines/integ.pipeline-with-customsynthesizer.ts +105 -0
- {konokenj_cdk_api_mcp_server-0.31.0.dist-info → konokenj_cdk_api_mcp_server-0.33.0.dist-info}/METADATA +2 -2
- {konokenj_cdk_api_mcp_server-0.31.0.dist-info → konokenj_cdk_api_mcp_server-0.33.0.dist-info}/RECORD +40 -31
- {konokenj_cdk_api_mcp_server-0.31.0.dist-info → konokenj_cdk_api_mcp_server-0.33.0.dist-info}/WHEEL +0 -0
- {konokenj_cdk_api_mcp_server-0.31.0.dist-info → konokenj_cdk_api_mcp_server-0.33.0.dist-info}/entry_points.txt +0 -0
- {konokenj_cdk_api_mcp_server-0.31.0.dist-info → konokenj_cdk_api_mcp_server-0.33.0.dist-info}/licenses/LICENSE.txt +0 -0
cdk_api_mcp_server/__about__.py
CHANGED
cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigatewayv2/integ.api-dualstack.ts
CHANGED
|
@@ -4,14 +4,13 @@ import * as cdk from 'aws-cdk-lib';
|
|
|
4
4
|
import * as apigw from 'aws-cdk-lib/aws-apigatewayv2';
|
|
5
5
|
|
|
6
6
|
const app = new cdk.App();
|
|
7
|
-
const stack = new cdk.Stack(app, '
|
|
7
|
+
const stack = new cdk.Stack(app, 'DualStackWebsocketApiStack');
|
|
8
8
|
|
|
9
|
-
new apigw.
|
|
10
|
-
routeSelectionExpression: true,
|
|
9
|
+
new apigw.WebSocketApi(stack, 'WebSocketApi', {
|
|
11
10
|
ipAddressType: apigw.IpAddressType.DUAL_STACK,
|
|
12
11
|
});
|
|
13
12
|
|
|
14
|
-
new IntegTest(app, '
|
|
13
|
+
new IntegTest(app, 'DualStackWebsocketApiInteg', {
|
|
15
14
|
testCases: [stack],
|
|
16
15
|
});
|
|
17
16
|
|
|
@@ -6,11 +6,9 @@ import * as apigw from 'aws-cdk-lib/aws-apigatewayv2';
|
|
|
6
6
|
const app = new cdk.App();
|
|
7
7
|
const stack = new cdk.Stack(app, 'aws-cdk-aws-apigatewayv2');
|
|
8
8
|
|
|
9
|
-
new apigw.
|
|
10
|
-
routeSelectionExpression: true,
|
|
11
|
-
});
|
|
9
|
+
new apigw.WebSocketApi(stack, 'WebSocketApi');
|
|
12
10
|
|
|
13
|
-
new IntegTest(app, '
|
|
11
|
+
new IntegTest(app, 'web-socket-api', {
|
|
14
12
|
testCases: [stack],
|
|
15
13
|
});
|
|
16
14
|
|
|
@@ -1,33 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { IntegTest } from '@aws-cdk/integ-tests-alpha';
|
|
3
2
|
import * as cdk from 'aws-cdk-lib';
|
|
4
|
-
import * as
|
|
5
|
-
import * as apigw from 'aws-cdk-lib/aws-apigateway';
|
|
6
|
-
import * as logs from 'aws-cdk-lib/aws-logs';
|
|
3
|
+
import * as apigw from 'aws-cdk-lib/aws-apigatewayv2';
|
|
7
4
|
|
|
8
5
|
const app = new cdk.App();
|
|
9
|
-
const stack = new cdk.Stack(app, 'aws-cdk-aws-apigatewayv2-
|
|
6
|
+
const stack = new cdk.Stack(app, 'aws-cdk-aws-apigatewayv2-websocket-stage');
|
|
10
7
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
httpApi,
|
|
8
|
+
const webSocketApi = new apigw.WebSocketApi(stack, 'WebSocketApi');
|
|
9
|
+
new apigw.WebSocketStage(stack, 'WebSocketStage', {
|
|
10
|
+
webSocketApi,
|
|
11
|
+
stageName: 'dev',
|
|
16
12
|
throttle: {
|
|
17
13
|
rateLimit: 1000,
|
|
18
14
|
burstLimit: 1000,
|
|
19
15
|
},
|
|
20
16
|
detailedMetricsEnabled: true,
|
|
21
17
|
description: 'My Stage',
|
|
22
|
-
accessLogSettings: {
|
|
23
|
-
destination: new apigwv2.LogGroupLogDestination(testLogGroup),
|
|
24
|
-
format: apigw.AccessLogFormat.custom(JSON.stringify({
|
|
25
|
-
extendedRequestId: apigw.AccessLogField.contextExtendedRequestId(),
|
|
26
|
-
requestTime: apigw.AccessLogField.contextRequestTime(),
|
|
27
|
-
})),
|
|
28
|
-
},
|
|
29
18
|
});
|
|
30
19
|
|
|
31
|
-
|
|
32
|
-
testCases: [stack],
|
|
33
|
-
});
|
|
20
|
+
app.synth();
|
|
@@ -1,46 +1,53 @@
|
|
|
1
1
|
import * as apigatewayv2 from 'aws-cdk-lib/aws-apigatewayv2';
|
|
2
|
+
import { WebSocketLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations';
|
|
2
3
|
import * as iam from 'aws-cdk-lib/aws-iam';
|
|
4
|
+
import { Code, Function, Runtime } from 'aws-cdk-lib/aws-lambda';
|
|
3
5
|
import * as cdk from 'aws-cdk-lib';
|
|
4
|
-
import {
|
|
6
|
+
import { Stack } from 'aws-cdk-lib';
|
|
7
|
+
import * as integ from '@aws-cdk/integ-tests-alpha';
|
|
8
|
+
import { WebSocketIamAuthorizer } from 'aws-cdk-lib/aws-apigatewayv2-authorizers';
|
|
5
9
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
method: apigatewayv2.HttpMethod.GET,
|
|
12
|
-
uri: 'https://www.example.com/',
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const app = new cdk.App();
|
|
10
|
+
const app = new cdk.App({
|
|
11
|
+
postCliContext: {
|
|
12
|
+
'@aws-cdk/aws-lambda:useCdkManagedLogGroup': false,
|
|
13
|
+
},
|
|
14
|
+
});
|
|
18
15
|
const stack = new cdk.Stack(app, 'IntegApiGatewayV2Iam');
|
|
19
16
|
const user = new iam.User(stack, 'User');
|
|
20
17
|
const userAccessKey = new iam.AccessKey(stack, 'UserAccess', {
|
|
21
18
|
user,
|
|
22
19
|
});
|
|
23
20
|
|
|
24
|
-
const
|
|
25
|
-
|
|
21
|
+
const handler = new Function(stack, 'auth-function', {
|
|
22
|
+
runtime: Runtime.NODEJS_18_X,
|
|
23
|
+
code: Code.fromInline('exports.handler = () => {return true}'),
|
|
24
|
+
handler: 'index.handler',
|
|
26
25
|
});
|
|
27
26
|
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
const webSocketApi = new apigatewayv2.WebSocketApi(stack, 'WebSocketApi', {
|
|
28
|
+
connectRouteOptions: {
|
|
29
|
+
integration: new WebSocketLambdaIntegration('WebSocketLambdaIntegration', handler),
|
|
30
|
+
authorizer: new WebSocketIamAuthorizer(),
|
|
31
|
+
},
|
|
31
32
|
});
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
integration: new ExampleComIntegration('examplecom'),
|
|
37
|
-
path: '/books/{book}',
|
|
34
|
+
const arn = Stack.of(stack).formatArn({
|
|
35
|
+
service: 'execute-api',
|
|
36
|
+
resource: webSocketApi.apiId,
|
|
38
37
|
});
|
|
39
38
|
|
|
40
|
-
|
|
39
|
+
user.attachInlinePolicy(new iam.Policy(stack, 'AllowInvoke', {
|
|
40
|
+
statements: [
|
|
41
|
+
new iam.PolicyStatement({
|
|
42
|
+
actions: ['execute-api:Invoke'],
|
|
43
|
+
effect: iam.Effect.ALLOW,
|
|
44
|
+
resources: [arn],
|
|
45
|
+
}),
|
|
46
|
+
],
|
|
47
|
+
}));
|
|
41
48
|
|
|
42
|
-
new
|
|
43
|
-
|
|
49
|
+
new integ.IntegTest(app, 'ApiGatewayV2WebSocketIamTest', {
|
|
50
|
+
testCases: [stack],
|
|
44
51
|
});
|
|
45
52
|
|
|
46
53
|
new cdk.CfnOutput(stack, 'TESTACCESSKEYID', {
|
|
@@ -55,15 +62,4 @@ new cdk.CfnOutput(stack, 'TESTREGION', {
|
|
|
55
62
|
value: stack.region,
|
|
56
63
|
});
|
|
57
64
|
|
|
58
|
-
|
|
59
|
-
* Stack verification steps:
|
|
60
|
-
* * Get cURL version 7.75.0 or later so you can use the --aws-sigv4 option
|
|
61
|
-
* * Curl <url>/foo without sigv4 and expect a 403
|
|
62
|
-
* * Curl <url>/books/something without sigv4 and expect a 403
|
|
63
|
-
* * Curl <url>/foo with sigv4 from the authorized user and expect 200
|
|
64
|
-
* * Curl <url>/books/something with sigv4 from the authorized user and expect 200
|
|
65
|
-
*
|
|
66
|
-
* Reference:
|
|
67
|
-
* * Using cURL 7.75.0 or later via the official docker image: docker run --rm curlimages/curl -s -o/dev/null -w"%{http_code}" <url>
|
|
68
|
-
* * Args to enable sigv4 with authorized credentials: --user "$TESTACCESSKEYID:$TESTSECRETACCESSKEY" --aws-sigv4 "aws:amz:$TESTREGION:execute-api"
|
|
69
|
-
*/
|
|
65
|
+
app.synth();
|
|
@@ -1,85 +1,72 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { HttpMethod, PassthroughBehavior, WebSocketApi, WebSocketStage } from 'aws-cdk-lib/aws-apigatewayv2';
|
|
2
2
|
import * as sqs from 'aws-cdk-lib/aws-sqs';
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
3
|
+
import * as iam from 'aws-cdk-lib/aws-iam';
|
|
4
|
+
import { App, Stack, Aws } from 'aws-cdk-lib';
|
|
5
|
+
import { WebSocketAwsIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations';
|
|
6
|
+
import { IntegTest } from '@aws-cdk/integ-tests-alpha';
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
/*
|
|
9
|
+
* Stack verification steps:
|
|
10
|
+
* 1. Verify manually that the integration has type "AWS"
|
|
11
|
+
*/
|
|
9
12
|
|
|
10
|
-
const
|
|
13
|
+
const app = new App();
|
|
14
|
+
const stack = new Stack(app, 'integ-aws-websocket-sqs-integration');
|
|
11
15
|
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
methods: [apigwv2.HttpMethod.POST],
|
|
16
|
-
integration: new HttpSqsIntegration('defaultIntegration', {
|
|
17
|
-
queue,
|
|
18
|
-
}),
|
|
19
|
-
});
|
|
20
|
-
httpApi.addRoutes({
|
|
21
|
-
path: '/send-message',
|
|
22
|
-
methods: [apigwv2.HttpMethod.POST],
|
|
23
|
-
integration: new HttpSqsIntegration('sendMessageIntegration', {
|
|
24
|
-
queue,
|
|
25
|
-
subtype: apigwv2.HttpIntegrationSubtype.SQS_SEND_MESSAGE,
|
|
26
|
-
}),
|
|
27
|
-
});
|
|
28
|
-
httpApi.addRoutes({
|
|
29
|
-
path: '/receive-message',
|
|
30
|
-
methods: [apigwv2.HttpMethod.POST],
|
|
31
|
-
integration: new HttpSqsIntegration('receiveMessageIntegration', {
|
|
32
|
-
queue,
|
|
33
|
-
subtype: apigwv2.HttpIntegrationSubtype.SQS_RECEIVE_MESSAGE,
|
|
34
|
-
}),
|
|
35
|
-
});
|
|
36
|
-
httpApi.addRoutes({
|
|
37
|
-
path: '/delete-message',
|
|
38
|
-
methods: [apigwv2.HttpMethod.POST],
|
|
39
|
-
integration: new HttpSqsIntegration('deleteMessageIntegration', {
|
|
40
|
-
queue,
|
|
41
|
-
subtype: apigwv2.HttpIntegrationSubtype.SQS_DELETE_MESSAGE,
|
|
42
|
-
}),
|
|
16
|
+
const sqsMessageQueue = new sqs.Queue(stack, 'MessageSQSQueue', {
|
|
17
|
+
fifo: true,
|
|
18
|
+
queueName: 'MessageSQSQueue.fifo',
|
|
43
19
|
});
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
subtype: apigwv2.HttpIntegrationSubtype.SQS_PURGE_QUEUE,
|
|
50
|
-
}),
|
|
20
|
+
|
|
21
|
+
// API Gateway WebSocket API
|
|
22
|
+
const webSocketApi = new WebSocketApi(stack, 'webSocketApi', {
|
|
23
|
+
description: 'Send websocket data to SQS which is then processed by a Lambda 2',
|
|
24
|
+
routeSelectionExpression: '$request.body.action',
|
|
51
25
|
});
|
|
52
26
|
|
|
53
|
-
|
|
54
|
-
|
|
27
|
+
// Optionally, create a WebSocket stage
|
|
28
|
+
new WebSocketStage(stack, 'DevStage', {
|
|
29
|
+
webSocketApi: webSocketApi,
|
|
30
|
+
stageName: 'dev',
|
|
31
|
+
autoDeploy: true,
|
|
55
32
|
});
|
|
56
33
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
},
|
|
62
|
-
);
|
|
63
|
-
defaultAssertion.expect(integ.ExpectedResult.objectLike({ status: 200, statusText: 'OK' }));
|
|
34
|
+
// IAM Role for API Gateway
|
|
35
|
+
const webSocketApiRole = new iam.Role(stack, 'webSocketApiRole', {
|
|
36
|
+
assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com'),
|
|
37
|
+
});
|
|
64
38
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
39
|
+
webSocketApiRole.addToPolicy(
|
|
40
|
+
new iam.PolicyStatement({
|
|
41
|
+
actions: ['sqs:SendMessage'],
|
|
42
|
+
effect: iam.Effect.ALLOW,
|
|
43
|
+
resources: [sqsMessageQueue.queueArn],
|
|
44
|
+
}),
|
|
70
45
|
);
|
|
71
|
-
sendMessageAssertion.expect(integ.ExpectedResult.objectLike({ status: 200, statusText: 'OK' }));
|
|
72
46
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
47
|
+
webSocketApi.addRoute('$default', {
|
|
48
|
+
integration: new WebSocketAwsIntegration('SQSSendMessage', {
|
|
49
|
+
integrationUri: `arn:aws:apigateway:${Aws.REGION}:sqs:path/${Aws.ACCOUNT_ID}/${sqsMessageQueue.queueName}`,
|
|
50
|
+
integrationMethod: HttpMethod.POST,
|
|
51
|
+
credentialsRole: webSocketApiRole,
|
|
52
|
+
passthroughBehavior: PassthroughBehavior.NEVER,
|
|
53
|
+
templateSelectionExpression: '\\$default',
|
|
54
|
+
requestTemplates: {
|
|
55
|
+
$default: 'Action=SendMessage&MessageGroupId=$input.path(\'$.MessageGroupId\')&MessageDeduplicationId=$context.requestId&MessageAttribute.1.Name=connectionId&MessageAttribute.1.Value.StringValue=$context.connectionId&MessageAttribute.1.Value.DataType=String&MessageAttribute.2.Name=requestId&MessageAttribute.2.Value.StringValue=$context.requestId&MessageAttribute.2.Value.DataType=String&MessageBody=$input.json(\'$\')',
|
|
56
|
+
},
|
|
57
|
+
requestParameters: {
|
|
58
|
+
'integration.request.header.Content-Type': '\'application/x-www-form-urlencoded\'',
|
|
59
|
+
},
|
|
60
|
+
}),
|
|
61
|
+
});
|
|
79
62
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
63
|
+
new IntegTest(app, 'apigatewayv2-aws-integration-sqs-integ-test', {
|
|
64
|
+
testCases: [stack],
|
|
65
|
+
cdkCommandOptions: {
|
|
66
|
+
deploy: {
|
|
67
|
+
args: {
|
|
68
|
+
rollback: true,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
83
71
|
},
|
|
84
|
-
);
|
|
85
|
-
purgeQueueAssertion.expect(integ.ExpectedResult.objectLike({ status: 200, statusText: 'OK' }));
|
|
72
|
+
});
|
|
@@ -67,6 +67,297 @@ new cloudfront.Distribution(this, 'myDist', {
|
|
|
67
67
|
defaultBehavior: { origin: new origins.HttpOrigin('www.example.com') },
|
|
68
68
|
});
|
|
69
69
|
```
|
|
70
|
+
### CloudFront SaaS Manager resources
|
|
71
|
+
|
|
72
|
+
#### Multi-tenant distribution and tenant providing ACM certificates
|
|
73
|
+
You can use Cloudfront to build multi-tenant distributions to house applications.
|
|
74
|
+
|
|
75
|
+
To create a multi-tenant distribution w/parameters, create a Distribution construct, and then update DistributionConfig in the CfnDistribution to use connectionMode: "tenant-only"
|
|
76
|
+
|
|
77
|
+
Then create a tenant
|
|
78
|
+
```ts
|
|
79
|
+
// Create the simple Origin
|
|
80
|
+
const myBucket = new s3.Bucket(this, 'myBucket');
|
|
81
|
+
const s3Origin = origins.S3BucketOrigin.withOriginAccessControl(myBucket, {
|
|
82
|
+
originAccessLevels: [cloudfront.AccessLevel.READ, cloudfront.AccessLevel.LIST],
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Create the Distribution construct
|
|
86
|
+
const myMultiTenantDistribution = new cloudfront.Distribution(this, 'distribution', {
|
|
87
|
+
defaultBehavior: {
|
|
88
|
+
origin: s3Origin,
|
|
89
|
+
},
|
|
90
|
+
defaultRootObject: 'index.html', // recommended to specify
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Access the underlying L1 CfnDistribution to configure SaaS Manager properties which are not yet available in the L2 Distribution construct
|
|
94
|
+
const cfnDistribution = myMultiTenantDistribution.node.defaultChild as cloudfront.CfnDistribution;
|
|
95
|
+
|
|
96
|
+
const defaultCacheBehavior: cloudfront.CfnDistribution.DefaultCacheBehaviorProperty = {
|
|
97
|
+
targetOriginId: myBucket.bucketArn,
|
|
98
|
+
viewerProtocolPolicy: 'allow-all',
|
|
99
|
+
compress: false,
|
|
100
|
+
allowedMethods: ['GET', 'HEAD'],
|
|
101
|
+
cachePolicyId: cloudfront.CachePolicy.CACHING_OPTIMIZED.cachePolicyId
|
|
102
|
+
};
|
|
103
|
+
// Create the updated distributionConfig
|
|
104
|
+
const distributionConfig: cloudfront.CfnDistribution.DistributionConfigProperty = {
|
|
105
|
+
defaultCacheBehavior: defaultCacheBehavior,
|
|
106
|
+
enabled: true,
|
|
107
|
+
// the properties below are optional
|
|
108
|
+
connectionMode: 'tenant-only',
|
|
109
|
+
origins: [
|
|
110
|
+
{
|
|
111
|
+
id: myBucket.bucketArn,
|
|
112
|
+
domainName: myBucket.bucketDomainName,
|
|
113
|
+
s3OriginConfig: {},
|
|
114
|
+
originPath: "/{{tenantName}}"
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
tenantConfig: {
|
|
118
|
+
parameterDefinitions: [
|
|
119
|
+
{
|
|
120
|
+
definition: {
|
|
121
|
+
stringSchema: {
|
|
122
|
+
required: false,
|
|
123
|
+
// the properties below are optional
|
|
124
|
+
comment: 'tenantName',
|
|
125
|
+
defaultValue: 'root',
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
name: 'tenantName',
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// Override the distribution configuration to enable multi-tenancy.
|
|
135
|
+
cfnDistribution.distributionConfig = distributionConfig;
|
|
136
|
+
|
|
137
|
+
// Create a distribution tenant using an existing ACM certificate
|
|
138
|
+
const cfnDistributionTenant = new cloudfront.CfnDistributionTenant(this, 'distribution-tenant', {
|
|
139
|
+
distributionId: myMultiTenantDistribution.distributionId,
|
|
140
|
+
domains: ['my-tenant.my.domain.com'],
|
|
141
|
+
name: 'my-tenant',
|
|
142
|
+
enabled: true,
|
|
143
|
+
parameters: [ // Override the default 'tenantName' parameter (root) defined in the multi-tenant distribution.
|
|
144
|
+
{
|
|
145
|
+
name: 'tenantName',
|
|
146
|
+
value: 'app',
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
customizations: {
|
|
150
|
+
certificate: {
|
|
151
|
+
arn: 'REPLACE_WITH_ARN', // Certificate must be in us-east-1 region and cover 'my-tenant.my.domain.com'
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
#### Multi-tenant distribution and tenant with CloudFront-hosted certificate
|
|
158
|
+
A distribution tenant with CloudFront-hosted domain validation is useful if you don't currently have traffic to the domain.
|
|
159
|
+
|
|
160
|
+
Start by creating a parent multi-tenant distribution, then create the distribution tenant.
|
|
161
|
+
```ts
|
|
162
|
+
import * as route53 from 'aws-cdk-lib/aws-route53';
|
|
163
|
+
|
|
164
|
+
// Create the simple Origin
|
|
165
|
+
const myBucket = new s3.Bucket(this, 'myBucket');
|
|
166
|
+
const s3Origin = origins.S3BucketOrigin.withOriginAccessControl(myBucket, {
|
|
167
|
+
originAccessLevels: [cloudfront.AccessLevel.READ, cloudfront.AccessLevel.LIST],
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// Create the Distribution construct
|
|
171
|
+
const myMultiTenantDistribution = new cloudfront.Distribution(this, 'cf-hosted-distribution', {
|
|
172
|
+
defaultBehavior: {
|
|
173
|
+
origin: s3Origin,
|
|
174
|
+
},
|
|
175
|
+
defaultRootObject: 'index.html', // recommended to specify
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Access the underlying L1 CfnDistribution to configure SaaS Manager properties which are not yet available in the L2 Distribution construct
|
|
179
|
+
const cfnDistribution = myMultiTenantDistribution.node.defaultChild as cloudfront.CfnDistribution;
|
|
180
|
+
|
|
181
|
+
const defaultCacheBehavior: cloudfront.CfnDistribution.DefaultCacheBehaviorProperty = {
|
|
182
|
+
targetOriginId: myBucket.bucketArn,
|
|
183
|
+
viewerProtocolPolicy: 'allow-all',
|
|
184
|
+
compress: false,
|
|
185
|
+
allowedMethods: ['GET', 'HEAD'],
|
|
186
|
+
cachePolicyId: cloudfront.CachePolicy.CACHING_OPTIMIZED.cachePolicyId
|
|
187
|
+
};
|
|
188
|
+
// Create the updated distributionConfig
|
|
189
|
+
const distributionConfig: cloudfront.CfnDistribution.DistributionConfigProperty = {
|
|
190
|
+
defaultCacheBehavior: defaultCacheBehavior,
|
|
191
|
+
enabled: true,
|
|
192
|
+
// the properties below are optional
|
|
193
|
+
connectionMode: 'tenant-only',
|
|
194
|
+
origins: [
|
|
195
|
+
{
|
|
196
|
+
id: myBucket.bucketArn,
|
|
197
|
+
domainName: myBucket.bucketDomainName,
|
|
198
|
+
s3OriginConfig: {},
|
|
199
|
+
originPath: "/{{tenantName}}"
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
tenantConfig: {
|
|
203
|
+
parameterDefinitions: [
|
|
204
|
+
{
|
|
205
|
+
definition: {
|
|
206
|
+
stringSchema: {
|
|
207
|
+
required: false,
|
|
208
|
+
// the properties below are optional
|
|
209
|
+
comment: 'tenantName',
|
|
210
|
+
defaultValue: 'root',
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
name: 'tenantName',
|
|
214
|
+
},
|
|
215
|
+
],
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
// Override the distribution configuration to enable multi-tenancy.
|
|
220
|
+
cfnDistribution.distributionConfig = distributionConfig;
|
|
221
|
+
|
|
222
|
+
// Create a connection group and a cname record in an existing hosted zone to validate domain ownership
|
|
223
|
+
const connectionGroup = new cloudfront.CfnConnectionGroup(this, 'cf-hosted-connection-group', {
|
|
224
|
+
enabled: true,
|
|
225
|
+
ipv6Enabled: true,
|
|
226
|
+
name: 'my-connection-group',
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// Import the existing hosted zone info, replacing with your hostedZoneId and zoneName
|
|
230
|
+
const hostedZoneId = 'YOUR_HOSTED_ZONE_ID';
|
|
231
|
+
const zoneName = 'my.domain.com';
|
|
232
|
+
const hostedZone = route53.HostedZone.fromHostedZoneAttributes(this, 'hosted-zone', {
|
|
233
|
+
hostedZoneId,
|
|
234
|
+
zoneName,
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
const record = new route53.CnameRecord(this, 'cname-record', {
|
|
238
|
+
domainName: connectionGroup.attrRoutingEndpoint,
|
|
239
|
+
zone: hostedZone,
|
|
240
|
+
recordName: 'cf-hosted-tenant.my.domain.com',
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// Create the cloudfront-hosted tenant, passing in the previously created connection group
|
|
244
|
+
const cloudfrontHostedTenant = new cloudfront.CfnDistributionTenant(this, 'cf-hosted-tenant', {
|
|
245
|
+
distributionId: myMultiTenantDistribution.distributionId,
|
|
246
|
+
name: 'cf-hosted-tenant',
|
|
247
|
+
domains: ['cf-hosted-tenant.my.domain.com'],
|
|
248
|
+
connectionGroupId: connectionGroup.attrId,
|
|
249
|
+
enabled: true,
|
|
250
|
+
managedCertificateRequest: {
|
|
251
|
+
validationTokenHost: 'cloudfront'
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
#### Multi-tenant distribution and tenant with self-hosted certificate
|
|
257
|
+
A tenant with self-hosted domain validation is useful if you already have traffic to the domain and can't tolerate downtime during migration to multi-tenant architecture.
|
|
258
|
+
|
|
259
|
+
The tenant will be created, and the managed certificate will be awaiting validation of domain ownership. You can then validate domain ownership via http redirect or token file upload. [More details here](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/managed-cloudfront-certificates.html#complete-domain-ownership)
|
|
260
|
+
|
|
261
|
+
Traffic won't be migrated until you update your hosted zone to point the tenant domain to the CloudFront RoutingEndpoint.
|
|
262
|
+
|
|
263
|
+
Start by creating a parent multi-tenant distribution
|
|
264
|
+
```ts
|
|
265
|
+
// Create the simple Origin
|
|
266
|
+
const myBucket = new s3.Bucket(this, 'myBucket');
|
|
267
|
+
const s3Origin = origins.S3BucketOrigin.withOriginAccessControl(myBucket, {
|
|
268
|
+
originAccessLevels: [cloudfront.AccessLevel.READ, cloudfront.AccessLevel.LIST],
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// Create the Distribution construct
|
|
272
|
+
const myMultiTenantDistribution = new cloudfront.Distribution(this, 'cf-hosted-distribution', {
|
|
273
|
+
defaultBehavior: {
|
|
274
|
+
origin: s3Origin,
|
|
275
|
+
},
|
|
276
|
+
defaultRootObject: 'index.html', // recommended to specify
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// Access the underlying L1 CfnDistribution to configure SaaS Manager properties which are not yet available in the L2 Distribution construct
|
|
280
|
+
const cfnDistribution = myMultiTenantDistribution.node.defaultChild as cloudfront.CfnDistribution;
|
|
281
|
+
|
|
282
|
+
const defaultCacheBehavior: cloudfront.CfnDistribution.DefaultCacheBehaviorProperty = {
|
|
283
|
+
targetOriginId: myBucket.bucketArn,
|
|
284
|
+
viewerProtocolPolicy: 'allow-all',
|
|
285
|
+
compress: false,
|
|
286
|
+
allowedMethods: ['GET', 'HEAD'],
|
|
287
|
+
cachePolicyId: cloudfront.CachePolicy.CACHING_OPTIMIZED.cachePolicyId
|
|
288
|
+
};
|
|
289
|
+
// Create the updated distributionConfig
|
|
290
|
+
const distributionConfig: cloudfront.CfnDistribution.DistributionConfigProperty = {
|
|
291
|
+
defaultCacheBehavior: defaultCacheBehavior,
|
|
292
|
+
enabled: true,
|
|
293
|
+
// the properties below are optional
|
|
294
|
+
connectionMode: 'tenant-only',
|
|
295
|
+
origins: [
|
|
296
|
+
{
|
|
297
|
+
id: myBucket.bucketArn,
|
|
298
|
+
domainName: myBucket.bucketDomainName,
|
|
299
|
+
s3OriginConfig: {},
|
|
300
|
+
originPath: "/{{tenantName}}"
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
tenantConfig: {
|
|
304
|
+
parameterDefinitions: [
|
|
305
|
+
{
|
|
306
|
+
definition: {
|
|
307
|
+
stringSchema: {
|
|
308
|
+
required: false,
|
|
309
|
+
// the properties below are optional
|
|
310
|
+
comment: 'tenantName',
|
|
311
|
+
defaultValue: 'root',
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
name: 'tenantName',
|
|
315
|
+
},
|
|
316
|
+
],
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
// Override the distribution configuration to enable multi-tenancy.
|
|
321
|
+
cfnDistribution.distributionConfig = distributionConfig;
|
|
322
|
+
|
|
323
|
+
// Create a connection group so we have access to the RoutingEndpoint associated with the tenant we are about to create
|
|
324
|
+
const connectionGroup = new cloudfront.CfnConnectionGroup(this, 'self-hosted-connection-group', {
|
|
325
|
+
enabled: true,
|
|
326
|
+
ipv6Enabled: true,
|
|
327
|
+
name: 'self-hosted-connection-group',
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// Export the RoutingEndpoint, skip this step if you'd prefer to fetch it from the CloudFront console or via Cloudfront.ListConnectionGroups API
|
|
331
|
+
new CfnOutput(this, 'RoutingEndpoint', {
|
|
332
|
+
value: connectionGroup.attrRoutingEndpoint,
|
|
333
|
+
description: 'CloudFront Routing Endpoint to be added to my hosted zone CNAME records',
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
// Create a distribution tenant with a self-hosted domain.
|
|
337
|
+
const selfHostedTenant = new cloudfront.CfnDistributionTenant(this, 'self-hosted-tenant', {
|
|
338
|
+
distributionId: myMultiTenantDistribution.distributionId,
|
|
339
|
+
connectionGroupId: connectionGroup.attrId,
|
|
340
|
+
name: 'self-hosted-tenant',
|
|
341
|
+
domains: ['self-hosted-tenant.my.domain.com'],
|
|
342
|
+
enabled: true,
|
|
343
|
+
managedCertificateRequest: {
|
|
344
|
+
primaryDomainName: 'self-hosted-tenant.my.domain.com',
|
|
345
|
+
validationTokenHost: 'self-hosted',
|
|
346
|
+
},
|
|
347
|
+
});
|
|
348
|
+
```
|
|
349
|
+
While CDK is deploying, it will attempt to validate domain ownership by confirming that a validation token is served directly from your domain, or via http redirect.
|
|
350
|
+
|
|
351
|
+
[follow the steps here](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/managed-cloudfront-certificates.html#complete-domain-ownership) to complete domain setup before deploying this CDK stack, or while CDK is in the waiting state during tenant creation. Refer to the section "I have existing traffic"
|
|
352
|
+
|
|
353
|
+
A simple option for validating via http redirect, would be to add a rewrite rule like so to your server (Apache in this example)
|
|
354
|
+
```
|
|
355
|
+
RewriteEngine On
|
|
356
|
+
RewriteCond %{REQUEST_URI} ^/\.well-known/pki-validation/(.+)$ [NC]
|
|
357
|
+
RewriteRule ^(.*)$ https://validation.us-east-1.acm-validations.aws/%{ENV:AWS_ACCOUNT_ID}/.well-known/pki-validation/%1 [R=301,L]
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Then, when you are ready to accept traffic, follow the steps [here](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/managed-cloudfront-certificates.html#point-domains-to-cloudfront) using the RoutingEndpoint from above to configure DNS to point to CloudFront.
|
|
70
361
|
|
|
71
362
|
### VPC origins
|
|
72
363
|
|
|
@@ -548,7 +839,7 @@ const functionVersion = lambda.Version.fromVersionArn(this, 'Version', 'arn:aws:
|
|
|
548
839
|
|
|
549
840
|
new cloudfront.Distribution(this, 'distro', {
|
|
550
841
|
defaultBehavior: {
|
|
551
|
-
origin:
|
|
842
|
+
origin: origins.S3BucketOrigin.withOriginAccessControl(s3Bucket),
|
|
552
843
|
edgeLambdas: [
|
|
553
844
|
{
|
|
554
845
|
functionVersion,
|