From 22f9aa9452b20fc4944cd98ec72f40667c1d921c Mon Sep 17 00:00:00 2001 From: biffgaut <78155736+biffgaut@users.noreply.github.com> Date: Mon, 4 Dec 2023 07:46:33 -0500 Subject: [PATCH 01/14] chore(all): complete integration support infrastructure to reduce false positives in security scan (#1034) * CLean up false positives in scan * Suppress warnings * Clean up new Lambda functions --- ...ustomCloudfrontLoggingBucket.expected.json | 541 ++++++++++-------- ...eg.cftapi-customCloudfrontLoggingBucket.ts | 30 +- .../integ.cftapi-no-arguments.expected.json | 541 ++++++++++-------- .../test/integ.cftapi-no-arguments.ts | 30 +- .../test/integ.cfts3-existing-bucket.ts | 2 +- .../integ.cfts3-no-arguments.expected.json | 123 ++++ .../test/integ.cfts3-no-arguments.ts | 1 - .../eventbridge-kinesisfirehose-s3.test.ts | 2 +- .../integ.evtfhss3-existingLoggingBucket.ts | 2 +- .../test/integ.fars3-existing-resources.ts | 2 +- ...eg.iots3-iot-s3-defaultprops.expected.json | 123 ++++ .../test/integ.iots3-iot-s3-defaultprops.ts | 1 - .../integ.iots3-iot-s3-existing-bucket.ts | 2 +- .../test/integ.fhss3-pre-existing-bucket.ts | 2 +- ...integ.fhss3-pre-existing-logging-bucket.ts | 2 +- .../test/kinesisfirehose-s3.test.ts | 6 +- .../test/integ.kinfhss3-existing-bucket.ts | 2 +- .../integ.kinfhss3-existing-logging-bucket.ts | 2 +- .../kinesisstreams-kinesisfirehose-s3.test.ts | 4 +- ...teg.lamken-minimal-arguments.expected.json | 24 +- .../test/integ.lamken-minimal-arguments.ts | 5 +- .../test/integ.lamken-multiple-sources.ts | 2 +- .../test/integ.lamken-with-vpc.ts | 2 +- .../integ.lams3-deployFunction.expected.json | 123 ++++ .../test/integ.lams3-deployFunction.ts | 3 +- .../test/integ.lams3-pre-existing-bucket.ts | 2 +- .../aws-lambda-s3/test/lambda-s3.test.ts | 2 +- .../test/test.openapigateway-lambda.test.ts | 6 +- .../test/integ.s3lam-existing-s3-bucket.ts | 2 +- .../aws-s3-lambda/test/s3-lambda.test.ts | 2 +- .../test/integ.s3sns-existingS3Bucket.ts | 2 +- .../test/integ.s3sqs-existingLoggingBucket.ts | 2 +- .../test/integ.s3sqs-existingS3Bucket.ts | 2 +- .../test/integ.s3stp-pre-existing-bucket.ts | 2 +- ...s3-stepfunctions-no-argument.expected.json | 123 ++++ ...nteg.s3stp-s3-stepfunctions-no-argument.ts | 3 +- ...ing-waf-to-multiple-gateways.expected.json | 374 +++++++++++- ...afapi-existing-waf-to-multiple-gateways.ts | 3 +- .../integ.wafapi-no-arguments.expected.json | 187 +++++- .../test/integ.wafapi-no-arguments.ts | 3 +- ...teg.wafapi-partial-arguments.expected.json | 187 +++++- .../test/integ.wafapi-partial-arguments.ts | 3 +- .../test/test-helper.ts | 50 -- .../core/test/kinesis-analytics.test.ts | 2 +- .../core/test/s3-bucket.test.ts | 2 +- .../core/test/test-helper.ts | 56 +- 46 files changed, 1914 insertions(+), 678 deletions(-) delete mode 100644 source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/test-helper.ts diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-customCloudfrontLoggingBucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-customCloudfrontLoggingBucket.expected.json index 4396bedcb..2b77bbd2e 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-customCloudfrontLoggingBucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-customCloudfrontLoggingBucket.expected.json @@ -1,7 +1,7 @@ { "Description": "Integration Test for aws-cloudfront-apigateway custom Cloudfront Logging Bucket", "Resources": { - "LambdaFunctionServiceRole0C4CDE0B": { + "cftapicustomCloudfrontLoggingBucketapiFunctionServiceRole487119AE": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -16,235 +16,363 @@ ], "Version": "2012-10-17" }, - "Policies": [ + "ManagedPolicyArns": [ { - "PolicyDocument": { - "Statement": [ + "Fn::Join": [ + "", + [ + "arn:", { - "Action": [ - "logs:CreateLogGroup", - "logs:CreateLogStream", - "logs:PutLogEvents" - ], - "Effect": "Allow", - "Resource": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":logs:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":log-group:/aws/lambda/*" - ] - ] - } - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "LambdaFunctionServiceRolePolicy" + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] } ] } }, - "LambdaFunctionServiceRoleDefaultPolicy126C8897": { - "Type": "AWS::IAM::Policy", + "cftapicustomCloudfrontLoggingBucketapiFunctionA537178D": { + "Type": "AWS::Lambda::Function", "Properties": { - "PolicyDocument": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "42a35bbf0dec9ef0ac5b0dde87e71a1b8929e8d2d178dd09ccfb2c928ec0198c.zip" + }, + "Handler": ".handler", + "Role": { + "Fn::GetAtt": [ + "cftapicustomCloudfrontLoggingBucketapiFunctionServiceRole487119AE", + "Arn" + ] + }, + "Runtime": "nodejs16.x" + }, + "DependsOn": [ + "cftapicustomCloudfrontLoggingBucketapiFunctionServiceRole487119AE" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "Test Resource" + }, + { + "id": "W89", + "reason": "Test Resource" + }, + { + "id": "W92", + "reason": "Test Resource" + } + ] + } + } + }, + "cftapicustomCloudfrontLoggingBucketapiAuthFunctionServiceRole3CEABBB5": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { "Statement": [ { - "Action": [ - "xray:PutTraceSegments", - "xray:PutTelemetryRecords" - ], + "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": "*" + "Principal": { + "Service": "lambda.amazonaws.com" + } } ], "Version": "2012-10-17" }, - "PolicyName": "LambdaFunctionServiceRoleDefaultPolicy126C8897", - "Roles": [ + "ManagedPolicyArns": [ { - "Ref": "LambdaFunctionServiceRole0C4CDE0B" + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] } ] - }, - "Metadata": { - "cfn_nag": { - "rules_to_suppress": [ - { - "id": "W12", - "reason": "Lambda needs the following minimum required permissions to send trace data to X-Ray and access ENIs in a VPC." - } - ] - } } }, - "LambdaFunctionBF21E41F": { + "cftapicustomCloudfrontLoggingBucketapiAuthFunctionC39181D9": { "Type": "AWS::Lambda::Function", "Properties": { "Code": { "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "3a18fd3de4803f72260a058823accffd4f8d69986c6862a23cd86265f6cafa0f.zip" - }, - "Environment": { - "Variables": { - "AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1" - } + "S3Key": "42a35bbf0dec9ef0ac5b0dde87e71a1b8929e8d2d178dd09ccfb2c928ec0198c.zip" }, - "Handler": "index.handler", + "Handler": ".handler", "Role": { "Fn::GetAtt": [ - "LambdaFunctionServiceRole0C4CDE0B", + "cftapicustomCloudfrontLoggingBucketapiAuthFunctionServiceRole3CEABBB5", "Arn" ] }, - "Runtime": "nodejs16.x", - "TracingConfig": { - "Mode": "Active" - } + "Runtime": "nodejs16.x" }, "DependsOn": [ - "LambdaFunctionServiceRoleDefaultPolicy126C8897", - "LambdaFunctionServiceRole0C4CDE0B" + "cftapicustomCloudfrontLoggingBucketapiAuthFunctionServiceRole3CEABBB5" ], "Metadata": { "cfn_nag": { "rules_to_suppress": [ { "id": "W58", - "reason": "Lambda functions has the required permission to write CloudWatch Logs. It uses custom policy instead of arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole with tighter permissions." + "reason": "Test Resource" }, { "id": "W89", - "reason": "This is not a rule for the general case, just for specific use cases/industries" + "reason": "Test Resource" }, { "id": "W92", - "reason": "Impossible for us to define the correct concurrency for clients" + "reason": "Test Resource" } ] } } }, - "ApiAccessLogGroupCEA70788": { - "Type": "AWS::Logs::LogGroup", - "UpdateReplacePolicy": "Retain", - "DeletionPolicy": "Retain", - "Metadata": { - "cfn_nag": { - "rules_to_suppress": [ - { - "id": "W86", - "reason": "Retention period for CloudWatchLogs LogGroups are set to 'Never Expire' to preserve customer data indefinitely" - }, - { - "id": "W84", - "reason": "By default CloudWatchLogs LogGroups data is encrypted using the CloudWatch server-side encryption keys (AWS Managed Keys)" - } + "cftapicustomCloudfrontLoggingBucketapiAuthFunctioncftapicustomCloudfrontLoggingBucketcftapicustomCloudfrontLoggingBucketapiauthorizerFD948D42Permissions0550732B": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "cftapicustomCloudfrontLoggingBucketapiAuthFunctionC39181D9", + "Arn" + ] + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "cftapicustomCloudfrontLoggingBucketapiApiBB89469A" + }, + "/authorizers/", + { + "Ref": "cftapicustomCloudfrontLoggingBucketapiauthorizerEDC48D75" + } + ] ] } } }, - "LambdaRestApi95870433": { - "Type": "AWS::ApiGateway::RestApi", + "cftapicustomCloudfrontLoggingBucketapiauthorizerEDC48D75": { + "Type": "AWS::ApiGateway::Authorizer", "Properties": { - "EndpointConfiguration": { - "Types": [ - "REGIONAL" + "AuthorizerUri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + ":", + { + "Fn::GetAtt": [ + "cftapicustomCloudfrontLoggingBucketapiAuthFunctionC39181D9", + "Arn" + ] + } + ] + } + ] + }, + ":apigateway:", + { + "Fn::Select": [ + 3, + { + "Fn::Split": [ + ":", + { + "Fn::GetAtt": [ + "cftapicustomCloudfrontLoggingBucketapiAuthFunctionC39181D9", + "Arn" + ] + } + ] + } + ] + }, + ":lambda:path/2015-03-31/functions/", + { + "Fn::GetAtt": [ + "cftapicustomCloudfrontLoggingBucketapiAuthFunctionC39181D9", + "Arn" + ] + }, + "/invocations" + ] ] }, - "Name": "LambdaRestApi" + "IdentitySource": "method.request.header.Authorization", + "Name": "cftapicustomCloudfrontLoggingBucketcftapicustomCloudfrontLoggingBucketapiauthorizerFD948D42", + "RestApiId": { + "Ref": "cftapicustomCloudfrontLoggingBucketapiApiBB89469A" + }, + "Type": "REQUEST" + } + }, + "cftapicustomCloudfrontLoggingBucketapiApiBB89469A": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Name": "cftapi-customCloudfrontLoggingBucket-apiApi" } }, - "LambdaRestApiDeploymentBA640578812946cff1910fe2b8b339ee3a8d51c7": { + "cftapicustomCloudfrontLoggingBucketapiApiCloudWatchRoleF87873B7": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "apigateway.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs" + ] + ] + } + ] + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "cftapicustomCloudfrontLoggingBucketapiApiAccount77D61D99": { + "Type": "AWS::ApiGateway::Account", + "Properties": { + "CloudWatchRoleArn": { + "Fn::GetAtt": [ + "cftapicustomCloudfrontLoggingBucketapiApiCloudWatchRoleF87873B7", + "Arn" + ] + } + }, + "DependsOn": [ + "cftapicustomCloudfrontLoggingBucketapiApiBB89469A" + ], + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "cftapicustomCloudfrontLoggingBucketapiApiDeploymentDB7FF039005fcff962471e808f78ef9f07a5bed2": { "Type": "AWS::ApiGateway::Deployment", "Properties": { "Description": "Automatically created by the RestApi construct", "RestApiId": { - "Ref": "LambdaRestApi95870433" + "Ref": "cftapicustomCloudfrontLoggingBucketapiApiBB89469A" } }, "DependsOn": [ - "LambdaRestApiproxyANY93D43CC0", - "LambdaRestApiproxy9F99E187", - "LambdaRestApiANYA831AD87" + "cftapicustomCloudfrontLoggingBucketapiApiproxyANYEA322D75", + "cftapicustomCloudfrontLoggingBucketapiApiproxy98487F5F", + "cftapicustomCloudfrontLoggingBucketapiApiANYCCBB70F0" ], "Metadata": { "cfn_nag": { "rules_to_suppress": [ { - "id": "W45", - "reason": "ApiGateway has AccessLogging enabled in AWS::ApiGateway::Stage resource, but cfn_nag checks for it in AWS::ApiGateway::Deployment resource" + "id": "W68", + "reason": "Test Resource" } ] } } }, - "LambdaRestApiDeploymentStageprodB1F3862A": { + "cftapicustomCloudfrontLoggingBucketapiApiDeploymentStageprod71B9DCD4": { "Type": "AWS::ApiGateway::Stage", "Properties": { - "AccessLogSetting": { - "DestinationArn": { - "Fn::GetAtt": [ - "ApiAccessLogGroupCEA70788", - "Arn" - ] - }, - "Format": "{\"requestId\":\"$context.requestId\",\"ip\":\"$context.identity.sourceIp\",\"user\":\"$context.identity.user\",\"caller\":\"$context.identity.caller\",\"requestTime\":\"$context.requestTime\",\"httpMethod\":\"$context.httpMethod\",\"resourcePath\":\"$context.resourcePath\",\"status\":\"$context.status\",\"protocol\":\"$context.protocol\",\"responseLength\":\"$context.responseLength\"}" - }, "DeploymentId": { - "Ref": "LambdaRestApiDeploymentBA640578812946cff1910fe2b8b339ee3a8d51c7" + "Ref": "cftapicustomCloudfrontLoggingBucketapiApiDeploymentDB7FF039005fcff962471e808f78ef9f07a5bed2" }, - "MethodSettings": [ - { - "DataTraceEnabled": false, - "HttpMethod": "*", - "LoggingLevel": "INFO", - "ResourcePath": "/*" - } - ], "RestApiId": { - "Ref": "LambdaRestApi95870433" + "Ref": "cftapicustomCloudfrontLoggingBucketapiApiBB89469A" }, - "StageName": "prod", - "TracingEnabled": true + "StageName": "prod" + }, + "DependsOn": [ + "cftapicustomCloudfrontLoggingBucketapiApiAccount77D61D99" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W64", + "reason": "Test Resource" + }, + { + "id": "W69", + "reason": "Test Resource" + } + ] + } } }, - "LambdaRestApiproxy9F99E187": { + "cftapicustomCloudfrontLoggingBucketapiApiproxy98487F5F": { "Type": "AWS::ApiGateway::Resource", "Properties": { "ParentId": { "Fn::GetAtt": [ - "LambdaRestApi95870433", + "cftapicustomCloudfrontLoggingBucketapiApiBB89469A", "RootResourceId" ] }, "PathPart": "{proxy+}", "RestApiId": { - "Ref": "LambdaRestApi95870433" + "Ref": "cftapicustomCloudfrontLoggingBucketapiApiBB89469A" } } }, - "LambdaRestApiproxyANYApiPermissioncftapicustomCloudfrontLoggingBucketLambdaRestApi99E9E0A2ANYproxyD8618381": { + "cftapicustomCloudfrontLoggingBucketapiApiproxyANYApiPermissioncftapicustomCloudfrontLoggingBucketcftapicustomCloudfrontLoggingBucketapiApi78AFA9D9ANYproxyE8502938": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", "FunctionName": { "Fn::GetAtt": [ - "LambdaFunctionBF21E41F", + "cftapicustomCloudfrontLoggingBucketapiFunctionA537178D", "Arn" ] }, @@ -267,11 +395,11 @@ }, ":", { - "Ref": "LambdaRestApi95870433" + "Ref": "cftapicustomCloudfrontLoggingBucketapiApiBB89469A" }, "/", { - "Ref": "LambdaRestApiDeploymentStageprodB1F3862A" + "Ref": "cftapicustomCloudfrontLoggingBucketapiApiDeploymentStageprod71B9DCD4" }, "/*/*" ] @@ -279,13 +407,13 @@ } } }, - "LambdaRestApiproxyANYApiPermissionTestcftapicustomCloudfrontLoggingBucketLambdaRestApi99E9E0A2ANYproxy9C09767F": { + "cftapicustomCloudfrontLoggingBucketapiApiproxyANYApiPermissionTestcftapicustomCloudfrontLoggingBucketcftapicustomCloudfrontLoggingBucketapiApi78AFA9D9ANYproxy6DD665F5": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", "FunctionName": { "Fn::GetAtt": [ - "LambdaFunctionBF21E41F", + "cftapicustomCloudfrontLoggingBucketapiFunctionA537178D", "Arn" ] }, @@ -308,7 +436,7 @@ }, ":", { - "Ref": "LambdaRestApi95870433" + "Ref": "cftapicustomCloudfrontLoggingBucketapiApiBB89469A" }, "/test-invoke-stage/*/*" ] @@ -316,10 +444,13 @@ } } }, - "LambdaRestApiproxyANY93D43CC0": { + "cftapicustomCloudfrontLoggingBucketapiApiproxyANYEA322D75": { "Type": "AWS::ApiGateway::Method", "Properties": { - "AuthorizationType": "NONE", + "AuthorizationType": "CUSTOM", + "AuthorizerId": { + "Ref": "cftapicustomCloudfrontLoggingBucketapiauthorizerEDC48D75" + }, "HttpMethod": "ANY", "Integration": { "IntegrationHttpMethod": "POST", @@ -339,7 +470,7 @@ ":lambda:path/2015-03-31/functions/", { "Fn::GetAtt": [ - "LambdaFunctionBF21E41F", + "cftapicustomCloudfrontLoggingBucketapiFunctionA537178D", "Arn" ] }, @@ -349,10 +480,10 @@ } }, "ResourceId": { - "Ref": "LambdaRestApiproxy9F99E187" + "Ref": "cftapicustomCloudfrontLoggingBucketapiApiproxy98487F5F" }, "RestApiId": { - "Ref": "LambdaRestApi95870433" + "Ref": "cftapicustomCloudfrontLoggingBucketapiApiBB89469A" } }, "Metadata": { @@ -360,19 +491,19 @@ "rules_to_suppress": [ { "id": "W59", - "reason": "AWS::ApiGateway::Method AuthorizationType is set to 'NONE' because API Gateway behind CloudFront does not support AWS_IAM authentication" + "reason": "Test Resource" } ] } } }, - "LambdaRestApiANYApiPermissioncftapicustomCloudfrontLoggingBucketLambdaRestApi99E9E0A2ANY7163B9C3": { + "cftapicustomCloudfrontLoggingBucketapiApiANYApiPermissioncftapicustomCloudfrontLoggingBucketcftapicustomCloudfrontLoggingBucketapiApi78AFA9D9ANY9D88CB33": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", "FunctionName": { "Fn::GetAtt": [ - "LambdaFunctionBF21E41F", + "cftapicustomCloudfrontLoggingBucketapiFunctionA537178D", "Arn" ] }, @@ -395,11 +526,11 @@ }, ":", { - "Ref": "LambdaRestApi95870433" + "Ref": "cftapicustomCloudfrontLoggingBucketapiApiBB89469A" }, "/", { - "Ref": "LambdaRestApiDeploymentStageprodB1F3862A" + "Ref": "cftapicustomCloudfrontLoggingBucketapiApiDeploymentStageprod71B9DCD4" }, "/*/" ] @@ -407,13 +538,13 @@ } } }, - "LambdaRestApiANYApiPermissionTestcftapicustomCloudfrontLoggingBucketLambdaRestApi99E9E0A2ANY57980E7B": { + "cftapicustomCloudfrontLoggingBucketapiApiANYApiPermissionTestcftapicustomCloudfrontLoggingBucketcftapicustomCloudfrontLoggingBucketapiApi78AFA9D9ANY548DF46C": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", "FunctionName": { "Fn::GetAtt": [ - "LambdaFunctionBF21E41F", + "cftapicustomCloudfrontLoggingBucketapiFunctionA537178D", "Arn" ] }, @@ -436,7 +567,7 @@ }, ":", { - "Ref": "LambdaRestApi95870433" + "Ref": "cftapicustomCloudfrontLoggingBucketapiApiBB89469A" }, "/test-invoke-stage/*/" ] @@ -444,10 +575,13 @@ } } }, - "LambdaRestApiANYA831AD87": { + "cftapicustomCloudfrontLoggingBucketapiApiANYCCBB70F0": { "Type": "AWS::ApiGateway::Method", "Properties": { - "AuthorizationType": "NONE", + "AuthorizationType": "CUSTOM", + "AuthorizerId": { + "Ref": "cftapicustomCloudfrontLoggingBucketapiauthorizerEDC48D75" + }, "HttpMethod": "ANY", "Integration": { "IntegrationHttpMethod": "POST", @@ -467,7 +601,7 @@ ":lambda:path/2015-03-31/functions/", { "Fn::GetAtt": [ - "LambdaFunctionBF21E41F", + "cftapicustomCloudfrontLoggingBucketapiFunctionA537178D", "Arn" ] }, @@ -478,12 +612,12 @@ }, "ResourceId": { "Fn::GetAtt": [ - "LambdaRestApi95870433", + "cftapicustomCloudfrontLoggingBucketapiApiBB89469A", "RootResourceId" ] }, "RestApiId": { - "Ref": "LambdaRestApi95870433" + "Ref": "cftapicustomCloudfrontLoggingBucketapiApiBB89469A" } }, "Metadata": { @@ -491,101 +625,12 @@ "rules_to_suppress": [ { "id": "W59", - "reason": "AWS::ApiGateway::Method AuthorizationType is set to 'NONE' because API Gateway behind CloudFront does not support AWS_IAM authentication" + "reason": "Test Resource" } ] } } }, - "LambdaRestApiUsagePlanB4DF55D0": { - "Type": "AWS::ApiGateway::UsagePlan", - "Properties": { - "ApiStages": [ - { - "ApiId": { - "Ref": "LambdaRestApi95870433" - }, - "Stage": { - "Ref": "LambdaRestApiDeploymentStageprodB1F3862A" - }, - "Throttle": {} - } - ] - } - }, - "LambdaRestApiCloudWatchRoleF339D4E6": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "apigateway.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "Policies": [ - { - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "logs:CreateLogGroup", - "logs:CreateLogStream", - "logs:DescribeLogGroups", - "logs:DescribeLogStreams", - "logs:PutLogEvents", - "logs:GetLogEvents", - "logs:FilterLogEvents" - ], - "Effect": "Allow", - "Resource": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":logs:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":*" - ] - ] - } - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "LambdaRestApiCloudWatchRolePolicy" - } - ] - } - }, - "LambdaRestApiAccount": { - "Type": "AWS::ApiGateway::Account", - "Properties": { - "CloudWatchRoleArn": { - "Fn::GetAtt": [ - "LambdaRestApiCloudWatchRoleF339D4E6", - "Arn" - ] - } - }, - "DependsOn": [ - "LambdaRestApi95870433" - ] - }, "cfapigwSetHttpSecurityHeaders07A0F0C0": { "Type": "AWS::CloudFront::Function", "Properties": { @@ -810,7 +855,7 @@ [ "https://", { - "Ref": "LambdaRestApi95870433" + "Ref": "cftapicustomCloudfrontLoggingBucketapiApiBB89469A" }, ".execute-api.", { @@ -822,7 +867,7 @@ }, "/", { - "Ref": "LambdaRestApiDeploymentStageprodB1F3862A" + "Ref": "cftapicustomCloudfrontLoggingBucketapiApiDeploymentStageprod71B9DCD4" }, "/" ] @@ -843,7 +888,7 @@ [ "/", { - "Ref": "LambdaRestApiDeploymentStageprodB1F3862A" + "Ref": "cftapicustomCloudfrontLoggingBucketapiApiDeploymentStageprod71B9DCD4" } ] ] @@ -941,14 +986,14 @@ } }, "Outputs": { - "LambdaRestApiEndpointCCECE4C1": { + "cftapicustomCloudfrontLoggingBucketapiApiEndpoint6E0D4DE8": { "Value": { "Fn::Join": [ "", [ "https://", { - "Ref": "LambdaRestApi95870433" + "Ref": "cftapicustomCloudfrontLoggingBucketapiApiBB89469A" }, ".execute-api.", { @@ -960,7 +1005,7 @@ }, "/", { - "Ref": "LambdaRestApiDeploymentStageprodB1F3862A" + "Ref": "cftapicustomCloudfrontLoggingBucketapiApiDeploymentStageprod71B9DCD4" }, "/" ] diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-customCloudfrontLoggingBucket.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-customCloudfrontLoggingBucket.ts index 9c31a9136..9d3397c87 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-customCloudfrontLoggingBucket.ts +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-customCloudfrontLoggingBucket.ts @@ -15,9 +15,7 @@ import { App, Stack, RemovalPolicy } from "aws-cdk-lib"; import { CloudFrontToApiGateway } from "../lib"; import { BucketEncryption } from "aws-cdk-lib/aws-s3"; -import * as lambda from 'aws-cdk-lib/aws-lambda'; import * as defaults from '@aws-solutions-constructs/core'; -import * as api from 'aws-cdk-lib/aws-apigateway'; import { generateIntegStackName } from '@aws-solutions-constructs/core'; // Setup @@ -25,34 +23,10 @@ const app = new App(); const stack = new Stack(app, generateIntegStackName(__filename)); stack.templateOptions.description = 'Integration Test for aws-cloudfront-apigateway custom Cloudfront Logging Bucket'; -const inProps: lambda.FunctionProps = { - code: lambda.Code.fromAsset(`${__dirname}/lambda`), - runtime: lambda.Runtime.NODEJS_16_X, - handler: 'index.handler' -}; - -const func = defaults.deployLambdaFunction(stack, inProps); - -const regionalLambdaRestApiResponse = defaults.RegionalLambdaRestApi(stack, func); - -regionalLambdaRestApiResponse.api.methods.forEach((apiMethod) => { - // Override the API Gateway Authorization Type from AWS_IAM to NONE - const child = apiMethod.node.findChild('Resource') as api.CfnMethod; - if (child.authorizationType === 'AWS_IAM') { - child.addPropertyOverride('AuthorizationType', 'NONE'); - - defaults.addCfnSuppressRules(apiMethod, [ - { - id: "W59", - reason: `AWS::ApiGateway::Method AuthorizationType is set to 'NONE' because API Gateway behind CloudFront does not support AWS_IAM authentication`, - }, - ]); - - } -}); +const testApi = defaults.CreateTestApi(stack, `${generateIntegStackName(__filename)}-api`); new CloudFrontToApiGateway(stack, 'cf-apigw', { - existingApiGatewayObj: regionalLambdaRestApiResponse.api, + existingApiGatewayObj: testApi, cloudFrontLoggingBucketProps: { removalPolicy: RemovalPolicy.DESTROY, autoDeleteObjects: true, diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-no-arguments.expected.json index 911ae50a2..fe72b7872 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-no-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-no-arguments.expected.json @@ -1,7 +1,7 @@ { "Description": "Integration Test for aws-cloudfront-apigateway", "Resources": { - "LambdaFunctionServiceRole0C4CDE0B": { + "cftapinoargumentsapiFunctionServiceRole6D0B7CBA": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -16,235 +16,363 @@ ], "Version": "2012-10-17" }, - "Policies": [ + "ManagedPolicyArns": [ { - "PolicyDocument": { - "Statement": [ + "Fn::Join": [ + "", + [ + "arn:", { - "Action": [ - "logs:CreateLogGroup", - "logs:CreateLogStream", - "logs:PutLogEvents" - ], - "Effect": "Allow", - "Resource": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":logs:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":log-group:/aws/lambda/*" - ] - ] - } - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "LambdaFunctionServiceRolePolicy" + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] } ] } }, - "LambdaFunctionServiceRoleDefaultPolicy126C8897": { - "Type": "AWS::IAM::Policy", + "cftapinoargumentsapiFunctionFF0A9430": { + "Type": "AWS::Lambda::Function", "Properties": { - "PolicyDocument": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "42a35bbf0dec9ef0ac5b0dde87e71a1b8929e8d2d178dd09ccfb2c928ec0198c.zip" + }, + "Handler": ".handler", + "Role": { + "Fn::GetAtt": [ + "cftapinoargumentsapiFunctionServiceRole6D0B7CBA", + "Arn" + ] + }, + "Runtime": "nodejs16.x" + }, + "DependsOn": [ + "cftapinoargumentsapiFunctionServiceRole6D0B7CBA" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "Test Resource" + }, + { + "id": "W89", + "reason": "Test Resource" + }, + { + "id": "W92", + "reason": "Test Resource" + } + ] + } + } + }, + "cftapinoargumentsapiAuthFunctionServiceRole97907F32": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { "Statement": [ { - "Action": [ - "xray:PutTraceSegments", - "xray:PutTelemetryRecords" - ], + "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": "*" + "Principal": { + "Service": "lambda.amazonaws.com" + } } ], "Version": "2012-10-17" }, - "PolicyName": "LambdaFunctionServiceRoleDefaultPolicy126C8897", - "Roles": [ + "ManagedPolicyArns": [ { - "Ref": "LambdaFunctionServiceRole0C4CDE0B" + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] } ] - }, - "Metadata": { - "cfn_nag": { - "rules_to_suppress": [ - { - "id": "W12", - "reason": "Lambda needs the following minimum required permissions to send trace data to X-Ray and access ENIs in a VPC." - } - ] - } } }, - "LambdaFunctionBF21E41F": { + "cftapinoargumentsapiAuthFunctionFCC239B2": { "Type": "AWS::Lambda::Function", "Properties": { "Code": { "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "3a18fd3de4803f72260a058823accffd4f8d69986c6862a23cd86265f6cafa0f.zip" - }, - "Environment": { - "Variables": { - "AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1" - } + "S3Key": "42a35bbf0dec9ef0ac5b0dde87e71a1b8929e8d2d178dd09ccfb2c928ec0198c.zip" }, - "Handler": "index.handler", + "Handler": ".handler", "Role": { "Fn::GetAtt": [ - "LambdaFunctionServiceRole0C4CDE0B", + "cftapinoargumentsapiAuthFunctionServiceRole97907F32", "Arn" ] }, - "Runtime": "nodejs16.x", - "TracingConfig": { - "Mode": "Active" - } + "Runtime": "nodejs16.x" }, "DependsOn": [ - "LambdaFunctionServiceRoleDefaultPolicy126C8897", - "LambdaFunctionServiceRole0C4CDE0B" + "cftapinoargumentsapiAuthFunctionServiceRole97907F32" ], "Metadata": { "cfn_nag": { "rules_to_suppress": [ { "id": "W58", - "reason": "Lambda functions has the required permission to write CloudWatch Logs. It uses custom policy instead of arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole with tighter permissions." + "reason": "Test Resource" }, { "id": "W89", - "reason": "This is not a rule for the general case, just for specific use cases/industries" + "reason": "Test Resource" }, { "id": "W92", - "reason": "Impossible for us to define the correct concurrency for clients" + "reason": "Test Resource" } ] } } }, - "ApiAccessLogGroupCEA70788": { - "Type": "AWS::Logs::LogGroup", - "UpdateReplacePolicy": "Retain", - "DeletionPolicy": "Retain", - "Metadata": { - "cfn_nag": { - "rules_to_suppress": [ - { - "id": "W86", - "reason": "Retention period for CloudWatchLogs LogGroups are set to 'Never Expire' to preserve customer data indefinitely" - }, - { - "id": "W84", - "reason": "By default CloudWatchLogs LogGroups data is encrypted using the CloudWatch server-side encryption keys (AWS Managed Keys)" - } + "cftapinoargumentsapiAuthFunctioncftapinoargumentscftapinoargumentsapiauthorizerCA624E68Permissions8A31DDB0": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "cftapinoargumentsapiAuthFunctionFCC239B2", + "Arn" + ] + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "cftapinoargumentsapiApiF33C30EC" + }, + "/authorizers/", + { + "Ref": "cftapinoargumentsapiauthorizer4CAD6709" + } + ] ] } } }, - "LambdaRestApi95870433": { - "Type": "AWS::ApiGateway::RestApi", + "cftapinoargumentsapiauthorizer4CAD6709": { + "Type": "AWS::ApiGateway::Authorizer", "Properties": { - "EndpointConfiguration": { - "Types": [ - "REGIONAL" + "AuthorizerUri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + ":", + { + "Fn::GetAtt": [ + "cftapinoargumentsapiAuthFunctionFCC239B2", + "Arn" + ] + } + ] + } + ] + }, + ":apigateway:", + { + "Fn::Select": [ + 3, + { + "Fn::Split": [ + ":", + { + "Fn::GetAtt": [ + "cftapinoargumentsapiAuthFunctionFCC239B2", + "Arn" + ] + } + ] + } + ] + }, + ":lambda:path/2015-03-31/functions/", + { + "Fn::GetAtt": [ + "cftapinoargumentsapiAuthFunctionFCC239B2", + "Arn" + ] + }, + "/invocations" + ] ] }, - "Name": "LambdaRestApi" + "IdentitySource": "method.request.header.Authorization", + "Name": "cftapinoargumentscftapinoargumentsapiauthorizerCA624E68", + "RestApiId": { + "Ref": "cftapinoargumentsapiApiF33C30EC" + }, + "Type": "REQUEST" + } + }, + "cftapinoargumentsapiApiF33C30EC": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Name": "cftapi-no-arguments-apiApi" } }, - "LambdaRestApiDeploymentBA640578812946cff1910fe2b8b339ee3a8d51c7": { + "cftapinoargumentsapiApiCloudWatchRole0947E97B": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "apigateway.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs" + ] + ] + } + ] + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "cftapinoargumentsapiApiAccountBA7FD660": { + "Type": "AWS::ApiGateway::Account", + "Properties": { + "CloudWatchRoleArn": { + "Fn::GetAtt": [ + "cftapinoargumentsapiApiCloudWatchRole0947E97B", + "Arn" + ] + } + }, + "DependsOn": [ + "cftapinoargumentsapiApiF33C30EC" + ], + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "cftapinoargumentsapiApiDeployment65E83A27fd449b78a628621868949153459a26d7": { "Type": "AWS::ApiGateway::Deployment", "Properties": { "Description": "Automatically created by the RestApi construct", "RestApiId": { - "Ref": "LambdaRestApi95870433" + "Ref": "cftapinoargumentsapiApiF33C30EC" } }, "DependsOn": [ - "LambdaRestApiproxyANY93D43CC0", - "LambdaRestApiproxy9F99E187", - "LambdaRestApiANYA831AD87" + "cftapinoargumentsapiApiproxyANYDB439B85", + "cftapinoargumentsapiApiproxy1C21A7B6", + "cftapinoargumentsapiApiANY1E5CEE5C" ], "Metadata": { "cfn_nag": { "rules_to_suppress": [ { - "id": "W45", - "reason": "ApiGateway has AccessLogging enabled in AWS::ApiGateway::Stage resource, but cfn_nag checks for it in AWS::ApiGateway::Deployment resource" + "id": "W68", + "reason": "Test Resource" } ] } } }, - "LambdaRestApiDeploymentStageprodB1F3862A": { + "cftapinoargumentsapiApiDeploymentStageprod006D7243": { "Type": "AWS::ApiGateway::Stage", "Properties": { - "AccessLogSetting": { - "DestinationArn": { - "Fn::GetAtt": [ - "ApiAccessLogGroupCEA70788", - "Arn" - ] - }, - "Format": "{\"requestId\":\"$context.requestId\",\"ip\":\"$context.identity.sourceIp\",\"user\":\"$context.identity.user\",\"caller\":\"$context.identity.caller\",\"requestTime\":\"$context.requestTime\",\"httpMethod\":\"$context.httpMethod\",\"resourcePath\":\"$context.resourcePath\",\"status\":\"$context.status\",\"protocol\":\"$context.protocol\",\"responseLength\":\"$context.responseLength\"}" - }, "DeploymentId": { - "Ref": "LambdaRestApiDeploymentBA640578812946cff1910fe2b8b339ee3a8d51c7" + "Ref": "cftapinoargumentsapiApiDeployment65E83A27fd449b78a628621868949153459a26d7" }, - "MethodSettings": [ - { - "DataTraceEnabled": false, - "HttpMethod": "*", - "LoggingLevel": "INFO", - "ResourcePath": "/*" - } - ], "RestApiId": { - "Ref": "LambdaRestApi95870433" + "Ref": "cftapinoargumentsapiApiF33C30EC" }, - "StageName": "prod", - "TracingEnabled": true + "StageName": "prod" + }, + "DependsOn": [ + "cftapinoargumentsapiApiAccountBA7FD660" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W64", + "reason": "Test Resource" + }, + { + "id": "W69", + "reason": "Test Resource" + } + ] + } } }, - "LambdaRestApiproxy9F99E187": { + "cftapinoargumentsapiApiproxy1C21A7B6": { "Type": "AWS::ApiGateway::Resource", "Properties": { "ParentId": { "Fn::GetAtt": [ - "LambdaRestApi95870433", + "cftapinoargumentsapiApiF33C30EC", "RootResourceId" ] }, "PathPart": "{proxy+}", "RestApiId": { - "Ref": "LambdaRestApi95870433" + "Ref": "cftapinoargumentsapiApiF33C30EC" } } }, - "LambdaRestApiproxyANYApiPermissioncftapinoargumentsLambdaRestApiF940E0B7ANYproxyB0743A1B": { + "cftapinoargumentsapiApiproxyANYApiPermissioncftapinoargumentscftapinoargumentsapiApiC6105EBEANYproxy7EB3E388": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", "FunctionName": { "Fn::GetAtt": [ - "LambdaFunctionBF21E41F", + "cftapinoargumentsapiFunctionFF0A9430", "Arn" ] }, @@ -267,11 +395,11 @@ }, ":", { - "Ref": "LambdaRestApi95870433" + "Ref": "cftapinoargumentsapiApiF33C30EC" }, "/", { - "Ref": "LambdaRestApiDeploymentStageprodB1F3862A" + "Ref": "cftapinoargumentsapiApiDeploymentStageprod006D7243" }, "/*/*" ] @@ -279,13 +407,13 @@ } } }, - "LambdaRestApiproxyANYApiPermissionTestcftapinoargumentsLambdaRestApiF940E0B7ANYproxy3C8A7D38": { + "cftapinoargumentsapiApiproxyANYApiPermissionTestcftapinoargumentscftapinoargumentsapiApiC6105EBEANYproxyA27FB655": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", "FunctionName": { "Fn::GetAtt": [ - "LambdaFunctionBF21E41F", + "cftapinoargumentsapiFunctionFF0A9430", "Arn" ] }, @@ -308,7 +436,7 @@ }, ":", { - "Ref": "LambdaRestApi95870433" + "Ref": "cftapinoargumentsapiApiF33C30EC" }, "/test-invoke-stage/*/*" ] @@ -316,10 +444,13 @@ } } }, - "LambdaRestApiproxyANY93D43CC0": { + "cftapinoargumentsapiApiproxyANYDB439B85": { "Type": "AWS::ApiGateway::Method", "Properties": { - "AuthorizationType": "NONE", + "AuthorizationType": "CUSTOM", + "AuthorizerId": { + "Ref": "cftapinoargumentsapiauthorizer4CAD6709" + }, "HttpMethod": "ANY", "Integration": { "IntegrationHttpMethod": "POST", @@ -339,7 +470,7 @@ ":lambda:path/2015-03-31/functions/", { "Fn::GetAtt": [ - "LambdaFunctionBF21E41F", + "cftapinoargumentsapiFunctionFF0A9430", "Arn" ] }, @@ -349,10 +480,10 @@ } }, "ResourceId": { - "Ref": "LambdaRestApiproxy9F99E187" + "Ref": "cftapinoargumentsapiApiproxy1C21A7B6" }, "RestApiId": { - "Ref": "LambdaRestApi95870433" + "Ref": "cftapinoargumentsapiApiF33C30EC" } }, "Metadata": { @@ -360,19 +491,19 @@ "rules_to_suppress": [ { "id": "W59", - "reason": "AWS::ApiGateway::Method AuthorizationType is set to 'NONE' because API Gateway behind CloudFront does not support AWS_IAM authentication" + "reason": "Test Resource" } ] } } }, - "LambdaRestApiANYApiPermissioncftapinoargumentsLambdaRestApiF940E0B7ANY40A5EEFB": { + "cftapinoargumentsapiApiANYApiPermissioncftapinoargumentscftapinoargumentsapiApiC6105EBEANY116F587E": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", "FunctionName": { "Fn::GetAtt": [ - "LambdaFunctionBF21E41F", + "cftapinoargumentsapiFunctionFF0A9430", "Arn" ] }, @@ -395,11 +526,11 @@ }, ":", { - "Ref": "LambdaRestApi95870433" + "Ref": "cftapinoargumentsapiApiF33C30EC" }, "/", { - "Ref": "LambdaRestApiDeploymentStageprodB1F3862A" + "Ref": "cftapinoargumentsapiApiDeploymentStageprod006D7243" }, "/*/" ] @@ -407,13 +538,13 @@ } } }, - "LambdaRestApiANYApiPermissionTestcftapinoargumentsLambdaRestApiF940E0B7ANYDA8F06BF": { + "cftapinoargumentsapiApiANYApiPermissionTestcftapinoargumentscftapinoargumentsapiApiC6105EBEANY3FBA5B10": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", "FunctionName": { "Fn::GetAtt": [ - "LambdaFunctionBF21E41F", + "cftapinoargumentsapiFunctionFF0A9430", "Arn" ] }, @@ -436,7 +567,7 @@ }, ":", { - "Ref": "LambdaRestApi95870433" + "Ref": "cftapinoargumentsapiApiF33C30EC" }, "/test-invoke-stage/*/" ] @@ -444,10 +575,13 @@ } } }, - "LambdaRestApiANYA831AD87": { + "cftapinoargumentsapiApiANY1E5CEE5C": { "Type": "AWS::ApiGateway::Method", "Properties": { - "AuthorizationType": "NONE", + "AuthorizationType": "CUSTOM", + "AuthorizerId": { + "Ref": "cftapinoargumentsapiauthorizer4CAD6709" + }, "HttpMethod": "ANY", "Integration": { "IntegrationHttpMethod": "POST", @@ -467,7 +601,7 @@ ":lambda:path/2015-03-31/functions/", { "Fn::GetAtt": [ - "LambdaFunctionBF21E41F", + "cftapinoargumentsapiFunctionFF0A9430", "Arn" ] }, @@ -478,12 +612,12 @@ }, "ResourceId": { "Fn::GetAtt": [ - "LambdaRestApi95870433", + "cftapinoargumentsapiApiF33C30EC", "RootResourceId" ] }, "RestApiId": { - "Ref": "LambdaRestApi95870433" + "Ref": "cftapinoargumentsapiApiF33C30EC" } }, "Metadata": { @@ -491,101 +625,12 @@ "rules_to_suppress": [ { "id": "W59", - "reason": "AWS::ApiGateway::Method AuthorizationType is set to 'NONE' because API Gateway behind CloudFront does not support AWS_IAM authentication" + "reason": "Test Resource" } ] } } }, - "LambdaRestApiUsagePlanB4DF55D0": { - "Type": "AWS::ApiGateway::UsagePlan", - "Properties": { - "ApiStages": [ - { - "ApiId": { - "Ref": "LambdaRestApi95870433" - }, - "Stage": { - "Ref": "LambdaRestApiDeploymentStageprodB1F3862A" - }, - "Throttle": {} - } - ] - } - }, - "LambdaRestApiCloudWatchRoleF339D4E6": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "apigateway.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "Policies": [ - { - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "logs:CreateLogGroup", - "logs:CreateLogStream", - "logs:DescribeLogGroups", - "logs:DescribeLogStreams", - "logs:PutLogEvents", - "logs:GetLogEvents", - "logs:FilterLogEvents" - ], - "Effect": "Allow", - "Resource": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":logs:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":*" - ] - ] - } - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "LambdaRestApiCloudWatchRolePolicy" - } - ] - } - }, - "LambdaRestApiAccount": { - "Type": "AWS::ApiGateway::Account", - "Properties": { - "CloudWatchRoleArn": { - "Fn::GetAtt": [ - "LambdaRestApiCloudWatchRoleF339D4E6", - "Arn" - ] - } - }, - "DependsOn": [ - "LambdaRestApi95870433" - ] - }, "testcloudfrontapigatewaySetHttpSecurityHeadersD8DBA642": { "Type": "AWS::CloudFront::Function", "Properties": { @@ -810,7 +855,7 @@ [ "https://", { - "Ref": "LambdaRestApi95870433" + "Ref": "cftapinoargumentsapiApiF33C30EC" }, ".execute-api.", { @@ -822,7 +867,7 @@ }, "/", { - "Ref": "LambdaRestApiDeploymentStageprodB1F3862A" + "Ref": "cftapinoargumentsapiApiDeploymentStageprod006D7243" }, "/" ] @@ -843,7 +888,7 @@ [ "/", { - "Ref": "LambdaRestApiDeploymentStageprodB1F3862A" + "Ref": "cftapinoargumentsapiApiDeploymentStageprod006D7243" } ] ] @@ -941,14 +986,14 @@ } }, "Outputs": { - "LambdaRestApiEndpointCCECE4C1": { + "cftapinoargumentsapiApiEndpointBBA93A2A": { "Value": { "Fn::Join": [ "", [ "https://", { - "Ref": "LambdaRestApi95870433" + "Ref": "cftapinoargumentsapiApiF33C30EC" }, ".execute-api.", { @@ -960,7 +1005,7 @@ }, "/", { - "Ref": "LambdaRestApiDeploymentStageprodB1F3862A" + "Ref": "cftapinoargumentsapiApiDeploymentStageprod006D7243" }, "/" ] diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-no-arguments.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-no-arguments.ts index 615e14c0c..639196e63 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-no-arguments.ts +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-no-arguments.ts @@ -14,9 +14,7 @@ /// !cdk-integ * import { App, RemovalPolicy, Stack } from "aws-cdk-lib"; import { CloudFrontToApiGateway } from "../lib"; -import * as lambda from 'aws-cdk-lib/aws-lambda'; import * as defaults from '@aws-solutions-constructs/core'; -import * as api from 'aws-cdk-lib/aws-apigateway'; import { generateIntegStackName } from '@aws-solutions-constructs/core'; // Setup @@ -24,34 +22,10 @@ const app = new App(); const stack = new Stack(app, generateIntegStackName(__filename)); stack.templateOptions.description = 'Integration Test for aws-cloudfront-apigateway'; -const inProps: lambda.FunctionProps = { - code: lambda.Code.fromAsset(`${__dirname}/lambda`), - runtime: lambda.Runtime.NODEJS_16_X, - handler: 'index.handler' -}; - -const func = defaults.deployLambdaFunction(stack, inProps); - -const regionalLambdaRestApiResponse = defaults.RegionalLambdaRestApi(stack, func); - -regionalLambdaRestApiResponse.api.methods.forEach((apiMethod) => { - // Override the API Gateway Authorization Type from AWS_IAM to NONE - const child = apiMethod.node.findChild('Resource') as api.CfnMethod; - if (child.authorizationType === 'AWS_IAM') { - child.addPropertyOverride('AuthorizationType', 'NONE'); - - defaults.addCfnSuppressRules(apiMethod, [ - { - id: "W59", - reason: `AWS::ApiGateway::Method AuthorizationType is set to 'NONE' because API Gateway behind CloudFront does not support AWS_IAM authentication`, - }, - ]); - - } -}); +const testApi = defaults.CreateTestApi(stack, `${generateIntegStackName(__filename)}-api`); new CloudFrontToApiGateway(stack, 'test-cloudfront-apigateway', { - existingApiGatewayObj: regionalLambdaRestApiResponse.api, + existingApiGatewayObj: testApi, cloudFrontLoggingBucketProps: { removalPolicy: RemovalPolicy.DESTROY, autoDeleteObjects: true diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-existing-bucket.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-existing-bucket.ts index 7b5daaea0..e4ecd8548 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-existing-bucket.ts +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-existing-bucket.ts @@ -27,7 +27,7 @@ const stack = new Stack(app, generateIntegStackName(__filename)); stack.node.setContext("@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy", true); let mybucket: s3.Bucket; -mybucket = defaults.CreateScrapBucket(stack, { +mybucket = defaults.CreateScrapBucket(stack, "scrapBucket", { removalPolicy: RemovalPolicy.DESTROY, autoDeleteObjects: true }); diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-arguments.expected.json index f050b41cd..00fa661af 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-arguments.expected.json @@ -1,6 +1,124 @@ { "Description": "Integration Test for aws-cloudfront-s3", "Resources": { + "testcloudfronts3S3LoggingBucket90D239DD": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W35", + "reason": "This S3 bucket is used as the access logging bucket for another bucket" + } + ] + } + } + }, + "testcloudfronts3S3LoggingBucketPolicy529D4CFF": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfronts3S3LoggingBucket90D239DD" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3S3LoggingBucket90D239DD", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3S3LoggingBucket90D239DD", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "testcloudfronts3S3BucketE0C5F76E", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3S3LoggingBucket90D239DD", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, "testcloudfronts3S3BucketE0C5F76E": { "Type": "AWS::S3::Bucket", "Properties": { @@ -26,6 +144,11 @@ } ] }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "testcloudfronts3S3LoggingBucket90D239DD" + } + }, "PublicAccessBlockConfiguration": { "BlockPublicAcls": true, "BlockPublicPolicy": true, diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-arguments.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-arguments.ts index 5b1764f1d..6b7341ad1 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-arguments.ts +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-arguments.ts @@ -32,7 +32,6 @@ const construct = new CloudFrontToS3(stack, 'test-cloudfront-s3', { removalPolicy: RemovalPolicy.DESTROY, autoDeleteObjects: true }, - logS3AccessLogs: false }); const s3Bucket = construct.s3Bucket as s3.Bucket; diff --git a/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/eventbridge-kinesisfirehose-s3.test.ts b/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/eventbridge-kinesisfirehose-s3.test.ts index 908783b5c..0d4f17d03 100644 --- a/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/eventbridge-kinesisfirehose-s3.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/eventbridge-kinesisfirehose-s3.test.ts @@ -252,7 +252,7 @@ test('Supply an existing logging bucket', () => { const stack = new cdk.Stack(); const logBucketName = 'log-bucket-name'; - const logBucket = defaults.CreateScrapBucket(stack, { + const logBucket = defaults.CreateScrapBucket(stack, "scrapBucket", { bucketName: logBucketName }); diff --git a/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/integ.evtfhss3-existingLoggingBucket.ts b/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/integ.evtfhss3-existingLoggingBucket.ts index f416558fa..9657f5dc3 100644 --- a/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/integ.evtfhss3-existingLoggingBucket.ts +++ b/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/integ.evtfhss3-existingLoggingBucket.ts @@ -24,7 +24,7 @@ const app = new App(); const stack = new Stack(app, generateIntegStackName(__filename)); stack.node.setContext("@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy", true); -const logBucket = defaults.CreateScrapBucket(stack); +const logBucket = defaults.CreateScrapBucket(stack, "scrapBucket"); new EventbridgeToKinesisFirehoseToS3(stack, 'evtfhss3-existing-log-bucket', { eventRuleProps: { diff --git a/source/patterns/@aws-solutions-constructs/aws-fargate-s3/test/integ.fars3-existing-resources.ts b/source/patterns/@aws-solutions-constructs/aws-fargate-s3/test/integ.fars3-existing-resources.ts index 9d5626943..e968e5e55 100644 --- a/source/patterns/@aws-solutions-constructs/aws-fargate-s3/test/integ.fars3-existing-resources.ts +++ b/source/patterns/@aws-solutions-constructs/aws-fargate-s3/test/integ.fars3-existing-resources.ts @@ -27,7 +27,7 @@ stack.templateOptions.description = 'Integration Test with existing VPC, Service stack.node.setContext("@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy", true); const existingVpc = getTestVpc(stack); -const existingBucket = defaults.CreateScrapBucket(stack, { +const existingBucket = defaults.CreateScrapBucket(stack, "scrapBucket", { removalPolicy: RemovalPolicy.DESTROY, autoDeleteObjects: true }); diff --git a/source/patterns/@aws-solutions-constructs/aws-iot-s3/test/integ.iots3-iot-s3-defaultprops.expected.json b/source/patterns/@aws-solutions-constructs/aws-iot-s3/test/integ.iots3-iot-s3-defaultprops.expected.json index 278c1bb9b..9ed57ae0b 100644 --- a/source/patterns/@aws-solutions-constructs/aws-iot-s3/test/integ.iots3-iot-s3-defaultprops.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-iot-s3/test/integ.iots3-iot-s3-defaultprops.expected.json @@ -1,5 +1,123 @@ { "Resources": { + "testiots3integrationS3LoggingBucket606446CC": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W35", + "reason": "This S3 bucket is used as the access logging bucket for another bucket" + } + ] + } + } + }, + "testiots3integrationS3LoggingBucketPolicy2DB45D12": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testiots3integrationS3LoggingBucket606446CC" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testiots3integrationS3LoggingBucket606446CC", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testiots3integrationS3LoggingBucket606446CC", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "testiots3integrationS3Bucket9B8B180C", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testiots3integrationS3LoggingBucket606446CC", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, "testiots3integrationS3Bucket9B8B180C": { "Type": "AWS::S3::Bucket", "Properties": { @@ -25,6 +143,11 @@ } ] }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "testiots3integrationS3LoggingBucket606446CC" + } + }, "PublicAccessBlockConfiguration": { "BlockPublicAcls": true, "BlockPublicPolicy": true, diff --git a/source/patterns/@aws-solutions-constructs/aws-iot-s3/test/integ.iots3-iot-s3-defaultprops.ts b/source/patterns/@aws-solutions-constructs/aws-iot-s3/test/integ.iots3-iot-s3-defaultprops.ts index 92c46c6af..47e4dd1c3 100644 --- a/source/patterns/@aws-solutions-constructs/aws-iot-s3/test/integ.iots3-iot-s3-defaultprops.ts +++ b/source/patterns/@aws-solutions-constructs/aws-iot-s3/test/integ.iots3-iot-s3-defaultprops.ts @@ -29,7 +29,6 @@ const props: IotToS3Props = { actions: [] } }, - logS3AccessLogs: false, bucketProps: { removalPolicy: RemovalPolicy.DESTROY, } diff --git a/source/patterns/@aws-solutions-constructs/aws-iot-s3/test/integ.iots3-iot-s3-existing-bucket.ts b/source/patterns/@aws-solutions-constructs/aws-iot-s3/test/integ.iots3-iot-s3-existing-bucket.ts index 96f8a6f2b..84595bffb 100644 --- a/source/patterns/@aws-solutions-constructs/aws-iot-s3/test/integ.iots3-iot-s3-existing-bucket.ts +++ b/source/patterns/@aws-solutions-constructs/aws-iot-s3/test/integ.iots3-iot-s3-existing-bucket.ts @@ -22,7 +22,7 @@ const app = new App(); const stack = new Stack(app, generateIntegStackName(__filename)); stack.node.setContext("@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy", true); -const existingBucket = defaults.CreateScrapBucket(stack, { +const existingBucket = defaults.CreateScrapBucket(stack, "scrapBucket", { bucketProps: { encryption: BucketEncryption.KMS_MANAGED, } diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.fhss3-pre-existing-bucket.ts b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.fhss3-pre-existing-bucket.ts index 136265003..78855a214 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.fhss3-pre-existing-bucket.ts +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.fhss3-pre-existing-bucket.ts @@ -25,7 +25,7 @@ const stack = new Stack(app, generateIntegStackName(__filename)); stack.templateOptions.description = 'Integration Test for aws-kinesisfirehose-s3'; stack.node.setContext("@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy", true); -const existingBucket = CreateScrapBucket(stack, {}); +const existingBucket = CreateScrapBucket(stack, "scrapBucket"); const mybucket: s3.IBucket = s3.Bucket.fromBucketName(stack, 'mybucket', existingBucket.bucketName); new KinesisFirehoseToS3(stack, 'test-firehose-s3-pre-existing-bucket-stack', { diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.fhss3-pre-existing-logging-bucket.ts b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.fhss3-pre-existing-logging-bucket.ts index 12d84b50e..e42d8fbf5 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.fhss3-pre-existing-logging-bucket.ts +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.fhss3-pre-existing-logging-bucket.ts @@ -23,7 +23,7 @@ const stack = new Stack(app, generateIntegStackName(__filename)); stack.templateOptions.description = 'Integration Test for aws-kinesisfirehose-s3'; stack.node.setContext("@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy", true); -const existingBucket = CreateScrapBucket(stack, { +const existingBucket = CreateScrapBucket(stack, "scrapBucket", { }); new KinesisFirehoseToS3(stack, 'test-firehose-s3-pre-existing-logging-bucket-stack', { diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/kinesisfirehose-s3.test.ts b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/kinesisfirehose-s3.test.ts index f1eb0b435..f224d95d0 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/kinesisfirehose-s3.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/kinesisfirehose-s3.test.ts @@ -115,7 +115,7 @@ test('check default properties', () => { test('check properties with existing S3 bucket', () => { const stack = new cdk.Stack(); - const existingBucket = CreateScrapBucket(stack, {}); + const existingBucket = CreateScrapBucket(stack, "scrapBucket"); const mybucket: s3.IBucket = s3.Bucket.fromBucketName(stack, 'mybucket', existingBucket.bucketName); const construct: KinesisFirehoseToS3 = deploy(stack, { existingBucketObj: mybucket @@ -130,7 +130,7 @@ test('check properties with existing S3 bucket', () => { test('check properties with existing logging S3 bucket', () => { const stack = new cdk.Stack(); - const existingBucket = CreateScrapBucket(stack, {}); + const existingBucket = CreateScrapBucket(stack, "scrapBucket"); const myLoggingBucket: s3.IBucket = s3.Bucket.fromBucketName(stack, 'myLoggingBucket', existingBucket.bucketName); const construct: KinesisFirehoseToS3 = deploy(stack, { existingLoggingBucketObj: myLoggingBucket @@ -145,7 +145,7 @@ test('check properties with existing logging S3 bucket', () => { test('check properties with existing logging S3 bucket and S3 bucket props', () => { const stack = new cdk.Stack(); - const existingBucket = CreateScrapBucket(stack, {}); + const existingBucket = CreateScrapBucket(stack, "scrapBucket"); const myLoggingBucket: s3.IBucket = s3.Bucket.fromBucketName(stack, 'myLoggingBucket', existingBucket.bucketName); const construct: KinesisFirehoseToS3 = deploy(stack, { bucketProps: { diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-existing-bucket.ts b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-existing-bucket.ts index 8c084e09b..e430fa388 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-existing-bucket.ts +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-existing-bucket.ts @@ -24,7 +24,7 @@ const stack = new Stack(app, generateIntegStackName(__filename)); stack.templateOptions.description = 'Integration Test for aws-kinesisstreams-kinesisfirehose-s3'; stack.node.setContext("@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy", true); -const existingBucket = CreateScrapBucket(stack, { removalPolicy: RemovalPolicy.DESTROY }); +const existingBucket = CreateScrapBucket(stack, "scrapBucket", { removalPolicy: RemovalPolicy.DESTROY }); const mybucket: s3.IBucket = s3.Bucket.fromBucketName(stack, 'mybucket', existingBucket.bucketName); new KinesisStreamsToKinesisFirehoseToS3(stack, 'test-existing-bucket-firehose-s3-stack', { existingBucketObj: mybucket, diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-existing-logging-bucket.ts b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-existing-logging-bucket.ts index c5ff2a5f6..e4f79dd82 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-existing-logging-bucket.ts +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-existing-logging-bucket.ts @@ -24,7 +24,7 @@ const stack = new Stack(app, generateIntegStackName(__filename)); stack.templateOptions.description = 'Integration Test for aws-kinesisstreams-kinesisfirehose-s3'; stack.node.setContext("@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy", true); -const existingBucket = CreateScrapBucket(stack, { +const existingBucket = CreateScrapBucket(stack, "scrapBucket", { bucketProps: { removalPolicy: RemovalPolicy.DESTROY, } diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/kinesisstreams-kinesisfirehose-s3.test.ts b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/kinesisstreams-kinesisfirehose-s3.test.ts index ecd7174ca..c61344f85 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/kinesisstreams-kinesisfirehose-s3.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/kinesisstreams-kinesisfirehose-s3.test.ts @@ -141,7 +141,7 @@ test('Test properties with no CW Alarms', () => { test('Test properties with existing S3 bucket', () => { const stack = new cdk.Stack(); - const existingBucket = defaults.CreateScrapBucket(stack, {}); + const existingBucket = defaults.CreateScrapBucket(stack, "scrapBucket"); const mybucket: s3.IBucket = s3.Bucket.fromBucketName(stack, 'mybucket', existingBucket.bucketName); const construct: KinesisStreamsToKinesisFirehoseToS3 = deploy(stack, { existingBucketObj: mybucket @@ -153,7 +153,7 @@ test('Test properties with existing S3 bucket', () => { test('Test properties with existing logging S3 bucket', () => { const stack = new cdk.Stack(); - const existingBucket = defaults.CreateScrapBucket(stack, {}); + const existingBucket = defaults.CreateScrapBucket(stack, "scrapBucket"); const myLoggingBucket: s3.IBucket = s3.Bucket.fromBucketName(stack, 'myLoggingBucket', existingBucket.bucketName); const construct: KinesisStreamsToKinesisFirehoseToS3 = deploy(stack, { existingLoggingBucketObj: myLoggingBucket diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-minimal-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-minimal-arguments.expected.json index 59e2e4791..0ccfca9ff 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-minimal-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-minimal-arguments.expected.json @@ -1,7 +1,7 @@ { "Description": "Integration Test for aws-lambda-kendra", "Resources": { - "scrapBucketB11863B7": { + "contentBucket356CF7A5": { "Type": "AWS::S3::Bucket", "Properties": { "BucketEncryption": { @@ -44,11 +44,11 @@ } } }, - "scrapBucketPolicy189B0607": { + "contentBucketPolicy71256B1B": { "Type": "AWS::S3::BucketPolicy", "Properties": { "Bucket": { - "Ref": "scrapBucketB11863B7" + "Ref": "contentBucket356CF7A5" }, "PolicyDocument": { "Statement": [ @@ -71,7 +71,7 @@ "Resource": [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "contentBucket356CF7A5", "Arn" ] }, @@ -81,7 +81,7 @@ [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "contentBucket356CF7A5", "Arn" ] }, @@ -96,7 +96,7 @@ } } }, - "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "contentBucketAutoDeleteObjectsCustomResourceD6F9707E": { "Type": "Custom::S3AutoDeleteObjects", "Properties": { "ServiceToken": { @@ -106,11 +106,11 @@ ] }, "BucketName": { - "Ref": "scrapBucketB11863B7" + "Ref": "contentBucket356CF7A5" } }, "DependsOn": [ - "scrapBucketPolicy189B0607" + "contentBucketPolicy71256B1B" ], "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -162,7 +162,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "scrapBucketB11863B7" + "Ref": "contentBucket356CF7A5" }, " S3 bucket." ] @@ -553,7 +553,7 @@ [ "arn:aws:s3:::", { - "Ref": "scrapBucketB11863B7" + "Ref": "contentBucket356CF7A5" }, "/*" ] @@ -569,7 +569,7 @@ [ "arn:aws:s3:::", { - "Ref": "scrapBucketB11863B7" + "Ref": "contentBucket356CF7A5" } ] ] @@ -602,7 +602,7 @@ "DataSourceConfiguration": { "S3Configuration": { "BucketName": { - "Ref": "scrapBucketB11863B7" + "Ref": "contentBucket356CF7A5" } } }, diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-minimal-arguments.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-minimal-arguments.ts index 0cd2fcb64..b1fad141a 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-minimal-arguments.ts +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-minimal-arguments.ts @@ -23,7 +23,10 @@ const app = new App(); const stack = new Stack(app, generateIntegStackName(__filename)); stack.templateOptions.description = 'Integration Test for aws-lambda-kendra'; -const testBucket = defaults.CreateScrapBucket(stack); +// const loggingBucket = defaults.CreateScrapBucket(stack, "logBucket"); +const testBucket = defaults.CreateScrapBucket(stack, "contentBucket", { +// serverAccessLogsBucket: loggingBucket, +}); new LambdaToKendra(stack, 'minimal-arguments', { lambdaFunctionProps: { diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-multiple-sources.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-multiple-sources.ts index 947f0f8c1..0051662e1 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-multiple-sources.ts +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-multiple-sources.ts @@ -25,7 +25,7 @@ const app = new App(); const stack = new Stack(app, generateIntegStackName(__filename)); stack.templateOptions.description = 'Integration Test for aws-lambda-kendra'; -const testBucket = defaults.CreateScrapBucket(stack); +const testBucket = defaults.CreateScrapBucket(stack, "scrapBucket"); const existingIamRole = new iam.Role(stack, 'existingRole', { assumedBy: new iam.ServicePrincipal('kendra.amazonaws.com') }); diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-with-vpc.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-with-vpc.ts index 776d83f26..7c14827b4 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-with-vpc.ts +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-with-vpc.ts @@ -23,7 +23,7 @@ const app = new App(); const stack = new Stack(app, generateIntegStackName(__filename)); stack.templateOptions.description = 'Integration Test for aws-lambda-kendra'; -const testBucket = defaults.CreateScrapBucket(stack); +const testBucket = defaults.CreateScrapBucket(stack, "scrapBucket"); new LambdaToKendra(stack, 'minimal-arguments', { lambdaFunctionProps: { diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-deployFunction.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-deployFunction.expected.json index c0a10dbae..c416982c0 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-deployFunction.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-deployFunction.expected.json @@ -179,6 +179,124 @@ } } }, + "testlambdas3S3LoggingBucketD42FC73D": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W35", + "reason": "This S3 bucket is used as the access logging bucket for another bucket" + } + ] + } + } + }, + "testlambdas3S3LoggingBucketPolicyCEAFB213": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testlambdas3S3LoggingBucketD42FC73D" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testlambdas3S3LoggingBucketD42FC73D", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testlambdas3S3LoggingBucketD42FC73D", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "testlambdas3S3Bucket179A52E6", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testlambdas3S3LoggingBucketD42FC73D", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, "testlambdas3S3Bucket179A52E6": { "Type": "AWS::S3::Bucket", "Properties": { @@ -204,6 +322,11 @@ } ] }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "testlambdas3S3LoggingBucketD42FC73D" + } + }, "PublicAccessBlockConfiguration": { "BlockPublicAcls": true, "BlockPublicPolicy": true, diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-deployFunction.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-deployFunction.ts index 3fcb74de5..41618b436 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-deployFunction.ts +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-deployFunction.ts @@ -33,8 +33,7 @@ const props: LambdaToS3Props = { }, bucketProps: { removalPolicy: RemovalPolicy.DESTROY, - }, - logS3AccessLogs: false + } }; const construct = new LambdaToS3(stack, 'test-lambda-s3', props); diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-pre-existing-bucket.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-pre-existing-bucket.ts index b109470f8..85a87bf29 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-pre-existing-bucket.ts +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-pre-existing-bucket.ts @@ -25,7 +25,7 @@ const stack = new Stack(app, generateIntegStackName(__filename)); stack.templateOptions.description = 'Integration Test for aws-lambda-s3'; stack.node.setContext("@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy", true); -const existingBucket = CreateScrapBucket(stack, { removalPolicy: RemovalPolicy.DESTROY }); +const existingBucket = CreateScrapBucket(stack, "scrapBucket", { removalPolicy: RemovalPolicy.DESTROY }); const mybucket: s3.IBucket = s3.Bucket.fromBucketName(stack, 'mybucket', existingBucket.bucketName); // Definitions diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/lambda-s3.test.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/lambda-s3.test.ts index 031ce1242..a05aaaf95 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/lambda-s3.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/lambda-s3.test.ts @@ -240,7 +240,7 @@ test('Test lambda function custom environment variable', () => { const stack = new Stack(); // Helper declaration - const existingBucket = defaults.CreateScrapBucket(stack, {}); + const existingBucket = defaults.CreateScrapBucket(stack, "scrapBucket"); const mybucket: s3.IBucket = s3.Bucket.fromBucketName(stack, 'mybucket', existingBucket.bucketName); new LambdaToS3(stack, 'lambda-to-s3-stack', { existingBucketObj: mybucket, diff --git a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/test.openapigateway-lambda.test.ts b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/test.openapigateway-lambda.test.ts index 82baf379d..91b3f3f9a 100644 --- a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/test.openapigateway-lambda.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/test.openapigateway-lambda.test.ts @@ -78,7 +78,7 @@ test('API Definition can be specified from Asset', () => { test('API Definition can be specified from S3 Bucket and Key', () => { const stack = new Stack(); - const apiDefinitionBucket = CreateScrapBucket(stack, {}); + const apiDefinitionBucket = CreateScrapBucket(stack, "scrapBucket"); const apiDefinitionKey = 'api.yaml'; const construct = new OpenApiGatewayToLambda(stack, 'test-apigateway-lambda', { @@ -262,7 +262,7 @@ test('Throws error when no api integration is specified', () => { test('Throws error when api definition s3 bucket is specified but s3 object key is missing', () => { const stack = new Stack(); - const apiDefinitionBucket = CreateScrapBucket(stack, {}); + const apiDefinitionBucket = CreateScrapBucket(stack, "scrapBucket"); const app = () => { new OpenApiGatewayToLambda(stack, 'test-apigateway-lambda', { @@ -291,7 +291,7 @@ test('Throws error when api is defined as asset and s3 bucket is specified', () const apiDefinitionAsset = new Asset(stack, 'OpenApiAsset', { path: path.join(__dirname, 'openapi/apiDefinition.yaml') }); - const apiDefinitionBucket = CreateScrapBucket(stack, {}); + const apiDefinitionBucket = CreateScrapBucket(stack, "scrapBucket"); const app = () => { new OpenApiGatewayToLambda(stack, 'test-apigateway-lambda', { diff --git a/source/patterns/@aws-solutions-constructs/aws-s3-lambda/test/integ.s3lam-existing-s3-bucket.ts b/source/patterns/@aws-solutions-constructs/aws-s3-lambda/test/integ.s3lam-existing-s3-bucket.ts index ddedd7bbf..4d630fff8 100644 --- a/source/patterns/@aws-solutions-constructs/aws-s3-lambda/test/integ.s3lam-existing-s3-bucket.ts +++ b/source/patterns/@aws-solutions-constructs/aws-s3-lambda/test/integ.s3lam-existing-s3-bucket.ts @@ -24,7 +24,7 @@ const app = new App(); const stack = new Stack(app, generateIntegStackName(__filename)); stack.node.setContext("@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy", true); -const myBucket = CreateScrapBucket(stack, {}); +const myBucket = CreateScrapBucket(stack, "scrapBucket"); const props: S3ToLambdaProps = { lambdaFunctionProps: { diff --git a/source/patterns/@aws-solutions-constructs/aws-s3-lambda/test/s3-lambda.test.ts b/source/patterns/@aws-solutions-constructs/aws-s3-lambda/test/s3-lambda.test.ts index e8c707bb1..0bb0da9fe 100644 --- a/source/patterns/@aws-solutions-constructs/aws-s3-lambda/test/s3-lambda.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-s3-lambda/test/s3-lambda.test.ts @@ -122,7 +122,7 @@ test('s3 bucket with one content bucket and no logging bucket', () => { test('check properties with existing S3 bucket', () => { const stack = new cdk.Stack(); - const existingBucket = CreateScrapBucket(stack, {}); + const existingBucket = CreateScrapBucket(stack, "scrapBucket"); const construct = new S3ToLambda(stack, 's3-lambda', { lambdaFunctionProps: { code: lambda.Code.fromAsset(`${__dirname}/lambda`), diff --git a/source/patterns/@aws-solutions-constructs/aws-s3-sns/test/integ.s3sns-existingS3Bucket.ts b/source/patterns/@aws-solutions-constructs/aws-s3-sns/test/integ.s3sns-existingS3Bucket.ts index d1c8e2c01..6ebf5df38 100644 --- a/source/patterns/@aws-solutions-constructs/aws-s3-sns/test/integ.s3sns-existingS3Bucket.ts +++ b/source/patterns/@aws-solutions-constructs/aws-s3-sns/test/integ.s3sns-existingS3Bucket.ts @@ -21,7 +21,7 @@ const stack = new Stack(app, generateIntegStackName(__filename)); stack.node.setContext("@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy", true); new S3ToSns(stack, 'test-s3-sns', { - existingBucketObj: CreateScrapBucket(stack) + existingBucketObj: CreateScrapBucket(stack, "scrapBucket") }); suppressAutoDeleteHandlerWarnings(stack); diff --git a/source/patterns/@aws-solutions-constructs/aws-s3-sqs/test/integ.s3sqs-existingLoggingBucket.ts b/source/patterns/@aws-solutions-constructs/aws-s3-sqs/test/integ.s3sqs-existingLoggingBucket.ts index e94794b7e..c0a46c6cf 100644 --- a/source/patterns/@aws-solutions-constructs/aws-s3-sqs/test/integ.s3sqs-existingLoggingBucket.ts +++ b/source/patterns/@aws-solutions-constructs/aws-s3-sqs/test/integ.s3sqs-existingLoggingBucket.ts @@ -22,7 +22,7 @@ const app = new App(); const stack = new Stack(app, generateIntegStackName(__filename)); stack.node.setContext("@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy", true); -const scrapLogBucket = defaults.CreateScrapBucket(stack); +const scrapLogBucket = defaults.CreateScrapBucket(stack, "scrapBucket"); // Currently there is no way to customize the logging bucket, so this // test will leave a bucket behind diff --git a/source/patterns/@aws-solutions-constructs/aws-s3-sqs/test/integ.s3sqs-existingS3Bucket.ts b/source/patterns/@aws-solutions-constructs/aws-s3-sqs/test/integ.s3sqs-existingS3Bucket.ts index f3e5302d8..cdfed8ded 100644 --- a/source/patterns/@aws-solutions-constructs/aws-s3-sqs/test/integ.s3sqs-existingS3Bucket.ts +++ b/source/patterns/@aws-solutions-constructs/aws-s3-sqs/test/integ.s3sqs-existingS3Bucket.ts @@ -22,7 +22,7 @@ const app = new App(); const stack = new Stack(app, generateIntegStackName(__filename)); stack.node.setContext("@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy", true); -const newExternalBucket = defaults.CreateScrapBucket(stack); +const newExternalBucket = defaults.CreateScrapBucket(stack, "scrapBucket"); // Currently there is no way to customize the logging bucket, so this // test will leave a bucket behind diff --git a/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-pre-existing-bucket.ts b/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-pre-existing-bucket.ts index b87f81680..350db0b6f 100644 --- a/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-pre-existing-bucket.ts +++ b/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-pre-existing-bucket.ts @@ -21,7 +21,7 @@ const app = new App(); const stack = new Stack(app, defaults.generateIntegStackName(__filename)); stack.node.setContext("@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy", true); -const existingBucket = defaults.CreateScrapBucket(stack, { +const existingBucket = defaults.CreateScrapBucket(stack, "scrapBucket", { eventBridgeEnabled: true }); diff --git a/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-s3-stepfunctions-no-argument.expected.json b/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-s3-stepfunctions-no-argument.expected.json index 08cf2043b..1a6f2a8a2 100644 --- a/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-s3-stepfunctions-no-argument.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-s3-stepfunctions-no-argument.expected.json @@ -1,5 +1,123 @@ { "Resources": { + "tests3stepfunctionsconstructS3LoggingBucket706AEC25": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W35", + "reason": "This S3 bucket is used as the access logging bucket for another bucket" + } + ] + } + } + }, + "tests3stepfunctionsconstructS3LoggingBucketPolicy4FEACD99": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "tests3stepfunctionsconstructS3LoggingBucket706AEC25" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "tests3stepfunctionsconstructS3LoggingBucket706AEC25", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "tests3stepfunctionsconstructS3LoggingBucket706AEC25", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "tests3stepfunctionsconstructS3Bucket78CA0724", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "tests3stepfunctionsconstructS3LoggingBucket706AEC25", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, "tests3stepfunctionsconstructS3Bucket78CA0724": { "Type": "AWS::S3::Bucket", "Properties": { @@ -25,6 +143,11 @@ } ] }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "tests3stepfunctionsconstructS3LoggingBucket706AEC25" + } + }, "PublicAccessBlockConfiguration": { "BlockPublicAcls": true, "BlockPublicPolicy": true, diff --git a/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-s3-stepfunctions-no-argument.ts b/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-s3-stepfunctions-no-argument.ts index e79e25c07..42100c932 100644 --- a/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-s3-stepfunctions-no-argument.ts +++ b/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-s3-stepfunctions-no-argument.ts @@ -33,8 +33,7 @@ const props: S3ToStepfunctionsProps = { }, logGroupProps: { removalPolicy: RemovalPolicy.DESTROY, - }, - logS3AccessLogs: false + } }; const construct = new S3ToStepfunctions(stack, 'test-s3-stepfunctions-construct', props); diff --git a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-existing-waf-to-multiple-gateways.expected.json b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-existing-waf-to-multiple-gateways.expected.json index 0d1fc45b9..95d812c94 100644 --- a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-existing-waf-to-multiple-gateways.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-existing-waf-to-multiple-gateways.expected.json @@ -38,7 +38,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "0904d3723480fed2daf7885caa427b930881caae6879d1e6b0d395020173ef6f.zip" + "S3Key": "42a35bbf0dec9ef0ac5b0dde87e71a1b8929e8d2d178dd09ccfb2c928ec0198c.zip" }, "Handler": ".handler", "Role": { @@ -71,6 +71,177 @@ } } }, + "testOneAuthFunctionServiceRole08948D81": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "testOneAuthFunction09B411BE": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "42a35bbf0dec9ef0ac5b0dde87e71a1b8929e8d2d178dd09ccfb2c928ec0198c.zip" + }, + "Handler": ".handler", + "Role": { + "Fn::GetAtt": [ + "testOneAuthFunctionServiceRole08948D81", + "Arn" + ] + }, + "Runtime": "nodejs16.x" + }, + "DependsOn": [ + "testOneAuthFunctionServiceRole08948D81" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "Test Resource" + }, + { + "id": "W89", + "reason": "Test Resource" + }, + { + "id": "W92", + "reason": "Test Resource" + } + ] + } + } + }, + "testOneAuthFunctionwafapiexistingwaftomultiplegatewaystestOneauthorizer7F4D0710PermissionsB6FB990A": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "testOneAuthFunction09B411BE", + "Arn" + ] + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "testOneApi71064556" + }, + "/authorizers/", + { + "Ref": "testOneauthorizerA10D8AB0" + } + ] + ] + } + } + }, + "testOneauthorizerA10D8AB0": { + "Type": "AWS::ApiGateway::Authorizer", + "Properties": { + "AuthorizerUri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + ":", + { + "Fn::GetAtt": [ + "testOneAuthFunction09B411BE", + "Arn" + ] + } + ] + } + ] + }, + ":apigateway:", + { + "Fn::Select": [ + 3, + { + "Fn::Split": [ + ":", + { + "Fn::GetAtt": [ + "testOneAuthFunction09B411BE", + "Arn" + ] + } + ] + } + ] + }, + ":lambda:path/2015-03-31/functions/", + { + "Fn::GetAtt": [ + "testOneAuthFunction09B411BE", + "Arn" + ] + }, + "/invocations" + ] + ] + }, + "IdentitySource": "method.request.header.Authorization", + "Name": "wafapiexistingwaftomultiplegatewaystestOneauthorizer7F4D0710", + "RestApiId": { + "Ref": "testOneApi71064556" + }, + "Type": "REQUEST" + } + }, "testOneApi71064556": { "Type": "AWS::ApiGateway::RestApi", "Properties": { @@ -126,7 +297,7 @@ "UpdateReplacePolicy": "Retain", "DeletionPolicy": "Retain" }, - "testOneApiDeploymentEF63BD86e15daf42da17689cb6d56b07d074b512": { + "testOneApiDeploymentEF63BD86a865c36eea9cba5c46fac363ae4bd2fe": { "Type": "AWS::ApiGateway::Deployment", "Properties": { "Description": "Automatically created by the RestApi construct", @@ -154,7 +325,7 @@ "Type": "AWS::ApiGateway::Stage", "Properties": { "DeploymentId": { - "Ref": "testOneApiDeploymentEF63BD86e15daf42da17689cb6d56b07d074b512" + "Ref": "testOneApiDeploymentEF63BD86a865c36eea9cba5c46fac363ae4bd2fe" }, "RestApiId": { "Ref": "testOneApi71064556" @@ -275,7 +446,10 @@ "testOneApiproxyANY7F46A6A5": { "Type": "AWS::ApiGateway::Method", "Properties": { - "AuthorizationType": "NONE", + "AuthorizationType": "CUSTOM", + "AuthorizerId": { + "Ref": "testOneauthorizerA10D8AB0" + }, "HttpMethod": "ANY", "Integration": { "IntegrationHttpMethod": "POST", @@ -403,7 +577,10 @@ "testOneApiANY899DD2DA": { "Type": "AWS::ApiGateway::Method", "Properties": { - "AuthorizationType": "NONE", + "AuthorizationType": "CUSTOM", + "AuthorizerId": { + "Ref": "testOneauthorizerA10D8AB0" + }, "HttpMethod": "ANY", "Integration": { "IntegrationHttpMethod": "POST", @@ -491,7 +668,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "0904d3723480fed2daf7885caa427b930881caae6879d1e6b0d395020173ef6f.zip" + "S3Key": "42a35bbf0dec9ef0ac5b0dde87e71a1b8929e8d2d178dd09ccfb2c928ec0198c.zip" }, "Handler": ".handler", "Role": { @@ -524,6 +701,177 @@ } } }, + "testTwoAuthFunctionServiceRole711DF356": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "testTwoAuthFunctionCD4DE414": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "42a35bbf0dec9ef0ac5b0dde87e71a1b8929e8d2d178dd09ccfb2c928ec0198c.zip" + }, + "Handler": ".handler", + "Role": { + "Fn::GetAtt": [ + "testTwoAuthFunctionServiceRole711DF356", + "Arn" + ] + }, + "Runtime": "nodejs16.x" + }, + "DependsOn": [ + "testTwoAuthFunctionServiceRole711DF356" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "Test Resource" + }, + { + "id": "W89", + "reason": "Test Resource" + }, + { + "id": "W92", + "reason": "Test Resource" + } + ] + } + } + }, + "testTwoAuthFunctionwafapiexistingwaftomultiplegatewaystestTwoauthorizer9B2C525EPermissionsE4DE5C10": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "testTwoAuthFunctionCD4DE414", + "Arn" + ] + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "testTwoApi17A08EF5" + }, + "/authorizers/", + { + "Ref": "testTwoauthorizer627F159B" + } + ] + ] + } + } + }, + "testTwoauthorizer627F159B": { + "Type": "AWS::ApiGateway::Authorizer", + "Properties": { + "AuthorizerUri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + ":", + { + "Fn::GetAtt": [ + "testTwoAuthFunctionCD4DE414", + "Arn" + ] + } + ] + } + ] + }, + ":apigateway:", + { + "Fn::Select": [ + 3, + { + "Fn::Split": [ + ":", + { + "Fn::GetAtt": [ + "testTwoAuthFunctionCD4DE414", + "Arn" + ] + } + ] + } + ] + }, + ":lambda:path/2015-03-31/functions/", + { + "Fn::GetAtt": [ + "testTwoAuthFunctionCD4DE414", + "Arn" + ] + }, + "/invocations" + ] + ] + }, + "IdentitySource": "method.request.header.Authorization", + "Name": "wafapiexistingwaftomultiplegatewaystestTwoauthorizer9B2C525E", + "RestApiId": { + "Ref": "testTwoApi17A08EF5" + }, + "Type": "REQUEST" + } + }, "testTwoApi17A08EF5": { "Type": "AWS::ApiGateway::RestApi", "Properties": { @@ -579,7 +927,7 @@ "UpdateReplacePolicy": "Retain", "DeletionPolicy": "Retain" }, - "testTwoApiDeployment4EDBF4DF26b103f50979cda2b3c8314c6d694485": { + "testTwoApiDeployment4EDBF4DF95b82d465529f1ef03094fcbb57621d9": { "Type": "AWS::ApiGateway::Deployment", "Properties": { "Description": "Automatically created by the RestApi construct", @@ -607,7 +955,7 @@ "Type": "AWS::ApiGateway::Stage", "Properties": { "DeploymentId": { - "Ref": "testTwoApiDeployment4EDBF4DF26b103f50979cda2b3c8314c6d694485" + "Ref": "testTwoApiDeployment4EDBF4DF95b82d465529f1ef03094fcbb57621d9" }, "RestApiId": { "Ref": "testTwoApi17A08EF5" @@ -728,7 +1076,10 @@ "testTwoApiproxyANY63FFF74A": { "Type": "AWS::ApiGateway::Method", "Properties": { - "AuthorizationType": "NONE", + "AuthorizationType": "CUSTOM", + "AuthorizerId": { + "Ref": "testTwoauthorizer627F159B" + }, "HttpMethod": "ANY", "Integration": { "IntegrationHttpMethod": "POST", @@ -856,7 +1207,10 @@ "testTwoApiANY91121C6F": { "Type": "AWS::ApiGateway::Method", "Properties": { - "AuthorizationType": "NONE", + "AuthorizationType": "CUSTOM", + "AuthorizerId": { + "Ref": "testTwoauthorizer627F159B" + }, "HttpMethod": "ANY", "Integration": { "IntegrationHttpMethod": "POST", diff --git a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-existing-waf-to-multiple-gateways.ts b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-existing-waf-to-multiple-gateways.ts index 5f18a14c2..e17b9cdfd 100644 --- a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-existing-waf-to-multiple-gateways.ts +++ b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-existing-waf-to-multiple-gateways.ts @@ -14,8 +14,7 @@ /// !cdk-integ * import { App, Stack } from "aws-cdk-lib"; import { WafwebaclToApiGateway } from "../lib"; -import { generateIntegStackName } from '@aws-solutions-constructs/core'; -import { CreateTestApi } from './test-helper'; +import { generateIntegStackName, CreateTestApi } from '@aws-solutions-constructs/core'; const app = new App(); const stack = new Stack(app, generateIntegStackName(__filename)); diff --git a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-no-arguments.expected.json index 9d9a39774..86097e7fe 100644 --- a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-no-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-no-arguments.expected.json @@ -38,7 +38,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "0904d3723480fed2daf7885caa427b930881caae6879d1e6b0d395020173ef6f.zip" + "S3Key": "42a35bbf0dec9ef0ac5b0dde87e71a1b8929e8d2d178dd09ccfb2c928ec0198c.zip" }, "Handler": ".handler", "Role": { @@ -71,6 +71,177 @@ } } }, + "testAuthFunctionServiceRole67229217": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "testAuthFunctionDEE315BB": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "42a35bbf0dec9ef0ac5b0dde87e71a1b8929e8d2d178dd09ccfb2c928ec0198c.zip" + }, + "Handler": ".handler", + "Role": { + "Fn::GetAtt": [ + "testAuthFunctionServiceRole67229217", + "Arn" + ] + }, + "Runtime": "nodejs16.x" + }, + "DependsOn": [ + "testAuthFunctionServiceRole67229217" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "Test Resource" + }, + { + "id": "W89", + "reason": "Test Resource" + }, + { + "id": "W92", + "reason": "Test Resource" + } + ] + } + } + }, + "testAuthFunctionwafapinoargumentstestauthorizerB886165DPermissions26C374CC": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "testAuthFunctionDEE315BB", + "Arn" + ] + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "testApiD6ECAB50" + }, + "/authorizers/", + { + "Ref": "testauthorizer92817493" + } + ] + ] + } + } + }, + "testauthorizer92817493": { + "Type": "AWS::ApiGateway::Authorizer", + "Properties": { + "AuthorizerUri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + ":", + { + "Fn::GetAtt": [ + "testAuthFunctionDEE315BB", + "Arn" + ] + } + ] + } + ] + }, + ":apigateway:", + { + "Fn::Select": [ + 3, + { + "Fn::Split": [ + ":", + { + "Fn::GetAtt": [ + "testAuthFunctionDEE315BB", + "Arn" + ] + } + ] + } + ] + }, + ":lambda:path/2015-03-31/functions/", + { + "Fn::GetAtt": [ + "testAuthFunctionDEE315BB", + "Arn" + ] + }, + "/invocations" + ] + ] + }, + "IdentitySource": "method.request.header.Authorization", + "Name": "wafapinoargumentstestauthorizerB886165D", + "RestApiId": { + "Ref": "testApiD6ECAB50" + }, + "Type": "REQUEST" + } + }, "testApiD6ECAB50": { "Type": "AWS::ApiGateway::RestApi", "Properties": { @@ -126,7 +297,7 @@ "UpdateReplacePolicy": "Retain", "DeletionPolicy": "Retain" }, - "testApiDeployment3727A0B98616f677c89f00f84016798f65d107c0": { + "testApiDeployment3727A0B97295752083e4c65ca6a2bed01f04b1cd": { "Type": "AWS::ApiGateway::Deployment", "Properties": { "Description": "Automatically created by the RestApi construct", @@ -154,7 +325,7 @@ "Type": "AWS::ApiGateway::Stage", "Properties": { "DeploymentId": { - "Ref": "testApiDeployment3727A0B98616f677c89f00f84016798f65d107c0" + "Ref": "testApiDeployment3727A0B97295752083e4c65ca6a2bed01f04b1cd" }, "RestApiId": { "Ref": "testApiD6ECAB50" @@ -275,7 +446,10 @@ "testApiproxyANYC53F2608": { "Type": "AWS::ApiGateway::Method", "Properties": { - "AuthorizationType": "NONE", + "AuthorizationType": "CUSTOM", + "AuthorizerId": { + "Ref": "testauthorizer92817493" + }, "HttpMethod": "ANY", "Integration": { "IntegrationHttpMethod": "POST", @@ -403,7 +577,10 @@ "testApiANYDC600770": { "Type": "AWS::ApiGateway::Method", "Properties": { - "AuthorizationType": "NONE", + "AuthorizationType": "CUSTOM", + "AuthorizerId": { + "Ref": "testauthorizer92817493" + }, "HttpMethod": "ANY", "Integration": { "IntegrationHttpMethod": "POST", diff --git a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-no-arguments.ts b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-no-arguments.ts index 1464ca23c..60615a3fd 100644 --- a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-no-arguments.ts +++ b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-no-arguments.ts @@ -14,8 +14,7 @@ /// !cdk-integ * import { App, Stack } from "aws-cdk-lib"; import { WafwebaclToApiGateway } from "../lib"; -import { generateIntegStackName } from "@aws-solutions-constructs/core"; -import { CreateTestApi } from './test-helper'; +import { generateIntegStackName, CreateTestApi } from "@aws-solutions-constructs/core"; const app = new App(); diff --git a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-partial-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-partial-arguments.expected.json index 87d37dc69..7c6638d28 100644 --- a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-partial-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-partial-arguments.expected.json @@ -38,7 +38,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "0904d3723480fed2daf7885caa427b930881caae6879d1e6b0d395020173ef6f.zip" + "S3Key": "42a35bbf0dec9ef0ac5b0dde87e71a1b8929e8d2d178dd09ccfb2c928ec0198c.zip" }, "Handler": ".handler", "Role": { @@ -71,6 +71,177 @@ } } }, + "testAuthFunctionServiceRole67229217": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "testAuthFunctionDEE315BB": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "42a35bbf0dec9ef0ac5b0dde87e71a1b8929e8d2d178dd09ccfb2c928ec0198c.zip" + }, + "Handler": ".handler", + "Role": { + "Fn::GetAtt": [ + "testAuthFunctionServiceRole67229217", + "Arn" + ] + }, + "Runtime": "nodejs16.x" + }, + "DependsOn": [ + "testAuthFunctionServiceRole67229217" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "Test Resource" + }, + { + "id": "W89", + "reason": "Test Resource" + }, + { + "id": "W92", + "reason": "Test Resource" + } + ] + } + } + }, + "testAuthFunctionwafapipartialargumentstestauthorizerDC0C2973Permissions3E01E2C8": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "testAuthFunctionDEE315BB", + "Arn" + ] + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "testApiD6ECAB50" + }, + "/authorizers/", + { + "Ref": "testauthorizer92817493" + } + ] + ] + } + } + }, + "testauthorizer92817493": { + "Type": "AWS::ApiGateway::Authorizer", + "Properties": { + "AuthorizerUri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + ":", + { + "Fn::GetAtt": [ + "testAuthFunctionDEE315BB", + "Arn" + ] + } + ] + } + ] + }, + ":apigateway:", + { + "Fn::Select": [ + 3, + { + "Fn::Split": [ + ":", + { + "Fn::GetAtt": [ + "testAuthFunctionDEE315BB", + "Arn" + ] + } + ] + } + ] + }, + ":lambda:path/2015-03-31/functions/", + { + "Fn::GetAtt": [ + "testAuthFunctionDEE315BB", + "Arn" + ] + }, + "/invocations" + ] + ] + }, + "IdentitySource": "method.request.header.Authorization", + "Name": "wafapipartialargumentstestauthorizerDC0C2973", + "RestApiId": { + "Ref": "testApiD6ECAB50" + }, + "Type": "REQUEST" + } + }, "testApiD6ECAB50": { "Type": "AWS::ApiGateway::RestApi", "Properties": { @@ -126,7 +297,7 @@ "UpdateReplacePolicy": "Retain", "DeletionPolicy": "Retain" }, - "testApiDeployment3727A0B98616f677c89f00f84016798f65d107c0": { + "testApiDeployment3727A0B97295752083e4c65ca6a2bed01f04b1cd": { "Type": "AWS::ApiGateway::Deployment", "Properties": { "Description": "Automatically created by the RestApi construct", @@ -154,7 +325,7 @@ "Type": "AWS::ApiGateway::Stage", "Properties": { "DeploymentId": { - "Ref": "testApiDeployment3727A0B98616f677c89f00f84016798f65d107c0" + "Ref": "testApiDeployment3727A0B97295752083e4c65ca6a2bed01f04b1cd" }, "RestApiId": { "Ref": "testApiD6ECAB50" @@ -275,7 +446,10 @@ "testApiproxyANYC53F2608": { "Type": "AWS::ApiGateway::Method", "Properties": { - "AuthorizationType": "NONE", + "AuthorizationType": "CUSTOM", + "AuthorizerId": { + "Ref": "testauthorizer92817493" + }, "HttpMethod": "ANY", "Integration": { "IntegrationHttpMethod": "POST", @@ -403,7 +577,10 @@ "testApiANYDC600770": { "Type": "AWS::ApiGateway::Method", "Properties": { - "AuthorizationType": "NONE", + "AuthorizationType": "CUSTOM", + "AuthorizerId": { + "Ref": "testauthorizer92817493" + }, "HttpMethod": "ANY", "Integration": { "IntegrationHttpMethod": "POST", diff --git a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-partial-arguments.ts b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-partial-arguments.ts index a94a553fb..b2fa07523 100644 --- a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-partial-arguments.ts +++ b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-partial-arguments.ts @@ -14,8 +14,7 @@ /// !cdk-integ * import { App, Stack } from "aws-cdk-lib"; import { WafwebaclToApiGateway } from "../lib"; -import { generateIntegStackName } from "@aws-solutions-constructs/core"; -import { CreateTestApi } from './test-helper'; +import { generateIntegStackName, CreateTestApi } from "@aws-solutions-constructs/core"; const app = new App(); diff --git a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/test-helper.ts b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/test-helper.ts deleted file mode 100644 index 3fa2ad9ab..000000000 --- a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/test-helper.ts +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance - * with the License. A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES - * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions - * and limitations under the License. - */ - -import { Stack } from "aws-cdk-lib"; -import * as lambda from "aws-cdk-lib/aws-lambda"; -import * as api from "aws-cdk-lib/aws-apigateway"; -import { addCfnSuppressRules } from "@aws-solutions-constructs/core"; - -export function CreateTestApi(stack: Stack, id: string): api.LambdaRestApi { - const lambdaFunction = new lambda.Function(stack, `${id}Function`, { - code: lambda.Code.fromAsset(`${__dirname}/lambda`), - runtime: lambda.Runtime.NODEJS_16_X, - handler: ".handler", - }); - addCfnSuppressRules(lambdaFunction, [{ id: "W58", reason: "Test Resource" }]); - addCfnSuppressRules(lambdaFunction, [{ id: "W89", reason: "Test Resource" }]); - addCfnSuppressRules(lambdaFunction, [{ id: "W92", reason: "Test Resource" }]); - - const restApi = new api.LambdaRestApi(stack, `${id}Api`, { - handler: lambdaFunction, - }); - - const newDeployment = restApi.latestDeployment; - if (newDeployment) { - addCfnSuppressRules(newDeployment, [ - { id: "W68", reason: "Test Resource" }, - ]); - } - - const newMethod = restApi.methods[0]; - addCfnSuppressRules(newMethod, [{ id: "W59", reason: "Test Resource" }]); - const newMethodTwo = restApi.methods[1]; - addCfnSuppressRules(newMethodTwo, [{ id: "W59", reason: "Test Resource" }]); - - const newStage = restApi.deploymentStage; - addCfnSuppressRules(newStage, [{ id: "W64", reason: "Test Resource" }]); - addCfnSuppressRules(newStage, [{ id: "W69", reason: "Test Resource" }]); - - return restApi; -} diff --git a/source/patterns/@aws-solutions-constructs/core/test/kinesis-analytics.test.ts b/source/patterns/@aws-solutions-constructs/core/test/kinesis-analytics.test.ts index 9a36a8699..cb3db124e 100644 --- a/source/patterns/@aws-solutions-constructs/core/test/kinesis-analytics.test.ts +++ b/source/patterns/@aws-solutions-constructs/core/test/kinesis-analytics.test.ts @@ -123,7 +123,7 @@ function CreateFirehose(stack: Stack): kinesisFirehose.CfnDeliveryStream { // Creating the Firehose is kind of a big deal. FirehoseToS3 is not readily available here in core, // so this routine pretty much replicates it. If this function ceases to work correctly, look at // FirehoseToS3 and see if that changed. - const destinationBucket = defaults.CreateScrapBucket(stack, { + const destinationBucket = defaults.CreateScrapBucket(stack, "scrapBucket", { removalPolicy: RemovalPolicy.DESTROY, autoDeleteObjects: true, }); diff --git a/source/patterns/@aws-solutions-constructs/core/test/s3-bucket.test.ts b/source/patterns/@aws-solutions-constructs/core/test/s3-bucket.test.ts index f36fd43e8..bddb93aa7 100644 --- a/source/patterns/@aws-solutions-constructs/core/test/s3-bucket.test.ts +++ b/source/patterns/@aws-solutions-constructs/core/test/s3-bucket.test.ts @@ -369,7 +369,7 @@ test('Test fail S3 check', () => { const stack = new Stack(); const props: defaults.S3Props = { - existingBucketObj: CreateScrapBucket(stack, {}), + existingBucketObj: CreateScrapBucket(stack, "scrapBucket"), bucketProps: {}, }; diff --git a/source/patterns/@aws-solutions-constructs/core/test/test-helper.ts b/source/patterns/@aws-solutions-constructs/core/test/test-helper.ts index cf1ca2f85..cec9b68ef 100644 --- a/source/patterns/@aws-solutions-constructs/core/test/test-helper.ts +++ b/source/patterns/@aws-solutions-constructs/core/test/test-helper.ts @@ -26,11 +26,13 @@ import * as acm from 'aws-cdk-lib/aws-certificatemanager'; import { CfnFunction } from "aws-cdk-lib/aws-lambda"; import { GetDefaultCachePort } from "../lib/elasticache-defaults"; import { Match, Template } from 'aws-cdk-lib/assertions'; +import * as lambda from "aws-cdk-lib/aws-lambda"; +import * as api from "aws-cdk-lib/aws-apigateway"; export const fakeEcrRepoArn = 'arn:aws:ecr:us-east-1:123456789012:repository/fake-repo'; // Creates a bucket used for testing - minimal properties, destroyed after test -export function CreateScrapBucket(scope: Construct, props?: BucketProps | any) { +export function CreateScrapBucket(scope: Construct, id: string, props?: BucketProps | any) { const defaultProps: BucketProps = { versioned: true, @@ -48,7 +50,7 @@ export function CreateScrapBucket(scope: Construct, props?: BucketProps | any) { const scriptBucket = new Bucket( scope, - "scrapBucket", + id, synthesizedProps ); @@ -194,6 +196,56 @@ class CfnNagLambdaAspect implements IAspect { } } +export function CreateTestApi(stack: Stack, id: string): api.LambdaRestApi { + const lambdaFunction = new lambda.Function(stack, `${id}Function`, { + code: lambda.Code.fromAsset(`${__dirname}/lambda`), + runtime: lambda.Runtime.NODEJS_16_X, + handler: ".handler", + }); + addCfnSuppressRules(lambdaFunction, [{ id: "W58", reason: "Test Resource" }]); + addCfnSuppressRules(lambdaFunction, [{ id: "W89", reason: "Test Resource" }]); + addCfnSuppressRules(lambdaFunction, [{ id: "W92", reason: "Test Resource" }]); + const authFn = new lambda.Function(stack, `${id}AuthFunction`, { + code: lambda.Code.fromAsset(`${__dirname}/lambda`), + runtime: lambda.Runtime.NODEJS_16_X, + handler: ".handler", + }); + addCfnSuppressRules(authFn, [{ id: "W58", reason: "Test Resource" }]); + addCfnSuppressRules(authFn, [{ id: "W89", reason: "Test Resource" }]); + addCfnSuppressRules(authFn, [{ id: "W92", reason: "Test Resource" }]); + + const auth = new api.RequestAuthorizer(stack, `${id}-authorizer`, { + handler: authFn, + identitySources: [api.IdentitySource.header('Authorization')] + }); + + const restApi = new api.LambdaRestApi(stack, `${id}Api`, { + handler: lambdaFunction, + defaultMethodOptions: { + authorizationType: api.AuthorizationType.CUSTOM, + authorizer: auth + } + }); + + const newDeployment = restApi.latestDeployment; + if (newDeployment) { + addCfnSuppressRules(newDeployment, [ + { id: "W68", reason: "Test Resource" }, + ]); + } + + const newMethod = restApi.methods[0]; + addCfnSuppressRules(newMethod, [{ id: "W59", reason: "Test Resource" }]); + const newMethodTwo = restApi.methods[1]; + addCfnSuppressRules(newMethodTwo, [{ id: "W59", reason: "Test Resource" }]); + + const newStage = restApi.deploymentStage; + addCfnSuppressRules(newStage, [{ id: "W64", reason: "Test Resource" }]); + addCfnSuppressRules(newStage, [{ id: "W69", reason: "Test Resource" }]); + + return restApi; +} + /** * Used to suppress cfn nag W58, W89, and W92 rules on lambda integration test resources. * From 41e99d352ccba9908307aae8c242d36c14881349 Mon Sep 17 00:00:00 2001 From: biffgaut <78155736+biffgaut@users.noreply.github.com> Date: Tue, 5 Dec 2023 16:41:14 -0500 Subject: [PATCH 02/14] Naming Collisions, autoDeleteObjects (#1035) --- refresh-multiple-tests.sh | 51 +++-- .../integ.kinglu-code-asset-job.expected.json | 24 ++- .../integ.kinglu-no-arguments.expected.json | 24 ++- .../test/kinesisstream-gluejob.test.ts | 12 +- .../integ.kinfhss3-no-arguments.expected.json | 204 ++++++++++++++++++ .../test/integ.kinfhss3-no-arguments.ts | 8 +- .../core/lib/glue-job-helper.ts | 2 +- .../core/test/glue-job-helper.test.ts | 51 +++-- 8 files changed, 341 insertions(+), 35 deletions(-) diff --git a/refresh-multiple-tests.sh b/refresh-multiple-tests.sh index 2ff5aa022..7d7d0fc46 100755 --- a/refresh-multiple-tests.sh +++ b/refresh-multiple-tests.sh @@ -20,27 +20,52 @@ # dynamodbstreams-lambda and lambda-elasticsearch) export constructs=" - aws-s3-lambda - aws-s3-sns - aws-s3-sqs - aws-s3-stepfunctions + aws-kinesisstreams-gluejob aws-lambda-s3 - aws-fargate-s3 - aws-cloudfront-s3 " -# deployment_dir is top level aws-solutions-constructs -deployment_dir=$(cd $(dirname $0) && pwd) +constructs_root_dir=$(cd $(dirname $0) && pwd) +source_dir="$constructs_root_dir/source" + +echo "=============================================================================================" +echo "aligning versions and updating package.json for CDK v2..." +/bin/bash $constructs_root_dir/align-version.sh + +bail="--bail" +runtarget="build+lint+test" +cd $source_dir/ + +export PATH=$source_dir/node_modules/.bin:$PATH +export NODE_OPTIONS="--max-old-space-size=4096 ${NODE_OPTIONS:-}" + +echo "=============================================================================================" +echo "installing..." +yarn install --frozen-lockfile + +# echo "=============================================================================================" +# echo "updating Import statements for CDK v2..." +# /bin/bash $constructs_root_dir/rewrite-imports.sh + +echo "=============================================================================================" +echo "building cdk-integ-tools..." +cd $source_dir/tools/cdk-integ-tools +npm install +npm run build +npm link + +cd $source_dir +echo "=============================================================================================" +echo "building..." +time lerna run $bail --stream $runtarget || fail -source ./deployment/v2/allow-partial-builds.sh for construct in $constructs; do - cd $deployment_dir/source/patterns/@aws-solutions-constructs/$construct + cd $constructs_root_dir/source/patterns/@aws-solutions-constructs/$construct echo Running in $PWD npm run jsii && npm run lint - cdk-integ + cdk-integ --no-clean npm run build+lint+test - cd $deployment_dir/source/patterns/@aws-solutions-constructs + cd $constructs_root_dir/source/patterns/@aws-solutions-constructs done -cd $deployment_dir +cd $constructs_root_dir ./deployment/v2/align-version.sh revert diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-gluejob/test/integ.kinglu-code-asset-job.expected.json b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-gluejob/test/integ.kinglu-code-asset-job.expected.json index 81b1bf65b..d04678fc1 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-gluejob/test/integ.kinglu-code-asset-job.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-gluejob/test/integ.kinglu-code-asset-job.expected.json @@ -45,7 +45,17 @@ } ] }, - "Name": "ETLJobSecurityConfig" + "Name": { + "Fn::Join": [ + "", + [ + "ETLJobSecurityConfig", + { + "Ref": "AWS::StackId" + } + ] + ] + } } }, "testkinesisstreamslambdaLogPolicy5FB58427": { @@ -452,7 +462,17 @@ "Arn" ] }, - "SecurityConfiguration": "ETLJobSecurityConfig", + "SecurityConfiguration": { + "Fn::Join": [ + "", + [ + "ETLJobSecurityConfig", + { + "Ref": "AWS::StackId" + } + ] + ] + }, "WorkerType": "G.1X" } }, diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-gluejob/test/integ.kinglu-no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-gluejob/test/integ.kinglu-no-arguments.expected.json index c3e70887b..7d9a0f7ab 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-gluejob/test/integ.kinglu-no-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-gluejob/test/integ.kinglu-no-arguments.expected.json @@ -45,7 +45,17 @@ } ] }, - "Name": "ETLJobSecurityConfig" + "Name": { + "Fn::Join": [ + "", + [ + "ETLJobSecurityConfig", + { + "Ref": "AWS::StackId" + } + ] + ] + } } }, "testkinesisstreamslambdaLogPolicy5FB58427": { @@ -443,7 +453,17 @@ "Arn" ] }, - "SecurityConfiguration": "ETLJobSecurityConfig", + "SecurityConfiguration": { + "Fn::Join": [ + "", + [ + "ETLJobSecurityConfig", + { + "Ref": "AWS::StackId" + } + ] + ] + }, "WorkerType": "G.1X" } }, diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-gluejob/test/kinesisstream-gluejob.test.ts b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-gluejob/test/kinesisstream-gluejob.test.ts index 5e0f4a5f4..f02850c40 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-gluejob/test/kinesisstream-gluejob.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-gluejob/test/kinesisstream-gluejob.test.ts @@ -829,7 +829,17 @@ test('When Asset for local file is defined', () => { DefaultArguments: {}, GlueVersion: "2.0", NumberOfWorkers: 2, - SecurityConfiguration: "ETLJobSecurityConfig", + SecurityConfiguration: { + "Fn::Join": [ + "", + [ + "ETLJobSecurityConfig", + { + Ref: "AWS::StackId" + } + ] + ] + }, WorkerType: "G.1X" } }); diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-no-arguments.expected.json index ee1895cbd..c017d75cd 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-no-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-no-arguments.expected.json @@ -30,6 +30,12 @@ "IgnorePublicAcls": true, "RestrictPublicBuckets": true }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], "VersioningConfiguration": { "Status": "Enabled" } @@ -89,6 +95,45 @@ } ] }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "teststreamfirehoses3KinesisFirehoseToS3S3LoggingBucketFB87BEBC", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "teststreamfirehoses3KinesisFirehoseToS3S3LoggingBucketFB87BEBC", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": "s3:PutObject", "Condition": { @@ -130,6 +175,25 @@ } } }, + "teststreamfirehoses3KinesisFirehoseToS3S3LoggingBucketAutoDeleteObjectsCustomResource52F612C8": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "teststreamfirehoses3KinesisFirehoseToS3S3LoggingBucketFB87BEBC" + } + }, + "DependsOn": [ + "teststreamfirehoses3KinesisFirehoseToS3S3LoggingBucketPolicy4A393931" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "teststreamfirehoses3KinesisFirehoseToS3S3Bucket315B67A3": { "Type": "AWS::S3::Bucket", "Properties": { @@ -166,6 +230,12 @@ "IgnorePublicAcls": true, "RestrictPublicBuckets": true }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], "VersioningConfiguration": { "Status": "Enabled" } @@ -214,12 +284,70 @@ ] } ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "teststreamfirehoses3KinesisFirehoseToS3S3Bucket315B67A3", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "teststreamfirehoses3KinesisFirehoseToS3S3Bucket315B67A3", + "Arn" + ] + }, + "/*" + ] + ] + } + ] } ], "Version": "2012-10-17" } } }, + "teststreamfirehoses3KinesisFirehoseToS3S3BucketAutoDeleteObjectsCustomResource3DBAB3FD": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "teststreamfirehoses3KinesisFirehoseToS3S3Bucket315B67A3" + } + }, + "DependsOn": [ + "teststreamfirehoses3KinesisFirehoseToS3S3BucketPolicy6A903D55" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "teststreamfirehoses3KinesisFirehoseToS3firehoseloggroupEE4052E0": { "Type": "AWS::Logs::LogGroup", "UpdateReplacePolicy": "Delete", @@ -486,6 +614,82 @@ } ] } + }, + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ] + } + }, + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "b7f33614a69548d6bafe224d751a7ef238cde19097415e553fe8b63a4c8fd8a6.zip" + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + }, + "Runtime": "nodejs18.x", + "Description": { + "Fn::Join": [ + "", + [ + "Lambda function for auto-deleting objects in ", + { + "Ref": "teststreamfirehoses3KinesisFirehoseToS3S3LoggingBucketFB87BEBC" + }, + " S3 bucket." + ] + ] + } + }, + "DependsOn": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "CDK generated custom resource" + }, + { + "id": "W89", + "reason": "CDK generated custom resource" + }, + { + "id": "W92", + "reason": "CDK generated custom resource" + } + ] + } + } } }, "Parameters": { diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-no-arguments.ts b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-no-arguments.ts index 98edfbea3..008dcaad0 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-no-arguments.ts +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-no-arguments.ts @@ -14,7 +14,7 @@ // Imports import { App, Stack, RemovalPolicy } from "aws-cdk-lib"; import { KinesisStreamsToKinesisFirehoseToS3 } from '../lib'; -import { generateIntegStackName } from '@aws-solutions-constructs/core'; +import { generateIntegStackName, suppressAutoDeleteHandlerWarnings } from '@aws-solutions-constructs/core'; // Setup const app = new App(); @@ -24,11 +24,17 @@ stack.templateOptions.description = 'Integration Test for aws-kinesisstreams-kin new KinesisStreamsToKinesisFirehoseToS3(stack, 'test-stream-firehose-s3', { bucketProps: { removalPolicy: RemovalPolicy.DESTROY, + autoDeleteObjects: true + }, + loggingBucketProps: { + removalPolicy: RemovalPolicy.DESTROY, + autoDeleteObjects: true }, logGroupProps: { removalPolicy: RemovalPolicy.DESTROY } }); +suppressAutoDeleteHandlerWarnings(stack); // Synth app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/core/lib/glue-job-helper.ts b/source/patterns/@aws-solutions-constructs/core/lib/glue-job-helper.ts index d5e61b6eb..02752e744 100644 --- a/source/patterns/@aws-solutions-constructs/core/lib/glue-job-helper.ts +++ b/source/patterns/@aws-solutions-constructs/core/lib/glue-job-helper.ts @@ -146,7 +146,7 @@ export function deployGlueJob(scope: Construct, glueJobProps: glue.CfnJobProps, let glueSecurityConfigName: string; if (glueJobProps.securityConfiguration === undefined) { - glueSecurityConfigName = 'ETLJobSecurityConfig'; + glueSecurityConfigName = `ETLJobSecurityConfig${Aws.STACK_ID}`; const glueKMSKey = `arn:${Aws.PARTITION}:kms:${Aws.REGION}:${Aws.ACCOUNT_ID}:alias/aws/glue`; const securityConfigurationProps: glue.CfnSecurityConfigurationProps = { diff --git a/source/patterns/@aws-solutions-constructs/core/test/glue-job-helper.test.ts b/source/patterns/@aws-solutions-constructs/core/test/glue-job-helper.test.ts index 9a2e159d8..545f282af 100644 --- a/source/patterns/@aws-solutions-constructs/core/test/glue-job-helper.test.ts +++ b/source/patterns/@aws-solutions-constructs/core/test/glue-job-helper.test.ts @@ -46,7 +46,7 @@ test('Test deployment with role creation', () => { name: "id", type: "int", comment: "" - }], 'kinesis', {STREAM_NAME: 'testStream'}) + }], 'kinesis', { STREAM_NAME: 'testStream' }) }); expect(glueJob.bucket).toBeDefined(); @@ -105,7 +105,7 @@ test('Create a Glue Job outside the construct', () => { name: "id", type: "int", comment: "" - }], 'kinesis', {STREAM_NAME: 'testStream'}) + }], 'kinesis', { STREAM_NAME: 'testStream' }) }); expect(glueJob.bucket).not.toBeDefined(); @@ -165,10 +165,11 @@ test('Test custom deployment properties', () => { name: "id", type: "int", comment: "" - }], 'kinesis', {STREAM_NAME: 'testStream'}) + }], 'kinesis', { STREAM_NAME: 'testStream' }) }); const template = Template.fromStack(stack); + defaults.printWarning(`***********${JSON.stringify(template)}`); // check if Glue Job Resource was created correctly template.hasResource('AWS::Glue::Job', { Properties: { @@ -185,14 +186,24 @@ test('Test custom deployment properties', () => { "Arn", ], }, - SecurityConfiguration: "ETLJobSecurityConfig", + SecurityConfiguration: { + "Fn::Join": [ + "", + [ + "ETLJobSecurityConfig", + { + Ref: "AWS::StackId" + } + ] + ] + }, WorkerType: "Standard", }, Type: "AWS::Glue::Job" }); // check if the role is created - template.hasResource('AWS::IAM::Role', { + template.hasResource('AWS::IAM::Role', { Type: "AWS::IAM::Role", Properties: { AssumeRolePolicyDocument: { @@ -238,7 +249,17 @@ test('Test custom deployment properties', () => { S3EncryptionMode: "SSE-S3", }], }, - Name: "ETLJobSecurityConfig", + Name: { + "Fn::Join": [ + "", + [ + "ETLJobSecurityConfig", + { + Ref: "AWS::StackId" + } + ] + ] + }, }, Type: "AWS::Glue::SecurityConfiguration", }); @@ -257,7 +278,7 @@ test('Do no supply glueJobProps or existingCfnJob and error out', () => { name: "id", type: "int", comment: "" - }], 'kinesis', {STREAM_NAME: 'testStream'})) + }], 'kinesis', { STREAM_NAME: 'testStream' })) }); } catch (error) { expect(error).toBeInstanceOf(Error); @@ -289,7 +310,7 @@ test('llow the construct to create the job role required', () => { name: "id", type: "int", comment: "" - }], 'kinesis', {STREAM_NAME: 'testStream'}) + }], 'kinesis', { STREAM_NAME: 'testStream' }) }); Template.fromStack(stack).hasResource('AWS::IAM::Role', { Type: "AWS::IAM::Role", @@ -340,7 +361,7 @@ test('Test deployment when output location is provided', () => { name: "id", type: "int", comment: "" - }], 'kinesis', {STREAM_NAME: 'testStream'}) + }], 'kinesis', { STREAM_NAME: 'testStream' }) }); Template.fromStack(stack).hasResource('AWS::S3::Bucket', { Type: 'AWS::S3::Bucket', @@ -370,7 +391,7 @@ test('Test for incorrect Job Command property', () => { name: "id", type: "int", comment: "" - }], 'kinesis', {STREAM_NAME: 'testStream'}) + }], 'kinesis', { STREAM_NAME: 'testStream' }) }); } catch (error) { expect(error).toBeInstanceOf(Error); @@ -395,7 +416,7 @@ test('check for JobCommandProperty type', () => { name: "id", type: "int", comment: "" - }], 'kinesis', {STREAM_NAME: 'testStream'}) + }], 'kinesis', { STREAM_NAME: 'testStream' }) }); } catch (error) { expect(error).toBeInstanceOf(Error); @@ -418,7 +439,7 @@ test('GlueJob configuration with glueVersion 2.0 should not support maxCapacity name: "id", type: "int", comment: "" - }], 'kinesis', {STREAM_NAME: 'testStream'})), + }], 'kinesis', { STREAM_NAME: 'testStream' })), glueJobProps: { glueVersion: '2.0', maxCapacity: '2' @@ -442,7 +463,7 @@ test('Cannot use maxCapacity and WorkerType, so error out', () => { name: "id", type: "int", comment: "" - }], 'kinesis', {STREAM_NAME: 'testStream'})), + }], 'kinesis', { STREAM_NAME: 'testStream' })), glueJobProps: { command: { name: "gluejob1.0", @@ -471,7 +492,7 @@ test('Cannot use maxCapacity and WorkerType, so error out', () => { name: "id", type: "int", comment: "" - }], 'kinesis', {STREAM_NAME: 'testStream'})), + }], 'kinesis', { STREAM_NAME: 'testStream' })), glueJobProps: { command: { name: "gluejob1.0", @@ -500,7 +521,7 @@ test('Cannot use maxCapacity and WorkerType, so error out', () => { name: "id", type: "int", comment: "" - }], 'kinesis', {STREAM_NAME: 'testStream'})), + }], 'kinesis', { STREAM_NAME: 'testStream' })), glueJobProps: { command: { name: "gluejob1.0", From 86d8ac20c436fc32a54177669cd8540e8511a7db Mon Sep 17 00:00:00 2001 From: biffgaut <78155736+biffgaut@users.noreply.github.com> Date: Thu, 7 Dec 2023 14:05:06 -0500 Subject: [PATCH 03/14] chore(all): clean up bucket behavior in integration tests (#1037) * Clean up bucket behavior in integration tests * Suppress CDK Custom Resource messages --------- Co-authored-by: AWS Solutions Constructs --- refresh-multiple-tests.sh | 36 +- .../integ.cfts3-custom-headers.expected.json | 66 +- .../test/integ.cfts3-custom-headers.ts | 6 +- ...nteg.cfts3-custom-originPath.expected.json | 66 +- .../test/integ.cfts3-custom-originPath.ts | 6 +- ...ustomCloudFrontLoggingBucket.expected.json | 667 ------------------ ...teg.cfts3-customCloudFrontLoggingBucket.ts | 40 -- ....cfts3-customLoggingBuckets.expected.json} | 37 +- ...ts => integ.cfts3-customLoggingBuckets.ts} | 26 +- .../integ.cfts3-existing-bucket.expected.json | 317 +++++++-- .../integ.cfts3-no-arguments.expected.json | 66 +- .../test/integ.cfts3-no-arguments.ts | 4 + ...eg.cfts3-no-security-headers.expected.json | 66 +- .../test/integ.cfts3-no-security-headers.ts | 4 + .../eventbridge-kinesisfirehose-s3.test.ts | 3 +- ...evtfhss3-customLoggingBucket.expected.json | 14 +- .../integ.evtfhss3-customLoggingBucket.ts | 12 +- ...tfhss3-existingLoggingBucket.expected.json | 237 ++++++- ...teg.fars3-existing-resources.expected.json | 233 +++++- ...iotfhss3-customLoggingBucket.expected.json | 14 +- .../integ.iotfhss3-customLoggingBucket.ts | 16 +- ...eg.iots3-iot-s3-defaultprops.expected.json | 152 +++- .../test/integ.iots3-iot-s3-defaultprops.ts | 12 +- ...iots3-iot-s3-existing-bucket.expected.json | 233 +++++- ...eg.fhss3-customLoggingBucket.expected.json | 14 +- .../test/integ.fhss3-customLoggingBucket.ts | 16 +- ...eg.fhss3-pre-existing-bucket.expected.json | 233 +++++- ...-pre-existing-logging-bucket.expected.json | 237 ++++++- ...kinfhss3-customLoggingBucket.expected.json | 13 + .../integ.kinfhss3-customLoggingBucket.ts | 15 +- ...teg.kinfhss3-existing-bucket.expected.json | 233 +++++- ...hss3-existing-logging-bucket.expected.json | 233 +++++- ...teg.lamken-minimal-arguments.expected.json | 233 +++++- .../test/integ.lamken-minimal-arguments.ts | 6 +- ...nteg.lamken-multiple-sources.expected.json | 233 +++++- .../test/integ.lamken-multiple-sources.ts | 1 + .../test/integ.lamken-with-vpc.expected.json | 233 +++++- .../test/integ.lamken-with-vpc.ts | 1 + ...eg.lams3-customLoggingBucket.expected.json | 14 +- .../test/integ.lams3-customLoggingBucket.ts | 16 +- .../integ.lams3-deployFunction.expected.json | 204 ++++++ .../test/integ.lams3-deployFunction.ts | 6 + ...eg.lams3-pre-existing-bucket.expected.json | 233 +++++- ...teg.s3lam-existing-s3-bucket.expected.json | 343 +++++++-- ...eg.s3sns-customLoggingBucket.expected.json | 13 + .../test/integ.s3sns-customLoggingBucket.ts | 14 +- ...integ.s3sns-existingS3Bucket.expected.json | 295 ++++++-- ...eg.s3sqs-customLoggingBucket.expected.json | 13 + .../test/integ.s3sqs-customLoggingBucket.ts | 15 +- ....s3sqs-existingLoggingBucket.expected.json | 237 ++++++- ...integ.s3sqs-existingS3Bucket.expected.json | 301 ++++++-- ...eg.s3stp-customLoggingBucket.expected.json | 13 + .../test/integ.s3stp-customLoggingBucket.ts | 15 +- ...eg.s3stp-pre-existing-bucket.expected.json | 269 ++++++- ...s3-stepfunctions-no-argument.expected.json | 204 ++++++ ...nteg.s3stp-s3-stepfunctions-no-argument.ts | 6 + .../core/test/test-helper.ts | 27 +- 57 files changed, 4974 insertions(+), 1298 deletions(-) delete mode 100644 source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customCloudFrontLoggingBucket.expected.json delete mode 100644 source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customCloudFrontLoggingBucket.ts rename source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/{integ.cfts3-customLoggingBucket.expected.json => integ.cfts3-customLoggingBuckets.expected.json} (94%) rename source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/{integ.cfts3-customLoggingBucket.ts => integ.cfts3-customLoggingBuckets.ts} (63%) diff --git a/refresh-multiple-tests.sh b/refresh-multiple-tests.sh index 7d7d0fc46..f2a2ed4ab 100755 --- a/refresh-multiple-tests.sh +++ b/refresh-multiple-tests.sh @@ -6,22 +6,33 @@ # for you. # Open a docker build environment -# Run build-patterns.sh through at least the building of core, then you can ctrl-c # List all the constructs whose integration tests you want to refresh in the # export constructs list (you can delete the examples that are there) # Run this script from the top level aws-solutions-constructs folder. # -# Advanced use - if you have too many constructs to wait for, you can create -# another copy of this script to run concurrently with a different list of constructs -# 1) Remove the align-version.sh revert lines from this script and the copy -# 2) Remove source allow-partial-builds.sh line from the copy -# 3) Keep any dependencies together and in order -# (e.g. dynamodbstreams-lambda-elasticsearch must be built AFTER -# dynamodbstreams-lambda and lambda-elasticsearch) +# Options to accelerate +# * adding --no-clean to the cdk-integ command will allow it to +# finish without destroying the stack. You can then destroy the stack manually +# from the console or command line so the stack destruction does not slow the process +# * adding & to the end of the cdk-integ command will execute it asynchronously. This +# allows you to refresh MANY constructs' tests simultaneously. Probably good to add +# a sleep 10 command before the end of the loop to keep from overwhelming CloudFormation export constructs=" - aws-kinesisstreams-gluejob + aws-cloudfront-s3 + aws-fargate-s3 + aws-iot-s3 + aws-kinesisfirehose-s3 + aws-lambda-kendra aws-lambda-s3 + aws-openapigateway-lambda + aws-s3-lambda + aws-s3-sns + aws-s3-sqs + aws-eventbridge-stepfunctions + aws-s3-stepfunctions + aws-eventbridge-kinesisfirehose-s3 + aws-kinesisstreams-kinesisfirehose-s3 " constructs_root_dir=$(cd $(dirname $0) && pwd) @@ -32,7 +43,7 @@ echo "aligning versions and updating package.json for CDK v2..." /bin/bash $constructs_root_dir/align-version.sh bail="--bail" -runtarget="build+lint+test" +runtarget="jsii" cd $source_dir/ export PATH=$source_dir/node_modules/.bin:$PATH @@ -62,9 +73,8 @@ for construct in $constructs; do cd $constructs_root_dir/source/patterns/@aws-solutions-constructs/$construct echo Running in $PWD - npm run jsii && npm run lint - cdk-integ --no-clean - npm run build+lint+test + cdk-integ --no-clean & + sleep 10 cd $constructs_root_dir/source/patterns/@aws-solutions-constructs done cd $constructs_root_dir diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-headers.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-headers.expected.json index f00c3051e..6bbf9bf82 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-headers.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-headers.expected.json @@ -51,6 +51,12 @@ "IgnorePublicAcls": true, "RestrictPublicBuckets": true }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], "VersioningConfiguration": { "Status": "Enabled" } @@ -110,6 +116,45 @@ } ] }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3S3LoggingBucket90D239DD", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3S3LoggingBucket90D239DD", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": "s3:PutObject", "Condition": { @@ -151,6 +196,25 @@ } } }, + "testcloudfronts3S3LoggingBucketAutoDeleteObjectsCustomResource6EE37727": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "testcloudfronts3S3LoggingBucket90D239DD" + } + }, + "DependsOn": [ + "testcloudfronts3S3LoggingBucketPolicy529D4CFF" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "testcloudfronts3S3BucketE0C5F76E": { "Type": "AWS::S3::Bucket", "Properties": { @@ -632,7 +696,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "testcloudfronts3S3BucketE0C5F76E" + "Ref": "testcloudfronts3S3LoggingBucket90D239DD" }, " S3 bucket." ] diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-headers.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-headers.ts index 02672e453..2341d85f4 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-headers.ts +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-headers.ts @@ -54,7 +54,11 @@ new CloudFrontToS3(stack, 'test-cloudfront-s3', { bucketProps: { removalPolicy: RemovalPolicy.DESTROY, autoDeleteObjects: true - } + }, + loggingBucketProps: { + removalPolicy: RemovalPolicy.DESTROY, + autoDeleteObjects: true + }, }); suppressAutoDeleteHandlerWarnings(stack); diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-originPath.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-originPath.expected.json index e49b49920..5b2bfe275 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-originPath.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-originPath.expected.json @@ -19,6 +19,12 @@ "IgnorePublicAcls": true, "RestrictPublicBuckets": true }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], "VersioningConfiguration": { "Status": "Enabled" } @@ -78,6 +84,45 @@ } ] }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3S3LoggingBucket90D239DD", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3S3LoggingBucket90D239DD", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": "s3:PutObject", "Condition": { @@ -119,6 +164,25 @@ } } }, + "testcloudfronts3S3LoggingBucketAutoDeleteObjectsCustomResource6EE37727": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "testcloudfronts3S3LoggingBucket90D239DD" + } + }, + "DependsOn": [ + "testcloudfronts3S3LoggingBucketPolicy529D4CFF" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "testcloudfronts3S3BucketE0C5F76E": { "Type": "AWS::S3::Bucket", "Properties": { @@ -601,7 +665,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "testcloudfronts3S3BucketE0C5F76E" + "Ref": "testcloudfronts3S3LoggingBucket90D239DD" }, " S3 bucket." ] diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-originPath.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-originPath.ts index 2d1d149b6..b47667be6 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-originPath.ts +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-originPath.ts @@ -30,7 +30,11 @@ new CloudFrontToS3(stack, 'test-cloudfront-s3', { bucketProps: { removalPolicy: RemovalPolicy.DESTROY, autoDeleteObjects: true - } + }, + loggingBucketProps: { + removalPolicy: RemovalPolicy.DESTROY, + autoDeleteObjects: true + }, }); suppressAutoDeleteHandlerWarnings(stack); diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customCloudFrontLoggingBucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customCloudFrontLoggingBucket.expected.json deleted file mode 100644 index ecd890e66..000000000 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customCloudFrontLoggingBucket.expected.json +++ /dev/null @@ -1,667 +0,0 @@ -{ - "Description": "Integration Test for aws-cloudfront-s3 custom CloudFront Logging Bubkcet", - "Resources": { - "testcloudfronts3S3LoggingBucket90D239DD": { - "Type": "AWS::S3::Bucket", - "Properties": { - "BucketEncryption": { - "ServerSideEncryptionConfiguration": [ - { - "ServerSideEncryptionByDefault": { - "SSEAlgorithm": "AES256" - } - } - ] - }, - "PublicAccessBlockConfiguration": { - "BlockPublicAcls": true, - "BlockPublicPolicy": true, - "IgnorePublicAcls": true, - "RestrictPublicBuckets": true - }, - "VersioningConfiguration": { - "Status": "Enabled" - } - }, - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete", - "Metadata": { - "cfn_nag": { - "rules_to_suppress": [ - { - "id": "W35", - "reason": "This S3 bucket is used as the access logging bucket for another bucket" - } - ] - } - } - }, - "testcloudfronts3S3LoggingBucketPolicy529D4CFF": { - "Type": "AWS::S3::BucketPolicy", - "Properties": { - "Bucket": { - "Ref": "testcloudfronts3S3LoggingBucket90D239DD" - }, - "PolicyDocument": { - "Statement": [ - { - "Action": "s3:*", - "Condition": { - "Bool": { - "aws:SecureTransport": "false" - } - }, - "Effect": "Deny", - "Principal": { - "AWS": "*" - }, - "Resource": [ - { - "Fn::GetAtt": [ - "testcloudfronts3S3LoggingBucket90D239DD", - "Arn" - ] - }, - { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "testcloudfronts3S3LoggingBucket90D239DD", - "Arn" - ] - }, - "/*" - ] - ] - } - ] - }, - { - "Action": "s3:PutObject", - "Condition": { - "ArnLike": { - "aws:SourceArn": { - "Fn::GetAtt": [ - "testcloudfronts3S3BucketE0C5F76E", - "Arn" - ] - } - }, - "StringEquals": { - "aws:SourceAccount": { - "Ref": "AWS::AccountId" - } - } - }, - "Effect": "Allow", - "Principal": { - "Service": "logging.s3.amazonaws.com" - }, - "Resource": { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "testcloudfronts3S3LoggingBucket90D239DD", - "Arn" - ] - }, - "/*" - ] - ] - } - } - ], - "Version": "2012-10-17" - } - } - }, - "testcloudfronts3S3BucketE0C5F76E": { - "Type": "AWS::S3::Bucket", - "Properties": { - "BucketEncryption": { - "ServerSideEncryptionConfiguration": [ - { - "ServerSideEncryptionByDefault": { - "SSEAlgorithm": "AES256" - } - } - ] - }, - "LifecycleConfiguration": { - "Rules": [ - { - "NoncurrentVersionTransitions": [ - { - "StorageClass": "GLACIER", - "TransitionInDays": 90 - } - ], - "Status": "Enabled" - } - ] - }, - "LoggingConfiguration": { - "DestinationBucketName": { - "Ref": "testcloudfronts3S3LoggingBucket90D239DD" - } - }, - "PublicAccessBlockConfiguration": { - "BlockPublicAcls": true, - "BlockPublicPolicy": true, - "IgnorePublicAcls": true, - "RestrictPublicBuckets": true - }, - "Tags": [ - { - "Key": "aws-cdk:auto-delete-objects", - "Value": "true" - } - ], - "VersioningConfiguration": { - "Status": "Enabled" - } - }, - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, - "testcloudfronts3S3BucketPolicy250F1F61": { - "Type": "AWS::S3::BucketPolicy", - "Properties": { - "Bucket": { - "Ref": "testcloudfronts3S3BucketE0C5F76E" - }, - "PolicyDocument": { - "Statement": [ - { - "Action": "s3:*", - "Condition": { - "Bool": { - "aws:SecureTransport": "false" - } - }, - "Effect": "Deny", - "Principal": { - "AWS": "*" - }, - "Resource": [ - { - "Fn::GetAtt": [ - "testcloudfronts3S3BucketE0C5F76E", - "Arn" - ] - }, - { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "testcloudfronts3S3BucketE0C5F76E", - "Arn" - ] - }, - "/*" - ] - ] - } - ] - }, - { - "Action": [ - "s3:PutBucketPolicy", - "s3:GetBucket*", - "s3:List*", - "s3:DeleteObject*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", - "Arn" - ] - } - }, - "Resource": [ - { - "Fn::GetAtt": [ - "testcloudfronts3S3BucketE0C5F76E", - "Arn" - ] - }, - { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "testcloudfronts3S3BucketE0C5F76E", - "Arn" - ] - }, - "/*" - ] - ] - } - ] - }, - { - "Action": "s3:GetObject", - "Effect": "Allow", - "Principal": { - "CanonicalUser": { - "Fn::GetAtt": [ - "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058", - "S3CanonicalUserId" - ] - } - }, - "Resource": { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "testcloudfronts3S3BucketE0C5F76E", - "Arn" - ] - }, - "/*" - ] - ] - } - } - ], - "Version": "2012-10-17" - } - }, - "Metadata": { - "cfn_nag": { - "rules_to_suppress": [ - { - "id": "F16", - "reason": "Public website bucket policy requires a wildcard principal" - } - ] - } - } - }, - "testcloudfronts3S3BucketAutoDeleteObjectsCustomResourceA13DD8F7": { - "Type": "Custom::S3AutoDeleteObjects", - "Properties": { - "ServiceToken": { - "Fn::GetAtt": [ - "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", - "Arn" - ] - }, - "BucketName": { - "Ref": "testcloudfronts3S3BucketE0C5F76E" - } - }, - "DependsOn": [ - "testcloudfronts3S3BucketPolicy250F1F61" - ], - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, - "testcloudfronts3SetHttpSecurityHeaders6C5A1E69": { - "Type": "AWS::CloudFront::Function", - "Properties": { - "AutoPublish": true, - "FunctionCode": "function handler(event) { var response = event.response; var headers = response.headers; headers['strict-transport-security'] = { value: 'max-age=63072000; includeSubdomains; preload'}; headers['content-security-policy'] = { value: \"default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'\"}; headers['x-content-type-options'] = { value: 'nosniff'}; headers['x-frame-options'] = {value: 'DENY'}; headers['x-xss-protection'] = {value: '1; mode=block'}; return response; }", - "FunctionConfig": { - "Comment": "SetHttpSecurityHeadersc853f5cf48adabb9680b666a0c549e9b779fe54127", - "Runtime": "cloudfront-js-1.0" - }, - "Name": "SetHttpSecurityHeadersc853f5cf48adabb9680b666a0c549e9b779fe54127" - } - }, - "testcloudfronts3CloudfrontLoggingBucket985C0FE8": { - "Type": "AWS::S3::Bucket", - "Properties": { - "AccessControl": "LogDeliveryWrite", - "BucketEncryption": { - "ServerSideEncryptionConfiguration": [ - { - "ServerSideEncryptionByDefault": { - "SSEAlgorithm": "AES256" - } - } - ] - }, - "OwnershipControls": { - "Rules": [ - { - "ObjectOwnership": "ObjectWriter" - } - ] - }, - "PublicAccessBlockConfiguration": { - "BlockPublicAcls": true, - "BlockPublicPolicy": true, - "IgnorePublicAcls": true, - "RestrictPublicBuckets": true - }, - "Tags": [ - { - "Key": "aws-cdk:auto-delete-objects", - "Value": "true" - } - ], - "VersioningConfiguration": { - "Status": "Enabled" - } - }, - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete", - "Metadata": { - "cfn_nag": { - "rules_to_suppress": [ - { - "id": "W35", - "reason": "This S3 bucket is used as the access logging bucket for CloudFront Distribution" - } - ] - } - } - }, - "testcloudfronts3CloudfrontLoggingBucketPolicyDF55851B": { - "Type": "AWS::S3::BucketPolicy", - "Properties": { - "Bucket": { - "Ref": "testcloudfronts3CloudfrontLoggingBucket985C0FE8" - }, - "PolicyDocument": { - "Statement": [ - { - "Action": "s3:*", - "Condition": { - "Bool": { - "aws:SecureTransport": "false" - } - }, - "Effect": "Deny", - "Principal": { - "AWS": "*" - }, - "Resource": [ - { - "Fn::GetAtt": [ - "testcloudfronts3CloudfrontLoggingBucket985C0FE8", - "Arn" - ] - }, - { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "testcloudfronts3CloudfrontLoggingBucket985C0FE8", - "Arn" - ] - }, - "/*" - ] - ] - } - ] - }, - { - "Action": [ - "s3:PutBucketPolicy", - "s3:GetBucket*", - "s3:List*", - "s3:DeleteObject*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", - "Arn" - ] - } - }, - "Resource": [ - { - "Fn::GetAtt": [ - "testcloudfronts3CloudfrontLoggingBucket985C0FE8", - "Arn" - ] - }, - { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "testcloudfronts3CloudfrontLoggingBucket985C0FE8", - "Arn" - ] - }, - "/*" - ] - ] - } - ] - } - ], - "Version": "2012-10-17" - } - } - }, - "testcloudfronts3CloudfrontLoggingBucketAutoDeleteObjectsCustomResource19604D88": { - "Type": "Custom::S3AutoDeleteObjects", - "Properties": { - "ServiceToken": { - "Fn::GetAtt": [ - "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", - "Arn" - ] - }, - "BucketName": { - "Ref": "testcloudfronts3CloudfrontLoggingBucket985C0FE8" - } - }, - "DependsOn": [ - "testcloudfronts3CloudfrontLoggingBucketPolicyDF55851B" - ], - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, - "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058": { - "Type": "AWS::CloudFront::CloudFrontOriginAccessIdentity", - "Properties": { - "CloudFrontOriginAccessIdentityConfig": { - "Comment": "Identity for cfts3customCloudFrontLoggingBuckettestcloudfronts3CloudFrontDistributionOrigin18A4ECB64" - } - } - }, - "testcloudfronts3CloudFrontDistribution0565DEE8": { - "Type": "AWS::CloudFront::Distribution", - "Properties": { - "DistributionConfig": { - "DefaultCacheBehavior": { - "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", - "Compress": true, - "FunctionAssociations": [ - { - "EventType": "viewer-response", - "FunctionARN": { - "Fn::GetAtt": [ - "testcloudfronts3SetHttpSecurityHeaders6C5A1E69", - "FunctionARN" - ] - } - } - ], - "TargetOriginId": "cfts3customCloudFrontLoggingBuckettestcloudfronts3CloudFrontDistributionOrigin18A4ECB64", - "ViewerProtocolPolicy": "redirect-to-https" - }, - "DefaultRootObject": "index.html", - "Enabled": true, - "HttpVersion": "http2", - "IPV6Enabled": true, - "Logging": { - "Bucket": { - "Fn::GetAtt": [ - "testcloudfronts3CloudfrontLoggingBucket985C0FE8", - "RegionalDomainName" - ] - } - }, - "Origins": [ - { - "DomainName": { - "Fn::GetAtt": [ - "testcloudfronts3S3BucketE0C5F76E", - "RegionalDomainName" - ] - }, - "Id": "cfts3customCloudFrontLoggingBuckettestcloudfronts3CloudFrontDistributionOrigin18A4ECB64", - "S3OriginConfig": { - "OriginAccessIdentity": { - "Fn::Join": [ - "", - [ - "origin-access-identity/cloudfront/", - { - "Ref": "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058" - } - ] - ] - } - } - } - ] - } - }, - "Metadata": { - "cfn_nag": { - "rules_to_suppress": [ - { - "id": "W70", - "reason": "Since the distribution uses the CloudFront domain name, CloudFront automatically sets the security policy to TLSv1 regardless of the value of MinimumProtocolVersion" - } - ] - } - } - }, - "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "lambda.amazonaws.com" - } - } - ] - }, - "ManagedPolicyArns": [ - { - "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - } - ] - } - }, - "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "S3Bucket": { - "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" - }, - "S3Key": "b7f33614a69548d6bafe224d751a7ef238cde19097415e553fe8b63a4c8fd8a6.zip" - }, - "Timeout": 900, - "MemorySize": 128, - "Handler": "index.handler", - "Role": { - "Fn::GetAtt": [ - "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", - "Arn" - ] - }, - "Runtime": "nodejs18.x", - "Description": { - "Fn::Join": [ - "", - [ - "Lambda function for auto-deleting objects in ", - { - "Ref": "testcloudfronts3S3BucketE0C5F76E" - }, - " S3 bucket." - ] - ] - } - }, - "DependsOn": [ - "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" - ], - "Metadata": { - "cfn_nag": { - "rules_to_suppress": [ - { - "id": "W58", - "reason": "CDK generated custom resource" - }, - { - "id": "W89", - "reason": "CDK generated custom resource" - }, - { - "id": "W92", - "reason": "CDK generated custom resource" - } - ] - } - } - } - }, - "Parameters": { - "BootstrapVersion": { - "Type": "AWS::SSM::Parameter::Value", - "Default": "/cdk-bootstrap/hnb659fds/version", - "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" - } - }, - "Rules": { - "CheckBootstrapVersion": { - "Assertions": [ - { - "Assert": { - "Fn::Not": [ - { - "Fn::Contains": [ - [ - "1", - "2", - "3", - "4", - "5" - ], - { - "Ref": "BootstrapVersion" - } - ] - } - ] - }, - "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." - } - ] - } - } -} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customCloudFrontLoggingBucket.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customCloudFrontLoggingBucket.ts deleted file mode 100644 index 1a10f3abf..000000000 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customCloudFrontLoggingBucket.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance - * with the License. A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES - * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions - * and limitations under the License. - */ - -// Imports -import { App, Stack, RemovalPolicy } from "aws-cdk-lib"; -import { BucketEncryption } from "aws-cdk-lib/aws-s3"; -import { CloudFrontToS3 } from "../lib"; -import { generateIntegStackName, suppressAutoDeleteHandlerWarnings } from '@aws-solutions-constructs/core'; - -// Setup -const app = new App(); -const stack = new Stack(app, generateIntegStackName(__filename)); -stack.templateOptions.description = 'Integration Test for aws-cloudfront-s3 custom CloudFront Logging Bubkcet'; - -new CloudFrontToS3(stack, 'test-cloudfront-s3', { - bucketProps: { - removalPolicy: RemovalPolicy.DESTROY, - autoDeleteObjects: true - }, - cloudFrontLoggingBucketProps: { - removalPolicy: RemovalPolicy.DESTROY, - autoDeleteObjects: true, - encryption: BucketEncryption.S3_MANAGED, - versioned: true - } -}); - -suppressAutoDeleteHandlerWarnings(stack); -// Synth -app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customLoggingBucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customLoggingBuckets.expected.json similarity index 94% rename from source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customLoggingBucket.expected.json rename to source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customLoggingBuckets.expected.json index 9654a03cf..4b00af10c 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customLoggingBucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customLoggingBuckets.expected.json @@ -12,7 +12,19 @@ } ] }, - "BucketName": "custom-logging-bucket", + "LifecycleConfiguration": { + "Rules": [ + { + "Status": "Enabled", + "Transitions": [ + { + "StorageClass": "GLACIER", + "TransitionInDays": 7 + } + ] + } + ] + }, "PublicAccessBlockConfiguration": { "BlockPublicAcls": true, "BlockPublicPolicy": true, @@ -379,10 +391,10 @@ "AutoPublish": true, "FunctionCode": "function handler(event) { var response = event.response; var headers = response.headers; headers['strict-transport-security'] = { value: 'max-age=63072000; includeSubdomains; preload'}; headers['content-security-policy'] = { value: \"default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'\"}; headers['x-content-type-options'] = { value: 'nosniff'}; headers['x-frame-options'] = {value: 'DENY'}; headers['x-xss-protection'] = {value: '1; mode=block'}; return response; }", "FunctionConfig": { - "Comment": "SetHttpSecurityHeadersc8c3d2243132d57b810e98b12d9e3c4c64e6e1cabe", + "Comment": "SetHttpSecurityHeadersc844fcbc00f82925aea73bcda195f6b5551bdcf3d4", "Runtime": "cloudfront-js-1.0" }, - "Name": "SetHttpSecurityHeadersc8c3d2243132d57b810e98b12d9e3c4c64e6e1cabe" + "Name": "SetHttpSecurityHeadersc844fcbc00f82925aea73bcda195f6b5551bdcf3d4" } }, "testcloudfronts3CloudfrontLoggingBucket985C0FE8": { @@ -398,6 +410,19 @@ } ] }, + "LifecycleConfiguration": { + "Rules": [ + { + "Status": "Enabled", + "Transitions": [ + { + "StorageClass": "GLACIER", + "TransitionInDays": 7 + } + ] + } + ] + }, "OwnershipControls": { "Rules": [ { @@ -543,7 +568,7 @@ "Type": "AWS::CloudFront::CloudFrontOriginAccessIdentity", "Properties": { "CloudFrontOriginAccessIdentityConfig": { - "Comment": "Identity for cfts3customLoggingBuckettestcloudfronts3CloudFrontDistributionOrigin169BF6A51" + "Comment": "Identity for cfts3customLoggingBucketstestcloudfronts3CloudFrontDistributionOrigin1BBEA7E26" } } }, @@ -565,7 +590,7 @@ } } ], - "TargetOriginId": "cfts3customLoggingBuckettestcloudfronts3CloudFrontDistributionOrigin169BF6A51", + "TargetOriginId": "cfts3customLoggingBucketstestcloudfronts3CloudFrontDistributionOrigin1BBEA7E26", "ViewerProtocolPolicy": "redirect-to-https" }, "DefaultRootObject": "index.html", @@ -588,7 +613,7 @@ "RegionalDomainName" ] }, - "Id": "cfts3customLoggingBuckettestcloudfronts3CloudFrontDistributionOrigin169BF6A51", + "Id": "cfts3customLoggingBucketstestcloudfronts3CloudFrontDistributionOrigin1BBEA7E26", "S3OriginConfig": { "OriginAccessIdentity": { "Fn::Join": [ diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customLoggingBucket.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customLoggingBuckets.ts similarity index 63% rename from source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customLoggingBucket.ts rename to source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customLoggingBuckets.ts index d7750b5e1..5843b4072 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customLoggingBucket.ts +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customLoggingBuckets.ts @@ -12,10 +12,10 @@ */ /// !cdk-integ * -import { App, Stack, RemovalPolicy } from "aws-cdk-lib"; -import { BucketEncryption } from "aws-cdk-lib/aws-s3"; +import { App, Stack, RemovalPolicy, Duration } from "aws-cdk-lib"; import { CloudFrontToS3 } from "../lib"; import { generateIntegStackName, suppressAutoDeleteHandlerWarnings } from '@aws-solutions-constructs/core'; +import * as s3 from 'aws-cdk-lib/aws-s3'; const app = new App(); @@ -27,16 +27,32 @@ new CloudFrontToS3(stack, 'test-cloudfront-s3', { removalPolicy: RemovalPolicy.DESTROY, autoDeleteObjects: true }, + // Confirms custom log buckets for S3 AND CloudFront loggingBucketProps: { removalPolicy: RemovalPolicy.DESTROY, autoDeleteObjects: true, - bucketName: 'custom-logging-bucket', - encryption: BucketEncryption.S3_MANAGED, - versioned: true + // This functionality is inconsequential, it just confirms + // that these props continue to be utilized + lifecycleRules: [{ + enabled: true, + transitions: [{ + storageClass: s3.StorageClass.GLACIER, + transitionAfter: Duration.days(7) + }] + }] }, cloudFrontLoggingBucketProps: { removalPolicy: RemovalPolicy.DESTROY, autoDeleteObjects: true, + // This functionality is inconsequential, it just confirms + // that these props continue to be utilized + lifecycleRules: [{ + enabled: true, + transitions: [{ + storageClass: s3.StorageClass.GLACIER, + transitionAfter: Duration.days(7) + }] + }] } }); suppressAutoDeleteHandlerWarnings(stack); diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-existing-bucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-existing-bucket.expected.json index de1f4c341..de72d90e6 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-existing-bucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-existing-bucket.expected.json @@ -1,6 +1,6 @@ { "Resources": { - "scrapBucketB11863B7": { + "scrapBucketLog7B53B25C": { "Type": "AWS::S3::Bucket", "Properties": { "BucketEncryption": { @@ -27,30 +27,56 @@ "Metadata": { "cfn_nag": { "rules_to_suppress": [ - { - "id": "W51", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, { "id": "W35", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, - { - "id": "W41", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct" + "reason": "This is a log bucket" } ] } } }, - "scrapBucketPolicy189B0607": { + "scrapBucketLogPolicy2972C573": { "Type": "AWS::S3::BucketPolicy", "Properties": { "Bucket": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, "PolicyDocument": { "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": [ "s3:PutBucketPolicy", @@ -70,7 +96,7 @@ "Resource": [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -80,7 +106,7 @@ [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -91,41 +117,25 @@ ] }, { - "Action": "s3:GetObject", - "Effect": "Allow", - "Principal": { - "CanonicalUser": { - "Fn::GetAtt": [ - "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058", - "S3CanonicalUserId" - ] + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } } }, - "Resource": { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "scrapBucketB11863B7", - "Arn" - ] - }, - "/*" - ] - ] - } - }, - { - "Action": "s3:GetObject", "Effect": "Allow", "Principal": { - "CanonicalUser": { - "Fn::GetAtt": [ - "testcloudfronts3CloudFrontDistributionOrigin2S3OriginC54B5C65", - "S3CanonicalUserId" - ] - } + "Service": "logging.s3.amazonaws.com" }, "Resource": { "Fn::Join": [ @@ -133,7 +143,7 @@ [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -145,19 +155,9 @@ ], "Version": "2012-10-17" } - }, - "Metadata": { - "cfn_nag": { - "rules_to_suppress": [ - { - "id": "F16", - "reason": "Public website bucket policy requires a wildcard principal" - } - ] - } } }, - "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "scrapBucketLogAutoDeleteObjectsCustomResource307F3D47": { "Type": "Custom::S3AutoDeleteObjects", "Properties": { "ServiceToken": { @@ -167,11 +167,11 @@ ] }, "BucketName": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" } }, "DependsOn": [ - "scrapBucketPolicy189B0607" + "scrapBucketLogPolicy2972C573" ], "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -223,7 +223,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, " S3 bucket." ] @@ -252,6 +252,203 @@ } } }, + "scrapBucketB11863B7": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "scrapBucketLog7B53B25C" + } + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "scrapBucketPolicy189B0607": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "scrapBucketB11863B7" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:GetObject", + "Effect": "Allow", + "Principal": { + "CanonicalUser": { + "Fn::GetAtt": [ + "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058", + "S3CanonicalUserId" + ] + } + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + }, + { + "Action": "s3:GetObject", + "Effect": "Allow", + "Principal": { + "CanonicalUser": { + "Fn::GetAtt": [ + "testcloudfronts3CloudFrontDistributionOrigin2S3OriginC54B5C65", + "S3CanonicalUserId" + ] + } + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "F16", + "reason": "Public website bucket policy requires a wildcard principal" + } + ] + } + } + }, + "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "scrapBucketB11863B7" + } + }, + "DependsOn": [ + "scrapBucketPolicy189B0607" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "testcloudfronts3SetHttpSecurityHeaders6C5A1E69": { "Type": "AWS::CloudFront::Function", "Properties": { diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-arguments.expected.json index 00fa661af..204fe94b6 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-arguments.expected.json @@ -19,6 +19,12 @@ "IgnorePublicAcls": true, "RestrictPublicBuckets": true }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], "VersioningConfiguration": { "Status": "Enabled" } @@ -78,6 +84,45 @@ } ] }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3S3LoggingBucket90D239DD", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3S3LoggingBucket90D239DD", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": "s3:PutObject", "Condition": { @@ -119,6 +164,25 @@ } } }, + "testcloudfronts3S3LoggingBucketAutoDeleteObjectsCustomResource6EE37727": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "testcloudfronts3S3LoggingBucket90D239DD" + } + }, + "DependsOn": [ + "testcloudfronts3S3LoggingBucketPolicy529D4CFF" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "testcloudfronts3S3BucketE0C5F76E": { "Type": "AWS::S3::Bucket", "Properties": { @@ -610,7 +674,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "testcloudfronts3S3BucketE0C5F76E" + "Ref": "testcloudfronts3S3LoggingBucket90D239DD" }, " S3 bucket." ] diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-arguments.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-arguments.ts index 6b7341ad1..de806ad32 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-arguments.ts +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-arguments.ts @@ -32,6 +32,10 @@ const construct = new CloudFrontToS3(stack, 'test-cloudfront-s3', { removalPolicy: RemovalPolicy.DESTROY, autoDeleteObjects: true }, + loggingBucketProps: { + removalPolicy: RemovalPolicy.DESTROY, + autoDeleteObjects: true + }, }); const s3Bucket = construct.s3Bucket as s3.Bucket; diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-security-headers.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-security-headers.expected.json index fa33e7460..a101651df 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-security-headers.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-security-headers.expected.json @@ -19,6 +19,12 @@ "IgnorePublicAcls": true, "RestrictPublicBuckets": true }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], "VersioningConfiguration": { "Status": "Enabled" } @@ -78,6 +84,45 @@ } ] }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3nosecurityheadersS3LoggingBucketF644B35F", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3nosecurityheadersS3LoggingBucketF644B35F", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": "s3:PutObject", "Condition": { @@ -119,6 +164,25 @@ } } }, + "testcloudfronts3nosecurityheadersS3LoggingBucketAutoDeleteObjectsCustomResourceB6D397D3": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "testcloudfronts3nosecurityheadersS3LoggingBucketF644B35F" + } + }, + "DependsOn": [ + "testcloudfronts3nosecurityheadersS3LoggingBucketPolicy264DE8B6" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "testcloudfronts3nosecurityheadersS3Bucket4D06173D": { "Type": "AWS::S3::Bucket", "Properties": { @@ -577,7 +641,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "testcloudfronts3nosecurityheadersS3Bucket4D06173D" + "Ref": "testcloudfronts3nosecurityheadersS3LoggingBucketF644B35F" }, " S3 bucket." ] diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-security-headers.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-security-headers.ts index 6d04cd9bf..13423a082 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-security-headers.ts +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-security-headers.ts @@ -32,6 +32,10 @@ const props: CloudFrontToS3Props = { removalPolicy: RemovalPolicy.DESTROY, autoDeleteObjects: true }, + loggingBucketProps: { + removalPolicy: RemovalPolicy.DESTROY, + autoDeleteObjects: true + }, }; new CloudFrontToS3(stack, 'test-cloudfront-s3-no-security-headers', props); diff --git a/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/eventbridge-kinesisfirehose-s3.test.ts b/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/eventbridge-kinesisfirehose-s3.test.ts index 0d4f17d03..98b98ebca 100644 --- a/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/eventbridge-kinesisfirehose-s3.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/eventbridge-kinesisfirehose-s3.test.ts @@ -267,7 +267,8 @@ test('Supply an existing logging bucket', () => { }); const template = Template.fromStack(stack); - template.resourceCountIs("AWS::S3::Bucket", 2); + // Construct bucket + ScrapBucket + LogBucketNowCreatedByScrapBucket = 3 + template.resourceCountIs("AWS::S3::Bucket", 3); }); diff --git a/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/integ.evtfhss3-customLoggingBucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/integ.evtfhss3-customLoggingBucket.expected.json index 55a5c20f8..d3907e853 100644 --- a/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/integ.evtfhss3-customLoggingBucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/integ.evtfhss3-customLoggingBucket.expected.json @@ -12,7 +12,19 @@ } ] }, - "BucketName": "evtfhss3-customloggingbucket-custom-logging-bucket", + "LifecycleConfiguration": { + "Rules": [ + { + "Status": "Enabled", + "Transitions": [ + { + "StorageClass": "GLACIER", + "TransitionInDays": 7 + } + ] + } + ] + }, "PublicAccessBlockConfiguration": { "BlockPublicAcls": true, "BlockPublicPolicy": true, diff --git a/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/integ.evtfhss3-customLoggingBucket.ts b/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/integ.evtfhss3-customLoggingBucket.ts index 39c552526..dc58cfadf 100644 --- a/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/integ.evtfhss3-customLoggingBucket.ts +++ b/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/integ.evtfhss3-customLoggingBucket.ts @@ -34,9 +34,15 @@ new EventbridgeToKinesisFirehoseToS3(stack, 'evtfhss3-custom-log-bucket', { loggingBucketProps: { removalPolicy: RemovalPolicy.DESTROY, autoDeleteObjects: true, - bucketName: `${generateIntegStackName(__filename).toLowerCase()}-custom-logging-bucket`, - encryption: s3.BucketEncryption.S3_MANAGED, - versioned: true + // This functionality is inconsequential, it just confirms + // that these props continue to be utilized + lifecycleRules: [{ + enabled: true, + transitions: [{ + storageClass: s3.StorageClass.GLACIER, + transitionAfter: Duration.days(7) + }] + }] }, logGroupProps: { removalPolicy: RemovalPolicy.DESTROY diff --git a/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/integ.evtfhss3-existingLoggingBucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/integ.evtfhss3-existingLoggingBucket.expected.json index ee0e520f5..f3f96f734 100644 --- a/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/integ.evtfhss3-existingLoggingBucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-eventbridge-kinesisfirehose-s3/test/integ.evtfhss3-existingLoggingBucket.expected.json @@ -1,6 +1,6 @@ { "Resources": { - "scrapBucketB11863B7": { + "scrapBucketLog7B53B25C": { "Type": "AWS::S3::Bucket", "Properties": { "BucketEncryption": { @@ -27,30 +27,56 @@ "Metadata": { "cfn_nag": { "rules_to_suppress": [ - { - "id": "W51", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, { "id": "W35", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, - { - "id": "W41", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct" + "reason": "This is a log bucket" } ] } } }, - "scrapBucketPolicy189B0607": { + "scrapBucketLogPolicy2972C573": { "Type": "AWS::S3::BucketPolicy", "Properties": { "Bucket": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, "PolicyDocument": { "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": [ "s3:PutBucketPolicy", @@ -70,7 +96,7 @@ "Resource": [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -80,7 +106,7 @@ [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -96,7 +122,7 @@ "ArnLike": { "aws:SourceArn": { "Fn::GetAtt": [ - "evtfhss3existinglogbucketKinesisFirehoseToS3S3BucketC991C6BB", + "scrapBucketB11863B7", "Arn" ] } @@ -117,7 +143,7 @@ [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -131,7 +157,7 @@ } } }, - "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "scrapBucketLogAutoDeleteObjectsCustomResource307F3D47": { "Type": "Custom::S3AutoDeleteObjects", "Properties": { "ServiceToken": { @@ -141,11 +167,11 @@ ] }, "BucketName": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" } }, "DependsOn": [ - "scrapBucketPolicy189B0607" + "scrapBucketLogPolicy2972C573" ], "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -197,7 +223,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, " S3 bucket." ] @@ -226,6 +252,177 @@ } } }, + "scrapBucketB11863B7": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "scrapBucketLog7B53B25C" + } + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "scrapBucketPolicy189B0607": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "scrapBucketB11863B7" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "evtfhss3existinglogbucketKinesisFirehoseToS3S3BucketC991C6BB", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "scrapBucketB11863B7" + } + }, + "DependsOn": [ + "scrapBucketPolicy189B0607" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "evtfhss3existinglogbucketKinesisFirehoseToS3S3BucketC991C6BB": { "Type": "AWS::S3::Bucket", "Properties": { diff --git a/source/patterns/@aws-solutions-constructs/aws-fargate-s3/test/integ.fars3-existing-resources.expected.json b/source/patterns/@aws-solutions-constructs/aws-fargate-s3/test/integ.fars3-existing-resources.expected.json index adf6b6827..0fb5c5e26 100644 --- a/source/patterns/@aws-solutions-constructs/aws-fargate-s3/test/integ.fars3-existing-resources.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-fargate-s3/test/integ.fars3-existing-resources.expected.json @@ -771,7 +771,7 @@ } } }, - "scrapBucketB11863B7": { + "scrapBucketLog7B53B25C": { "Type": "AWS::S3::Bucket", "Properties": { "BucketEncryption": { @@ -798,30 +798,56 @@ "Metadata": { "cfn_nag": { "rules_to_suppress": [ - { - "id": "W51", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, { "id": "W35", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, - { - "id": "W41", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct" + "reason": "This is a log bucket" } ] } } }, - "scrapBucketPolicy189B0607": { + "scrapBucketLogPolicy2972C573": { "Type": "AWS::S3::BucketPolicy", "Properties": { "Bucket": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, "PolicyDocument": { "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": [ "s3:PutBucketPolicy", @@ -841,7 +867,7 @@ "Resource": [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -851,7 +877,7 @@ [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -860,13 +886,49 @@ ] } ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } } ], "Version": "2012-10-17" } } }, - "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "scrapBucketLogAutoDeleteObjectsCustomResource307F3D47": { "Type": "Custom::S3AutoDeleteObjects", "Properties": { "ServiceToken": { @@ -876,11 +938,11 @@ ] }, "BucketName": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" } }, "DependsOn": [ - "scrapBucketPolicy189B0607" + "scrapBucketLogPolicy2972C573" ], "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -932,7 +994,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, " S3 bucket." ] @@ -961,6 +1023,141 @@ } } }, + "scrapBucketB11863B7": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "scrapBucketLog7B53B25C" + } + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "scrapBucketPolicy189B0607": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "scrapBucketB11863B7" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "scrapBucketB11863B7" + } + }, + "DependsOn": [ + "scrapBucketPolicy189B0607" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "fars3existingresourcesECRAPIsecuritygroup62B31B6C": { "Type": "AWS::EC2::SecurityGroup", "Properties": { diff --git a/source/patterns/@aws-solutions-constructs/aws-iot-kinesisfirehose-s3/test/integ.iotfhss3-customLoggingBucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-iot-kinesisfirehose-s3/test/integ.iotfhss3-customLoggingBucket.expected.json index 909a89475..8934da714 100644 --- a/source/patterns/@aws-solutions-constructs/aws-iot-kinesisfirehose-s3/test/integ.iotfhss3-customLoggingBucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-iot-kinesisfirehose-s3/test/integ.iotfhss3-customLoggingBucket.expected.json @@ -12,7 +12,19 @@ } ] }, - "BucketName": "iotfhss3-customloggingbucket-custom-logging-bucket", + "LifecycleConfiguration": { + "Rules": [ + { + "Status": "Enabled", + "Transitions": [ + { + "StorageClass": "GLACIER", + "TransitionInDays": 7 + } + ] + } + ] + }, "PublicAccessBlockConfiguration": { "BlockPublicAcls": true, "BlockPublicPolicy": true, diff --git a/source/patterns/@aws-solutions-constructs/aws-iot-kinesisfirehose-s3/test/integ.iotfhss3-customLoggingBucket.ts b/source/patterns/@aws-solutions-constructs/aws-iot-kinesisfirehose-s3/test/integ.iotfhss3-customLoggingBucket.ts index 3c6b3a6f5..765b120b6 100644 --- a/source/patterns/@aws-solutions-constructs/aws-iot-kinesisfirehose-s3/test/integ.iotfhss3-customLoggingBucket.ts +++ b/source/patterns/@aws-solutions-constructs/aws-iot-kinesisfirehose-s3/test/integ.iotfhss3-customLoggingBucket.ts @@ -12,10 +12,10 @@ */ /// !cdk-integ * -import { App, Stack, RemovalPolicy } from "aws-cdk-lib"; -import { BucketEncryption } from "aws-cdk-lib/aws-s3"; +import { Duration, App, Stack, RemovalPolicy } from "aws-cdk-lib"; import { IotToKinesisFirehoseToS3 } from "../lib"; import { generateIntegStackName, suppressAutoDeleteHandlerWarnings } from '@aws-solutions-constructs/core'; +import * as s3 from 'aws-cdk-lib/aws-s3'; const app = new App(); @@ -37,9 +37,15 @@ new IotToKinesisFirehoseToS3(stack, 'test-iot-kinesisfirehose-s3', { loggingBucketProps: { removalPolicy: RemovalPolicy.DESTROY, autoDeleteObjects: true, - bucketName: `${generateIntegStackName(__filename).toLowerCase()}-custom-logging-bucket`, - encryption: BucketEncryption.S3_MANAGED, - versioned: true + // This functionality is inconsequential, it just confirms + // that these props continue to be utilized + lifecycleRules: [{ + enabled: true, + transitions: [{ + storageClass: s3.StorageClass.GLACIER, + transitionAfter: Duration.days(7) + }] + }] } }); diff --git a/source/patterns/@aws-solutions-constructs/aws-iot-s3/test/integ.iots3-iot-s3-defaultprops.expected.json b/source/patterns/@aws-solutions-constructs/aws-iot-s3/test/integ.iots3-iot-s3-defaultprops.expected.json index 9ed57ae0b..3ce9e629f 100644 --- a/source/patterns/@aws-solutions-constructs/aws-iot-s3/test/integ.iots3-iot-s3-defaultprops.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-iot-s3/test/integ.iots3-iot-s3-defaultprops.expected.json @@ -18,6 +18,12 @@ "IgnorePublicAcls": true, "RestrictPublicBuckets": true }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], "VersioningConfiguration": { "Status": "Enabled" } @@ -77,6 +83,45 @@ } ] }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testiots3integrationS3LoggingBucket606446CC", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testiots3integrationS3LoggingBucket606446CC", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": "s3:PutObject", "Condition": { @@ -118,6 +163,25 @@ } } }, + "testiots3integrationS3LoggingBucketAutoDeleteObjectsCustomResource98F8BDFE": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "testiots3integrationS3LoggingBucket606446CC" + } + }, + "DependsOn": [ + "testiots3integrationS3LoggingBucketPolicy2DB45D12" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "testiots3integrationS3Bucket9B8B180C": { "Type": "AWS::S3::Bucket", "Properties": { @@ -159,17 +223,7 @@ } }, "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete", - "Metadata": { - "cfn_nag": { - "rules_to_suppress": [ - { - "id": "W35", - "reason": "This S3 bucket is created for unit/ integration testing purposes only." - } - ] - } - } + "DeletionPolicy": "Delete" }, "testiots3integrationS3BucketPolicy18905375": { "Type": "AWS::S3::BucketPolicy", @@ -310,6 +364,82 @@ "Sql": "SELECT * FROM 'solutions/constructs'" } } + }, + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ] + } + }, + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "b7f33614a69548d6bafe224d751a7ef238cde19097415e553fe8b63a4c8fd8a6.zip" + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + }, + "Runtime": "nodejs18.x", + "Description": { + "Fn::Join": [ + "", + [ + "Lambda function for auto-deleting objects in ", + { + "Ref": "testiots3integrationS3LoggingBucket606446CC" + }, + " S3 bucket." + ] + ] + } + }, + "DependsOn": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "CDK generated custom resource" + }, + { + "id": "W89", + "reason": "CDK generated custom resource" + }, + { + "id": "W92", + "reason": "CDK generated custom resource" + } + ] + } + } } }, "Parameters": { diff --git a/source/patterns/@aws-solutions-constructs/aws-iot-s3/test/integ.iots3-iot-s3-defaultprops.ts b/source/patterns/@aws-solutions-constructs/aws-iot-s3/test/integ.iots3-iot-s3-defaultprops.ts index 47e4dd1c3..b45658c4a 100644 --- a/source/patterns/@aws-solutions-constructs/aws-iot-s3/test/integ.iots3-iot-s3-defaultprops.ts +++ b/source/patterns/@aws-solutions-constructs/aws-iot-s3/test/integ.iots3-iot-s3-defaultprops.ts @@ -31,14 +31,14 @@ const props: IotToS3Props = { }, bucketProps: { removalPolicy: RemovalPolicy.DESTROY, + }, + loggingBucketProps: { + removalPolicy: RemovalPolicy.DESTROY, + autoDeleteObjects: true } }; -const testConstruct = new IotToS3(stack, 'test-iot-s3-integration', props); - -defaults.addCfnSuppressRules(testConstruct.s3Bucket!, [ - { id: 'W35', - reason: 'This S3 bucket is created for unit/ integration testing purposes only.' }, -]); +new IotToS3(stack, 'test-iot-s3-integration', props); +defaults.suppressAutoDeleteHandlerWarnings(stack); app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-iot-s3/test/integ.iots3-iot-s3-existing-bucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-iot-s3/test/integ.iots3-iot-s3-existing-bucket.expected.json index 5b981057c..2cc33ec81 100644 --- a/source/patterns/@aws-solutions-constructs/aws-iot-s3/test/integ.iots3-iot-s3-existing-bucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-iot-s3/test/integ.iots3-iot-s3-existing-bucket.expected.json @@ -1,6 +1,6 @@ { "Resources": { - "scrapBucketB11863B7": { + "scrapBucketLog7B53B25C": { "Type": "AWS::S3::Bucket", "Properties": { "BucketEncryption": { @@ -27,30 +27,56 @@ "Metadata": { "cfn_nag": { "rules_to_suppress": [ - { - "id": "W51", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, { "id": "W35", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, - { - "id": "W41", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct" + "reason": "This is a log bucket" } ] } } }, - "scrapBucketPolicy189B0607": { + "scrapBucketLogPolicy2972C573": { "Type": "AWS::S3::BucketPolicy", "Properties": { "Bucket": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, "PolicyDocument": { "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": [ "s3:PutBucketPolicy", @@ -70,7 +96,7 @@ "Resource": [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -80,7 +106,7 @@ [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -89,13 +115,49 @@ ] } ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } } ], "Version": "2012-10-17" } } }, - "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "scrapBucketLogAutoDeleteObjectsCustomResource307F3D47": { "Type": "Custom::S3AutoDeleteObjects", "Properties": { "ServiceToken": { @@ -105,11 +167,11 @@ ] }, "BucketName": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" } }, "DependsOn": [ - "scrapBucketPolicy189B0607" + "scrapBucketLogPolicy2972C573" ], "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -161,7 +223,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, " S3 bucket." ] @@ -190,6 +252,141 @@ } } }, + "scrapBucketB11863B7": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "scrapBucketLog7B53B25C" + } + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "scrapBucketPolicy189B0607": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "scrapBucketB11863B7" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "scrapBucketB11863B7" + } + }, + "DependsOn": [ + "scrapBucketPolicy189B0607" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "testiots3integrationiotactionsrole04473665": { "Type": "AWS::IAM::Role", "Properties": { diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.fhss3-customLoggingBucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.fhss3-customLoggingBucket.expected.json index bf9cb8c7f..82b037b57 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.fhss3-customLoggingBucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.fhss3-customLoggingBucket.expected.json @@ -12,7 +12,19 @@ } ] }, - "BucketName": "custom-logging-bucket", + "LifecycleConfiguration": { + "Rules": [ + { + "Status": "Enabled", + "Transitions": [ + { + "StorageClass": "GLACIER", + "TransitionInDays": 7 + } + ] + } + ] + }, "PublicAccessBlockConfiguration": { "BlockPublicAcls": true, "BlockPublicPolicy": true, diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.fhss3-customLoggingBucket.ts b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.fhss3-customLoggingBucket.ts index 1457e1ae7..29a303a44 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.fhss3-customLoggingBucket.ts +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.fhss3-customLoggingBucket.ts @@ -12,8 +12,8 @@ */ /// !cdk-integ * -import { App, Stack, RemovalPolicy } from "aws-cdk-lib"; -import { BucketEncryption } from "aws-cdk-lib/aws-s3"; +import { Duration, App, Stack, RemovalPolicy } from "aws-cdk-lib"; +import * as s3 from "aws-cdk-lib/aws-s3"; import { KinesisFirehoseToS3 } from "../lib"; import { generateIntegStackName, suppressAutoDeleteHandlerWarnings } from '@aws-solutions-constructs/core'; @@ -29,9 +29,15 @@ new KinesisFirehoseToS3(stack, 'test-kinesisfirehose-s3', { loggingBucketProps: { removalPolicy: RemovalPolicy.DESTROY, autoDeleteObjects: true, - bucketName: 'custom-logging-bucket', - encryption: BucketEncryption.S3_MANAGED, - versioned: true + // This functionality is inconsequential, it just confirms + // that these props continue to be utilized + lifecycleRules: [{ + enabled: true, + transitions: [{ + storageClass: s3.StorageClass.GLACIER, + transitionAfter: Duration.days(7) + }] + }] } }); suppressAutoDeleteHandlerWarnings(stack); diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.fhss3-pre-existing-bucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.fhss3-pre-existing-bucket.expected.json index 541e92ccb..fbc45a6c1 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.fhss3-pre-existing-bucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.fhss3-pre-existing-bucket.expected.json @@ -1,7 +1,7 @@ { "Description": "Integration Test for aws-kinesisfirehose-s3", "Resources": { - "scrapBucketB11863B7": { + "scrapBucketLog7B53B25C": { "Type": "AWS::S3::Bucket", "Properties": { "BucketEncryption": { @@ -28,30 +28,56 @@ "Metadata": { "cfn_nag": { "rules_to_suppress": [ - { - "id": "W51", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, { "id": "W35", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, - { - "id": "W41", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct" + "reason": "This is a log bucket" } ] } } }, - "scrapBucketPolicy189B0607": { + "scrapBucketLogPolicy2972C573": { "Type": "AWS::S3::BucketPolicy", "Properties": { "Bucket": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, "PolicyDocument": { "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": [ "s3:PutBucketPolicy", @@ -71,7 +97,7 @@ "Resource": [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -81,7 +107,7 @@ [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -90,13 +116,49 @@ ] } ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } } ], "Version": "2012-10-17" } } }, - "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "scrapBucketLogAutoDeleteObjectsCustomResource307F3D47": { "Type": "Custom::S3AutoDeleteObjects", "Properties": { "ServiceToken": { @@ -106,11 +168,11 @@ ] }, "BucketName": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" } }, "DependsOn": [ - "scrapBucketPolicy189B0607" + "scrapBucketLogPolicy2972C573" ], "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -162,7 +224,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, " S3 bucket." ] @@ -191,6 +253,141 @@ } } }, + "scrapBucketB11863B7": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "scrapBucketLog7B53B25C" + } + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "scrapBucketPolicy189B0607": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "scrapBucketB11863B7" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "scrapBucketB11863B7" + } + }, + "DependsOn": [ + "scrapBucketPolicy189B0607" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "testfirehoses3preexistingbucketstackfirehoseloggroupC6AF6572": { "Type": "AWS::Logs::LogGroup", "UpdateReplacePolicy": "Retain", diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.fhss3-pre-existing-logging-bucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.fhss3-pre-existing-logging-bucket.expected.json index 9bf4ae7dd..19dd75b17 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.fhss3-pre-existing-logging-bucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.fhss3-pre-existing-logging-bucket.expected.json @@ -1,7 +1,7 @@ { "Description": "Integration Test for aws-kinesisfirehose-s3", "Resources": { - "scrapBucketB11863B7": { + "scrapBucketLog7B53B25C": { "Type": "AWS::S3::Bucket", "Properties": { "BucketEncryption": { @@ -28,30 +28,56 @@ "Metadata": { "cfn_nag": { "rules_to_suppress": [ - { - "id": "W51", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, { "id": "W35", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, - { - "id": "W41", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct" + "reason": "This is a log bucket" } ] } } }, - "scrapBucketPolicy189B0607": { + "scrapBucketLogPolicy2972C573": { "Type": "AWS::S3::BucketPolicy", "Properties": { "Bucket": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, "PolicyDocument": { "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": [ "s3:PutBucketPolicy", @@ -71,7 +97,7 @@ "Resource": [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -81,7 +107,7 @@ [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -97,7 +123,7 @@ "ArnLike": { "aws:SourceArn": { "Fn::GetAtt": [ - "testfirehoses3preexistingloggingbucketstackS3BucketD14D0F4F", + "scrapBucketB11863B7", "Arn" ] } @@ -118,7 +144,7 @@ [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -132,7 +158,7 @@ } } }, - "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "scrapBucketLogAutoDeleteObjectsCustomResource307F3D47": { "Type": "Custom::S3AutoDeleteObjects", "Properties": { "ServiceToken": { @@ -142,11 +168,11 @@ ] }, "BucketName": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" } }, "DependsOn": [ - "scrapBucketPolicy189B0607" + "scrapBucketLogPolicy2972C573" ], "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -198,7 +224,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, " S3 bucket." ] @@ -227,6 +253,177 @@ } } }, + "scrapBucketB11863B7": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "scrapBucketLog7B53B25C" + } + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "scrapBucketPolicy189B0607": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "scrapBucketB11863B7" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "testfirehoses3preexistingloggingbucketstackS3BucketD14D0F4F", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "scrapBucketB11863B7" + } + }, + "DependsOn": [ + "scrapBucketPolicy189B0607" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "testfirehoses3preexistingloggingbucketstackS3BucketD14D0F4F": { "Type": "AWS::S3::Bucket", "Properties": { diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-customLoggingBucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-customLoggingBucket.expected.json index acd9b0194..3e6282cdc 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-customLoggingBucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-customLoggingBucket.expected.json @@ -23,6 +23,19 @@ } ] }, + "LifecycleConfiguration": { + "Rules": [ + { + "Status": "Enabled", + "Transitions": [ + { + "StorageClass": "GLACIER", + "TransitionInDays": 7 + } + ] + } + ] + }, "PublicAccessBlockConfiguration": { "BlockPublicAcls": true, "BlockPublicPolicy": true, diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-customLoggingBucket.ts b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-customLoggingBucket.ts index 52597fe33..88f71d636 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-customLoggingBucket.ts +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-customLoggingBucket.ts @@ -12,8 +12,8 @@ */ /// !cdk-integ * -import { App, Stack, RemovalPolicy } from "aws-cdk-lib"; -import { BucketEncryption } from "aws-cdk-lib/aws-s3"; +import { App, Stack, RemovalPolicy, Duration } from "aws-cdk-lib"; +import * as s3 from "aws-cdk-lib/aws-s3"; import { KinesisStreamsToKinesisFirehoseToS3 } from "../lib"; import { generateIntegStackName, suppressAutoDeleteHandlerWarnings } from '@aws-solutions-constructs/core'; @@ -29,8 +29,15 @@ new KinesisStreamsToKinesisFirehoseToS3(stack, 'test-kinesisfirehose-s3', { loggingBucketProps: { removalPolicy: RemovalPolicy.DESTROY, autoDeleteObjects: true, - encryption: BucketEncryption.S3_MANAGED, - versioned: true + // This functionality is inconsequential, it just confirms + // that these props continue to be utilized + lifecycleRules: [{ + enabled: true, + transitions: [{ + storageClass: s3.StorageClass.GLACIER, + transitionAfter: Duration.days(7) + }] + }] }, logGroupProps: { removalPolicy: RemovalPolicy.DESTROY diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-existing-bucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-existing-bucket.expected.json index 7dc7b46c3..6165ec073 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-existing-bucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-existing-bucket.expected.json @@ -1,7 +1,7 @@ { "Description": "Integration Test for aws-kinesisstreams-kinesisfirehose-s3", "Resources": { - "scrapBucketB11863B7": { + "scrapBucketLog7B53B25C": { "Type": "AWS::S3::Bucket", "Properties": { "BucketEncryption": { @@ -28,30 +28,56 @@ "Metadata": { "cfn_nag": { "rules_to_suppress": [ - { - "id": "W51", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, { "id": "W35", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, - { - "id": "W41", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct" + "reason": "This is a log bucket" } ] } } }, - "scrapBucketPolicy189B0607": { + "scrapBucketLogPolicy2972C573": { "Type": "AWS::S3::BucketPolicy", "Properties": { "Bucket": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, "PolicyDocument": { "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": [ "s3:PutBucketPolicy", @@ -71,7 +97,7 @@ "Resource": [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -81,7 +107,7 @@ [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -90,13 +116,49 @@ ] } ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } } ], "Version": "2012-10-17" } } }, - "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "scrapBucketLogAutoDeleteObjectsCustomResource307F3D47": { "Type": "Custom::S3AutoDeleteObjects", "Properties": { "ServiceToken": { @@ -106,11 +168,11 @@ ] }, "BucketName": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" } }, "DependsOn": [ - "scrapBucketPolicy189B0607" + "scrapBucketLogPolicy2972C573" ], "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -162,7 +224,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, " S3 bucket." ] @@ -191,6 +253,141 @@ } } }, + "scrapBucketB11863B7": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "scrapBucketLog7B53B25C" + } + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "scrapBucketPolicy189B0607": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "scrapBucketB11863B7" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "scrapBucketB11863B7" + } + }, + "DependsOn": [ + "scrapBucketPolicy189B0607" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "testexistingbucketfirehoses3stackKinesisStreamCA3487EE": { "Type": "AWS::Kinesis::Stream", "Properties": { diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-existing-logging-bucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-existing-logging-bucket.expected.json index 8dca0cfcf..58ed9dfd4 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-existing-logging-bucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.kinfhss3-existing-logging-bucket.expected.json @@ -1,7 +1,7 @@ { "Description": "Integration Test for aws-kinesisstreams-kinesisfirehose-s3", "Resources": { - "scrapBucketB11863B7": { + "scrapBucketLog7B53B25C": { "Type": "AWS::S3::Bucket", "Properties": { "BucketEncryption": { @@ -28,30 +28,56 @@ "Metadata": { "cfn_nag": { "rules_to_suppress": [ - { - "id": "W51", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, { "id": "W35", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, - { - "id": "W41", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct" + "reason": "This is a log bucket" } ] } } }, - "scrapBucketPolicy189B0607": { + "scrapBucketLogPolicy2972C573": { "Type": "AWS::S3::BucketPolicy", "Properties": { "Bucket": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, "PolicyDocument": { "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": [ "s3:PutBucketPolicy", @@ -71,7 +97,7 @@ "Resource": [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -81,7 +107,7 @@ [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -90,13 +116,49 @@ ] } ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } } ], "Version": "2012-10-17" } } }, - "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "scrapBucketLogAutoDeleteObjectsCustomResource307F3D47": { "Type": "Custom::S3AutoDeleteObjects", "Properties": { "ServiceToken": { @@ -106,11 +168,11 @@ ] }, "BucketName": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" } }, "DependsOn": [ - "scrapBucketPolicy189B0607" + "scrapBucketLogPolicy2972C573" ], "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -162,7 +224,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, " S3 bucket." ] @@ -191,6 +253,141 @@ } } }, + "scrapBucketB11863B7": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "scrapBucketLog7B53B25C" + } + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "scrapBucketPolicy189B0607": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "scrapBucketB11863B7" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "scrapBucketB11863B7" + } + }, + "DependsOn": [ + "scrapBucketPolicy189B0607" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "testexistingloggingbucketstreamsfirehoses3stackKinesisStreamDBBCC46F": { "Type": "AWS::Kinesis::Stream", "Properties": { diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-minimal-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-minimal-arguments.expected.json index 0ccfca9ff..46e69bd75 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-minimal-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-minimal-arguments.expected.json @@ -1,7 +1,7 @@ { "Description": "Integration Test for aws-lambda-kendra", "Resources": { - "contentBucket356CF7A5": { + "contentBucketLog5B9803A6": { "Type": "AWS::S3::Bucket", "Properties": { "BucketEncryption": { @@ -28,30 +28,56 @@ "Metadata": { "cfn_nag": { "rules_to_suppress": [ - { - "id": "W51", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, { "id": "W35", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, - { - "id": "W41", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct" + "reason": "This is a log bucket" } ] } } }, - "contentBucketPolicy71256B1B": { + "contentBucketLogPolicy52155F9C": { "Type": "AWS::S3::BucketPolicy", "Properties": { "Bucket": { - "Ref": "contentBucket356CF7A5" + "Ref": "contentBucketLog5B9803A6" }, "PolicyDocument": { "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "contentBucketLog5B9803A6", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "contentBucketLog5B9803A6", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": [ "s3:PutBucketPolicy", @@ -71,7 +97,7 @@ "Resource": [ { "Fn::GetAtt": [ - "contentBucket356CF7A5", + "contentBucketLog5B9803A6", "Arn" ] }, @@ -81,7 +107,7 @@ [ { "Fn::GetAtt": [ - "contentBucket356CF7A5", + "contentBucketLog5B9803A6", "Arn" ] }, @@ -90,13 +116,49 @@ ] } ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "contentBucket356CF7A5", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "contentBucketLog5B9803A6", + "Arn" + ] + }, + "/*" + ] + ] + } } ], "Version": "2012-10-17" } } }, - "contentBucketAutoDeleteObjectsCustomResourceD6F9707E": { + "contentBucketLogAutoDeleteObjectsCustomResource533B8207": { "Type": "Custom::S3AutoDeleteObjects", "Properties": { "ServiceToken": { @@ -106,11 +168,11 @@ ] }, "BucketName": { - "Ref": "contentBucket356CF7A5" + "Ref": "contentBucketLog5B9803A6" } }, "DependsOn": [ - "contentBucketPolicy71256B1B" + "contentBucketLogPolicy52155F9C" ], "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -162,7 +224,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "contentBucket356CF7A5" + "Ref": "contentBucketLog5B9803A6" }, " S3 bucket." ] @@ -191,6 +253,141 @@ } } }, + "contentBucket356CF7A5": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "contentBucketLog5B9803A6" + } + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "contentBucketPolicy71256B1B": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "contentBucket356CF7A5" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "contentBucket356CF7A5", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "contentBucket356CF7A5", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "contentBucket356CF7A5", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "contentBucket356CF7A5", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "contentBucketAutoDeleteObjectsCustomResourceD6F9707E": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "contentBucket356CF7A5" + } + }, + "DependsOn": [ + "contentBucketPolicy71256B1B" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "minimalargumentsLambdaFunctionServiceRole73B77FF7": { "Type": "AWS::IAM::Role", "Properties": { diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-minimal-arguments.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-minimal-arguments.ts index b1fad141a..278aa5278 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-minimal-arguments.ts +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-minimal-arguments.ts @@ -22,11 +22,9 @@ import { generateIntegStackName, suppressAutoDeleteHandlerWarnings } from '@aws- const app = new App(); const stack = new Stack(app, generateIntegStackName(__filename)); stack.templateOptions.description = 'Integration Test for aws-lambda-kendra'; +stack.node.setContext("@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy", true); -// const loggingBucket = defaults.CreateScrapBucket(stack, "logBucket"); -const testBucket = defaults.CreateScrapBucket(stack, "contentBucket", { -// serverAccessLogsBucket: loggingBucket, -}); +const testBucket = defaults.CreateScrapBucket(stack, "contentBucket"); new LambdaToKendra(stack, 'minimal-arguments', { lambdaFunctionProps: { diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-multiple-sources.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-multiple-sources.expected.json index ca73db9b8..0245a09f1 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-multiple-sources.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-multiple-sources.expected.json @@ -1,7 +1,7 @@ { "Description": "Integration Test for aws-lambda-kendra", "Resources": { - "scrapBucketB11863B7": { + "scrapBucketLog7B53B25C": { "Type": "AWS::S3::Bucket", "Properties": { "BucketEncryption": { @@ -28,30 +28,56 @@ "Metadata": { "cfn_nag": { "rules_to_suppress": [ - { - "id": "W51", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, { "id": "W35", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, - { - "id": "W41", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct" + "reason": "This is a log bucket" } ] } } }, - "scrapBucketPolicy189B0607": { + "scrapBucketLogPolicy2972C573": { "Type": "AWS::S3::BucketPolicy", "Properties": { "Bucket": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, "PolicyDocument": { "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": [ "s3:PutBucketPolicy", @@ -71,7 +97,7 @@ "Resource": [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -81,7 +107,7 @@ [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -90,13 +116,49 @@ ] } ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } } ], "Version": "2012-10-17" } } }, - "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "scrapBucketLogAutoDeleteObjectsCustomResource307F3D47": { "Type": "Custom::S3AutoDeleteObjects", "Properties": { "ServiceToken": { @@ -106,11 +168,11 @@ ] }, "BucketName": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" } }, "DependsOn": [ - "scrapBucketPolicy189B0607" + "scrapBucketLogPolicy2972C573" ], "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -162,7 +224,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, " S3 bucket." ] @@ -191,6 +253,141 @@ } } }, + "scrapBucketB11863B7": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "scrapBucketLog7B53B25C" + } + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "scrapBucketPolicy189B0607": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "scrapBucketB11863B7" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "scrapBucketB11863B7" + } + }, + "DependsOn": [ + "scrapBucketPolicy189B0607" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "existingRole3E995BBA": { "Type": "AWS::IAM::Role", "Properties": { diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-multiple-sources.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-multiple-sources.ts index 0051662e1..75108b5c2 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-multiple-sources.ts +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-multiple-sources.ts @@ -24,6 +24,7 @@ import { generateIntegStackName, suppressAutoDeleteHandlerWarnings } from '@aws- const app = new App(); const stack = new Stack(app, generateIntegStackName(__filename)); stack.templateOptions.description = 'Integration Test for aws-lambda-kendra'; +stack.node.setContext("@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy", true); const testBucket = defaults.CreateScrapBucket(stack, "scrapBucket"); const existingIamRole = new iam.Role(stack, 'existingRole', { diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-with-vpc.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-with-vpc.expected.json index 0e39ea284..b53d1b930 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-with-vpc.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-with-vpc.expected.json @@ -1,7 +1,7 @@ { "Description": "Integration Test for aws-lambda-kendra", "Resources": { - "scrapBucketB11863B7": { + "scrapBucketLog7B53B25C": { "Type": "AWS::S3::Bucket", "Properties": { "BucketEncryption": { @@ -28,30 +28,56 @@ "Metadata": { "cfn_nag": { "rules_to_suppress": [ - { - "id": "W51", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, { "id": "W35", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, - { - "id": "W41", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct" + "reason": "This is a log bucket" } ] } } }, - "scrapBucketPolicy189B0607": { + "scrapBucketLogPolicy2972C573": { "Type": "AWS::S3::BucketPolicy", "Properties": { "Bucket": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, "PolicyDocument": { "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": [ "s3:PutBucketPolicy", @@ -71,7 +97,7 @@ "Resource": [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -81,7 +107,7 @@ [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -90,13 +116,49 @@ ] } ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } } ], "Version": "2012-10-17" } } }, - "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "scrapBucketLogAutoDeleteObjectsCustomResource307F3D47": { "Type": "Custom::S3AutoDeleteObjects", "Properties": { "ServiceToken": { @@ -106,11 +168,11 @@ ] }, "BucketName": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" } }, "DependsOn": [ - "scrapBucketPolicy189B0607" + "scrapBucketLogPolicy2972C573" ], "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -162,7 +224,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, " S3 bucket." ] @@ -191,6 +253,141 @@ } } }, + "scrapBucketB11863B7": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "scrapBucketLog7B53B25C" + } + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "scrapBucketPolicy189B0607": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "scrapBucketB11863B7" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "scrapBucketB11863B7" + } + }, + "DependsOn": [ + "scrapBucketPolicy189B0607" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "minimalargumentsLambdaFunctionServiceRole73B77FF7": { "Type": "AWS::IAM::Role", "Properties": { diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-with-vpc.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-with-vpc.ts index 7c14827b4..d241476c1 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-with-vpc.ts +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-kendra/test/integ.lamken-with-vpc.ts @@ -22,6 +22,7 @@ import { generateIntegStackName, suppressAutoDeleteHandlerWarnings } from '@aws- const app = new App(); const stack = new Stack(app, generateIntegStackName(__filename)); stack.templateOptions.description = 'Integration Test for aws-lambda-kendra'; +stack.node.setContext("@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy", true); const testBucket = defaults.CreateScrapBucket(stack, "scrapBucket"); diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-customLoggingBucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-customLoggingBucket.expected.json index d9ae239f6..7a480585c 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-customLoggingBucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-customLoggingBucket.expected.json @@ -190,7 +190,19 @@ } ] }, - "BucketName": "custom-logging-bucket", + "LifecycleConfiguration": { + "Rules": [ + { + "Status": "Enabled", + "Transitions": [ + { + "StorageClass": "GLACIER", + "TransitionInDays": 7 + } + ] + } + ] + }, "PublicAccessBlockConfiguration": { "BlockPublicAcls": true, "BlockPublicPolicy": true, diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-customLoggingBucket.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-customLoggingBucket.ts index 55ea15e14..e6874df98 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-customLoggingBucket.ts +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-customLoggingBucket.ts @@ -12,8 +12,8 @@ */ /// !cdk-integ * -import { App, Stack, RemovalPolicy } from "aws-cdk-lib"; -import { BucketEncryption } from "aws-cdk-lib/aws-s3"; +import { App, Stack, RemovalPolicy, Duration } from "aws-cdk-lib"; +import * as s3 from "aws-cdk-lib/aws-s3"; import { LambdaToS3 } from "../lib"; import * as lambda from 'aws-cdk-lib/aws-lambda'; import { generateIntegStackName, suppressAutoDeleteHandlerWarnings } from '@aws-solutions-constructs/core'; @@ -35,9 +35,15 @@ new LambdaToS3(stack, 'test-lambda-s3', { loggingBucketProps: { removalPolicy: RemovalPolicy.DESTROY, autoDeleteObjects: true, - bucketName: 'custom-logging-bucket', - encryption: BucketEncryption.S3_MANAGED, - versioned: true + // This functionality is inconsequential, it just confirms + // that these props continue to be utilized + lifecycleRules: [{ + enabled: true, + transitions: [{ + storageClass: s3.StorageClass.GLACIER, + transitionAfter: Duration.days(7) + }] + }] } }); suppressAutoDeleteHandlerWarnings(stack); diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-deployFunction.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-deployFunction.expected.json index c416982c0..0cf79d0a9 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-deployFunction.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-deployFunction.expected.json @@ -197,6 +197,12 @@ "IgnorePublicAcls": true, "RestrictPublicBuckets": true }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], "VersioningConfiguration": { "Status": "Enabled" } @@ -256,6 +262,45 @@ } ] }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testlambdas3S3LoggingBucketD42FC73D", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testlambdas3S3LoggingBucketD42FC73D", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": "s3:PutObject", "Condition": { @@ -297,6 +342,25 @@ } } }, + "testlambdas3S3LoggingBucketAutoDeleteObjectsCustomResource9AE70F3A": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "testlambdas3S3LoggingBucketD42FC73D" + } + }, + "DependsOn": [ + "testlambdas3S3LoggingBucketPolicyCEAFB213" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "testlambdas3S3Bucket179A52E6": { "Type": "AWS::S3::Bucket", "Properties": { @@ -333,6 +397,12 @@ "IgnorePublicAcls": true, "RestrictPublicBuckets": true }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], "VersioningConfiguration": { "Status": "Enabled" } @@ -391,11 +461,145 @@ ] } ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testlambdas3S3Bucket179A52E6", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testlambdas3S3Bucket179A52E6", + "Arn" + ] + }, + "/*" + ] + ] + } + ] } ], "Version": "2012-10-17" } } + }, + "testlambdas3S3BucketAutoDeleteObjectsCustomResourceF8A326E6": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "testlambdas3S3Bucket179A52E6" + } + }, + "DependsOn": [ + "testlambdas3S3BucketPolicyE899B211" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ] + } + }, + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "b7f33614a69548d6bafe224d751a7ef238cde19097415e553fe8b63a4c8fd8a6.zip" + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + }, + "Runtime": "nodejs18.x", + "Description": { + "Fn::Join": [ + "", + [ + "Lambda function for auto-deleting objects in ", + { + "Ref": "testlambdas3S3LoggingBucketD42FC73D" + }, + " S3 bucket." + ] + ] + } + }, + "DependsOn": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "CDK generated custom resource" + }, + { + "id": "W89", + "reason": "CDK generated custom resource" + }, + { + "id": "W92", + "reason": "CDK generated custom resource" + } + ] + } + } } }, "Parameters": { diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-deployFunction.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-deployFunction.ts index 41618b436..510d74343 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-deployFunction.ts +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-deployFunction.ts @@ -33,11 +33,17 @@ const props: LambdaToS3Props = { }, bucketProps: { removalPolicy: RemovalPolicy.DESTROY, + autoDeleteObjects: true + }, + loggingBucketProps: { + removalPolicy: RemovalPolicy.DESTROY, + autoDeleteObjects: true } }; const construct = new LambdaToS3(stack, 'test-lambda-s3', props); +defaults.suppressAutoDeleteHandlerWarnings(stack); const s3Bucket = construct.s3Bucket as s3.Bucket; defaults.addCfnSuppressRules(s3Bucket, [ diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-pre-existing-bucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-pre-existing-bucket.expected.json index e9c101292..ca49844f1 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-pre-existing-bucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-s3/test/integ.lams3-pre-existing-bucket.expected.json @@ -1,7 +1,7 @@ { "Description": "Integration Test for aws-lambda-s3", "Resources": { - "scrapBucketB11863B7": { + "scrapBucketLog7B53B25C": { "Type": "AWS::S3::Bucket", "Properties": { "BucketEncryption": { @@ -28,30 +28,56 @@ "Metadata": { "cfn_nag": { "rules_to_suppress": [ - { - "id": "W51", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, { "id": "W35", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, - { - "id": "W41", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct" + "reason": "This is a log bucket" } ] } } }, - "scrapBucketPolicy189B0607": { + "scrapBucketLogPolicy2972C573": { "Type": "AWS::S3::BucketPolicy", "Properties": { "Bucket": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, "PolicyDocument": { "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": [ "s3:PutBucketPolicy", @@ -71,7 +97,7 @@ "Resource": [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -81,7 +107,7 @@ [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -90,13 +116,49 @@ ] } ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } } ], "Version": "2012-10-17" } } }, - "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "scrapBucketLogAutoDeleteObjectsCustomResource307F3D47": { "Type": "Custom::S3AutoDeleteObjects", "Properties": { "ServiceToken": { @@ -106,11 +168,11 @@ ] }, "BucketName": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" } }, "DependsOn": [ - "scrapBucketPolicy189B0607" + "scrapBucketLogPolicy2972C573" ], "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -162,7 +224,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, " S3 bucket." ] @@ -191,6 +253,141 @@ } } }, + "scrapBucketB11863B7": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "scrapBucketLog7B53B25C" + } + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "scrapBucketPolicy189B0607": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "scrapBucketB11863B7" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "scrapBucketB11863B7" + } + }, + "DependsOn": [ + "scrapBucketPolicy189B0607" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "testlambdas3preexistingbucketLambdaFunctionServiceRole9AC7CED0": { "Type": "AWS::IAM::Role", "Properties": { diff --git a/source/patterns/@aws-solutions-constructs/aws-s3-lambda/test/integ.s3lam-existing-s3-bucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-s3-lambda/test/integ.s3lam-existing-s3-bucket.expected.json index 261450cc1..9637834b4 100644 --- a/source/patterns/@aws-solutions-constructs/aws-s3-lambda/test/integ.s3lam-existing-s3-bucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-s3-lambda/test/integ.s3lam-existing-s3-bucket.expected.json @@ -1,6 +1,6 @@ { "Resources": { - "scrapBucketB11863B7": { + "scrapBucketLog7B53B25C": { "Type": "AWS::S3::Bucket", "Properties": { "BucketEncryption": { @@ -27,30 +27,56 @@ "Metadata": { "cfn_nag": { "rules_to_suppress": [ - { - "id": "W51", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, { "id": "W35", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, - { - "id": "W41", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct" + "reason": "This is a log bucket" } ] } } }, - "scrapBucketPolicy189B0607": { + "scrapBucketLogPolicy2972C573": { "Type": "AWS::S3::BucketPolicy", "Properties": { "Bucket": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, "PolicyDocument": { "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": [ "s3:PutBucketPolicy", @@ -70,7 +96,7 @@ "Resource": [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -80,7 +106,7 @@ [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -89,13 +115,49 @@ ] } ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } } ], "Version": "2012-10-17" } } }, - "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "scrapBucketLogAutoDeleteObjectsCustomResource307F3D47": { "Type": "Custom::S3AutoDeleteObjects", "Properties": { "ServiceToken": { @@ -105,70 +167,15 @@ ] }, "BucketName": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" } }, "DependsOn": [ - "scrapBucketPolicy189B0607" + "scrapBucketLogPolicy2972C573" ], "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, - "scrapBucketNotifications065035E3": { - "Type": "Custom::S3BucketNotifications", - "Properties": { - "ServiceToken": { - "Fn::GetAtt": [ - "BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691", - "Arn" - ] - }, - "BucketName": { - "Ref": "scrapBucketB11863B7" - }, - "NotificationConfiguration": { - "LambdaFunctionConfigurations": [ - { - "Events": [ - "s3:ObjectCreated:*" - ], - "LambdaFunctionArn": { - "Fn::GetAtt": [ - "tests3lambdaLambdaFunctionB56B7023", - "Arn" - ] - } - } - ] - }, - "Managed": true - }, - "DependsOn": [ - "scrapBucketAllowBucketNotificationsTos3lamexistings3buckettests3lambdaLambdaFunctionCA1AFC4CBBE7A87C" - ] - }, - "scrapBucketAllowBucketNotificationsTos3lamexistings3buckettests3lambdaLambdaFunctionCA1AFC4CBBE7A87C": { - "Type": "AWS::Lambda::Permission", - "Properties": { - "Action": "lambda:InvokeFunction", - "FunctionName": { - "Fn::GetAtt": [ - "tests3lambdaLambdaFunctionB56B7023", - "Arn" - ] - }, - "Principal": "s3.amazonaws.com", - "SourceAccount": { - "Ref": "AWS::AccountId" - }, - "SourceArn": { - "Fn::GetAtt": [ - "scrapBucketB11863B7", - "Arn" - ] - } - } - }, "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092": { "Type": "AWS::IAM::Role", "Properties": { @@ -216,7 +223,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, " S3 bucket." ] @@ -245,6 +252,196 @@ } } }, + "scrapBucketB11863B7": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "scrapBucketLog7B53B25C" + } + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "scrapBucketPolicy189B0607": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "scrapBucketB11863B7" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "scrapBucketB11863B7" + } + }, + "DependsOn": [ + "scrapBucketPolicy189B0607" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "scrapBucketNotifications065035E3": { + "Type": "Custom::S3BucketNotifications", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691", + "Arn" + ] + }, + "BucketName": { + "Ref": "scrapBucketB11863B7" + }, + "NotificationConfiguration": { + "LambdaFunctionConfigurations": [ + { + "Events": [ + "s3:ObjectCreated:*" + ], + "LambdaFunctionArn": { + "Fn::GetAtt": [ + "tests3lambdaLambdaFunctionB56B7023", + "Arn" + ] + } + } + ] + }, + "Managed": true + }, + "DependsOn": [ + "scrapBucketAllowBucketNotificationsTos3lamexistings3buckettests3lambdaLambdaFunctionCA1AFC4CBBE7A87C" + ] + }, + "scrapBucketAllowBucketNotificationsTos3lamexistings3buckettests3lambdaLambdaFunctionCA1AFC4CBBE7A87C": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "tests3lambdaLambdaFunctionB56B7023", + "Arn" + ] + }, + "Principal": "s3.amazonaws.com", + "SourceAccount": { + "Ref": "AWS::AccountId" + }, + "SourceArn": { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + } + } + }, "tests3lambdaLambdaFunctionServiceRoleA74F4427": { "Type": "AWS::IAM::Role", "Properties": { diff --git a/source/patterns/@aws-solutions-constructs/aws-s3-sns/test/integ.s3sns-customLoggingBucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-s3-sns/test/integ.s3sns-customLoggingBucket.expected.json index fac652b13..7be2d26a8 100644 --- a/source/patterns/@aws-solutions-constructs/aws-s3-sns/test/integ.s3sns-customLoggingBucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-s3-sns/test/integ.s3sns-customLoggingBucket.expected.json @@ -12,6 +12,19 @@ } ] }, + "LifecycleConfiguration": { + "Rules": [ + { + "Status": "Enabled", + "Transitions": [ + { + "StorageClass": "GLACIER", + "TransitionInDays": 7 + } + ] + } + ] + }, "PublicAccessBlockConfiguration": { "BlockPublicAcls": true, "BlockPublicPolicy": true, diff --git a/source/patterns/@aws-solutions-constructs/aws-s3-sns/test/integ.s3sns-customLoggingBucket.ts b/source/patterns/@aws-solutions-constructs/aws-s3-sns/test/integ.s3sns-customLoggingBucket.ts index 654f51366..df77b8ad0 100644 --- a/source/patterns/@aws-solutions-constructs/aws-s3-sns/test/integ.s3sns-customLoggingBucket.ts +++ b/source/patterns/@aws-solutions-constructs/aws-s3-sns/test/integ.s3sns-customLoggingBucket.ts @@ -12,7 +12,8 @@ */ /// !cdk-integ * -import { App, RemovalPolicy, Stack } from "aws-cdk-lib"; +import { App, RemovalPolicy, Stack, Duration } from "aws-cdk-lib"; +import * as s3 from 'aws-cdk-lib/aws-s3'; import { S3ToSns } from "../lib"; import { generateIntegStackName, suppressAutoDeleteHandlerWarnings } from '@aws-solutions-constructs/core'; @@ -25,7 +26,16 @@ new S3ToSns(stack, 'test-s3-sns', { }, loggingBucketProps: { autoDeleteObjects: true, - removalPolicy: RemovalPolicy.DESTROY + removalPolicy: RemovalPolicy.DESTROY, + // This functionality is inconsequential, it just confirms + // that these props continue to be utilized + lifecycleRules: [{ + enabled: true, + transitions: [{ + storageClass: s3.StorageClass.GLACIER, + transitionAfter: Duration.days(7) + }] + }] } }); diff --git a/source/patterns/@aws-solutions-constructs/aws-s3-sns/test/integ.s3sns-existingS3Bucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-s3-sns/test/integ.s3sns-existingS3Bucket.expected.json index 205169da2..553a63ee7 100644 --- a/source/patterns/@aws-solutions-constructs/aws-s3-sns/test/integ.s3sns-existingS3Bucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-s3-sns/test/integ.s3sns-existingS3Bucket.expected.json @@ -1,6 +1,6 @@ { "Resources": { - "scrapBucketB11863B7": { + "scrapBucketLog7B53B25C": { "Type": "AWS::S3::Bucket", "Properties": { "BucketEncryption": { @@ -27,30 +27,56 @@ "Metadata": { "cfn_nag": { "rules_to_suppress": [ - { - "id": "W51", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, { "id": "W35", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, - { - "id": "W41", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct" + "reason": "This is a log bucket" } ] } } }, - "scrapBucketPolicy189B0607": { + "scrapBucketLogPolicy2972C573": { "Type": "AWS::S3::BucketPolicy", "Properties": { "Bucket": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, "PolicyDocument": { "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": [ "s3:PutBucketPolicy", @@ -70,7 +96,7 @@ "Resource": [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -80,7 +106,7 @@ [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -89,13 +115,49 @@ ] } ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } } ], "Version": "2012-10-17" } } }, - "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "scrapBucketLogAutoDeleteObjectsCustomResource307F3D47": { "Type": "Custom::S3AutoDeleteObjects", "Properties": { "ServiceToken": { @@ -105,46 +167,15 @@ ] }, "BucketName": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" } }, "DependsOn": [ - "scrapBucketPolicy189B0607" + "scrapBucketLogPolicy2972C573" ], "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, - "scrapBucketNotifications065035E3": { - "Type": "Custom::S3BucketNotifications", - "Properties": { - "ServiceToken": { - "Fn::GetAtt": [ - "BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691", - "Arn" - ] - }, - "BucketName": { - "Ref": "scrapBucketB11863B7" - }, - "NotificationConfiguration": { - "TopicConfigurations": [ - { - "Events": [ - "s3:ObjectCreated:*" - ], - "TopicArn": { - "Ref": "tests3snsSnsTopicF02F6BD0" - } - } - ] - }, - "Managed": true - }, - "DependsOn": [ - "tests3snsSnsTopicPolicyBB44DF16", - "tests3snsSnsTopicF02F6BD0" - ] - }, "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092": { "Type": "AWS::IAM::Role", "Properties": { @@ -192,7 +223,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, " S3 bucket." ] @@ -221,6 +252,172 @@ } } }, + "scrapBucketB11863B7": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "scrapBucketLog7B53B25C" + } + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "scrapBucketPolicy189B0607": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "scrapBucketB11863B7" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "scrapBucketB11863B7" + } + }, + "DependsOn": [ + "scrapBucketPolicy189B0607" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "scrapBucketNotifications065035E3": { + "Type": "Custom::S3BucketNotifications", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691", + "Arn" + ] + }, + "BucketName": { + "Ref": "scrapBucketB11863B7" + }, + "NotificationConfiguration": { + "TopicConfigurations": [ + { + "Events": [ + "s3:ObjectCreated:*" + ], + "TopicArn": { + "Ref": "tests3snsSnsTopicF02F6BD0" + } + } + ] + }, + "Managed": true + }, + "DependsOn": [ + "tests3snsSnsTopicPolicyBB44DF16", + "tests3snsSnsTopicF02F6BD0" + ] + }, "tests3snsEncryptionKey6C553584": { "Type": "AWS::KMS::Key", "Properties": { diff --git a/source/patterns/@aws-solutions-constructs/aws-s3-sqs/test/integ.s3sqs-customLoggingBucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-s3-sqs/test/integ.s3sqs-customLoggingBucket.expected.json index 34ec0ea7c..81efd9ce1 100644 --- a/source/patterns/@aws-solutions-constructs/aws-s3-sqs/test/integ.s3sqs-customLoggingBucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-s3-sqs/test/integ.s3sqs-customLoggingBucket.expected.json @@ -12,6 +12,19 @@ } ] }, + "LifecycleConfiguration": { + "Rules": [ + { + "Status": "Enabled", + "Transitions": [ + { + "StorageClass": "GLACIER", + "TransitionInDays": 7 + } + ] + } + ] + }, "PublicAccessBlockConfiguration": { "BlockPublicAcls": true, "BlockPublicPolicy": true, diff --git a/source/patterns/@aws-solutions-constructs/aws-s3-sqs/test/integ.s3sqs-customLoggingBucket.ts b/source/patterns/@aws-solutions-constructs/aws-s3-sqs/test/integ.s3sqs-customLoggingBucket.ts index e667e7085..455c50efc 100644 --- a/source/patterns/@aws-solutions-constructs/aws-s3-sqs/test/integ.s3sqs-customLoggingBucket.ts +++ b/source/patterns/@aws-solutions-constructs/aws-s3-sqs/test/integ.s3sqs-customLoggingBucket.ts @@ -12,8 +12,8 @@ */ /// !cdk-integ * -import {App, Stack, RemovalPolicy} from "aws-cdk-lib"; -import { BucketEncryption } from "aws-cdk-lib/aws-s3"; +import {App, Stack, RemovalPolicy, Duration } from "aws-cdk-lib"; +import * as s3 from "aws-cdk-lib/aws-s3"; import {S3ToSqs} from "../lib"; import { generateIntegStackName, suppressAutoDeleteHandlerWarnings } from '@aws-solutions-constructs/core'; @@ -29,8 +29,15 @@ new S3ToSqs(stack, 'test-s3-sqs', { loggingBucketProps: { removalPolicy: RemovalPolicy.DESTROY, autoDeleteObjects: true, - encryption: BucketEncryption.S3_MANAGED, - versioned: true + // This functionality is inconsequential, it just confirms + // that these props continue to be utilized + lifecycleRules: [{ + enabled: true, + transitions: [{ + storageClass: s3.StorageClass.GLACIER, + transitionAfter: Duration.days(7) + }] + }] } }); suppressAutoDeleteHandlerWarnings(stack); diff --git a/source/patterns/@aws-solutions-constructs/aws-s3-sqs/test/integ.s3sqs-existingLoggingBucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-s3-sqs/test/integ.s3sqs-existingLoggingBucket.expected.json index 0e6edef9d..e135427af 100644 --- a/source/patterns/@aws-solutions-constructs/aws-s3-sqs/test/integ.s3sqs-existingLoggingBucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-s3-sqs/test/integ.s3sqs-existingLoggingBucket.expected.json @@ -1,6 +1,6 @@ { "Resources": { - "scrapBucketB11863B7": { + "scrapBucketLog7B53B25C": { "Type": "AWS::S3::Bucket", "Properties": { "BucketEncryption": { @@ -27,30 +27,56 @@ "Metadata": { "cfn_nag": { "rules_to_suppress": [ - { - "id": "W51", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, { "id": "W35", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, - { - "id": "W41", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct" + "reason": "This is a log bucket" } ] } } }, - "scrapBucketPolicy189B0607": { + "scrapBucketLogPolicy2972C573": { "Type": "AWS::S3::BucketPolicy", "Properties": { "Bucket": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, "PolicyDocument": { "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": [ "s3:PutBucketPolicy", @@ -70,7 +96,7 @@ "Resource": [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -80,7 +106,7 @@ [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -96,7 +122,7 @@ "ArnLike": { "aws:SourceArn": { "Fn::GetAtt": [ - "tests3sqstempS3Bucket579C3000", + "scrapBucketB11863B7", "Arn" ] } @@ -117,7 +143,7 @@ [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -131,7 +157,7 @@ } } }, - "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "scrapBucketLogAutoDeleteObjectsCustomResource307F3D47": { "Type": "Custom::S3AutoDeleteObjects", "Properties": { "ServiceToken": { @@ -141,11 +167,11 @@ ] }, "BucketName": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" } }, "DependsOn": [ - "scrapBucketPolicy189B0607" + "scrapBucketLogPolicy2972C573" ], "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -197,7 +223,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, " S3 bucket." ] @@ -226,6 +252,177 @@ } } }, + "scrapBucketB11863B7": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "scrapBucketLog7B53B25C" + } + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "scrapBucketPolicy189B0607": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "scrapBucketB11863B7" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "tests3sqstempS3Bucket579C3000", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "scrapBucketB11863B7" + } + }, + "DependsOn": [ + "scrapBucketPolicy189B0607" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "tests3sqstempS3Bucket579C3000": { "Type": "AWS::S3::Bucket", "Properties": { diff --git a/source/patterns/@aws-solutions-constructs/aws-s3-sqs/test/integ.s3sqs-existingS3Bucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-s3-sqs/test/integ.s3sqs-existingS3Bucket.expected.json index 0482b5ae2..ae5fc3d33 100644 --- a/source/patterns/@aws-solutions-constructs/aws-s3-sqs/test/integ.s3sqs-existingS3Bucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-s3-sqs/test/integ.s3sqs-existingS3Bucket.expected.json @@ -1,6 +1,6 @@ { "Resources": { - "scrapBucketB11863B7": { + "scrapBucketLog7B53B25C": { "Type": "AWS::S3::Bucket", "Properties": { "BucketEncryption": { @@ -27,30 +27,56 @@ "Metadata": { "cfn_nag": { "rules_to_suppress": [ - { - "id": "W51", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, { "id": "W35", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, - { - "id": "W41", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct" + "reason": "This is a log bucket" } ] } } }, - "scrapBucketPolicy189B0607": { + "scrapBucketLogPolicy2972C573": { "Type": "AWS::S3::BucketPolicy", "Properties": { "Bucket": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, "PolicyDocument": { "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": [ "s3:PutBucketPolicy", @@ -70,7 +96,7 @@ "Resource": [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -80,7 +106,7 @@ [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -89,13 +115,49 @@ ] } ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } } ], "Version": "2012-10-17" } } }, - "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "scrapBucketLogAutoDeleteObjectsCustomResource307F3D47": { "Type": "Custom::S3AutoDeleteObjects", "Properties": { "ServiceToken": { @@ -105,49 +167,15 @@ ] }, "BucketName": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" } }, "DependsOn": [ - "scrapBucketPolicy189B0607" + "scrapBucketLogPolicy2972C573" ], "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, - "scrapBucketNotifications065035E3": { - "Type": "Custom::S3BucketNotifications", - "Properties": { - "ServiceToken": { - "Fn::GetAtt": [ - "BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691", - "Arn" - ] - }, - "BucketName": { - "Ref": "scrapBucketB11863B7" - }, - "NotificationConfiguration": { - "QueueConfigurations": [ - { - "Events": [ - "s3:ObjectCreated:*" - ], - "QueueArn": { - "Fn::GetAtt": [ - "tests3sqsqueue810CCE19", - "Arn" - ] - } - } - ] - }, - "Managed": true - }, - "DependsOn": [ - "tests3sqsqueuePolicyDDC0D745", - "tests3sqsqueue810CCE19" - ] - }, "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092": { "Type": "AWS::IAM::Role", "Properties": { @@ -195,7 +223,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, " S3 bucket." ] @@ -224,6 +252,175 @@ } } }, + "scrapBucketB11863B7": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "scrapBucketLog7B53B25C" + } + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "scrapBucketPolicy189B0607": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "scrapBucketB11863B7" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "scrapBucketB11863B7" + } + }, + "DependsOn": [ + "scrapBucketPolicy189B0607" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "scrapBucketNotifications065035E3": { + "Type": "Custom::S3BucketNotifications", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691", + "Arn" + ] + }, + "BucketName": { + "Ref": "scrapBucketB11863B7" + }, + "NotificationConfiguration": { + "QueueConfigurations": [ + { + "Events": [ + "s3:ObjectCreated:*" + ], + "QueueArn": { + "Fn::GetAtt": [ + "tests3sqsqueue810CCE19", + "Arn" + ] + } + } + ] + }, + "Managed": true + }, + "DependsOn": [ + "tests3sqsqueuePolicyDDC0D745", + "tests3sqsqueue810CCE19" + ] + }, "tests3sqsdeadLetterQueueEABBC814": { "Type": "AWS::SQS::Queue", "Properties": { diff --git a/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-customLoggingBucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-customLoggingBucket.expected.json index bf5436dee..f887c8e6c 100644 --- a/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-customLoggingBucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-customLoggingBucket.expected.json @@ -12,6 +12,19 @@ } ] }, + "LifecycleConfiguration": { + "Rules": [ + { + "Status": "Enabled", + "Transitions": [ + { + "StorageClass": "GLACIER", + "TransitionInDays": 7 + } + ] + } + ] + }, "PublicAccessBlockConfiguration": { "BlockPublicAcls": true, "BlockPublicPolicy": true, diff --git a/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-customLoggingBucket.ts b/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-customLoggingBucket.ts index 5387cef3f..1a3f9afe3 100644 --- a/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-customLoggingBucket.ts +++ b/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-customLoggingBucket.ts @@ -12,8 +12,8 @@ */ /// !cdk-integ * -import { App, Stack, RemovalPolicy } from "aws-cdk-lib"; -import { BucketEncryption } from "aws-cdk-lib/aws-s3"; +import { App, Stack, RemovalPolicy, Duration } from "aws-cdk-lib"; +import * as s3 from "aws-cdk-lib/aws-s3"; import { S3ToStepfunctions } from "../lib"; import * as stepfunctions from 'aws-cdk-lib/aws-stepfunctions'; import { generateIntegStackName, suppressAutoDeleteHandlerWarnings } from '@aws-solutions-constructs/core'; @@ -36,8 +36,15 @@ new S3ToStepfunctions(stack, 'test-s3-stepfunctions', { loggingBucketProps: { removalPolicy: RemovalPolicy.DESTROY, autoDeleteObjects: true, - encryption: BucketEncryption.S3_MANAGED, - versioned: true + // This functionality is inconsequential, it just confirms + // that these props continue to be utilized + lifecycleRules: [{ + enabled: true, + transitions: [{ + storageClass: s3.StorageClass.GLACIER, + transitionAfter: Duration.days(7) + }] + }] }, logGroupProps: { removalPolicy: RemovalPolicy.DESTROY, diff --git a/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-pre-existing-bucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-pre-existing-bucket.expected.json index f4bd0c6d3..70ea6301d 100644 --- a/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-pre-existing-bucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-pre-existing-bucket.expected.json @@ -1,6 +1,6 @@ { "Resources": { - "scrapBucketB11863B7": { + "scrapBucketLog7B53B25C": { "Type": "AWS::S3::Bucket", "Properties": { "BucketEncryption": { @@ -27,30 +27,56 @@ "Metadata": { "cfn_nag": { "rules_to_suppress": [ - { - "id": "W51", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, { "id": "W35", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation" - }, - { - "id": "W41", - "reason": "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct" + "reason": "This is a log bucket" } ] } } }, - "scrapBucketPolicy189B0607": { + "scrapBucketLogPolicy2972C573": { "Type": "AWS::S3::BucketPolicy", "Properties": { "Bucket": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, "PolicyDocument": { "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": [ "s3:PutBucketPolicy", @@ -70,7 +96,7 @@ "Resource": [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -80,7 +106,7 @@ [ { "Fn::GetAtt": [ - "scrapBucketB11863B7", + "scrapBucketLog7B53B25C", "Arn" ] }, @@ -89,13 +115,49 @@ ] } ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketLog7B53B25C", + "Arn" + ] + }, + "/*" + ] + ] + } } ], "Version": "2012-10-17" } } }, - "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "scrapBucketLogAutoDeleteObjectsCustomResource307F3D47": { "Type": "Custom::S3AutoDeleteObjects", "Properties": { "ServiceToken": { @@ -105,33 +167,15 @@ ] }, "BucketName": { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" } }, "DependsOn": [ - "scrapBucketPolicy189B0607" + "scrapBucketLogPolicy2972C573" ], "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, - "scrapBucketNotifications065035E3": { - "Type": "Custom::S3BucketNotifications", - "Properties": { - "ServiceToken": { - "Fn::GetAtt": [ - "BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691", - "Arn" - ] - }, - "BucketName": { - "Ref": "scrapBucketB11863B7" - }, - "NotificationConfiguration": { - "EventBridgeConfiguration": {} - }, - "Managed": true - } - }, "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092": { "Type": "AWS::IAM::Role", "Properties": { @@ -179,7 +223,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "scrapBucketB11863B7" + "Ref": "scrapBucketLog7B53B25C" }, " S3 bucket." ] @@ -208,6 +252,159 @@ } } }, + "scrapBucketB11863B7": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "scrapBucketLog7B53B25C" + } + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "scrapBucketPolicy189B0607": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "scrapBucketB11863B7" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "scrapBucketB11863B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "scrapBucketAutoDeleteObjectsCustomResourceFFFC3275": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "scrapBucketB11863B7" + } + }, + "DependsOn": [ + "scrapBucketPolicy189B0607" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "scrapBucketNotifications065035E3": { + "Type": "Custom::S3BucketNotifications", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691", + "Arn" + ] + }, + "BucketName": { + "Ref": "scrapBucketB11863B7" + }, + "NotificationConfiguration": { + "EventBridgeConfiguration": {} + }, + "Managed": true + } + }, "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleB6FB88EC": { "Type": "AWS::IAM::Role", "Properties": { diff --git a/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-s3-stepfunctions-no-argument.expected.json b/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-s3-stepfunctions-no-argument.expected.json index 1a6f2a8a2..01de803ed 100644 --- a/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-s3-stepfunctions-no-argument.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-s3-stepfunctions-no-argument.expected.json @@ -18,6 +18,12 @@ "IgnorePublicAcls": true, "RestrictPublicBuckets": true }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], "VersioningConfiguration": { "Status": "Enabled" } @@ -77,6 +83,45 @@ } ] }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "tests3stepfunctionsconstructS3LoggingBucket706AEC25", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "tests3stepfunctionsconstructS3LoggingBucket706AEC25", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": "s3:PutObject", "Condition": { @@ -118,6 +163,25 @@ } } }, + "tests3stepfunctionsconstructS3LoggingBucketAutoDeleteObjectsCustomResource3223C39A": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "tests3stepfunctionsconstructS3LoggingBucket706AEC25" + } + }, + "DependsOn": [ + "tests3stepfunctionsconstructS3LoggingBucketPolicy4FEACD99" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "tests3stepfunctionsconstructS3Bucket78CA0724": { "Type": "AWS::S3::Bucket", "Properties": { @@ -154,6 +218,12 @@ "IgnorePublicAcls": true, "RestrictPublicBuckets": true }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], "VersioningConfiguration": { "Status": "Enabled" } @@ -212,12 +282,70 @@ ] } ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "tests3stepfunctionsconstructS3Bucket78CA0724", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "tests3stepfunctionsconstructS3Bucket78CA0724", + "Arn" + ] + }, + "/*" + ] + ] + } + ] } ], "Version": "2012-10-17" } } }, + "tests3stepfunctionsconstructS3BucketAutoDeleteObjectsCustomResource1FCBE949": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "tests3stepfunctionsconstructS3Bucket78CA0724" + } + }, + "DependsOn": [ + "tests3stepfunctionsconstructS3BucketPolicyC7A413B9" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "tests3stepfunctionsconstructS3BucketNotificationsD6B8ACDA": { "Type": "Custom::S3BucketNotifications", "Properties": { @@ -541,6 +669,82 @@ "Threshold": 1 } }, + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ] + } + }, + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "b7f33614a69548d6bafe224d751a7ef238cde19097415e553fe8b63a4c8fd8a6.zip" + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + }, + "Runtime": "nodejs18.x", + "Description": { + "Fn::Join": [ + "", + [ + "Lambda function for auto-deleting objects in ", + { + "Ref": "tests3stepfunctionsconstructS3LoggingBucket706AEC25" + }, + " S3 bucket." + ] + ] + } + }, + "DependsOn": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "CDK generated custom resource" + }, + { + "id": "W89", + "reason": "CDK generated custom resource" + }, + { + "id": "W92", + "reason": "CDK generated custom resource" + } + ] + } + } + }, "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleB6FB88EC": { "Type": "AWS::IAM::Role", "Properties": { diff --git a/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-s3-stepfunctions-no-argument.ts b/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-s3-stepfunctions-no-argument.ts index 42100c932..bc48422f6 100644 --- a/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-s3-stepfunctions-no-argument.ts +++ b/source/patterns/@aws-solutions-constructs/aws-s3-stepfunctions/test/integ.s3stp-s3-stepfunctions-no-argument.ts @@ -30,6 +30,11 @@ const props: S3ToStepfunctionsProps = { }, bucketProps: { removalPolicy: RemovalPolicy.DESTROY, + autoDeleteObjects: true + }, + loggingBucketProps: { + removalPolicy: RemovalPolicy.DESTROY, + autoDeleteObjects: true }, logGroupProps: { removalPolicy: RemovalPolicy.DESTROY, @@ -44,4 +49,5 @@ defaults.addCfnSuppressRules(s3Bucket, [ reason: 'This S3 bucket is created for unit/ integration testing purposes only.' }, ]); +defaults.suppressAutoDeleteHandlerWarnings(stack); app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/core/test/test-helper.ts b/source/patterns/@aws-solutions-constructs/core/test/test-helper.ts index cec9b68ef..08a78969e 100644 --- a/source/patterns/@aws-solutions-constructs/core/test/test-helper.ts +++ b/source/patterns/@aws-solutions-constructs/core/test/test-helper.ts @@ -34,13 +34,23 @@ export const fakeEcrRepoArn = 'arn:aws:ecr:us-east-1:123456789012:repository/fak // Creates a bucket used for testing - minimal properties, destroyed after test export function CreateScrapBucket(scope: Construct, id: string, props?: BucketProps | any) { + if (props?.serverAccessLogsBucket) { + throw new Error("Don't try to send a log bucket to CreateScrapBucket"); + } + + // Basic props for scrap and log buckets const defaultProps: BucketProps = { versioned: true, removalPolicy: RemovalPolicy.DESTROY, autoDeleteObjects: true, encryption: BucketEncryption.S3_MANAGED, + enforceSSL: true }; + // Create basic log bucket + const logBucket = new Bucket(scope, `${id}Log`, defaultProps); + + // Combine basic props with special props from test client let synthesizedProps: BucketProps; if (props) { synthesizedProps = overrideProps(defaultProps, props); @@ -48,24 +58,19 @@ export function CreateScrapBucket(scope: Construct, id: string, props?: BucketPr synthesizedProps = defaultProps; } + // Finally - set up logging for the scrap bucket + const finalProps = overrideProps(synthesizedProps, { serverAccessLogsBucket: logBucket }); + const scriptBucket = new Bucket( scope, id, - synthesizedProps + finalProps ); - addCfnSuppressRules(scriptBucket, [ - { - id: "W51", - reason: "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation", - }, + addCfnSuppressRules(logBucket, [ { id: "W35", - reason: "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct implementation", - }, - { - id: "W41", - reason: "This S3 bucket is created for unit/ integration testing purposes only and not part of the actual construct", + reason: "This is a log bucket", } ]); From 733fb35547e31a6c493cf69b146ccb352c288c06 Mon Sep 17 00:00:00 2001 From: biffgaut <78155736+biffgaut@users.noreply.github.com> Date: Mon, 11 Dec 2023 08:04:39 -0500 Subject: [PATCH 04/14] Fix domain names in search constructs (#1040) --- refresh-multiple-tests.sh | 66 ++++++- ...eg.dbslamels-deploy-with-vpc.expected.json | 165 +++++++++++++++++- .../test/integ.dbslamels-deploy-with-vpc.ts | 6 +- ...integ.dbslamels-no-arguments.expected.json | 165 +++++++++++++++++- .../test/integ.dbslamels-no-arguments.ts | 6 +- ...eg.faropn-existing-resources.expected.json | 165 +++++++++++++++++- .../test/integ.faropn-existing-resources.ts | 5 +- .../integ.faropn-new-resources.expected.json | 165 +++++++++++++++++- .../test/integ.faropn-new-resources.ts | 4 +- ...eg.farssm-existing-resources.expected.json | 1 - .../test/integ.farssm-existing-resources.ts | 1 - .../integ.farssm-new-resources.expected.json | 1 - .../test/integ.farssm-new-resources.ts | 3 +- ...loyFunctionWithClusterConfig.expected.json | 165 +++++++++++++++++- ....lamels-deployFunctionWithClusterConfig.ts | 2 +- ...eployFunctionWithExistingVpc.expected.json | 165 +++++++++++++++++- ...eg.lamels-deployFunctionWithExistingVpc.ts | 2 +- ...s-deployFunctionWithVpcProps.expected.json | 165 +++++++++++++++++- ...integ.lamels-deployFunctionWithVpcProps.ts | 2 +- ...teg.lamels-deployToFiveZones.expected.json | 165 +++++++++++++++++- .../test/integ.lamels-deployToFiveZones.ts | 2 +- ...lamels-disabledZoneAwareness.expected.json | 165 +++++++++++++++++- .../integ.lamels-disabledZoneAwareness.ts | 2 +- ...nteg.lamels-domain-arguments.expected.json | 165 +++++++++++++++++- .../test/integ.lamels-domain-arguments.ts | 8 +- .../integ.lamels-no-arguments.expected.json | 165 +++++++++++++++++- .../test/integ.lamels-no-arguments.ts | 8 +- .../integ.lamopn-cluster-config.expected.json | 165 +++++++++++++++++- .../test/integ.lamopn-cluster-config.ts | 4 +- ...mopn-disabled-zone-awareness.expected.json | 165 +++++++++++++++++- .../integ.lamopn-disabled-zone-awareness.ts | 2 +- ...nteg.lamopn-domain-arguments.expected.json | 165 +++++++++++++++++- .../test/integ.lamopn-domain-arguments.ts | 8 +- .../integ.lamopn-existing-vpc.expected.json | 165 +++++++++++++++++- .../test/integ.lamopn-existing-vpc.ts | 2 +- .../integ.lamopn-no-arguments.expected.json | 165 +++++++++++++++++- .../test/integ.lamopn-no-arguments.ts | 6 +- .../test/integ.lamopn-vpc-props.expected.json | 165 +++++++++++++++++- .../test/integ.lamopn-vpc-props.ts | 2 +- .../core/test/test-helper.ts | 12 +- 40 files changed, 2822 insertions(+), 138 deletions(-) diff --git a/refresh-multiple-tests.sh b/refresh-multiple-tests.sh index f2a2ed4ab..afd8b0e63 100755 --- a/refresh-multiple-tests.sh +++ b/refresh-multiple-tests.sh @@ -19,20 +19,78 @@ # a sleep 10 command before the end of the loop to keep from overwhelming CloudFormation export constructs=" + aws-alb-fargate + aws-alb-lambda + aws-apigateway-dynamodb + aws-apigateway-iot + aws-apigateway-kinesisstreams + aws-apigateway-lambda + aws-apigateway-sagemakerendpoint + aws-apigateway-sqs + aws-cloudfront-apigateway + aws-cloudfront-apigateway-lambda + aws-cloudfront-mediastore aws-cloudfront-s3 + aws-cognito-apigateway-lambda + aws-dynamodbstreams-lambda + aws-dynamodbstreams-lambda-elasticsearch-kibana + aws-eventbridge-kinesisfirehose-s3 + aws-eventbridge-kinesisstreams + aws-eventbridge-lambda + aws-eventbridge-sns + aws-eventbridge-sqs + aws-eventbridge-stepfunctions + aws-fargate-dynamodb + aws-fargate-eventbridge + aws-fargate-kinesisfirehose + aws-fargate-kinesisstreams + aws-fargate-opensearch aws-fargate-s3 + aws-fargate-secretsmanager + aws-fargate-sns + aws-fargate-sqs + aws-fargate-ssmstringparameter + aws-fargate-stepfunctions + aws-iot-kinesisfirehose-s3 + aws-iot-kinesisstreams + aws-iot-lambda + aws-iot-lambda-dynamodb aws-iot-s3 + aws-iot-sqs aws-kinesisfirehose-s3 + aws-kinesisstreams-gluejob + aws-kinesisstreams-kinesisfirehose-s3 + aws-kinesisstreams-lambda + aws-lambda-dynamodb + aws-lambda-elasticachememcached + aws-lambda-elasticsearch-kibana + aws-lambda-eventbridge aws-lambda-kendra + aws-lambda-kinesisfirehose + aws-lambda-kinesisstreams + aws-lambda-opensearch aws-lambda-s3 + aws-lambda-sagemakerendpoint + aws-lambda-secretsmanager + aws-lambda-sns + aws-lambda-sqs + aws-lambda-sqs-lambda + aws-lambda-ssmstringparameter + aws-lambda-stepfunctions aws-openapigateway-lambda + aws-route53-alb + aws-route53-apigateway aws-s3-lambda aws-s3-sns aws-s3-sqs - aws-eventbridge-stepfunctions aws-s3-stepfunctions - aws-eventbridge-kinesisfirehose-s3 - aws-kinesisstreams-kinesisfirehose-s3 + aws-sns-lambda + aws-sns-sqs + aws-sqs-lambda + aws-wafwebacl-alb + aws-wafwebacl-apigateway + aws-wafwebacl-appsync + aws-wafwebacl-cloudfront " constructs_root_dir=$(cd $(dirname $0) && pwd) @@ -40,7 +98,7 @@ source_dir="$constructs_root_dir/source" echo "=============================================================================================" echo "aligning versions and updating package.json for CDK v2..." -/bin/bash $constructs_root_dir/align-version.sh +/bin/bash $constructs_root_dir/deployment/v2/align-version.sh bail="--bail" runtarget="jsii" diff --git a/source/patterns/@aws-solutions-constructs/aws-dynamodbstreams-lambda-elasticsearch-kibana/test/integ.dbslamels-deploy-with-vpc.expected.json b/source/patterns/@aws-solutions-constructs/aws-dynamodbstreams-lambda-elasticsearch-kibana/test/integ.dbslamels-deploy-with-vpc.expected.json index 1c6d1bdfb..39d2440ea 100644 --- a/source/patterns/@aws-solutions-constructs/aws-dynamodbstreams-lambda-elasticsearch-kibana/test/integ.dbslamels-deploy-with-vpc.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-dynamodbstreams-lambda-elasticsearch-kibana/test/integ.dbslamels-deploy-with-vpc.expected.json @@ -359,7 +359,37 @@ "testddbstreamslambdaeskLambdaToElasticSearchUserPoolDomainA6C84014": { "Type": "AWS::Cognito::UserPoolDomain", "Properties": { - "Domain": "pooldomaintest-2342", + "Domain": { + "Fn::Join": [ + "-", + [ + "cog", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "UserPoolId": { "Ref": "testddbstreamslambdaeskLambdaToElasticSearchCognitoUserPool97EA2952" } @@ -416,7 +446,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/pooldomaintest-2342/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "cog", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -529,7 +591,38 @@ { "Ref": "AWS::AccountId" }, - ":domain/domaintest-1234" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + } ] ] } @@ -601,7 +694,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/domaintest-1234/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -628,7 +753,37 @@ "EnforceHTTPS": true, "TLSSecurityPolicy": "Policy-Min-TLS-1-2-2019-07" }, - "DomainName": "domaintest-1234", + "DomainName": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "EBSOptions": { "EBSEnabled": true, "VolumeSize": 10 diff --git a/source/patterns/@aws-solutions-constructs/aws-dynamodbstreams-lambda-elasticsearch-kibana/test/integ.dbslamels-deploy-with-vpc.ts b/source/patterns/@aws-solutions-constructs/aws-dynamodbstreams-lambda-elasticsearch-kibana/test/integ.dbslamels-deploy-with-vpc.ts index aeac16b60..c01b7575d 100644 --- a/source/patterns/@aws-solutions-constructs/aws-dynamodbstreams-lambda-elasticsearch-kibana/test/integ.dbslamels-deploy-with-vpc.ts +++ b/source/patterns/@aws-solutions-constructs/aws-dynamodbstreams-lambda-elasticsearch-kibana/test/integ.dbslamels-deploy-with-vpc.ts @@ -15,7 +15,7 @@ import { App, Stack } from "aws-cdk-lib"; import { DynamoDBStreamsToLambdaToElasticSearchAndKibanaProps, DynamoDBStreamsToLambdaToElasticSearchAndKibana } from "../lib"; import * as lambda from 'aws-cdk-lib/aws-lambda'; -import { generateIntegStackName } from '@aws-solutions-constructs/core'; +import { generateIntegStackName, CreateShortUniqueTestName } from '@aws-solutions-constructs/core'; const app = new App(); @@ -28,8 +28,8 @@ const props: DynamoDBStreamsToLambdaToElasticSearchAndKibanaProps = { runtime: lambda.Runtime.NODEJS_16_X, handler: 'index.handler' }, - domainName: 'domaintest-1234', - cognitoDomainName: 'pooldomaintest-2342', + domainName: CreateShortUniqueTestName("dmn"), + cognitoDomainName: CreateShortUniqueTestName("cog"), deployVpc: true }; diff --git a/source/patterns/@aws-solutions-constructs/aws-dynamodbstreams-lambda-elasticsearch-kibana/test/integ.dbslamels-no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-dynamodbstreams-lambda-elasticsearch-kibana/test/integ.dbslamels-no-arguments.expected.json index 55ed4697a..9b36da2ff 100644 --- a/source/patterns/@aws-solutions-constructs/aws-dynamodbstreams-lambda-elasticsearch-kibana/test/integ.dbslamels-no-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-dynamodbstreams-lambda-elasticsearch-kibana/test/integ.dbslamels-no-arguments.expected.json @@ -289,7 +289,37 @@ "testddbstreamslambdaeskLambdaToElasticSearchUserPoolDomainA6C84014": { "Type": "AWS::Cognito::UserPoolDomain", "Properties": { - "Domain": "pooldomaintest-98eb", + "Domain": { + "Fn::Join": [ + "-", + [ + "cog", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "UserPoolId": { "Ref": "testddbstreamslambdaeskLambdaToElasticSearchCognitoUserPool97EA2952" } @@ -346,7 +376,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/pooldomaintest-98eb/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "cog", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -459,7 +521,38 @@ { "Ref": "AWS::AccountId" }, - ":domain/domaintest-84ef" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + } ] ] } @@ -531,7 +624,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/domaintest-84ef/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -558,7 +683,37 @@ "EnforceHTTPS": true, "TLSSecurityPolicy": "Policy-Min-TLS-1-2-2019-07" }, - "DomainName": "domaintest-84ef", + "DomainName": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "EBSOptions": { "EBSEnabled": true, "VolumeSize": 10 diff --git a/source/patterns/@aws-solutions-constructs/aws-dynamodbstreams-lambda-elasticsearch-kibana/test/integ.dbslamels-no-arguments.ts b/source/patterns/@aws-solutions-constructs/aws-dynamodbstreams-lambda-elasticsearch-kibana/test/integ.dbslamels-no-arguments.ts index bc5a49757..644295af8 100644 --- a/source/patterns/@aws-solutions-constructs/aws-dynamodbstreams-lambda-elasticsearch-kibana/test/integ.dbslamels-no-arguments.ts +++ b/source/patterns/@aws-solutions-constructs/aws-dynamodbstreams-lambda-elasticsearch-kibana/test/integ.dbslamels-no-arguments.ts @@ -15,7 +15,7 @@ import { App, Stack } from "aws-cdk-lib"; import { DynamoDBStreamsToLambdaToElasticSearchAndKibanaProps, DynamoDBStreamsToLambdaToElasticSearchAndKibana } from "../lib"; import * as lambda from 'aws-cdk-lib/aws-lambda'; -import { generateIntegStackName } from '@aws-solutions-constructs/core'; +import { generateIntegStackName, CreateShortUniqueTestName } from '@aws-solutions-constructs/core'; const app = new App(); @@ -28,8 +28,8 @@ const props: DynamoDBStreamsToLambdaToElasticSearchAndKibanaProps = { runtime: lambda.Runtime.NODEJS_16_X, handler: 'index.handler' }, - domainName: 'domaintest-84ef', - cognitoDomainName: 'pooldomaintest-98eb' + domainName: CreateShortUniqueTestName("dmn"), + cognitoDomainName: CreateShortUniqueTestName("cog"), }; new DynamoDBStreamsToLambdaToElasticSearchAndKibana(stack, 'test-ddbstreams-lambda-esk', props); diff --git a/source/patterns/@aws-solutions-constructs/aws-fargate-opensearch/test/integ.faropn-existing-resources.expected.json b/source/patterns/@aws-solutions-constructs/aws-fargate-opensearch/test/integ.faropn-existing-resources.expected.json index 469ccbc84..e6ff2b8c3 100644 --- a/source/patterns/@aws-solutions-constructs/aws-fargate-opensearch/test/integ.faropn-existing-resources.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-fargate-opensearch/test/integ.faropn-existing-resources.expected.json @@ -1114,7 +1114,37 @@ "testconstructUserPoolDomain6B64F32F": { "Type": "AWS::Cognito::UserPoolDomain", "Properties": { - "Domain": "cogn-solution-constructs", + "Domain": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "UserPoolId": { "Ref": "testconstructCognitoUserPoolA4991355" } @@ -1171,7 +1201,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/cogn-solution-constructs/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -1284,7 +1346,38 @@ { "Ref": "AWS::AccountId" }, - ":domain/solution-constructs" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + } ] ] } @@ -1348,7 +1441,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/solution-constructs/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -1384,7 +1509,37 @@ "EnforceHTTPS": true, "TLSSecurityPolicy": "Policy-Min-TLS-1-2-2019-07" }, - "DomainName": "solution-constructs", + "DomainName": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "EBSOptions": { "EBSEnabled": true, "VolumeSize": 10 diff --git a/source/patterns/@aws-solutions-constructs/aws-fargate-opensearch/test/integ.faropn-existing-resources.ts b/source/patterns/@aws-solutions-constructs/aws-fargate-opensearch/test/integ.faropn-existing-resources.ts index 46742cf3d..75ea68106 100644 --- a/source/patterns/@aws-solutions-constructs/aws-fargate-opensearch/test/integ.faropn-existing-resources.ts +++ b/source/patterns/@aws-solutions-constructs/aws-fargate-opensearch/test/integ.faropn-existing-resources.ts @@ -38,8 +38,7 @@ const createFargateServiceResponse = CreateFargateService(stack, 'test', { const testProps: FargateToOpenSearchProps = { publicApi: true, existingVpc, - openSearchDomainName: 'solution-constructs', - cognitoDomainName: 'cogn-solution-constructs', + openSearchDomainName: defaults.CreateShortUniqueTestName("dmn"), existingContainerDefinitionObject: createFargateServiceResponse.containerDefinition, existingFargateServiceObject: createFargateServiceResponse.service, domainEndpointEnvironmentVariableName: 'CUSTOM_NAME', @@ -49,4 +48,4 @@ new FargateToOpenSearch(stack, 'test-construct', testProps); defaults.suppressAutoDeleteHandlerWarnings(stack); // Synth -app.synth(); \ No newline at end of file +app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-fargate-opensearch/test/integ.faropn-new-resources.expected.json b/source/patterns/@aws-solutions-constructs/aws-fargate-opensearch/test/integ.faropn-new-resources.expected.json index f554a190b..a4b9bb405 100644 --- a/source/patterns/@aws-solutions-constructs/aws-fargate-opensearch/test/integ.faropn-new-resources.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-fargate-opensearch/test/integ.faropn-new-resources.expected.json @@ -84,7 +84,37 @@ "testconstructUserPoolDomain6B64F32F": { "Type": "AWS::Cognito::UserPoolDomain", "Properties": { - "Domain": "faropn-new-resources", + "Domain": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "UserPoolId": { "Ref": "testconstructCognitoUserPoolA4991355" } @@ -141,7 +171,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/faropn-new-resources/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -254,7 +316,38 @@ { "Ref": "AWS::AccountId" }, - ":domain/faropn-new-resources" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + } ] ] } @@ -318,7 +411,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/faropn-new-resources/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -354,7 +479,37 @@ "EnforceHTTPS": true, "TLSSecurityPolicy": "Policy-Min-TLS-1-2-2019-07" }, - "DomainName": "faropn-new-resources", + "DomainName": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "EBSOptions": { "EBSEnabled": true, "VolumeSize": 10 diff --git a/source/patterns/@aws-solutions-constructs/aws-fargate-opensearch/test/integ.faropn-new-resources.ts b/source/patterns/@aws-solutions-constructs/aws-fargate-opensearch/test/integ.faropn-new-resources.ts index 916c87b7c..521ef343f 100644 --- a/source/patterns/@aws-solutions-constructs/aws-fargate-opensearch/test/integ.faropn-new-resources.ts +++ b/source/patterns/@aws-solutions-constructs/aws-fargate-opensearch/test/integ.faropn-new-resources.ts @@ -14,7 +14,7 @@ // Imports import { Aws, App, Stack } from "aws-cdk-lib"; import { FargateToOpenSearch, FargateToOpenSearchProps } from "../lib"; -import { generateIntegStackName, suppressAutoDeleteHandlerWarnings } from '@aws-solutions-constructs/core'; +import { generateIntegStackName, suppressAutoDeleteHandlerWarnings, CreateShortUniqueTestName } from '@aws-solutions-constructs/core'; import * as ecs from 'aws-cdk-lib/aws-ecs'; // Setup @@ -31,7 +31,7 @@ const testProps: FargateToOpenSearchProps = { containerDefinitionProps: { image }, - openSearchDomainName: `${generateIntegStackName(__filename)}`, + openSearchDomainName: CreateShortUniqueTestName("dmn"), }; new FargateToOpenSearch(stack, 'test-construct', testProps); diff --git a/source/patterns/@aws-solutions-constructs/aws-fargate-ssmstringparameter/test/integ.farssm-existing-resources.expected.json b/source/patterns/@aws-solutions-constructs/aws-fargate-ssmstringparameter/test/integ.farssm-existing-resources.expected.json index 176e69118..d33d81209 100644 --- a/source/patterns/@aws-solutions-constructs/aws-fargate-ssmstringparameter/test/integ.farssm-existing-resources.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-fargate-ssmstringparameter/test/integ.farssm-existing-resources.expected.json @@ -806,7 +806,6 @@ "Properties": { "AllowedPattern": ".*", "Description": "The value Foo", - "Name": "FooParameter", "Type": "String", "Value": "Foo" } diff --git a/source/patterns/@aws-solutions-constructs/aws-fargate-ssmstringparameter/test/integ.farssm-existing-resources.ts b/source/patterns/@aws-solutions-constructs/aws-fargate-ssmstringparameter/test/integ.farssm-existing-resources.ts index c9a97334d..4a62b510e 100644 --- a/source/patterns/@aws-solutions-constructs/aws-fargate-ssmstringparameter/test/integ.farssm-existing-resources.ts +++ b/source/patterns/@aws-solutions-constructs/aws-fargate-ssmstringparameter/test/integ.farssm-existing-resources.ts @@ -29,7 +29,6 @@ const existingVpc = getTestVpc(stack); const existingStringParameterObj = new ssm.StringParameter(stack, 'Parameter', { allowedPattern: '.*', description: 'The value Foo', - parameterName: 'FooParameter', stringValue: 'Foo', }); diff --git a/source/patterns/@aws-solutions-constructs/aws-fargate-ssmstringparameter/test/integ.farssm-new-resources.expected.json b/source/patterns/@aws-solutions-constructs/aws-fargate-ssmstringparameter/test/integ.farssm-new-resources.expected.json index d958f5906..7a16f4d55 100644 --- a/source/patterns/@aws-solutions-constructs/aws-fargate-ssmstringparameter/test/integ.farssm-new-resources.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-fargate-ssmstringparameter/test/integ.farssm-new-resources.expected.json @@ -4,7 +4,6 @@ "testconstructstringParameter4A9E7765": { "Type": "AWS::SSM::Parameter", "Properties": { - "Name": "FooParameter", "Type": "String", "Value": "Foo" } diff --git a/source/patterns/@aws-solutions-constructs/aws-fargate-ssmstringparameter/test/integ.farssm-new-resources.ts b/source/patterns/@aws-solutions-constructs/aws-fargate-ssmstringparameter/test/integ.farssm-new-resources.ts index 207b9257c..48a1796a0 100644 --- a/source/patterns/@aws-solutions-constructs/aws-fargate-ssmstringparameter/test/integ.farssm-new-resources.ts +++ b/source/patterns/@aws-solutions-constructs/aws-fargate-ssmstringparameter/test/integ.farssm-new-resources.ts @@ -14,7 +14,7 @@ // Imports import { Aws, App, Stack } from "aws-cdk-lib"; import { FargateToSsmstringparameter, FargateToSsmstringparameterProps } from "../lib"; -import { generateIntegStackName } from '@aws-solutions-constructs/core'; +import { generateIntegStackName } from '@aws-solutions-constructs/core'; import * as ecs from 'aws-cdk-lib/aws-ecs'; // Setup @@ -32,7 +32,6 @@ const testProps: FargateToSsmstringparameterProps = { image }, stringParameterProps: { - parameterName: "FooParameter", stringValue: "Foo" } }; diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployFunctionWithClusterConfig.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployFunctionWithClusterConfig.expected.json index bc1b69f2c..28a1075d6 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployFunctionWithClusterConfig.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployFunctionWithClusterConfig.expected.json @@ -288,7 +288,37 @@ "testlambdaelasticsearchkibana5UserPoolDomainE2693371": { "Type": "AWS::Cognito::UserPoolDomain", "Properties": { - "Domain": "deploytestwithclusterconfig", + "Domain": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "UserPoolId": { "Ref": "testlambdaelasticsearchkibana5CognitoUserPool4E321CD0" } @@ -345,7 +375,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/deploytestwithclusterconfig/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -458,7 +520,38 @@ { "Ref": "AWS::AccountId" }, - ":domain/deploytestwithclusterconfig" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + } ] ] } @@ -530,7 +623,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/deploytestwithclusterconfig/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -557,7 +682,37 @@ "EnforceHTTPS": true, "TLSSecurityPolicy": "Policy-Min-TLS-1-2-2019-07" }, - "DomainName": "deploytestwithclusterconfig", + "DomainName": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "EBSOptions": { "EBSEnabled": true, "VolumeSize": 10 diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployFunctionWithClusterConfig.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployFunctionWithClusterConfig.ts index 6feb474e3..04deb98a1 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployFunctionWithClusterConfig.ts +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployFunctionWithClusterConfig.ts @@ -41,7 +41,7 @@ const esDomainProps = { new LambdaToElasticSearchAndKibana(stack, 'test-lambda-elasticsearch-kibana5', { lambdaFunctionProps: lambdaProps, - domainName: "deploytestwithclusterconfig", + domainName: defaults.CreateShortUniqueTestName("dmn"), esDomainProps, deployVpc: true, vpcProps: { diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployFunctionWithExistingVpc.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployFunctionWithExistingVpc.expected.json index 13c66593b..016f36128 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployFunctionWithExistingVpc.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployFunctionWithExistingVpc.expected.json @@ -962,7 +962,37 @@ "testlambdaelasticsearchkibana4UserPoolDomain4CAAF2F6": { "Type": "AWS::Cognito::UserPoolDomain", "Properties": { - "Domain": "deploytestwithexistingvpc", + "Domain": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "UserPoolId": { "Ref": "testlambdaelasticsearchkibana4CognitoUserPool37A5CDE1" } @@ -1019,7 +1049,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/deploytestwithexistingvpc/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -1132,7 +1194,38 @@ { "Ref": "AWS::AccountId" }, - ":domain/deploytestwithexistingvpc" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + } ] ] } @@ -1204,7 +1297,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/deploytestwithexistingvpc/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -1231,7 +1356,37 @@ "EnforceHTTPS": true, "TLSSecurityPolicy": "Policy-Min-TLS-1-2-2019-07" }, - "DomainName": "deploytestwithexistingvpc", + "DomainName": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "EBSOptions": { "EBSEnabled": true, "VolumeSize": 10 diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployFunctionWithExistingVpc.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployFunctionWithExistingVpc.ts index bb91a5f77..8812b45f9 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployFunctionWithExistingVpc.ts +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployFunctionWithExistingVpc.ts @@ -36,7 +36,7 @@ const lambdaProps: lambda.FunctionProps = { new LambdaToElasticSearchAndKibana(stack, 'test-lambda-elasticsearch-kibana4', { lambdaFunctionProps: lambdaProps, - domainName: "deploytestwithexistingvpc", + domainName: defaults.CreateShortUniqueTestName("dmn"), existingVpc: vpc }); diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployFunctionWithVpcProps.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployFunctionWithVpcProps.expected.json index 9d673ea62..6ed91d419 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployFunctionWithVpcProps.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployFunctionWithVpcProps.expected.json @@ -290,7 +290,37 @@ "testlambdaelasticsearchkibana3UserPoolDomain11719EB9": { "Type": "AWS::Cognito::UserPoolDomain", "Properties": { - "Domain": "deploytestwithvpcprops", + "Domain": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "UserPoolId": { "Ref": "testlambdaelasticsearchkibana3CognitoUserPoolEF0D5793" } @@ -347,7 +377,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/deploytestwithvpcprops/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -460,7 +522,38 @@ { "Ref": "AWS::AccountId" }, - ":domain/deploytestwithvpcprops" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + } ] ] } @@ -532,7 +625,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/deploytestwithvpcprops/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -559,7 +684,37 @@ "EnforceHTTPS": true, "TLSSecurityPolicy": "Policy-Min-TLS-1-2-2019-07" }, - "DomainName": "deploytestwithvpcprops", + "DomainName": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "EBSOptions": { "EBSEnabled": true, "VolumeSize": 10 diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployFunctionWithVpcProps.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployFunctionWithVpcProps.ts index 5d5a93558..3e7c29f62 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployFunctionWithVpcProps.ts +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployFunctionWithVpcProps.ts @@ -34,7 +34,7 @@ const lambdaProps: lambda.FunctionProps = { new LambdaToElasticSearchAndKibana(stack, 'test-lambda-elasticsearch-kibana3', { lambdaFunctionProps: lambdaProps, - domainName: "deploytestwithvpcprops", + domainName: defaults.CreateShortUniqueTestName("dmn"), deployVpc: true, vpcProps: { ipAddresses: ec2.IpAddresses.cidr('172.168.0.0/16'), diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployToFiveZones.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployToFiveZones.expected.json index 1c61216cc..0feff4994 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployToFiveZones.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployToFiveZones.expected.json @@ -292,7 +292,37 @@ "testlambdaelasticsearchkibanaUserPoolDomainB9BDF063": { "Type": "AWS::Cognito::UserPoolDomain", "Properties": { - "Domain": "deploytestfivezones", + "Domain": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "UserPoolId": { "Ref": "testlambdaelasticsearchkibanaCognitoUserPool9537802B" } @@ -349,7 +379,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/deploytestfivezones/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -462,7 +524,38 @@ { "Ref": "AWS::AccountId" }, - ":domain/deploytestfivezones" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + } ] ] } @@ -534,7 +627,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/deploytestfivezones/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -561,7 +686,37 @@ "EnforceHTTPS": true, "TLSSecurityPolicy": "Policy-Min-TLS-1-2-2019-07" }, - "DomainName": "deploytestfivezones", + "DomainName": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "EBSOptions": { "EBSEnabled": true, "VolumeSize": 10 diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployToFiveZones.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployToFiveZones.ts index e722bedc5..6edfbfeef 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployToFiveZones.ts +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-deployToFiveZones.ts @@ -29,7 +29,7 @@ const lambdaProps: lambda.FunctionProps = { new LambdaToElasticSearchAndKibana(stack, 'test-lambda-elasticsearch-kibana', { lambdaFunctionProps: lambdaProps, - domainName: "deploytestfivezones", + domainName: defaults.CreateShortUniqueTestName("dmn"), deployVpc: true, vpcProps: { maxAzs: 5 diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-disabledZoneAwareness.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-disabledZoneAwareness.expected.json index 2d56aedbb..23934cf3f 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-disabledZoneAwareness.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-disabledZoneAwareness.expected.json @@ -284,7 +284,37 @@ "testlambdaelasticsearchkibanaUserPoolDomainB9BDF063": { "Type": "AWS::Cognito::UserPoolDomain", "Properties": { - "Domain": "disabledzoneawareness", + "Domain": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "UserPoolId": { "Ref": "testlambdaelasticsearchkibanaCognitoUserPool9537802B" } @@ -341,7 +371,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/disabledzoneawareness/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -454,7 +516,38 @@ { "Ref": "AWS::AccountId" }, - ":domain/disabledzoneawareness" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + } ] ] } @@ -526,7 +619,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/disabledzoneawareness/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -553,7 +678,37 @@ "EnforceHTTPS": true, "TLSSecurityPolicy": "Policy-Min-TLS-1-2-2019-07" }, - "DomainName": "disabledzoneawareness", + "DomainName": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "EBSOptions": { "EBSEnabled": true, "VolumeSize": 10 diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-disabledZoneAwareness.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-disabledZoneAwareness.ts index 06b73cc5c..9aaa4e96c 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-disabledZoneAwareness.ts +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-disabledZoneAwareness.ts @@ -38,7 +38,7 @@ const esDomainProps = { new LambdaToElasticSearchAndKibana(stack, 'test-lambda-elasticsearch-kibana', { lambdaFunctionProps: lambdaProps, - domainName: "disabledzoneawareness", + domainName: defaults.CreateShortUniqueTestName("dmn"), esDomainProps, deployVpc: true, vpcProps: { diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-domain-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-domain-arguments.expected.json index f76540ca7..329e430c4 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-domain-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-domain-arguments.expected.json @@ -227,7 +227,37 @@ "testlambdaelasticsearchkibana2UserPoolDomainF6BF7A6F": { "Type": "AWS::Cognito::UserPoolDomain", "Properties": { - "Domain": "domain-args-cogn-7c94", + "Domain": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "UserPoolId": { "Ref": "testlambdaelasticsearchkibana2CognitoUserPool9D73D9DA" } @@ -284,7 +314,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/domain-args-cogn-7c94/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -397,7 +459,38 @@ { "Ref": "AWS::AccountId" }, - ":domain/domain-args-7c94" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + } ] ] } @@ -469,7 +562,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/domain-args-7c94/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -496,7 +621,37 @@ "EnforceHTTPS": true, "TLSSecurityPolicy": "Policy-Min-TLS-1-2-2019-07" }, - "DomainName": "domain-args-7c94", + "DomainName": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "EBSOptions": { "EBSEnabled": true, "VolumeSize": 10 diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-domain-arguments.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-domain-arguments.ts index 3aa9895c5..387e79cb9 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-domain-arguments.ts +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-domain-arguments.ts @@ -15,7 +15,7 @@ import { App, Stack } from "aws-cdk-lib"; import { LambdaToElasticSearchAndKibana } from "../lib"; import * as lambda from 'aws-cdk-lib/aws-lambda'; -import { generateIntegStackName } from '@aws-solutions-constructs/core'; +import { generateIntegStackName, CreateShortUniqueTestName } from '@aws-solutions-constructs/core'; // Setup const app = new App(); @@ -27,13 +27,9 @@ const lambdaProps: lambda.FunctionProps = { handler: 'index.handler' }; -const esDomain = 'domain-args-7c94'; -const cognitoDomain = 'domain-args-cogn-7c94'; - new LambdaToElasticSearchAndKibana(stack, 'test-lambda-elasticsearch-kibana2', { lambdaFunctionProps: lambdaProps, - domainName: esDomain, - cognitoDomainName: cognitoDomain + domainName: CreateShortUniqueTestName("dmn"), }); // Synth diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-no-arguments.expected.json index 12fc8a8e0..ee6c1746a 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-no-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-no-arguments.expected.json @@ -227,7 +227,37 @@ "testlambdaelasticsearchkibanaUserPoolDomainB9BDF063": { "Type": "AWS::Cognito::UserPoolDomain", "Properties": { - "Domain": "domain-args-cogn-23e", + "Domain": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "UserPoolId": { "Ref": "testlambdaelasticsearchkibanaCognitoUserPool9537802B" } @@ -284,7 +314,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/domain-args-cogn-23e/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -397,7 +459,38 @@ { "Ref": "AWS::AccountId" }, - ":domain/domain-args-23e2" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + } ] ] } @@ -469,7 +562,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/domain-args-23e2/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -496,7 +621,37 @@ "EnforceHTTPS": true, "TLSSecurityPolicy": "Policy-Min-TLS-1-2-2019-07" }, - "DomainName": "domain-args-23e2", + "DomainName": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "EBSOptions": { "EBSEnabled": true, "VolumeSize": 10 diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-no-arguments.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-no-arguments.ts index 2a353276f..5a42c1d9b 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-no-arguments.ts +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-elasticsearch-kibana/test/integ.lamels-no-arguments.ts @@ -15,7 +15,7 @@ import { App, Stack } from "aws-cdk-lib"; import { LambdaToElasticSearchAndKibana } from "../lib"; import * as lambda from 'aws-cdk-lib/aws-lambda'; -import { generateIntegStackName } from '@aws-solutions-constructs/core'; +import { generateIntegStackName, CreateShortUniqueTestName } from '@aws-solutions-constructs/core'; // Setup const app = new App(); @@ -27,13 +27,9 @@ const lambdaProps: lambda.FunctionProps = { handler: 'index.handler' }; -const esDomain = 'domain-args-23e2'; -const cognitoDomain = 'domain-args-cogn-23e'; - new LambdaToElasticSearchAndKibana(stack, 'test-lambda-elasticsearch-kibana', { lambdaFunctionProps: lambdaProps, - domainName: esDomain, - cognitoDomainName: cognitoDomain + domainName: CreateShortUniqueTestName("dmn"), }); // Synth diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-cluster-config.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-cluster-config.expected.json index 91c2dcc33..aa0b4e0ed 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-cluster-config.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-cluster-config.expected.json @@ -288,7 +288,37 @@ "testlambdaopensearchUserPoolDomain98864920": { "Type": "AWS::Cognito::UserPoolDomain", "Properties": { - "Domain": "deploytestwithclusterconfig", + "Domain": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "UserPoolId": { "Ref": "testlambdaopensearchCognitoUserPoolA09096F9" } @@ -345,7 +375,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/deploytestwithclusterconfig/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -458,7 +520,38 @@ { "Ref": "AWS::AccountId" }, - ":domain/deploytestwithclusterconfig" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + } ] ] } @@ -530,7 +623,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/deploytestwithclusterconfig/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -566,7 +691,37 @@ "EnforceHTTPS": true, "TLSSecurityPolicy": "Policy-Min-TLS-1-2-2019-07" }, - "DomainName": "deploytestwithclusterconfig", + "DomainName": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "EBSOptions": { "EBSEnabled": true, "VolumeSize": 10 diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-cluster-config.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-cluster-config.ts index 3b3465e9a..8c1d27570 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-cluster-config.ts +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-cluster-config.ts @@ -41,7 +41,7 @@ const openSearchDomainProps = { new LambdaToOpenSearch(stack, 'test-lambda-opensearch', { lambdaFunctionProps: lambdaProps, - openSearchDomainName: "deploytestwithclusterconfig", + openSearchDomainName: defaults.CreateShortUniqueTestName("dmn"), openSearchDomainProps, deployVpc: true, vpcProps: { @@ -50,4 +50,4 @@ new LambdaToOpenSearch(stack, 'test-lambda-opensearch', { }); // Synth -app.synth(); \ No newline at end of file +app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-disabled-zone-awareness.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-disabled-zone-awareness.expected.json index 21a55d6b6..26fa4ff7f 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-disabled-zone-awareness.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-disabled-zone-awareness.expected.json @@ -284,7 +284,37 @@ "testlambdaopensearchUserPoolDomain98864920": { "Type": "AWS::Cognito::UserPoolDomain", "Properties": { - "Domain": "disabledzoneawareness", + "Domain": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "UserPoolId": { "Ref": "testlambdaopensearchCognitoUserPoolA09096F9" } @@ -341,7 +371,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/disabledzoneawareness/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -454,7 +516,38 @@ { "Ref": "AWS::AccountId" }, - ":domain/disabledzoneawareness" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + } ] ] } @@ -526,7 +619,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/disabledzoneawareness/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -559,7 +684,37 @@ "EnforceHTTPS": true, "TLSSecurityPolicy": "Policy-Min-TLS-1-2-2019-07" }, - "DomainName": "disabledzoneawareness", + "DomainName": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "EBSOptions": { "EBSEnabled": true, "VolumeSize": 10 diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-disabled-zone-awareness.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-disabled-zone-awareness.ts index 656d43688..ab8d6e425 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-disabled-zone-awareness.ts +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-disabled-zone-awareness.ts @@ -38,7 +38,7 @@ const openSearchDomainProps = { new LambdaToOpenSearch(stack, 'test-lambda-opensearch', { lambdaFunctionProps: lambdaProps, - openSearchDomainName: "disabledzoneawareness", + openSearchDomainName: defaults.CreateShortUniqueTestName("dmn"), openSearchDomainProps, deployVpc: true, vpcProps: { diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-domain-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-domain-arguments.expected.json index 997380f00..5d980df04 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-domain-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-domain-arguments.expected.json @@ -227,7 +227,37 @@ "testlambdaopensearchUserPoolDomain98864920": { "Type": "AWS::Cognito::UserPoolDomain", "Properties": { - "Domain": "cogn-solutions-constructs-domain", + "Domain": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "UserPoolId": { "Ref": "testlambdaopensearchCognitoUserPoolA09096F9" } @@ -284,7 +314,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/cogn-solutions-constructs-domain/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -397,7 +459,38 @@ { "Ref": "AWS::AccountId" }, - ":domain/solutions-constructs-domain" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + } ] ] } @@ -469,7 +562,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/solutions-constructs-domain/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -505,7 +630,37 @@ "EnforceHTTPS": true, "TLSSecurityPolicy": "Policy-Min-TLS-1-2-2019-07" }, - "DomainName": "solutions-constructs-domain", + "DomainName": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "EBSOptions": { "EBSEnabled": true, "VolumeSize": 10 diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-domain-arguments.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-domain-arguments.ts index f40040eae..62199bb2e 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-domain-arguments.ts +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-domain-arguments.ts @@ -15,7 +15,7 @@ import { App, Stack } from "aws-cdk-lib"; import { LambdaToOpenSearch } from "../lib"; import * as lambda from 'aws-cdk-lib/aws-lambda'; -import { generateIntegStackName } from '@aws-solutions-constructs/core'; +import { generateIntegStackName, CreateShortUniqueTestName } from '@aws-solutions-constructs/core'; // Setup const app = new App(); @@ -27,13 +27,9 @@ const lambdaProps: lambda.FunctionProps = { handler: 'index.handler' }; -const openSearchDomain = 'solutions-constructs-domain'; -const cognitoDomain = 'cogn-solutions-constructs-domain'; - new LambdaToOpenSearch(stack, 'test-lambda-opensearch', { lambdaFunctionProps: lambdaProps, - openSearchDomainName: openSearchDomain, - cognitoDomainName: cognitoDomain + openSearchDomainName: CreateShortUniqueTestName("dmn"), }); // Synth diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-existing-vpc.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-existing-vpc.expected.json index c8c5b4dff..1beace7c3 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-existing-vpc.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-existing-vpc.expected.json @@ -962,7 +962,37 @@ "testlambdaelasticsearchkibana4UserPoolDomain4CAAF2F6": { "Type": "AWS::Cognito::UserPoolDomain", "Properties": { - "Domain": "deploytestwithexistingvpc", + "Domain": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "UserPoolId": { "Ref": "testlambdaelasticsearchkibana4CognitoUserPool37A5CDE1" } @@ -1019,7 +1049,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/deploytestwithexistingvpc/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -1132,7 +1194,38 @@ { "Ref": "AWS::AccountId" }, - ":domain/deploytestwithexistingvpc" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + } ] ] } @@ -1204,7 +1297,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/deploytestwithexistingvpc/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -1240,7 +1365,37 @@ "EnforceHTTPS": true, "TLSSecurityPolicy": "Policy-Min-TLS-1-2-2019-07" }, - "DomainName": "deploytestwithexistingvpc", + "DomainName": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "EBSOptions": { "EBSEnabled": true, "VolumeSize": 10 diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-existing-vpc.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-existing-vpc.ts index e7ca370d0..98e0af06a 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-existing-vpc.ts +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-existing-vpc.ts @@ -36,7 +36,7 @@ const lambdaProps: lambda.FunctionProps = { new LambdaToOpenSearch(stack, 'test-lambda-elasticsearch-kibana4', { lambdaFunctionProps: lambdaProps, - openSearchDomainName: "deploytestwithexistingvpc", + openSearchDomainName: defaults.CreateShortUniqueTestName("dmn"), existingVpc: vpc }); diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-no-arguments.expected.json index 43b70f2ed..8fa049bd5 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-no-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-no-arguments.expected.json @@ -227,7 +227,37 @@ "testlambdaopensearchUserPoolDomain98864920": { "Type": "AWS::Cognito::UserPoolDomain", "Properties": { - "Domain": "lamopn-no-args", + "Domain": { + "Fn::Join": [ + "-", + [ + "dn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "UserPoolId": { "Ref": "testlambdaopensearchCognitoUserPoolA09096F9" } @@ -284,7 +314,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/lamopn-no-args/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -397,7 +459,38 @@ { "Ref": "AWS::AccountId" }, - ":domain/lamopn-no-args" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + } ] ] } @@ -469,7 +562,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/lamopn-no-args/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -505,7 +630,37 @@ "EnforceHTTPS": true, "TLSSecurityPolicy": "Policy-Min-TLS-1-2-2019-07" }, - "DomainName": "lamopn-no-args", + "DomainName": { + "Fn::Join": [ + "-", + [ + "dn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "EBSOptions": { "EBSEnabled": true, "VolumeSize": 10 diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-no-arguments.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-no-arguments.ts index af1e5450d..dacf711b2 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-no-arguments.ts +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-no-arguments.ts @@ -15,7 +15,7 @@ import { App, Stack } from "aws-cdk-lib"; import { LambdaToOpenSearch } from "../lib"; import * as lambda from 'aws-cdk-lib/aws-lambda'; -import { generateIntegStackName } from '@aws-solutions-constructs/core'; +import { generateIntegStackName, CreateShortUniqueTestName } from '@aws-solutions-constructs/core'; // Setup const app = new App(); @@ -27,11 +27,9 @@ const lambdaProps: lambda.FunctionProps = { handler: 'index.handler' }; -const openSearchDomain = 'lamopn-no-args'; - new LambdaToOpenSearch(stack, 'test-lambda-opensearch', { lambdaFunctionProps: lambdaProps, - openSearchDomainName: openSearchDomain, + openSearchDomainName: CreateShortUniqueTestName("dn"), }); // Synth diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-vpc-props.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-vpc-props.expected.json index e0178d93b..d4a9fec3d 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-vpc-props.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-vpc-props.expected.json @@ -290,7 +290,37 @@ "testlambdaopensearchUserPoolDomain98864920": { "Type": "AWS::Cognito::UserPoolDomain", "Properties": { - "Domain": "deploytestwithvpcprops", + "Domain": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "UserPoolId": { "Ref": "testlambdaopensearchCognitoUserPoolA09096F9" } @@ -347,7 +377,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/deploytestwithvpcprops/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -460,7 +522,38 @@ { "Ref": "AWS::AccountId" }, - ":domain/deploytestwithvpcprops" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + } ] ] } @@ -532,7 +625,39 @@ { "Ref": "AWS::AccountId" }, - ":domain/deploytestwithvpcprops/*" + ":domain/", + { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, + "/*" ] ] } @@ -568,7 +693,37 @@ "EnforceHTTPS": true, "TLSSecurityPolicy": "Policy-Min-TLS-1-2-2019-07" }, - "DomainName": "deploytestwithvpcprops", + "DomainName": { + "Fn::Join": [ + "-", + [ + "dmn", + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + }, "EBSOptions": { "EBSEnabled": true, "VolumeSize": 10 diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-vpc-props.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-vpc-props.ts index 5f3e4a932..cd4213d7a 100644 --- a/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-vpc-props.ts +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-opensearch/test/integ.lamopn-vpc-props.ts @@ -34,7 +34,7 @@ const lambdaProps: lambda.FunctionProps = { new LambdaToOpenSearch(stack, 'test-lambda-opensearch', { lambdaFunctionProps: lambdaProps, - openSearchDomainName: "deploytestwithvpcprops", + openSearchDomainName: defaults.CreateShortUniqueTestName("dmn"), deployVpc: true, vpcProps: { ipAddresses: ec2.IpAddresses.cidr('172.168.0.0/16'), diff --git a/source/patterns/@aws-solutions-constructs/core/test/test-helper.ts b/source/patterns/@aws-solutions-constructs/core/test/test-helper.ts index 08a78969e..4ed5c4cd5 100644 --- a/source/patterns/@aws-solutions-constructs/core/test/test-helper.ts +++ b/source/patterns/@aws-solutions-constructs/core/test/test-helper.ts @@ -14,7 +14,7 @@ // Imports import { Construct, IConstruct } from 'constructs'; import { Bucket, BucketProps, BucketEncryption } from "aws-cdk-lib/aws-s3"; -import { CfnResource, RemovalPolicy, Stack, Aspects, IAspect } from "aws-cdk-lib"; +import { CfnResource, RemovalPolicy, Stack, Aspects, IAspect, Aws, Fn } from "aws-cdk-lib"; import { buildVpc } from '../lib/vpc-helper'; import { DefaultPublicPrivateVpcProps, DefaultIsolatedVpcProps } from '../lib/vpc-defaults'; import { overrideProps, addCfnSuppressRules } from "../lib/utils"; @@ -251,6 +251,16 @@ export function CreateTestApi(stack: Stack, id: string): api.LambdaRestApi { return restApi; } +// Create a short, unique to this stack name +// technically this is not 100% OK, as it only uses a portion of the +// stack guid - but it's for tests only so if the last segment of 2 stack guids collide someday +// (VERY unlikely), just running again should take care of it. +export function CreateShortUniqueTestName(stub: string) { + const stackGuid = Fn.select(2, Fn.split('/', `${Aws.STACK_ID}`)); + const guidPortion = Fn.select(4, Fn.split('-', stackGuid)); + return Fn.join("-", [stub, guidPortion]); +} + /** * Used to suppress cfn nag W58, W89, and W92 rules on lambda integration test resources. * From 51ec028ebd4763965671483e74924e3b8e328337 Mon Sep 17 00:00:00 2001 From: biffgaut <78155736+biffgaut@users.noreply.github.com> Date: Wed, 13 Dec 2023 09:43:18 -0500 Subject: [PATCH 05/14] feat(cloudfront constructs): add s3 access logging to cloudfront access log buckets by default (#1042) * Add S3 Access Logging to CloudFront log buckets * Suppress cfn_nag warning * Default to all constructs --- .../v2/refresh-multiple-tests.sh | 149 ++++++------- .../lib/index.ts | 4 + ...ustomCloudfrontLoggingBucket.expected.json | 192 ++++++++++++++++- .../integ.cftaplam-no-arguments.expected.json | 192 ++++++++++++++++- ...g.cftaplam-override-behavior.expected.json | 192 ++++++++++++++++- .../aws-cloudfront-apigateway/lib/index.ts | 5 + ...ustomCloudfrontLoggingBucket.expected.json | 192 ++++++++++++++++- .../integ.cftapi-no-arguments.expected.json | 192 ++++++++++++++++- .../aws-cloudfront-mediastore/lib/index.ts | 5 + ...ustomCloudFrontLoggingBucket.expected.json | 192 ++++++++++++++++- .../test/integ.cftmed-default.expected.json | 192 ++++++++++++++++- ...teg.cftmed-existingContainer.expected.json | 192 ++++++++++++++++- ...eg.cftmed-overrideProperties.expected.json | 192 ++++++++++++++++- ...d-withSecurityHeaderBehavior.expected.json | 126 ++++++++++- ...d-withoutHttpSecurityHeaders.expected.json | 192 ++++++++++++++++- .../integ.cfts3-custom-headers.expected.json | 190 +++++++++++++++- ...nteg.cfts3-custom-originPath.expected.json | 190 +++++++++++++++- ...g.cfts3-customLoggingBuckets.expected.json | 203 +++++++++++++++++- .../integ.cfts3-existing-bucket.expected.json | 190 +++++++++++++++- .../integ.cfts3-no-arguments.expected.json | 190 +++++++++++++++- ...eg.cfts3-no-security-headers.expected.json | 190 +++++++++++++++- .../test/test.cloudfront-s3.test.ts | 6 +- .../aws-wafwebacl-cloudfront/lib/index.ts | 4 + .../lib/cloudfront-distribution-helper.ts | 4 +- .../core/lib/s3-bucket-helper.ts | 51 ++++- .../cloudfront-distribution-s3-helper.test.ts | 19 -- 26 files changed, 3277 insertions(+), 169 deletions(-) rename refresh-multiple-tests.sh => deployment/v2/refresh-multiple-tests.sh (56%) diff --git a/refresh-multiple-tests.sh b/deployment/v2/refresh-multiple-tests.sh similarity index 56% rename from refresh-multiple-tests.sh rename to deployment/v2/refresh-multiple-tests.sh index afd8b0e63..3527c98fc 100755 --- a/refresh-multiple-tests.sh +++ b/deployment/v2/refresh-multiple-tests.sh @@ -19,82 +19,83 @@ # a sleep 10 command before the end of the loop to keep from overwhelming CloudFormation export constructs=" - aws-alb-fargate - aws-alb-lambda - aws-apigateway-dynamodb - aws-apigateway-iot - aws-apigateway-kinesisstreams - aws-apigateway-lambda - aws-apigateway-sagemakerendpoint - aws-apigateway-sqs - aws-cloudfront-apigateway - aws-cloudfront-apigateway-lambda - aws-cloudfront-mediastore - aws-cloudfront-s3 - aws-cognito-apigateway-lambda - aws-dynamodbstreams-lambda - aws-dynamodbstreams-lambda-elasticsearch-kibana - aws-eventbridge-kinesisfirehose-s3 - aws-eventbridge-kinesisstreams - aws-eventbridge-lambda - aws-eventbridge-sns - aws-eventbridge-sqs - aws-eventbridge-stepfunctions - aws-fargate-dynamodb - aws-fargate-eventbridge - aws-fargate-kinesisfirehose - aws-fargate-kinesisstreams - aws-fargate-opensearch - aws-fargate-s3 - aws-fargate-secretsmanager - aws-fargate-sns - aws-fargate-sqs - aws-fargate-ssmstringparameter - aws-fargate-stepfunctions - aws-iot-kinesisfirehose-s3 - aws-iot-kinesisstreams - aws-iot-lambda - aws-iot-lambda-dynamodb - aws-iot-s3 - aws-iot-sqs - aws-kinesisfirehose-s3 - aws-kinesisstreams-gluejob - aws-kinesisstreams-kinesisfirehose-s3 - aws-kinesisstreams-lambda - aws-lambda-dynamodb - aws-lambda-elasticachememcached - aws-lambda-elasticsearch-kibana - aws-lambda-eventbridge - aws-lambda-kendra - aws-lambda-kinesisfirehose - aws-lambda-kinesisstreams - aws-lambda-opensearch - aws-lambda-s3 - aws-lambda-sagemakerendpoint - aws-lambda-secretsmanager - aws-lambda-sns - aws-lambda-sqs - aws-lambda-sqs-lambda - aws-lambda-ssmstringparameter - aws-lambda-stepfunctions - aws-openapigateway-lambda - aws-route53-alb - aws-route53-apigateway - aws-s3-lambda - aws-s3-sns - aws-s3-sqs - aws-s3-stepfunctions - aws-sns-lambda - aws-sns-sqs - aws-sqs-lambda - aws-wafwebacl-alb - aws-wafwebacl-apigateway - aws-wafwebacl-appsync - aws-wafwebacl-cloudfront +aws-alb-fargate +aws-alb-lambda +aws-apigateway-dynamodb +aws-apigateway-iot +aws-apigateway-kinesisstreams +aws-apigateway-lambda +aws-apigateway-sagemakerendpoint +aws-apigateway-sqs +aws-cloudfront-apigateway +aws-cloudfront-apigateway-lambda +aws-cloudfront-mediastore +aws-cloudfront-s3 +aws-cognito-apigateway-lambda +aws-dynamodbstreams-lambda +aws-dynamodbstreams-lambda-elasticsearch-kibana +aws-eventbridge-kinesisfirehose-s3 +aws-eventbridge-kinesisstreams +aws-eventbridge-lambda +aws-eventbridge-sns +aws-eventbridge-sqs +aws-eventbridge-stepfunctions +aws-fargate-dynamodb +aws-fargate-eventbridge +aws-fargate-kinesisfirehose +aws-fargate-kinesisstreams +aws-fargate-opensearch +aws-fargate-s3 +aws-fargate-secretsmanager +aws-fargate-sns +aws-fargate-sqs +aws-fargate-ssmstringparameter +aws-fargate-stepfunctions +aws-iot-kinesisfirehose-s3 +aws-iot-kinesisstreams +aws-iot-lambda +aws-iot-lambda-dynamodb +aws-iot-s3 +aws-iot-sqs +aws-kinesisfirehose-s3 +aws-kinesisstreams-gluejob +aws-kinesisstreams-kinesisfirehose-s3 +aws-kinesisstreams-lambda +aws-lambda-dynamodb +aws-lambda-elasticachememcached +aws-lambda-elasticsearch-kibana +aws-lambda-eventbridge +aws-lambda-kendra +aws-lambda-kinesisfirehose +aws-lambda-kinesisstreams +aws-lambda-opensearch +aws-lambda-s3 +aws-lambda-sagemakerendpoint +aws-lambda-secretsmanager +aws-lambda-sns +aws-lambda-sqs +aws-lambda-sqs-lambda +aws-lambda-ssmstringparameter +aws-lambda-stepfunctions +aws-openapigateway-lambda +aws-route53-alb +aws-route53-apigateway +aws-s3-lambda +aws-s3-sns +aws-s3-sqs +aws-s3-stepfunctions +aws-sns-lambda +aws-sns-sqs +aws-sqs-lambda +aws-wafwebacl-alb +aws-wafwebacl-apigateway +aws-wafwebacl-appsync +aws-wafwebacl-cloudfront " -constructs_root_dir=$(cd $(dirname $0) && pwd) -source_dir="$constructs_root_dir/source" +deployment_dir=$(cd $(dirname $0) && pwd) +constructs_root_dir="$deployment_dir/../.." +source_dir="$deployment_dir/../../source" echo "=============================================================================================" echo "aligning versions and updating package.json for CDK v2..." diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/lib/index.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/lib/index.ts index 6ef986a5a..0901aa0cd 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/lib/index.ts +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/lib/index.ts @@ -107,6 +107,10 @@ export class CloudFrontToApiGatewayToLambda extends Construct { defaults.CheckLambdaProps(props); // CheckCloudFrontProps() is called by internal aws-cloudfront-apigateway construct + // All our tests are based upon this behavior being on, so we're setting + // context here rather than assuming the client will set it + this.node.setContext("@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy", true); + this.lambdaFunction = defaults.buildLambdaFunction(this, { existingLambdaObj: props.existingLambdaObj, lambdaFunctionProps: props.lambdaFunctionProps diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-customCloudfrontLoggingBucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-customCloudfrontLoggingBucket.expected.json index 487b9c752..6424d959a 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-customCloudfrontLoggingBucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-customCloudfrontLoggingBucket.expected.json @@ -598,10 +598,9 @@ "Name": "SetHttpSecurityHeadersc860b559e82562b55d86431c32566a0eb839407df7" } }, - "cfapigwlambdaCloudFrontToApiGatewayCloudfrontLoggingBucket2E8E3DC2": { + "cfapigwlambdaCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog48BE423A": { "Type": "AWS::S3::Bucket", "Properties": { - "AccessControl": "LogDeliveryWrite", "BucketEncryption": { "ServerSideEncryptionConfiguration": [ { @@ -641,12 +640,197 @@ "rules_to_suppress": [ { "id": "W35", - "reason": "This S3 bucket is used as the access logging bucket for CloudFront Distribution" + "reason": "This S3 bucket is used as the access logging bucket for another bucket" } ] } } }, + "cfapigwlambdaCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLogPolicyC05E1C71": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "cfapigwlambdaCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog48BE423A" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "cfapigwlambdaCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog48BE423A", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "cfapigwlambdaCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog48BE423A", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "cfapigwlambdaCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog48BE423A", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "cfapigwlambdaCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog48BE423A", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "cfapigwlambdaCloudFrontToApiGatewayCloudfrontLoggingBucket2E8E3DC2", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "cfapigwlambdaCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog48BE423A", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "cfapigwlambdaCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLogAutoDeleteObjectsCustomResource296BC002": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "cfapigwlambdaCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog48BE423A" + } + }, + "DependsOn": [ + "cfapigwlambdaCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLogPolicyC05E1C71" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "cfapigwlambdaCloudFrontToApiGatewayCloudfrontLoggingBucket2E8E3DC2": { + "Type": "AWS::S3::Bucket", + "Properties": { + "AccessControl": "LogDeliveryWrite", + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "cfapigwlambdaCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog48BE423A" + } + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "cfapigwlambdaCloudFrontToApiGatewayCloudfrontLoggingBucketPolicy416A95E3": { "Type": "AWS::S3::BucketPolicy", "Properties": { @@ -910,7 +1094,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "cfapigwlambdaCloudFrontToApiGatewayCloudfrontLoggingBucket2E8E3DC2" + "Ref": "cfapigwlambdaCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog48BE423A" }, " S3 bucket." ] diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-no-arguments.expected.json index 3fe8ae626..e13891b09 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-no-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-no-arguments.expected.json @@ -598,10 +598,9 @@ "Name": "SetHttpSecurityHeadersc87d9e55c0a6a55f893f95e9a700c7ce19634229d0" } }, - "testcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudfrontLoggingBucket7F467421": { + "testcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog347EED57": { "Type": "AWS::S3::Bucket", "Properties": { - "AccessControl": "LogDeliveryWrite", "BucketEncryption": { "ServerSideEncryptionConfiguration": [ { @@ -641,12 +640,197 @@ "rules_to_suppress": [ { "id": "W35", - "reason": "This S3 bucket is used as the access logging bucket for CloudFront Distribution" + "reason": "This S3 bucket is used as the access logging bucket for another bucket" } ] } } }, + "testcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLogPolicy521355D8": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog347EED57" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog347EED57", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog347EED57", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog347EED57", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog347EED57", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "testcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudfrontLoggingBucket7F467421", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog347EED57", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLogAutoDeleteObjectsCustomResource2395E2A2": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "testcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog347EED57" + } + }, + "DependsOn": [ + "testcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLogPolicy521355D8" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "testcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudfrontLoggingBucket7F467421": { + "Type": "AWS::S3::Bucket", + "Properties": { + "AccessControl": "LogDeliveryWrite", + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "testcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog347EED57" + } + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "testcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudfrontLoggingBucketPolicy4A551B79": { "Type": "AWS::S3::BucketPolicy", "Properties": { @@ -910,7 +1094,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "testcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudfrontLoggingBucket7F467421" + "Ref": "testcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog347EED57" }, " S3 bucket." ] diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-override-behavior.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-override-behavior.expected.json index d5398b174..270e52188 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-override-behavior.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-override-behavior.expected.json @@ -559,10 +559,9 @@ "Name": "SetHttpSecurityHeadersc826c2a6a3ffe209aed33765f37752084820de0d3b" } }, - "cfapilambdaoverrideCloudFrontToApiGatewayCloudfrontLoggingBucket3A71B9E0": { + "cfapilambdaoverrideCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog9CEB5CD9": { "Type": "AWS::S3::Bucket", "Properties": { - "AccessControl": "LogDeliveryWrite", "BucketEncryption": { "ServerSideEncryptionConfiguration": [ { @@ -602,12 +601,197 @@ "rules_to_suppress": [ { "id": "W35", - "reason": "This S3 bucket is used as the access logging bucket for CloudFront Distribution" + "reason": "This S3 bucket is used as the access logging bucket for another bucket" } ] } } }, + "cfapilambdaoverrideCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLogPolicy53DB42E0": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "cfapilambdaoverrideCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog9CEB5CD9" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "cfapilambdaoverrideCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog9CEB5CD9", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "cfapilambdaoverrideCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog9CEB5CD9", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "cfapilambdaoverrideCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog9CEB5CD9", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "cfapilambdaoverrideCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog9CEB5CD9", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "cfapilambdaoverrideCloudFrontToApiGatewayCloudfrontLoggingBucket3A71B9E0", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "cfapilambdaoverrideCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog9CEB5CD9", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "cfapilambdaoverrideCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLogAutoDeleteObjectsCustomResource33279C95": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "cfapilambdaoverrideCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog9CEB5CD9" + } + }, + "DependsOn": [ + "cfapilambdaoverrideCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLogPolicy53DB42E0" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "cfapilambdaoverrideCloudFrontToApiGatewayCloudfrontLoggingBucket3A71B9E0": { + "Type": "AWS::S3::Bucket", + "Properties": { + "AccessControl": "LogDeliveryWrite", + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "cfapilambdaoverrideCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog9CEB5CD9" + } + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "cfapilambdaoverrideCloudFrontToApiGatewayCloudfrontLoggingBucketPolicyC3092436": { "Type": "AWS::S3::BucketPolicy", "Properties": { @@ -949,7 +1133,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "cfapilambdaoverrideCloudFrontToApiGatewayCloudfrontLoggingBucket3A71B9E0" + "Ref": "cfapilambdaoverrideCloudFrontToApiGatewayCloudfrontLoggingBucketAccessLog9CEB5CD9" }, " S3 bucket." ] diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/lib/index.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/lib/index.ts index 2114518b1..9588e3350 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/lib/index.ts +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/lib/index.ts @@ -79,6 +79,11 @@ export class CloudFrontToApiGateway extends Construct { */ constructor(scope: Construct, id: string, props: CloudFrontToApiGatewayProps) { super(scope, id); + + // All our tests are based upon this behavior being on, so we're setting + // context here rather than assuming the client will set it + this.node.setContext("@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy", true); + defaults.CheckCloudFrontProps(props); this.apiGateway = props.existingApiGatewayObj; diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-customCloudfrontLoggingBucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-customCloudfrontLoggingBucket.expected.json index 2b77bbd2e..410729c9b 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-customCloudfrontLoggingBucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-customCloudfrontLoggingBucket.expected.json @@ -643,10 +643,9 @@ "Name": "SetHttpSecurityHeadersc8cc607f355edae7717ef60e6468962d623a2d5ea9" } }, - "cfapigwCloudfrontLoggingBucket79FE4195": { + "cfapigwCloudfrontLoggingBucketAccessLog6DDE9754": { "Type": "AWS::S3::Bucket", "Properties": { - "AccessControl": "LogDeliveryWrite", "BucketEncryption": { "ServerSideEncryptionConfiguration": [ { @@ -686,12 +685,197 @@ "rules_to_suppress": [ { "id": "W35", - "reason": "This S3 bucket is used as the access logging bucket for CloudFront Distribution" + "reason": "This S3 bucket is used as the access logging bucket for another bucket" } ] } } }, + "cfapigwCloudfrontLoggingBucketAccessLogPolicyDB63EA7B": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "cfapigwCloudfrontLoggingBucketAccessLog6DDE9754" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "cfapigwCloudfrontLoggingBucketAccessLog6DDE9754", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "cfapigwCloudfrontLoggingBucketAccessLog6DDE9754", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "cfapigwCloudfrontLoggingBucketAccessLog6DDE9754", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "cfapigwCloudfrontLoggingBucketAccessLog6DDE9754", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "cfapigwCloudfrontLoggingBucket79FE4195", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "cfapigwCloudfrontLoggingBucketAccessLog6DDE9754", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "cfapigwCloudfrontLoggingBucketAccessLogAutoDeleteObjectsCustomResource861BCB32": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "cfapigwCloudfrontLoggingBucketAccessLog6DDE9754" + } + }, + "DependsOn": [ + "cfapigwCloudfrontLoggingBucketAccessLogPolicyDB63EA7B" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "cfapigwCloudfrontLoggingBucket79FE4195": { + "Type": "AWS::S3::Bucket", + "Properties": { + "AccessControl": "LogDeliveryWrite", + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "cfapigwCloudfrontLoggingBucketAccessLog6DDE9754" + } + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "cfapigwCloudfrontLoggingBucketPolicyF5181F4F": { "Type": "AWS::S3::BucketPolicy", "Properties": { @@ -955,7 +1139,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "cfapigwCloudfrontLoggingBucket79FE4195" + "Ref": "cfapigwCloudfrontLoggingBucketAccessLog6DDE9754" }, " S3 bucket." ] diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-no-arguments.expected.json index fe72b7872..8805504b5 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-no-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-no-arguments.expected.json @@ -643,10 +643,9 @@ "Name": "SetHttpSecurityHeadersc8b8093f33c7dec7c3b269919bb775882671d92f95" } }, - "testcloudfrontapigatewayCloudfrontLoggingBucket9811F6E8": { + "testcloudfrontapigatewayCloudfrontLoggingBucketAccessLog4D9DC9FB": { "Type": "AWS::S3::Bucket", "Properties": { - "AccessControl": "LogDeliveryWrite", "BucketEncryption": { "ServerSideEncryptionConfiguration": [ { @@ -686,12 +685,197 @@ "rules_to_suppress": [ { "id": "W35", - "reason": "This S3 bucket is used as the access logging bucket for CloudFront Distribution" + "reason": "This S3 bucket is used as the access logging bucket for another bucket" } ] } } }, + "testcloudfrontapigatewayCloudfrontLoggingBucketAccessLogPolicy1110B389": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfrontapigatewayCloudfrontLoggingBucketAccessLog4D9DC9FB" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfrontapigatewayCloudfrontLoggingBucketAccessLog4D9DC9FB", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfrontapigatewayCloudfrontLoggingBucketAccessLog4D9DC9FB", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfrontapigatewayCloudfrontLoggingBucketAccessLog4D9DC9FB", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfrontapigatewayCloudfrontLoggingBucketAccessLog4D9DC9FB", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "testcloudfrontapigatewayCloudfrontLoggingBucket9811F6E8", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfrontapigatewayCloudfrontLoggingBucketAccessLog4D9DC9FB", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfrontapigatewayCloudfrontLoggingBucketAccessLogAutoDeleteObjectsCustomResourceCAC0A05B": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "testcloudfrontapigatewayCloudfrontLoggingBucketAccessLog4D9DC9FB" + } + }, + "DependsOn": [ + "testcloudfrontapigatewayCloudfrontLoggingBucketAccessLogPolicy1110B389" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "testcloudfrontapigatewayCloudfrontLoggingBucket9811F6E8": { + "Type": "AWS::S3::Bucket", + "Properties": { + "AccessControl": "LogDeliveryWrite", + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "testcloudfrontapigatewayCloudfrontLoggingBucketAccessLog4D9DC9FB" + } + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "testcloudfrontapigatewayCloudfrontLoggingBucketPolicyAA14EB71": { "Type": "AWS::S3::BucketPolicy", "Properties": { @@ -955,7 +1139,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "testcloudfrontapigatewayCloudfrontLoggingBucket9811F6E8" + "Ref": "testcloudfrontapigatewayCloudfrontLoggingBucketAccessLog4D9DC9FB" }, " S3 bucket." ] diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/lib/index.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/lib/index.ts index f8110107f..21fed10c9 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/lib/index.ts +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/lib/index.ts @@ -87,6 +87,11 @@ export class CloudFrontToMediaStore extends Construct { */ constructor(scope: Construct, id: string, props: CloudFrontToMediaStoreProps) { super(scope, id); + + // All our tests are based upon this behavior being on, so we're setting + // context here rather than assuming the client will set it + this.node.setContext("@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy", true); + defaults.CheckMediaStoreProps(props); defaults.CheckCloudFrontProps(props); diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/test/integ.cftmed-customCloudFrontLoggingBucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/test/integ.cftmed-customCloudFrontLoggingBucket.expected.json index f6efa9c26..e3ed8e321 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/test/integ.cftmed-customCloudFrontLoggingBucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/test/integ.cftmed-customCloudFrontLoggingBucket.expected.json @@ -82,10 +82,9 @@ }, "DeletionPolicy": "Retain" }, - "cloudfrontmediastoreCloudfrontLoggingBucketE54A8D50": { + "cloudfrontmediastoreCloudfrontLoggingBucketAccessLogB829BA56": { "Type": "AWS::S3::Bucket", "Properties": { - "AccessControl": "LogDeliveryWrite", "BucketEncryption": { "ServerSideEncryptionConfiguration": [ { @@ -125,12 +124,197 @@ "rules_to_suppress": [ { "id": "W35", - "reason": "This S3 bucket is used as the access logging bucket for CloudFront Distribution" + "reason": "This S3 bucket is used as the access logging bucket for another bucket" } ] } } }, + "cloudfrontmediastoreCloudfrontLoggingBucketAccessLogPolicyB512EE2A": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "cloudfrontmediastoreCloudfrontLoggingBucketAccessLogB829BA56" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "cloudfrontmediastoreCloudfrontLoggingBucketAccessLogB829BA56", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "cloudfrontmediastoreCloudfrontLoggingBucketAccessLogB829BA56", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "cloudfrontmediastoreCloudfrontLoggingBucketAccessLogB829BA56", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "cloudfrontmediastoreCloudfrontLoggingBucketAccessLogB829BA56", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "cloudfrontmediastoreCloudfrontLoggingBucketE54A8D50", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "cloudfrontmediastoreCloudfrontLoggingBucketAccessLogB829BA56", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "cloudfrontmediastoreCloudfrontLoggingBucketAccessLogAutoDeleteObjectsCustomResource5D7907AB": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "cloudfrontmediastoreCloudfrontLoggingBucketAccessLogB829BA56" + } + }, + "DependsOn": [ + "cloudfrontmediastoreCloudfrontLoggingBucketAccessLogPolicyB512EE2A" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "cloudfrontmediastoreCloudfrontLoggingBucketE54A8D50": { + "Type": "AWS::S3::Bucket", + "Properties": { + "AccessControl": "LogDeliveryWrite", + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "cloudfrontmediastoreCloudfrontLoggingBucketAccessLogB829BA56" + } + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "cloudfrontmediastoreCloudfrontLoggingBucketPolicyBB2766C9": { "Type": "AWS::S3::BucketPolicy", "Properties": { @@ -436,7 +620,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "cloudfrontmediastoreCloudfrontLoggingBucketE54A8D50" + "Ref": "cloudfrontmediastoreCloudfrontLoggingBucketAccessLogB829BA56" }, " S3 bucket." ] diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/test/integ.cftmed-default.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/test/integ.cftmed-default.expected.json index 293b2d1e4..d6b6bea4b 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/test/integ.cftmed-default.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/test/integ.cftmed-default.expected.json @@ -82,10 +82,9 @@ }, "DeletionPolicy": "Retain" }, - "testcloudfrontmediastoreCloudfrontLoggingBucketA3A51E6A": { + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116": { "Type": "AWS::S3::Bucket", "Properties": { - "AccessControl": "LogDeliveryWrite", "BucketEncryption": { "ServerSideEncryptionConfiguration": [ { @@ -125,12 +124,197 @@ "rules_to_suppress": [ { "id": "W35", - "reason": "This S3 bucket is used as the access logging bucket for CloudFront Distribution" + "reason": "This S3 bucket is used as the access logging bucket for another bucket" } ] } } }, + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLogPolicy7B05AE89": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketA3A51E6A", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLogAutoDeleteObjectsCustomResourceAE9C7ABE": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116" + } + }, + "DependsOn": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLogPolicy7B05AE89" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "testcloudfrontmediastoreCloudfrontLoggingBucketA3A51E6A": { + "Type": "AWS::S3::Bucket", + "Properties": { + "AccessControl": "LogDeliveryWrite", + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116" + } + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "testcloudfrontmediastoreCloudfrontLoggingBucketPolicyF3B44DFD": { "Type": "AWS::S3::BucketPolicy", "Properties": { @@ -436,7 +620,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "testcloudfrontmediastoreCloudfrontLoggingBucketA3A51E6A" + "Ref": "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116" }, " S3 bucket." ] diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/test/integ.cftmed-existingContainer.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/test/integ.cftmed-existingContainer.expected.json index f696b89fb..a6fe70aea 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/test/integ.cftmed-existingContainer.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/test/integ.cftmed-existingContainer.expected.json @@ -7,10 +7,9 @@ "ContainerName": "MyExistingMediaStoreContainer" } }, - "testcloudfrontmediastoreCloudfrontLoggingBucketA3A51E6A": { + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116": { "Type": "AWS::S3::Bucket", "Properties": { - "AccessControl": "LogDeliveryWrite", "BucketEncryption": { "ServerSideEncryptionConfiguration": [ { @@ -50,12 +49,197 @@ "rules_to_suppress": [ { "id": "W35", - "reason": "This S3 bucket is used as the access logging bucket for CloudFront Distribution" + "reason": "This S3 bucket is used as the access logging bucket for another bucket" } ] } } }, + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLogPolicy7B05AE89": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketA3A51E6A", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLogAutoDeleteObjectsCustomResourceAE9C7ABE": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116" + } + }, + "DependsOn": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLogPolicy7B05AE89" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "testcloudfrontmediastoreCloudfrontLoggingBucketA3A51E6A": { + "Type": "AWS::S3::Bucket", + "Properties": { + "AccessControl": "LogDeliveryWrite", + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116" + } + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "testcloudfrontmediastoreCloudfrontLoggingBucketPolicyF3B44DFD": { "Type": "AWS::S3::BucketPolicy", "Properties": { @@ -353,7 +537,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "testcloudfrontmediastoreCloudfrontLoggingBucketA3A51E6A" + "Ref": "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116" }, " S3 bucket." ] diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/test/integ.cftmed-overrideProperties.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/test/integ.cftmed-overrideProperties.expected.json index b94760369..524fa87ce 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/test/integ.cftmed-overrideProperties.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/test/integ.cftmed-overrideProperties.expected.json @@ -46,10 +46,9 @@ }, "DeletionPolicy": "Retain" }, - "testcloudfrontmediastoreCloudfrontLoggingBucketA3A51E6A": { + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116": { "Type": "AWS::S3::Bucket", "Properties": { - "AccessControl": "LogDeliveryWrite", "BucketEncryption": { "ServerSideEncryptionConfiguration": [ { @@ -89,12 +88,197 @@ "rules_to_suppress": [ { "id": "W35", - "reason": "This S3 bucket is used as the access logging bucket for CloudFront Distribution" + "reason": "This S3 bucket is used as the access logging bucket for another bucket" } ] } } }, + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLogPolicy7B05AE89": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketA3A51E6A", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLogAutoDeleteObjectsCustomResourceAE9C7ABE": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116" + } + }, + "DependsOn": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLogPolicy7B05AE89" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "testcloudfrontmediastoreCloudfrontLoggingBucketA3A51E6A": { + "Type": "AWS::S3::Bucket", + "Properties": { + "AccessControl": "LogDeliveryWrite", + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116" + } + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "testcloudfrontmediastoreCloudfrontLoggingBucketPolicyF3B44DFD": { "Type": "AWS::S3::BucketPolicy", "Properties": { @@ -395,7 +579,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "testcloudfrontmediastoreCloudfrontLoggingBucketA3A51E6A" + "Ref": "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116" }, " S3 bucket." ] diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/test/integ.cftmed-withSecurityHeaderBehavior.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/test/integ.cftmed-withSecurityHeaderBehavior.expected.json index b587f4fbc..cf13ea83d 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/test/integ.cftmed-withSecurityHeaderBehavior.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/test/integ.cftmed-withSecurityHeaderBehavior.expected.json @@ -82,10 +82,9 @@ }, "DeletionPolicy": "Retain" }, - "testcloudfrontmediastoreCloudfrontLoggingBucketA3A51E6A": { + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116": { "Type": "AWS::S3::Bucket", "Properties": { - "AccessControl": "LogDeliveryWrite", "BucketEncryption": { "ServerSideEncryptionConfiguration": [ { @@ -119,12 +118,133 @@ "rules_to_suppress": [ { "id": "W35", - "reason": "This S3 bucket is used as the access logging bucket for CloudFront Distribution" + "reason": "This S3 bucket is used as the access logging bucket for another bucket" } ] } } }, + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLogPolicy7B05AE89": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketA3A51E6A", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfrontmediastoreCloudfrontLoggingBucketA3A51E6A": { + "Type": "AWS::S3::Bucket", + "Properties": { + "AccessControl": "LogDeliveryWrite", + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116" + } + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, "testcloudfrontmediastoreCloudfrontLoggingBucketPolicyF3B44DFD": { "Type": "AWS::S3::BucketPolicy", "Properties": { diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/test/integ.cftmed-withoutHttpSecurityHeaders.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/test/integ.cftmed-withoutHttpSecurityHeaders.expected.json index 12191e2fe..c8a78b70a 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/test/integ.cftmed-withoutHttpSecurityHeaders.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-mediastore/test/integ.cftmed-withoutHttpSecurityHeaders.expected.json @@ -82,10 +82,9 @@ }, "DeletionPolicy": "Retain" }, - "testcloudfrontmediastoreCloudfrontLoggingBucketA3A51E6A": { + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116": { "Type": "AWS::S3::Bucket", "Properties": { - "AccessControl": "LogDeliveryWrite", "BucketEncryption": { "ServerSideEncryptionConfiguration": [ { @@ -125,12 +124,197 @@ "rules_to_suppress": [ { "id": "W35", - "reason": "This S3 bucket is used as the access logging bucket for CloudFront Distribution" + "reason": "This S3 bucket is used as the access logging bucket for another bucket" } ] } } }, + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLogPolicy7B05AE89": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketA3A51E6A", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLogAutoDeleteObjectsCustomResourceAE9C7ABE": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116" + } + }, + "DependsOn": [ + "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLogPolicy7B05AE89" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "testcloudfrontmediastoreCloudfrontLoggingBucketA3A51E6A": { + "Type": "AWS::S3::Bucket", + "Properties": { + "AccessControl": "LogDeliveryWrite", + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116" + } + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "testcloudfrontmediastoreCloudfrontLoggingBucketPolicyF3B44DFD": { "Type": "AWS::S3::BucketPolicy", "Properties": { @@ -413,7 +597,7 @@ [ "Lambda function for auto-deleting objects in ", { - "Ref": "testcloudfrontmediastoreCloudfrontLoggingBucketA3A51E6A" + "Ref": "testcloudfrontmediastoreCloudfrontLoggingBucketAccessLog907A8116" }, " S3 bucket." ] diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-headers.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-headers.expected.json index 6bbf9bf82..6cb970be3 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-headers.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-headers.expected.json @@ -417,10 +417,9 @@ "Name": "SetHttpSecurityHeadersc8da5865185980f6eb00e7dd351786a8b49cd2929e" } }, - "testcloudfronts3CloudfrontLoggingBucket985C0FE8": { + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58": { "Type": "AWS::S3::Bucket", "Properties": { - "AccessControl": "LogDeliveryWrite", "BucketEncryption": { "ServerSideEncryptionConfiguration": [ { @@ -460,12 +459,197 @@ "rules_to_suppress": [ { "id": "W35", - "reason": "This S3 bucket is used as the access logging bucket for CloudFront Distribution" + "reason": "This S3 bucket is used as the access logging bucket for another bucket" } ] } } }, + "testcloudfronts3CloudfrontLoggingBucketAccessLogPolicy526F2E14": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucket985C0FE8", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfronts3CloudfrontLoggingBucketAccessLogAutoDeleteObjectsCustomResourceE16E063D": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58" + } + }, + "DependsOn": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLogPolicy526F2E14" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "testcloudfronts3CloudfrontLoggingBucket985C0FE8": { + "Type": "AWS::S3::Bucket", + "Properties": { + "AccessControl": "LogDeliveryWrite", + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58" + } + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "testcloudfronts3CloudfrontLoggingBucketPolicyDF55851B": { "Type": "AWS::S3::BucketPolicy", "Properties": { diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-originPath.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-originPath.expected.json index 5b2bfe275..b9458440d 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-originPath.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-originPath.expected.json @@ -385,10 +385,9 @@ "Name": "SetHttpSecurityHeadersc8966f7b24c95d47868a69c8831fbd3ccac3fa3d70" } }, - "testcloudfronts3CloudfrontLoggingBucket985C0FE8": { + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58": { "Type": "AWS::S3::Bucket", "Properties": { - "AccessControl": "LogDeliveryWrite", "BucketEncryption": { "ServerSideEncryptionConfiguration": [ { @@ -428,12 +427,197 @@ "rules_to_suppress": [ { "id": "W35", - "reason": "This S3 bucket is used as the access logging bucket for CloudFront Distribution" + "reason": "This S3 bucket is used as the access logging bucket for another bucket" } ] } } }, + "testcloudfronts3CloudfrontLoggingBucketAccessLogPolicy526F2E14": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucket985C0FE8", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfronts3CloudfrontLoggingBucketAccessLogAutoDeleteObjectsCustomResourceE16E063D": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58" + } + }, + "DependsOn": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLogPolicy526F2E14" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "testcloudfronts3CloudfrontLoggingBucket985C0FE8": { + "Type": "AWS::S3::Bucket", + "Properties": { + "AccessControl": "LogDeliveryWrite", + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58" + } + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "testcloudfronts3CloudfrontLoggingBucketPolicyDF55851B": { "Type": "AWS::S3::BucketPolicy", "Properties": { diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customLoggingBuckets.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customLoggingBuckets.expected.json index 4b00af10c..f250de061 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customLoggingBuckets.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customLoggingBuckets.expected.json @@ -397,10 +397,9 @@ "Name": "SetHttpSecurityHeadersc844fcbc00f82925aea73bcda195f6b5551bdcf3d4" } }, - "testcloudfronts3CloudfrontLoggingBucket985C0FE8": { + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58": { "Type": "AWS::S3::Bucket", "Properties": { - "AccessControl": "LogDeliveryWrite", "BucketEncryption": { "ServerSideEncryptionConfiguration": [ { @@ -453,12 +452,210 @@ "rules_to_suppress": [ { "id": "W35", - "reason": "This S3 bucket is used as the access logging bucket for CloudFront Distribution" + "reason": "This S3 bucket is used as the access logging bucket for another bucket" } ] } } }, + "testcloudfronts3CloudfrontLoggingBucketAccessLogPolicy526F2E14": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucket985C0FE8", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfronts3CloudfrontLoggingBucketAccessLogAutoDeleteObjectsCustomResourceE16E063D": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58" + } + }, + "DependsOn": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLogPolicy526F2E14" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "testcloudfronts3CloudfrontLoggingBucket985C0FE8": { + "Type": "AWS::S3::Bucket", + "Properties": { + "AccessControl": "LogDeliveryWrite", + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LifecycleConfiguration": { + "Rules": [ + { + "Status": "Enabled", + "Transitions": [ + { + "StorageClass": "GLACIER", + "TransitionInDays": 7 + } + ] + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58" + } + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "testcloudfronts3CloudfrontLoggingBucketPolicyDF55851B": { "Type": "AWS::S3::BucketPolicy", "Properties": { diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-existing-bucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-existing-bucket.expected.json index de72d90e6..00d8308b4 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-existing-bucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-existing-bucket.expected.json @@ -461,10 +461,9 @@ "Name": "SetHttpSecurityHeadersc8321a2c9fa54d380831d390bfbd7aff27f99fd427" } }, - "testcloudfronts3CloudfrontLoggingBucket985C0FE8": { + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58": { "Type": "AWS::S3::Bucket", "Properties": { - "AccessControl": "LogDeliveryWrite", "BucketEncryption": { "ServerSideEncryptionConfiguration": [ { @@ -504,12 +503,197 @@ "rules_to_suppress": [ { "id": "W35", - "reason": "This S3 bucket is used as the access logging bucket for CloudFront Distribution" + "reason": "This S3 bucket is used as the access logging bucket for another bucket" } ] } } }, + "testcloudfronts3CloudfrontLoggingBucketAccessLogPolicy526F2E14": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucket985C0FE8", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfronts3CloudfrontLoggingBucketAccessLogAutoDeleteObjectsCustomResourceE16E063D": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58" + } + }, + "DependsOn": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLogPolicy526F2E14" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "testcloudfronts3CloudfrontLoggingBucket985C0FE8": { + "Type": "AWS::S3::Bucket", + "Properties": { + "AccessControl": "LogDeliveryWrite", + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58" + } + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "testcloudfronts3CloudfrontLoggingBucketPolicyDF55851B": { "Type": "AWS::S3::BucketPolicy", "Properties": { diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-arguments.expected.json index 204fe94b6..ffe9fb145 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-arguments.expected.json @@ -395,10 +395,9 @@ "Name": "SetHttpSecurityHeadersc88b3e0fe5ebfb7f401b410752c35f74a3678d5cb1" } }, - "testcloudfronts3CloudfrontLoggingBucket985C0FE8": { + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58": { "Type": "AWS::S3::Bucket", "Properties": { - "AccessControl": "LogDeliveryWrite", "BucketEncryption": { "ServerSideEncryptionConfiguration": [ { @@ -438,12 +437,197 @@ "rules_to_suppress": [ { "id": "W35", - "reason": "This S3 bucket is used as the access logging bucket for CloudFront Distribution" + "reason": "This S3 bucket is used as the access logging bucket for another bucket" } ] } } }, + "testcloudfronts3CloudfrontLoggingBucketAccessLogPolicy526F2E14": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucket985C0FE8", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfronts3CloudfrontLoggingBucketAccessLogAutoDeleteObjectsCustomResourceE16E063D": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58" + } + }, + "DependsOn": [ + "testcloudfronts3CloudfrontLoggingBucketAccessLogPolicy526F2E14" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "testcloudfronts3CloudfrontLoggingBucket985C0FE8": { + "Type": "AWS::S3::Bucket", + "Properties": { + "AccessControl": "LogDeliveryWrite", + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "testcloudfronts3CloudfrontLoggingBucketAccessLog2E738D58" + } + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "testcloudfronts3CloudfrontLoggingBucketPolicyDF55851B": { "Type": "AWS::S3::BucketPolicy", "Properties": { diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-security-headers.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-security-headers.expected.json index a101651df..5299de8f5 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-security-headers.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-security-headers.expected.json @@ -373,10 +373,9 @@ "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, - "testcloudfronts3nosecurityheadersCloudfrontLoggingBucket92A5E2A5": { + "testcloudfronts3nosecurityheadersCloudfrontLoggingBucketAccessLogA3FF51B1": { "Type": "AWS::S3::Bucket", "Properties": { - "AccessControl": "LogDeliveryWrite", "BucketEncryption": { "ServerSideEncryptionConfiguration": [ { @@ -416,12 +415,197 @@ "rules_to_suppress": [ { "id": "W35", - "reason": "This S3 bucket is used as the access logging bucket for CloudFront Distribution" + "reason": "This S3 bucket is used as the access logging bucket for another bucket" } ] } } }, + "testcloudfronts3nosecurityheadersCloudfrontLoggingBucketAccessLogPolicy3DF5F522": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfronts3nosecurityheadersCloudfrontLoggingBucketAccessLogA3FF51B1" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3nosecurityheadersCloudfrontLoggingBucketAccessLogA3FF51B1", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3nosecurityheadersCloudfrontLoggingBucketAccessLogA3FF51B1", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3nosecurityheadersCloudfrontLoggingBucketAccessLogA3FF51B1", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3nosecurityheadersCloudfrontLoggingBucketAccessLogA3FF51B1", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "testcloudfronts3nosecurityheadersCloudfrontLoggingBucket92A5E2A5", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3nosecurityheadersCloudfrontLoggingBucketAccessLogA3FF51B1", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfronts3nosecurityheadersCloudfrontLoggingBucketAccessLogAutoDeleteObjectsCustomResource20738403": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "testcloudfronts3nosecurityheadersCloudfrontLoggingBucketAccessLogA3FF51B1" + } + }, + "DependsOn": [ + "testcloudfronts3nosecurityheadersCloudfrontLoggingBucketAccessLogPolicy3DF5F522" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "testcloudfronts3nosecurityheadersCloudfrontLoggingBucket92A5E2A5": { + "Type": "AWS::S3::Bucket", + "Properties": { + "AccessControl": "LogDeliveryWrite", + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "testcloudfronts3nosecurityheadersCloudfrontLoggingBucketAccessLogA3FF51B1" + } + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "testcloudfronts3nosecurityheadersCloudfrontLoggingBucketPolicy7D709982": { "Type": "AWS::S3::BucketPolicy", "Properties": { diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/test.cloudfront-s3.test.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/test.cloudfront-s3.test.ts index 156986af7..70438b604 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/test.cloudfront-s3.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/test.cloudfront-s3.test.ts @@ -329,7 +329,7 @@ test('Cloudfront logging bucket error when providing existing log bucket and log expect(app).toThrowError(); }); -test('s3 bucket with one content bucket and no logging bucket', () => { +test('s3 bucket with one content bucket and no access logging of CONTENT bucket', () => { const stack = new cdk.Stack(); const construct = new CloudFrontToS3(stack, 'cloudfront-s3', { @@ -340,7 +340,9 @@ test('s3 bucket with one content bucket and no logging bucket', () => { }); const template = Template.fromStack(stack); - template.resourceCountIs("AWS::S3::Bucket", 2); + // Content bucket+Cloudfront Logs bucket+ + // Access Log bucket for Cloudfront Logs bucket = 3 buckets + template.resourceCountIs("AWS::S3::Bucket", 3); expect(construct.s3LoggingBucket).toEqual(undefined); }); diff --git a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-cloudfront/lib/index.ts b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-cloudfront/lib/index.ts index 457d481a7..648acb89d 100644 --- a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-cloudfront/lib/index.ts +++ b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-cloudfront/lib/index.ts @@ -59,6 +59,10 @@ export class WafwebaclToCloudFront extends Construct { super(scope, id); defaults.CheckWafWebAclProps(props); + // All our tests are based upon this behavior being on, so we're setting + // context here rather than assuming the client will set it + this.node.setContext("@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy", true); + // Build the Web ACL this.webacl = defaults.buildWebacl(this, 'CLOUDFRONT', { existingWebaclObj: props.existingWebaclObj, diff --git a/source/patterns/@aws-solutions-constructs/core/lib/cloudfront-distribution-helper.ts b/source/patterns/@aws-solutions-constructs/core/lib/cloudfront-distribution-helper.ts index 19f7eec07..9040b0f8f 100644 --- a/source/patterns/@aws-solutions-constructs/core/lib/cloudfront-distribution-helper.ts +++ b/source/patterns/@aws-solutions-constructs/core/lib/cloudfront-distribution-helper.ts @@ -27,7 +27,7 @@ import { DefaultCloudFrontDistributionForMediaStoreProps } from './cloudfront-distribution-defaults'; import { addCfnSuppressRules, consolidateProps } from './utils'; -import { createLoggingBucket } from './s3-bucket-helper'; +import { createCloudFrontLoggingBucket } from './s3-bucket-helper'; import { DefaultS3Props } from './s3-bucket-defaults'; // Note: To ensure CDKv2 compatibility, keep the import statement for Construct separate import { Construct } from 'constructs'; @@ -250,7 +250,7 @@ function getLoggingBucket( } else if (userSuppliedLogBucket) { bucketResult = userSuppliedLogBucket; } else { - bucketResult = createLoggingBucket( + bucketResult = createCloudFrontLoggingBucket( scope, 'CloudfrontLoggingBucket', consolidateProps(DefaultS3Props(), cloudFrontLoggingBucketProps, { objectOwnership: s3.ObjectOwnership.OBJECT_WRITER })); diff --git a/source/patterns/@aws-solutions-constructs/core/lib/s3-bucket-helper.ts b/source/patterns/@aws-solutions-constructs/core/lib/s3-bucket-helper.ts index adcf285f6..2dd3314ce 100644 --- a/source/patterns/@aws-solutions-constructs/core/lib/s3-bucket-helper.ts +++ b/source/patterns/@aws-solutions-constructs/core/lib/s3-bucket-helper.ts @@ -52,7 +52,7 @@ export interface BuildS3BucketProps { /** * @internal This is an internal core function and should not be called directly by Solutions Constructs clients. */ -export function createLoggingBucket(scope: Construct, +export function createS3AccessLoggingBucket(scope: Construct, bucketId: string, loggingBucketProps: s3.BucketProps): s3.Bucket { @@ -75,23 +75,52 @@ export function createLoggingBucket(scope: Construct, // Verified by unit test 's3 bucket with default props' const loggingBucket: s3.Bucket = new s3.Bucket(scope, bucketId, combinedBucketProps); // NOSONAR - // Extract the CfnBucket from the loggingBucket - const loggingBucketResource = loggingBucket.node.findChild('Resource') as s3.CfnBucket; + addCfnSuppressRules(loggingBucket, [ + { + id: 'W35', + reason: "This S3 bucket is used as the access logging bucket for another bucket" + } + ]); - let _reason = "This S3 bucket is used as the access logging bucket for another bucket"; + return loggingBucket; +} - if (bucketId === 'CloudfrontLoggingBucket') { - _reason = "This S3 bucket is used as the access logging bucket for CloudFront Distribution"; - } +/** + * @internal This is an internal core function and should not be called directly by Solutions Constructs clients. + */ +export function createCloudFrontLoggingBucket(scope: Construct, + bucketId: string, + loggingBucketProps: s3.BucketProps): s3.Bucket { - addCfnSuppressRules(loggingBucketResource, [ + // Introduce the default props since we can't be certain the caller used them and + // they are important best practices + const combinedBucketProps = consolidateProps(DefaultS3Props(), loggingBucketProps); + + const accessLogBucket: s3.Bucket = new s3.Bucket(scope, `${bucketId}AccessLog`, combinedBucketProps); // NOSONAR + addCfnSuppressRules(accessLogBucket, [ { id: 'W35', - reason: _reason + reason: "This S3 bucket is used as the access logging bucket for another bucket" } ]); - return loggingBucket; + // Create the Logging Bucket + // NOSONAR (typescript:S6281) + // Block Public Access is set by DefaultS3Props, but Sonarqube can't detect it + // It is verified by 's3 bucket with default props' in the unit tests + // NOSONAR (typescript:S6245) + // Encryption is turned on in the default properties that Sonarqube doesn't see + // Verified by unit test 's3 bucket with default props' + // NOSONAR (typescript:S6249) + // enforceSSL is turned on in the default properties that Sonarqube doesn't see + // Verified by unit test 's3 bucket with default props' + // NOSONAR (typescript:typescript:S6249) + // versioning is turned on in the default properties that Sonarqube doesn't see + // Verified by unit test 's3 bucket with default props' + const cloudfrontLogBucketProps = overrideProps(combinedBucketProps, { serverAccessLogsBucket: accessLogBucket }); + const cloudfrontLogBucket: s3.Bucket = new s3.Bucket(scope, bucketId, cloudfrontLogBucketProps); // NOSONAR + + return cloudfrontLogBucket; } /** @@ -173,7 +202,7 @@ export function buildS3Bucket(scope: Construct, loggingBucketProps = overrideProps(loggingBucketProps, { removalPolicy: props.bucketProps.removalPolicy }); } - loggingBucket = createLoggingBucket(scope, loggingBucketId, loggingBucketProps); + loggingBucket = createS3AccessLoggingBucket(scope, loggingBucketId, loggingBucketProps); } else if (props.bucketProps?.serverAccessLogsBucket) { loggingBucket = props.bucketProps?.serverAccessLogsBucket as s3.Bucket; } diff --git a/source/patterns/@aws-solutions-constructs/core/test/cloudfront-distribution-s3-helper.test.ts b/source/patterns/@aws-solutions-constructs/core/test/cloudfront-distribution-s3-helper.test.ts index 71bf22f6f..065eb806e 100644 --- a/source/patterns/@aws-solutions-constructs/core/test/cloudfront-distribution-s3-helper.test.ts +++ b/source/patterns/@aws-solutions-constructs/core/test/cloudfront-distribution-s3-helper.test.ts @@ -42,25 +42,6 @@ test('check bucket policy metadata', () => { }); }); -test('check bucket metadata', () => { - const stack = new Stack(); - const buildS3BucketResponse = buildS3Bucket(stack, {}); - CloudFrontDistributionForS3(stack, buildS3BucketResponse.bucket); - const template = Template.fromStack(stack); - template.hasResource('AWS::S3::Bucket', { - Metadata: { - cfn_nag: { - rules_to_suppress: [ - { - id: "W35", - reason: "This S3 bucket is used as the access logging bucket for CloudFront Distribution" - } - ] - } - } - }); -}); - test('test cloudfront check bucket policy', () => { const stack = new Stack(); const buildS3BucketResponse = buildS3Bucket(stack, {}); From 720dec500a728a3c57832b7e479ee8eca1f08056 Mon Sep 17 00:00:00 2001 From: biffgaut <78155736+biffgaut@users.noreply.github.com> Date: Mon, 18 Dec 2023 17:34:10 -0500 Subject: [PATCH 06/14] feat(aws-cloudfront-apigateway-lambda): require explicit authentication type (#1044) * Implement Issue1043 * Refresh wafapi tests * Update README, stop override warning for auth type. --- .../README.md | 60 +++-- .../lib/index.ts | 41 ++-- ...ustomCloudfrontLoggingBucket.expected.json | 205 ++++++++++++++++-- ....cftaplam-customCloudfrontLoggingBucket.ts | 9 +- .../integ.cftaplam-no-arguments.expected.json | 205 ++++++++++++++++-- .../test/integ.cftaplam-no-arguments.ts | 9 +- ...g.cftaplam-override-behavior.expected.json | 185 +++++++++++++++- .../test/integ.cftaplam-override-behavior.ts | 5 +- .../test.cloudfront-apigateway-lambda.test.ts | 47 ++++ ...ustomCloudfrontLoggingBucket.expected.json | 18 +- .../integ.cftapi-no-arguments.expected.json | 18 +- ...ing-waf-to-multiple-gateways.expected.json | 36 +-- .../integ.wafapi-no-arguments.expected.json | 18 +- ...teg.wafapi-partial-arguments.expected.json | 18 +- .../core/lib/apigateway-defaults.ts | 22 +- .../core/lib/apigateway-helper.ts | 6 +- .../core/test/glue-job-helper.test.ts | 1 - .../core/test/test-helper.ts | 33 +-- 18 files changed, 761 insertions(+), 175 deletions(-) diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/README.md b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/README.md index 3787c1b9e..95bad1f33 100755 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/README.md +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/README.md @@ -35,7 +35,12 @@ new CloudFrontToApiGatewayToLambda(this, 'test-cloudfront-apigateway-lambda', { code: lambda.Code.fromAsset(`lambda`), runtime: lambda.Runtime.NODEJS_16_X, handler: 'index.handler' - } + }, + apiGatewayProps: { + defaultMethodOptions: { + authorizationType: api.AuthorizationType.NONE + } + }, }); ``` @@ -44,17 +49,28 @@ Python from aws_solutions_constructs.aws_cloudfront_apigateway_lambda import CloudFrontToApiGatewayToLambda from aws_cdk import ( aws_lambda as _lambda, + aws_apigateway as apigw, Stack ) from constructs import Construct -CloudFrontToApiGatewayToLambda(self, 'test-cloudfront-apigateway-lambda', - lambda_function_props=_lambda.FunctionProps( - code=_lambda.Code.from_asset('lambda'), - runtime=_lambda.Runtime.PYTHON_3_9, - handler='index.handler' - ) - ) + CloudFrontToApiGatewayToLambda( + self, 'CloudFrontApiGatewayToLambda', + lambda_function_props=_lambda.FunctionProps( + runtime=_lambda.Runtime.PYTHON_3_7, + code=_lambda.Code.from_asset('lambda'), + handler='hello.handler', + ), + # NOTE - we use RestApiProps here because the actual type, LambdaRestApiProps requires + # the handler function which does not yet exist. As RestApiProps is a subset of of LambdaRestApiProps + # (although does not *extend* that interface) this works fine when the props object reaches the + # underlying TypeScript code that implements Constructs + api_gateway_props=apigw.RestApiProps( + default_method_options=apigw.MethodOptions( + authorization_type=apigw.AuthorizationType.NONE + ) + ) + ) ``` Java @@ -66,25 +82,33 @@ import software.amazon.awscdk.StackProps; import software.amazon.awscdk.services.lambda.*; import software.amazon.awscdk.services.lambda.Runtime; import software.amazon.awsconstructs.services.cloudfrontapigatewaylambda.*; - -new CloudFrontToApiGatewayToLambda(this, "test-cloudfront-apigateway-lambda", - new CloudFrontToApiGatewayToLambdaProps.Builder() - .lambdaFunctionProps(new FunctionProps.Builder() - .runtime(Runtime.NODEJS_16_X) - .code(Code.fromAsset("lambda")) - .handler("index.handler") +import software.amazon.awsconstructs.services.cloudfrontapigatewaylambda.CloudFrontToApiGatewayToLambdaProps; + +new CloudFrontToApiGatewayToLambda(this, "ApiGatewayToLambdaPattern", new CloudFrontToApiGatewayToLambdaProps.Builder() + .lambdaFunctionProps(new FunctionProps.Builder() + .runtime(Runtime.NODEJS_16_X) // execution environment + .code(Code.fromAsset("lambda")) // code loaded from the `lambda` directory (under root, next to `src`) + .handler("hello.handler") // file is `hello`, function is `handler` + .build()) + // NOTE - we use RestApiProps here because the actual type, LambdaRestApiProps requires + // the handler function which does not yet exist. As RestApiProps is a subset of of LambdaRestApiProps + // (although does not *extend* that interface) this works fine when the props object reaches the + // underlying TypeScript code that implements Constructs + .apiGatewayProps(new RestApiProps.Builder() + .defaultMethodOptions(new MethodOptions.Builder() + .authorizationType(AuthorizationType.NONE) .build()) - .build()); + .build()) + .build()); ``` - ## Pattern Construct Props | **Name** | **Type** | **Description** | |:-------------|:----------------|-----------------| |existingLambdaObj?|[`lambda.Function`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda.Function.html)|Existing instance of Lambda Function object, providing both this and `lambdaFunctionProps` will cause an error.| |lambdaFunctionProps?|[`lambda.FunctionProps`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda.FunctionProps.html)|Optional user provided props to override the default props for the Lambda function.| -|apiGatewayProps?|[`api.LambdaRestApiProps`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_apigateway.LambdaRestApiProps.html)|Optional user provided props to override the default props for API Gateway| +|apiGatewayProps?|[`api.LambdaRestApiProps`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_apigateway.LambdaRestApiProps.html)|User provided props to override the default props for the API Gateway. As of release 2.48.0, clients must include this property with `defaultMethodOptions: { authorizationType: string }` specified. See Issue1043 in the github repo https://github.com/awslabs/aws-solutions-constructs/issues/1043 | |cloudFrontDistributionProps?|[`cloudfront.DistributionProps`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_cloudfront.DistributionProps.html)|Optional user provided props to override the default props for CloudFront Distribution| |insertHttpSecurityHeaders?|`boolean`|Optional user provided props to turn on/off the automatic injection of best practice HTTP security headers in all responses from CloudFront| | responseHeadersPolicyProps? | [`cloudfront.ResponseHeadersPolicyProps`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_cloudfront.ResponseHeadersPolicyProps.html) | Optional user provided configuration that cloudfront applies to all http responses. | diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/lib/index.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/lib/index.ts index 0901aa0cd..76abe5d3b 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/lib/index.ts +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/lib/index.ts @@ -39,11 +39,14 @@ export interface CloudFrontToApiGatewayToLambdaProps { */ readonly lambdaFunctionProps?: lambda.FunctionProps /** - * Optional user provided props to override the default props for the API Gateway. + * User provided props to override the default props for the API Gateway. As of release + * 2.48.0, clients must include this property with defaultMethodOptions: { authorizationType: string } specified. + * See Issue1043 in the github repo https://github.com/awslabs/aws-solutions-constructs/issues/1043 * - * @default - Default props are used + * @default - defaultMethodOptions/authorizationType is required, for other, unspecified values the + * default props are used */ - readonly apiGatewayProps?: api.LambdaRestApiProps | any + readonly apiGatewayProps: api.LambdaRestApiProps | any /** * Optional user provided props to override the default props * @@ -106,6 +109,14 @@ export class CloudFrontToApiGatewayToLambda extends Construct { super(scope, id); defaults.CheckLambdaProps(props); // CheckCloudFrontProps() is called by internal aws-cloudfront-apigateway construct + if (!props.apiGatewayProps?.defaultMethodOptions?.authorizationType) { + defaults.printWarning('As of v2.48.0, apiGatewayProps.defaultMethodOptions.authorizationType is\ + required. To update your instantiation call, add the following to your CloudFrontToApiGatewayToLambdaProps argument\ + \n\napiGatewayProps: { defaultMethodOptions: { authorizationType: api.AuthorizationType.NONE }},\n\nSee Issue1043 for an explanation.'); + throw new Error('As of v2.48.0, an explicit authorization type is required for CloudFront/API Gateway patterns'); + } else if (props.apiGatewayProps.defaultMethodOptions.authorizationType === "AWS_IAM") { + throw new Error('Amazon API Gateway Rest APIs integrated with Amazon CloudFront do not support AWS_IAM authorization'); + } // All our tests are based upon this behavior being on, so we're setting // context here rather than assuming the client will set it @@ -116,27 +127,17 @@ export class CloudFrontToApiGatewayToLambda extends Construct { lambdaFunctionProps: props.lambdaFunctionProps }); - const regionalLambdaRestApiResponse = defaults.RegionalLambdaRestApi(this, this.lambdaFunction, props.apiGatewayProps, props.logGroupProps); + // We can't default to IAM authentication with a CloudFront distribution, so + // we'll instruct core to not use any default auth to avoid override warnings + const regionalLambdaRestApiResponse = defaults.RegionalLambdaRestApi(this, + this.lambdaFunction, + props.apiGatewayProps, + props.logGroupProps, + false); this.apiGateway = regionalLambdaRestApiResponse.api; this.apiGatewayCloudWatchRole = regionalLambdaRestApiResponse.role; this.apiGatewayLogGroup = regionalLambdaRestApiResponse.group; - this.apiGateway.methods.forEach((apiMethod) => { - // Override the API Gateway Authorization Type from AWS_IAM to NONE - const child = apiMethod.node.findChild('Resource') as api.CfnMethod; - if (child.authorizationType === 'AWS_IAM') { - child.addPropertyOverride('AuthorizationType', 'NONE'); - - defaults.addCfnSuppressRules(apiMethod, [ - { - id: 'W59', - reason: `AWS::ApiGateway::Method AuthorizationType is set to 'NONE' because API Gateway behind CloudFront does not support AWS_IAM authentication` - }, - ]); - - } - }); - const apiCloudfront: CloudFrontToApiGateway = new CloudFrontToApiGateway(this, 'CloudFrontToApiGateway', { existingApiGatewayObj: this.apiGateway, cloudFrontDistributionProps: props.cloudFrontDistributionProps, diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-customCloudfrontLoggingBucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-customCloudfrontLoggingBucket.expected.json index 6424d959a..50cb35a3f 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-customCloudfrontLoggingBucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-customCloudfrontLoggingBucket.expected.json @@ -1,6 +1,177 @@ { "Description": "Integration Test for aws-cloudfront-apigateway-lambda custom Cloudfront Logging Bucket", "Resources": { + "cftaplamcustomCloudfrontLoggingBucketauthorizerAuthFunctionServiceRole00AAA44C": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "cftaplamcustomCloudfrontLoggingBucketauthorizerAuthFunction86ECA8C3": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "42a35bbf0dec9ef0ac5b0dde87e71a1b8929e8d2d178dd09ccfb2c928ec0198c.zip" + }, + "Handler": ".handler", + "Role": { + "Fn::GetAtt": [ + "cftaplamcustomCloudfrontLoggingBucketauthorizerAuthFunctionServiceRole00AAA44C", + "Arn" + ] + }, + "Runtime": "nodejs16.x" + }, + "DependsOn": [ + "cftaplamcustomCloudfrontLoggingBucketauthorizerAuthFunctionServiceRole00AAA44C" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "Test Resource" + }, + { + "id": "W89", + "reason": "Test Resource" + }, + { + "id": "W92", + "reason": "Test Resource" + } + ] + } + } + }, + "cftaplamcustomCloudfrontLoggingBucketauthorizerAuthFunctioncftaplamcustomCloudfrontLoggingBucketcftaplamcustomCloudfrontLoggingBucketauthorizer02C97B0FPermissionsBF8A1A3B": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "cftaplamcustomCloudfrontLoggingBucketauthorizerAuthFunction86ECA8C3", + "Arn" + ] + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "cfapigwlambdaLambdaRestApi775C255B" + }, + "/authorizers/", + { + "Ref": "cftaplamcustomCloudfrontLoggingBucketauthorizer4D180075" + } + ] + ] + } + } + }, + "cftaplamcustomCloudfrontLoggingBucketauthorizer4D180075": { + "Type": "AWS::ApiGateway::Authorizer", + "Properties": { + "AuthorizerUri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + ":", + { + "Fn::GetAtt": [ + "cftaplamcustomCloudfrontLoggingBucketauthorizerAuthFunction86ECA8C3", + "Arn" + ] + } + ] + } + ] + }, + ":apigateway:", + { + "Fn::Select": [ + 3, + { + "Fn::Split": [ + ":", + { + "Fn::GetAtt": [ + "cftaplamcustomCloudfrontLoggingBucketauthorizerAuthFunction86ECA8C3", + "Arn" + ] + } + ] + } + ] + }, + ":lambda:path/2015-03-31/functions/", + { + "Fn::GetAtt": [ + "cftaplamcustomCloudfrontLoggingBucketauthorizerAuthFunction86ECA8C3", + "Arn" + ] + }, + "/invocations" + ] + ] + }, + "IdentitySource": "method.request.header.Authorization", + "Name": "cftaplamcustomCloudfrontLoggingBucketcftaplamcustomCloudfrontLoggingBucketauthorizer02C97B0F", + "RestApiId": { + "Ref": "cfapigwlambdaLambdaRestApi775C255B" + }, + "Type": "REQUEST" + } + }, "cfapigwlambdaLambdaFunctionServiceRole9B40D826": { "Type": "AWS::IAM::Role", "Properties": { @@ -169,7 +340,7 @@ "Name": "LambdaRestApi" } }, - "cfapigwlambdaLambdaRestApiDeployment33C24C7D5b6eb6dc887b9e8b9bde9a765f4aacbb": { + "cfapigwlambdaLambdaRestApiDeployment33C24C7D41e6d6ff15b0c6d292b31cce930b3216": { "Type": "AWS::ApiGateway::Deployment", "Properties": { "Description": "Automatically created by the RestApi construct", @@ -206,7 +377,7 @@ "Format": "{\"requestId\":\"$context.requestId\",\"ip\":\"$context.identity.sourceIp\",\"user\":\"$context.identity.user\",\"caller\":\"$context.identity.caller\",\"requestTime\":\"$context.requestTime\",\"httpMethod\":\"$context.httpMethod\",\"resourcePath\":\"$context.resourcePath\",\"status\":\"$context.status\",\"protocol\":\"$context.protocol\",\"responseLength\":\"$context.responseLength\"}" }, "DeploymentId": { - "Ref": "cfapigwlambdaLambdaRestApiDeployment33C24C7D5b6eb6dc887b9e8b9bde9a765f4aacbb" + "Ref": "cfapigwlambdaLambdaRestApiDeployment33C24C7D41e6d6ff15b0c6d292b31cce930b3216" }, "MethodSettings": [ { @@ -319,7 +490,10 @@ "cfapigwlambdaLambdaRestApiproxyANY68181290": { "Type": "AWS::ApiGateway::Method", "Properties": { - "AuthorizationType": "NONE", + "AuthorizationType": "CUSTOM", + "AuthorizerId": { + "Ref": "cftaplamcustomCloudfrontLoggingBucketauthorizer4D180075" + }, "HttpMethod": "ANY", "Integration": { "IntegrationHttpMethod": "POST", @@ -354,16 +528,6 @@ "RestApiId": { "Ref": "cfapigwlambdaLambdaRestApi775C255B" } - }, - "Metadata": { - "cfn_nag": { - "rules_to_suppress": [ - { - "id": "W59", - "reason": "AWS::ApiGateway::Method AuthorizationType is set to 'NONE' because API Gateway behind CloudFront does not support AWS_IAM authentication" - } - ] - } } }, "cfapigwlambdaLambdaRestApiANYApiPermissioncftaplamcustomCloudfrontLoggingBucketcfapigwlambdaLambdaRestApi92F6CCCCANY63987F12": { @@ -447,7 +611,10 @@ "cfapigwlambdaLambdaRestApiANY81C176E9": { "Type": "AWS::ApiGateway::Method", "Properties": { - "AuthorizationType": "NONE", + "AuthorizationType": "CUSTOM", + "AuthorizerId": { + "Ref": "cftaplamcustomCloudfrontLoggingBucketauthorizer4D180075" + }, "HttpMethod": "ANY", "Integration": { "IntegrationHttpMethod": "POST", @@ -485,16 +652,6 @@ "RestApiId": { "Ref": "cfapigwlambdaLambdaRestApi775C255B" } - }, - "Metadata": { - "cfn_nag": { - "rules_to_suppress": [ - { - "id": "W59", - "reason": "AWS::ApiGateway::Method AuthorizationType is set to 'NONE' because API Gateway behind CloudFront does not support AWS_IAM authentication" - } - ] - } } }, "cfapigwlambdaLambdaRestApiUsagePlan11CE9748": { diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-customCloudfrontLoggingBucket.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-customCloudfrontLoggingBucket.ts index e091ed1a2..f9d71fa5c 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-customCloudfrontLoggingBucket.ts +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-customCloudfrontLoggingBucket.ts @@ -16,7 +16,8 @@ import { App, Stack, RemovalPolicy } from "aws-cdk-lib"; import { CloudFrontToApiGatewayToLambda } from "../lib"; import * as lambda from 'aws-cdk-lib/aws-lambda'; import { BucketEncryption } from "aws-cdk-lib/aws-s3"; -import { generateIntegStackName, suppressAutoDeleteHandlerWarnings } from '@aws-solutions-constructs/core'; +import { generateIntegStackName, suppressAutoDeleteHandlerWarnings, CreateApiAuthorizer } from '@aws-solutions-constructs/core'; +import * as apigateway from 'aws-cdk-lib/aws-apigateway'; // Setup const app = new App(); @@ -24,6 +25,12 @@ const stack = new Stack(app, generateIntegStackName(__filename)); stack.templateOptions.description = 'Integration Test for aws-cloudfront-apigateway-lambda custom Cloudfront Logging Bucket'; new CloudFrontToApiGatewayToLambda(stack, 'cf-apigw-lambda', { + apiGatewayProps: { + defaultMethodOptions: { + authorizationType: apigateway.AuthorizationType.CUSTOM, + authorizer: CreateApiAuthorizer(stack, `${generateIntegStackName(__filename)}-authorizer`) + }, + }, lambdaFunctionProps: { code: lambda.Code.fromAsset(`${__dirname}/lambda`), runtime: lambda.Runtime.NODEJS_16_X, diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-no-arguments.expected.json index e13891b09..d64b2ad5f 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-no-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-no-arguments.expected.json @@ -1,6 +1,177 @@ { "Description": "Integration Test for aws-cloudfront-apigateway-lambda", "Resources": { + "cftaplamnoargumentsauthorizerAuthFunctionServiceRole122160C6": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "cftaplamnoargumentsauthorizerAuthFunction9B127993": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "42a35bbf0dec9ef0ac5b0dde87e71a1b8929e8d2d178dd09ccfb2c928ec0198c.zip" + }, + "Handler": ".handler", + "Role": { + "Fn::GetAtt": [ + "cftaplamnoargumentsauthorizerAuthFunctionServiceRole122160C6", + "Arn" + ] + }, + "Runtime": "nodejs16.x" + }, + "DependsOn": [ + "cftaplamnoargumentsauthorizerAuthFunctionServiceRole122160C6" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "Test Resource" + }, + { + "id": "W89", + "reason": "Test Resource" + }, + { + "id": "W92", + "reason": "Test Resource" + } + ] + } + } + }, + "cftaplamnoargumentsauthorizerAuthFunctioncftaplamnoargumentscftaplamnoargumentsauthorizer14876A7BPermissionsE711C432": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "cftaplamnoargumentsauthorizerAuthFunction9B127993", + "Arn" + ] + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "testcloudfrontapigatewaylambdaLambdaRestApi6A4CBD44" + }, + "/authorizers/", + { + "Ref": "cftaplamnoargumentsauthorizerD7B341B1" + } + ] + ] + } + } + }, + "cftaplamnoargumentsauthorizerD7B341B1": { + "Type": "AWS::ApiGateway::Authorizer", + "Properties": { + "AuthorizerUri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + ":", + { + "Fn::GetAtt": [ + "cftaplamnoargumentsauthorizerAuthFunction9B127993", + "Arn" + ] + } + ] + } + ] + }, + ":apigateway:", + { + "Fn::Select": [ + 3, + { + "Fn::Split": [ + ":", + { + "Fn::GetAtt": [ + "cftaplamnoargumentsauthorizerAuthFunction9B127993", + "Arn" + ] + } + ] + } + ] + }, + ":lambda:path/2015-03-31/functions/", + { + "Fn::GetAtt": [ + "cftaplamnoargumentsauthorizerAuthFunction9B127993", + "Arn" + ] + }, + "/invocations" + ] + ] + }, + "IdentitySource": "method.request.header.Authorization", + "Name": "cftaplamnoargumentscftaplamnoargumentsauthorizer14876A7B", + "RestApiId": { + "Ref": "testcloudfrontapigatewaylambdaLambdaRestApi6A4CBD44" + }, + "Type": "REQUEST" + } + }, "testcloudfrontapigatewaylambdaLambdaFunctionServiceRoleCB74590F": { "Type": "AWS::IAM::Role", "Properties": { @@ -169,7 +340,7 @@ "Name": "LambdaRestApi" } }, - "testcloudfrontapigatewaylambdaLambdaRestApiDeployment0C4661C03abb023c303d9e3ff2b4d984cd5d60ab": { + "testcloudfrontapigatewaylambdaLambdaRestApiDeployment0C4661C0be4652c0c81d13dbdf8cd82fdf384ca9": { "Type": "AWS::ApiGateway::Deployment", "Properties": { "Description": "Automatically created by the RestApi construct", @@ -206,7 +377,7 @@ "Format": "{\"requestId\":\"$context.requestId\",\"ip\":\"$context.identity.sourceIp\",\"user\":\"$context.identity.user\",\"caller\":\"$context.identity.caller\",\"requestTime\":\"$context.requestTime\",\"httpMethod\":\"$context.httpMethod\",\"resourcePath\":\"$context.resourcePath\",\"status\":\"$context.status\",\"protocol\":\"$context.protocol\",\"responseLength\":\"$context.responseLength\"}" }, "DeploymentId": { - "Ref": "testcloudfrontapigatewaylambdaLambdaRestApiDeployment0C4661C03abb023c303d9e3ff2b4d984cd5d60ab" + "Ref": "testcloudfrontapigatewaylambdaLambdaRestApiDeployment0C4661C0be4652c0c81d13dbdf8cd82fdf384ca9" }, "MethodSettings": [ { @@ -319,7 +490,10 @@ "testcloudfrontapigatewaylambdaLambdaRestApiproxyANYAE500A13": { "Type": "AWS::ApiGateway::Method", "Properties": { - "AuthorizationType": "NONE", + "AuthorizationType": "CUSTOM", + "AuthorizerId": { + "Ref": "cftaplamnoargumentsauthorizerD7B341B1" + }, "HttpMethod": "ANY", "Integration": { "IntegrationHttpMethod": "POST", @@ -354,16 +528,6 @@ "RestApiId": { "Ref": "testcloudfrontapigatewaylambdaLambdaRestApi6A4CBD44" } - }, - "Metadata": { - "cfn_nag": { - "rules_to_suppress": [ - { - "id": "W59", - "reason": "AWS::ApiGateway::Method AuthorizationType is set to 'NONE' because API Gateway behind CloudFront does not support AWS_IAM authentication" - } - ] - } } }, "testcloudfrontapigatewaylambdaLambdaRestApiANYApiPermissioncftaplamnoargumentstestcloudfrontapigatewaylambdaLambdaRestApiF14A7709ANY1BADAD44": { @@ -447,7 +611,10 @@ "testcloudfrontapigatewaylambdaLambdaRestApiANYBC435DFD": { "Type": "AWS::ApiGateway::Method", "Properties": { - "AuthorizationType": "NONE", + "AuthorizationType": "CUSTOM", + "AuthorizerId": { + "Ref": "cftaplamnoargumentsauthorizerD7B341B1" + }, "HttpMethod": "ANY", "Integration": { "IntegrationHttpMethod": "POST", @@ -485,16 +652,6 @@ "RestApiId": { "Ref": "testcloudfrontapigatewaylambdaLambdaRestApi6A4CBD44" } - }, - "Metadata": { - "cfn_nag": { - "rules_to_suppress": [ - { - "id": "W59", - "reason": "AWS::ApiGateway::Method AuthorizationType is set to 'NONE' because API Gateway behind CloudFront does not support AWS_IAM authentication" - } - ] - } } }, "testcloudfrontapigatewaylambdaLambdaRestApiUsagePlan59548A66": { diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-no-arguments.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-no-arguments.ts index 88875587d..052a0dbbb 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-no-arguments.ts +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-no-arguments.ts @@ -15,7 +15,8 @@ import { App, RemovalPolicy, Stack } from "aws-cdk-lib"; import { CloudFrontToApiGatewayToLambda } from "../lib"; import * as lambda from 'aws-cdk-lib/aws-lambda'; -import { generateIntegStackName, suppressAutoDeleteHandlerWarnings } from '@aws-solutions-constructs/core'; +import { generateIntegStackName, suppressAutoDeleteHandlerWarnings, CreateApiAuthorizer } from '@aws-solutions-constructs/core'; +import * as apigateway from 'aws-cdk-lib/aws-apigateway'; // Setup const app = new App(); @@ -29,6 +30,12 @@ const lambdaProps: lambda.FunctionProps = { }; new CloudFrontToApiGatewayToLambda(stack, 'test-cloudfront-apigateway-lambda', { + apiGatewayProps: { + defaultMethodOptions: { + authorizationType: apigateway.AuthorizationType.CUSTOM, + authorizer: CreateApiAuthorizer(stack, `${generateIntegStackName(__filename)}-authorizer`) + }, + }, lambdaFunctionProps: lambdaProps, cloudFrontLoggingBucketProps: { removalPolicy: RemovalPolicy.DESTROY, diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-override-behavior.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-override-behavior.expected.json index 270e52188..9538f88df 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-override-behavior.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-override-behavior.expected.json @@ -49,6 +49,177 @@ } } }, + "cftaplamoverridebehaviorauthorizerAuthFunctionServiceRoleA606974F": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "cftaplamoverridebehaviorauthorizerAuthFunction9DD827D6": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "42a35bbf0dec9ef0ac5b0dde87e71a1b8929e8d2d178dd09ccfb2c928ec0198c.zip" + }, + "Handler": ".handler", + "Role": { + "Fn::GetAtt": [ + "cftaplamoverridebehaviorauthorizerAuthFunctionServiceRoleA606974F", + "Arn" + ] + }, + "Runtime": "nodejs16.x" + }, + "DependsOn": [ + "cftaplamoverridebehaviorauthorizerAuthFunctionServiceRoleA606974F" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "Test Resource" + }, + { + "id": "W89", + "reason": "Test Resource" + }, + { + "id": "W92", + "reason": "Test Resource" + } + ] + } + } + }, + "cftaplamoverridebehaviorauthorizerAuthFunctioncftaplamoverridebehaviorcftaplamoverridebehaviorauthorizer3042C32CPermissions33B8870B": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "cftaplamoverridebehaviorauthorizerAuthFunction9DD827D6", + "Arn" + ] + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "cfapilambdaoverrideLambdaRestApi6E7952FC" + }, + "/authorizers/", + { + "Ref": "cftaplamoverridebehaviorauthorizer74D77225" + } + ] + ] + } + } + }, + "cftaplamoverridebehaviorauthorizer74D77225": { + "Type": "AWS::ApiGateway::Authorizer", + "Properties": { + "AuthorizerUri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + ":", + { + "Fn::GetAtt": [ + "cftaplamoverridebehaviorauthorizerAuthFunction9DD827D6", + "Arn" + ] + } + ] + } + ] + }, + ":apigateway:", + { + "Fn::Select": [ + 3, + { + "Fn::Split": [ + ":", + { + "Fn::GetAtt": [ + "cftaplamoverridebehaviorauthorizerAuthFunction9DD827D6", + "Arn" + ] + } + ] + } + ] + }, + ":lambda:path/2015-03-31/functions/", + { + "Fn::GetAtt": [ + "cftaplamoverridebehaviorauthorizerAuthFunction9DD827D6", + "Arn" + ] + }, + "/invocations" + ] + ] + }, + "IdentitySource": "method.request.header.Authorization", + "Name": "cftaplamoverridebehaviorcftaplamoverridebehaviorauthorizer3042C32C", + "RestApiId": { + "Ref": "cfapilambdaoverrideLambdaRestApi6E7952FC" + }, + "Type": "REQUEST" + } + }, "cfapilambdaoverrideLambdaFunctionServiceRole4B1A4043": { "Type": "AWS::IAM::Role", "Properties": { @@ -217,7 +388,7 @@ "Name": "LambdaRestApi" } }, - "cfapilambdaoverrideLambdaRestApiDeployment82ACBB00e7f3a114a506221ddcaf53765c4dd518": { + "cfapilambdaoverrideLambdaRestApiDeployment82ACBB00a88f54114ca67f75c1f46116226ebd9d": { "Type": "AWS::ApiGateway::Deployment", "Properties": { "Description": "Automatically created by the RestApi construct", @@ -255,7 +426,7 @@ "Format": "{\"requestId\":\"$context.requestId\",\"ip\":\"$context.identity.sourceIp\",\"user\":\"$context.identity.user\",\"caller\":\"$context.identity.caller\",\"requestTime\":\"$context.requestTime\",\"httpMethod\":\"$context.httpMethod\",\"resourcePath\":\"$context.resourcePath\",\"status\":\"$context.status\",\"protocol\":\"$context.protocol\",\"responseLength\":\"$context.responseLength\"}" }, "DeploymentId": { - "Ref": "cfapilambdaoverrideLambdaRestApiDeployment82ACBB00e7f3a114a506221ddcaf53765c4dd518" + "Ref": "cfapilambdaoverrideLambdaRestApiDeployment82ACBB00a88f54114ca67f75c1f46116226ebd9d" }, "MethodSettings": [ { @@ -290,7 +461,10 @@ "cfapilambdaoverrideLambdaRestApistaticGET81EF9C24": { "Type": "AWS::ApiGateway::Method", "Properties": { - "AuthorizationType": "NONE", + "AuthorizationType": "CUSTOM", + "AuthorizerId": { + "Ref": "cftaplamoverridebehaviorauthorizer74D77225" + }, "HttpMethod": "GET", "Integration": { "IntegrationHttpMethod": "GET", @@ -411,7 +585,10 @@ "cfapilambdaoverrideLambdaRestApidynamicGET15050D54": { "Type": "AWS::ApiGateway::Method", "Properties": { - "AuthorizationType": "NONE", + "AuthorizationType": "CUSTOM", + "AuthorizerId": { + "Ref": "cftaplamoverridebehaviorauthorizer74D77225" + }, "HttpMethod": "GET", "Integration": { "IntegrationHttpMethod": "POST", diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-override-behavior.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-override-behavior.ts index 4899f519d..350777aea 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-override-behavior.ts +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.cftaplam-override-behavior.ts @@ -20,7 +20,7 @@ import * as apigateway from 'aws-cdk-lib/aws-apigateway'; import * as cdk from 'aws-cdk-lib'; import { Duration } from "aws-cdk-lib"; import * as origins from 'aws-cdk-lib/aws-cloudfront-origins'; -import { generateIntegStackName, suppressAutoDeleteHandlerWarnings } from '@aws-solutions-constructs/core'; +import { generateIntegStackName, suppressAutoDeleteHandlerWarnings, CreateApiAuthorizer } from '@aws-solutions-constructs/core'; // Setup const app = new App(); @@ -54,7 +54,8 @@ const construct = new CloudFrontToApiGatewayToLambda(stack, 'cf-api-lambda-overr apiGatewayProps: { proxy: false, defaultMethodOptions: { - authorizationType: apigateway.AuthorizationType.NONE, + authorizationType: apigateway.AuthorizationType.CUSTOM, + authorizer: CreateApiAuthorizer(stack, `${generateIntegStackName(__filename)}-authorizer`) }, }, cloudFrontDistributionProps: { diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/test.cloudfront-apigateway-lambda.test.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/test.cloudfront-apigateway-lambda.test.ts index e5e225b2d..3b0849a12 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/test.cloudfront-apigateway-lambda.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/test.cloudfront-apigateway-lambda.test.ts @@ -26,6 +26,7 @@ function deployNewFunc(stack: cdk.Stack) { }; return new CloudFrontToApiGatewayToLambda(stack, 'test-cloudfront-apigateway-lambda', { + apiGatewayProps: { defaultMethodOptions: { authorizationType: api.AuthorizationType.NONE }}, lambdaFunctionProps }); } @@ -38,6 +39,7 @@ function useExistingFunc(stack: cdk.Stack) { }; return new CloudFrontToApiGatewayToLambda(stack, 'test-cloudfront-apigateway-lambda', { + apiGatewayProps: { defaultMethodOptions: { authorizationType: api.AuthorizationType.NONE }}, existingLambdaObj: new lambda.Function(stack, 'MyExistingFunction', lambdaFunctionProps) }); } @@ -119,6 +121,7 @@ test('check exception for Missing existingObj from props', () => { const stack = new cdk.Stack(); const props: CloudFrontToApiGatewayToLambdaProps = { + apiGatewayProps: { defaultMethodOptions: { authorizationType: api.AuthorizationType.NONE }}, }; try { @@ -132,6 +135,7 @@ test('check no prop', () => { const stack = new cdk.Stack(); const props: CloudFrontToApiGatewayToLambdaProps = { + apiGatewayProps: { defaultMethodOptions: { authorizationType: api.AuthorizationType.NONE }}, }; try { @@ -155,6 +159,7 @@ test('override api gateway properties with existingLambdaObj', () => { new CloudFrontToApiGatewayToLambda(stack, 'test-cloudfront-apigateway-lambda', { existingLambdaObj: fn, apiGatewayProps: { + defaultMethodOptions: { authorizationType: api.AuthorizationType.NONE }, description: "Override description" } }); @@ -182,6 +187,7 @@ test('override api gateway properties without existingLambdaObj', () => { handler: 'index.handler' }, apiGatewayProps: { + defaultMethodOptions: { authorizationType: api.AuthorizationType.NONE }, endpointConfiguration: { types: [api.EndpointType.PRIVATE], }, @@ -212,6 +218,7 @@ test('Cloudfront logging bucket with destroy removal policy and auto delete obje handler: 'index.handler' }, apiGatewayProps: { + defaultMethodOptions: { authorizationType: api.AuthorizationType.NONE }, endpointConfiguration: { types: [api.EndpointType.PRIVATE], } @@ -277,6 +284,7 @@ test('Confirm CheckLambdaProps is being called', () => { }); const props: CloudFrontToApiGatewayToLambdaProps = { + apiGatewayProps: { defaultMethodOptions: { authorizationType: api.AuthorizationType.NONE }}, existingLambdaObj, lambdaFunctionProps: { runtime: lambda.Runtime.NODEJS_18_X, @@ -296,6 +304,7 @@ test("Confirm CheckCloudFrontProps is being called", () => { expect(() => { new CloudFrontToApiGatewayToLambda(stack, "test-cloudfront-apigateway-lambda", { + apiGatewayProps: { defaultMethodOptions: { authorizationType: api.AuthorizationType.NONE }}, lambdaFunctionProps: { runtime: lambda.Runtime.NODEJS_18_X, handler: 'index.handler', @@ -315,3 +324,41 @@ test("Confirm CheckCloudFrontProps is being called", () => { }); }).toThrowError('responseHeadersPolicyProps.securityHeadersBehavior can only be passed if httpSecurityHeaders is set to `false`.'); }); + +test('confirm error thrown for AWS_IAM authorization', () => { + const stack = new cdk.Stack(); + const lambdaFunctionProps: lambda.FunctionProps = { + code: lambda.Code.fromAsset(`${__dirname}/lambda`), + runtime: lambda.Runtime.NODEJS_16_X, + handler: 'index.handler' + }; + + const props = { + apiGatewayProps: { defaultMethodOptions: { authorizationType: 'AWS_IAM' }}, + lambdaFunctionProps + }; + + const app = () => { + new CloudFrontToApiGatewayToLambda(stack, 'test-one', props); + }; + expect(app).toThrowError(/Amazon API Gateway Rest APIs integrated with Amazon CloudFront do not support AWS_IAM authorization/); +}); + +test('confirm error thrown for unspecified authorization', () => { + const stack = new cdk.Stack(); + const lambdaFunctionProps: lambda.FunctionProps = { + code: lambda.Code.fromAsset(`${__dirname}/lambda`), + runtime: lambda.Runtime.NODEJS_16_X, + handler: 'index.handler' + }; + + const props = { + apiGatewayProps: { }, + lambdaFunctionProps + }; + + const app = () => { + new CloudFrontToApiGatewayToLambda(stack, 'test-one', props); + }; + expect(app).toThrowError(/As of v2.48.0, an explicit authorization type is required for CloudFront\/API Gateway patterns/); +}); diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-customCloudfrontLoggingBucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-customCloudfrontLoggingBucket.expected.json index 410729c9b..1286d45ab 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-customCloudfrontLoggingBucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-customCloudfrontLoggingBucket.expected.json @@ -72,7 +72,7 @@ } } }, - "cftapicustomCloudfrontLoggingBucketapiAuthFunctionServiceRole3CEABBB5": { + "cftapicustomCloudfrontLoggingBucketapiauthorizerAuthFunctionServiceRole13E010C8": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -103,7 +103,7 @@ ] } }, - "cftapicustomCloudfrontLoggingBucketapiAuthFunctionC39181D9": { + "cftapicustomCloudfrontLoggingBucketapiauthorizerAuthFunction38CBEC38": { "Type": "AWS::Lambda::Function", "Properties": { "Code": { @@ -115,14 +115,14 @@ "Handler": ".handler", "Role": { "Fn::GetAtt": [ - "cftapicustomCloudfrontLoggingBucketapiAuthFunctionServiceRole3CEABBB5", + "cftapicustomCloudfrontLoggingBucketapiauthorizerAuthFunctionServiceRole13E010C8", "Arn" ] }, "Runtime": "nodejs16.x" }, "DependsOn": [ - "cftapicustomCloudfrontLoggingBucketapiAuthFunctionServiceRole3CEABBB5" + "cftapicustomCloudfrontLoggingBucketapiauthorizerAuthFunctionServiceRole13E010C8" ], "Metadata": { "cfn_nag": { @@ -143,13 +143,13 @@ } } }, - "cftapicustomCloudfrontLoggingBucketapiAuthFunctioncftapicustomCloudfrontLoggingBucketcftapicustomCloudfrontLoggingBucketapiauthorizerFD948D42Permissions0550732B": { + "cftapicustomCloudfrontLoggingBucketapiauthorizerAuthFunctioncftapicustomCloudfrontLoggingBucketcftapicustomCloudfrontLoggingBucketapiauthorizerFD948D42Permissions5BB59610": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", "FunctionName": { "Fn::GetAtt": [ - "cftapicustomCloudfrontLoggingBucketapiAuthFunctionC39181D9", + "cftapicustomCloudfrontLoggingBucketapiauthorizerAuthFunction38CBEC38", "Arn" ] }, @@ -199,7 +199,7 @@ ":", { "Fn::GetAtt": [ - "cftapicustomCloudfrontLoggingBucketapiAuthFunctionC39181D9", + "cftapicustomCloudfrontLoggingBucketapiauthorizerAuthFunction38CBEC38", "Arn" ] } @@ -216,7 +216,7 @@ ":", { "Fn::GetAtt": [ - "cftapicustomCloudfrontLoggingBucketapiAuthFunctionC39181D9", + "cftapicustomCloudfrontLoggingBucketapiauthorizerAuthFunction38CBEC38", "Arn" ] } @@ -227,7 +227,7 @@ ":lambda:path/2015-03-31/functions/", { "Fn::GetAtt": [ - "cftapicustomCloudfrontLoggingBucketapiAuthFunctionC39181D9", + "cftapicustomCloudfrontLoggingBucketapiauthorizerAuthFunction38CBEC38", "Arn" ] }, diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-no-arguments.expected.json index 8805504b5..ac9b137df 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-no-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.cftapi-no-arguments.expected.json @@ -72,7 +72,7 @@ } } }, - "cftapinoargumentsapiAuthFunctionServiceRole97907F32": { + "cftapinoargumentsapiauthorizerAuthFunctionServiceRole33BC576C": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -103,7 +103,7 @@ ] } }, - "cftapinoargumentsapiAuthFunctionFCC239B2": { + "cftapinoargumentsapiauthorizerAuthFunction5A0061F7": { "Type": "AWS::Lambda::Function", "Properties": { "Code": { @@ -115,14 +115,14 @@ "Handler": ".handler", "Role": { "Fn::GetAtt": [ - "cftapinoargumentsapiAuthFunctionServiceRole97907F32", + "cftapinoargumentsapiauthorizerAuthFunctionServiceRole33BC576C", "Arn" ] }, "Runtime": "nodejs16.x" }, "DependsOn": [ - "cftapinoargumentsapiAuthFunctionServiceRole97907F32" + "cftapinoargumentsapiauthorizerAuthFunctionServiceRole33BC576C" ], "Metadata": { "cfn_nag": { @@ -143,13 +143,13 @@ } } }, - "cftapinoargumentsapiAuthFunctioncftapinoargumentscftapinoargumentsapiauthorizerCA624E68Permissions8A31DDB0": { + "cftapinoargumentsapiauthorizerAuthFunctioncftapinoargumentscftapinoargumentsapiauthorizerCA624E68Permissions3907BB66": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", "FunctionName": { "Fn::GetAtt": [ - "cftapinoargumentsapiAuthFunctionFCC239B2", + "cftapinoargumentsapiauthorizerAuthFunction5A0061F7", "Arn" ] }, @@ -199,7 +199,7 @@ ":", { "Fn::GetAtt": [ - "cftapinoargumentsapiAuthFunctionFCC239B2", + "cftapinoargumentsapiauthorizerAuthFunction5A0061F7", "Arn" ] } @@ -216,7 +216,7 @@ ":", { "Fn::GetAtt": [ - "cftapinoargumentsapiAuthFunctionFCC239B2", + "cftapinoargumentsapiauthorizerAuthFunction5A0061F7", "Arn" ] } @@ -227,7 +227,7 @@ ":lambda:path/2015-03-31/functions/", { "Fn::GetAtt": [ - "cftapinoargumentsapiAuthFunctionFCC239B2", + "cftapinoargumentsapiauthorizerAuthFunction5A0061F7", "Arn" ] }, diff --git a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-existing-waf-to-multiple-gateways.expected.json b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-existing-waf-to-multiple-gateways.expected.json index 95d812c94..0ad2c0e45 100644 --- a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-existing-waf-to-multiple-gateways.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-existing-waf-to-multiple-gateways.expected.json @@ -71,7 +71,7 @@ } } }, - "testOneAuthFunctionServiceRole08948D81": { + "testOneauthorizerAuthFunctionServiceRoleE1B4208E": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -102,7 +102,7 @@ ] } }, - "testOneAuthFunction09B411BE": { + "testOneauthorizerAuthFunctionB12D8D93": { "Type": "AWS::Lambda::Function", "Properties": { "Code": { @@ -114,14 +114,14 @@ "Handler": ".handler", "Role": { "Fn::GetAtt": [ - "testOneAuthFunctionServiceRole08948D81", + "testOneauthorizerAuthFunctionServiceRoleE1B4208E", "Arn" ] }, "Runtime": "nodejs16.x" }, "DependsOn": [ - "testOneAuthFunctionServiceRole08948D81" + "testOneauthorizerAuthFunctionServiceRoleE1B4208E" ], "Metadata": { "cfn_nag": { @@ -142,13 +142,13 @@ } } }, - "testOneAuthFunctionwafapiexistingwaftomultiplegatewaystestOneauthorizer7F4D0710PermissionsB6FB990A": { + "testOneauthorizerAuthFunctionwafapiexistingwaftomultiplegatewaystestOneauthorizer7F4D0710Permissions21D6CD33": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", "FunctionName": { "Fn::GetAtt": [ - "testOneAuthFunction09B411BE", + "testOneauthorizerAuthFunctionB12D8D93", "Arn" ] }, @@ -198,7 +198,7 @@ ":", { "Fn::GetAtt": [ - "testOneAuthFunction09B411BE", + "testOneauthorizerAuthFunctionB12D8D93", "Arn" ] } @@ -215,7 +215,7 @@ ":", { "Fn::GetAtt": [ - "testOneAuthFunction09B411BE", + "testOneauthorizerAuthFunctionB12D8D93", "Arn" ] } @@ -226,7 +226,7 @@ ":lambda:path/2015-03-31/functions/", { "Fn::GetAtt": [ - "testOneAuthFunction09B411BE", + "testOneauthorizerAuthFunctionB12D8D93", "Arn" ] }, @@ -701,7 +701,7 @@ } } }, - "testTwoAuthFunctionServiceRole711DF356": { + "testTwoauthorizerAuthFunctionServiceRole4C3292A3": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -732,7 +732,7 @@ ] } }, - "testTwoAuthFunctionCD4DE414": { + "testTwoauthorizerAuthFunction1DBBD707": { "Type": "AWS::Lambda::Function", "Properties": { "Code": { @@ -744,14 +744,14 @@ "Handler": ".handler", "Role": { "Fn::GetAtt": [ - "testTwoAuthFunctionServiceRole711DF356", + "testTwoauthorizerAuthFunctionServiceRole4C3292A3", "Arn" ] }, "Runtime": "nodejs16.x" }, "DependsOn": [ - "testTwoAuthFunctionServiceRole711DF356" + "testTwoauthorizerAuthFunctionServiceRole4C3292A3" ], "Metadata": { "cfn_nag": { @@ -772,13 +772,13 @@ } } }, - "testTwoAuthFunctionwafapiexistingwaftomultiplegatewaystestTwoauthorizer9B2C525EPermissionsE4DE5C10": { + "testTwoauthorizerAuthFunctionwafapiexistingwaftomultiplegatewaystestTwoauthorizer9B2C525EPermissions44287234": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", "FunctionName": { "Fn::GetAtt": [ - "testTwoAuthFunctionCD4DE414", + "testTwoauthorizerAuthFunction1DBBD707", "Arn" ] }, @@ -828,7 +828,7 @@ ":", { "Fn::GetAtt": [ - "testTwoAuthFunctionCD4DE414", + "testTwoauthorizerAuthFunction1DBBD707", "Arn" ] } @@ -845,7 +845,7 @@ ":", { "Fn::GetAtt": [ - "testTwoAuthFunctionCD4DE414", + "testTwoauthorizerAuthFunction1DBBD707", "Arn" ] } @@ -856,7 +856,7 @@ ":lambda:path/2015-03-31/functions/", { "Fn::GetAtt": [ - "testTwoAuthFunctionCD4DE414", + "testTwoauthorizerAuthFunction1DBBD707", "Arn" ] }, diff --git a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-no-arguments.expected.json index 86097e7fe..19c2de1b4 100644 --- a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-no-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-no-arguments.expected.json @@ -71,7 +71,7 @@ } } }, - "testAuthFunctionServiceRole67229217": { + "testauthorizerAuthFunctionServiceRole6F05059E": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -102,7 +102,7 @@ ] } }, - "testAuthFunctionDEE315BB": { + "testauthorizerAuthFunctionCE2F3743": { "Type": "AWS::Lambda::Function", "Properties": { "Code": { @@ -114,14 +114,14 @@ "Handler": ".handler", "Role": { "Fn::GetAtt": [ - "testAuthFunctionServiceRole67229217", + "testauthorizerAuthFunctionServiceRole6F05059E", "Arn" ] }, "Runtime": "nodejs16.x" }, "DependsOn": [ - "testAuthFunctionServiceRole67229217" + "testauthorizerAuthFunctionServiceRole6F05059E" ], "Metadata": { "cfn_nag": { @@ -142,13 +142,13 @@ } } }, - "testAuthFunctionwafapinoargumentstestauthorizerB886165DPermissions26C374CC": { + "testauthorizerAuthFunctionwafapinoargumentstestauthorizerB886165DPermissions0BD74048": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", "FunctionName": { "Fn::GetAtt": [ - "testAuthFunctionDEE315BB", + "testauthorizerAuthFunctionCE2F3743", "Arn" ] }, @@ -198,7 +198,7 @@ ":", { "Fn::GetAtt": [ - "testAuthFunctionDEE315BB", + "testauthorizerAuthFunctionCE2F3743", "Arn" ] } @@ -215,7 +215,7 @@ ":", { "Fn::GetAtt": [ - "testAuthFunctionDEE315BB", + "testauthorizerAuthFunctionCE2F3743", "Arn" ] } @@ -226,7 +226,7 @@ ":lambda:path/2015-03-31/functions/", { "Fn::GetAtt": [ - "testAuthFunctionDEE315BB", + "testauthorizerAuthFunctionCE2F3743", "Arn" ] }, diff --git a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-partial-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-partial-arguments.expected.json index 7c6638d28..6b66fbce4 100644 --- a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-partial-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.wafapi-partial-arguments.expected.json @@ -71,7 +71,7 @@ } } }, - "testAuthFunctionServiceRole67229217": { + "testauthorizerAuthFunctionServiceRole6F05059E": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -102,7 +102,7 @@ ] } }, - "testAuthFunctionDEE315BB": { + "testauthorizerAuthFunctionCE2F3743": { "Type": "AWS::Lambda::Function", "Properties": { "Code": { @@ -114,14 +114,14 @@ "Handler": ".handler", "Role": { "Fn::GetAtt": [ - "testAuthFunctionServiceRole67229217", + "testauthorizerAuthFunctionServiceRole6F05059E", "Arn" ] }, "Runtime": "nodejs16.x" }, "DependsOn": [ - "testAuthFunctionServiceRole67229217" + "testauthorizerAuthFunctionServiceRole6F05059E" ], "Metadata": { "cfn_nag": { @@ -142,13 +142,13 @@ } } }, - "testAuthFunctionwafapipartialargumentstestauthorizerDC0C2973Permissions3E01E2C8": { + "testauthorizerAuthFunctionwafapipartialargumentstestauthorizerDC0C2973PermissionsCD5A1000": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", "FunctionName": { "Fn::GetAtt": [ - "testAuthFunctionDEE315BB", + "testauthorizerAuthFunctionCE2F3743", "Arn" ] }, @@ -198,7 +198,7 @@ ":", { "Fn::GetAtt": [ - "testAuthFunctionDEE315BB", + "testauthorizerAuthFunctionCE2F3743", "Arn" ] } @@ -215,7 +215,7 @@ ":", { "Fn::GetAtt": [ - "testAuthFunctionDEE315BB", + "testauthorizerAuthFunctionCE2F3743", "Arn" ] } @@ -226,7 +226,7 @@ ":lambda:path/2015-03-31/functions/", { "Fn::GetAtt": [ - "testAuthFunctionDEE315BB", + "testauthorizerAuthFunctionCE2F3743", "Arn" ] }, diff --git a/source/patterns/@aws-solutions-constructs/core/lib/apigateway-defaults.ts b/source/patterns/@aws-solutions-constructs/core/lib/apigateway-defaults.ts index 470f33bdc..0cd63b322 100644 --- a/source/patterns/@aws-solutions-constructs/core/lib/apigateway-defaults.ts +++ b/source/patterns/@aws-solutions-constructs/core/lib/apigateway-defaults.ts @@ -26,25 +26,25 @@ import { generatePhysicalName } from './utils'; /** * Private function to configure an api.RestApiProps * @param scope - the construct to which the RestApi should be attached to. - * @param _endpointType - endpoint type for Api Gateway e.g. Regional, Global, Private - * @param _logGroup - CW Log group for Api Gateway access logging + * @param endpointType - endpoint type for Api Gateway e.g. Regional, Global, Private + * @param logGroup - CW Log group for Api Gateway access logging */ -function DefaultRestApiProps(_endpointType: api.EndpointType[], _logGroup: LogGroup): api.RestApiProps { +function DefaultRestApiProps(endpointType: api.EndpointType[], logGroup: LogGroup, includeAuth: boolean = true): api.RestApiProps { return { endpointConfiguration: { - types: _endpointType + types: endpointType }, cloudWatchRole: false, // Configure API Gateway Access logging deployOptions: { - accessLogDestination: new api.LogGroupLogDestination(_logGroup), + accessLogDestination: new api.LogGroupLogDestination(logGroup), accessLogFormat: api.AccessLogFormat.jsonWithStandardFields(), loggingLevel: api.MethodLoggingLevel.INFO, dataTraceEnabled: false, tracingEnabled: true }, defaultMethodOptions: { - authorizationType: api.AuthorizationType.IAM + authorizationType: includeAuth ? api.AuthorizationType.IAM : undefined } } as api.RestApiProps; @@ -74,13 +74,15 @@ export function DefaultGlobalLambdaRestApiProps(_existingLambdaObj: lambda.Funct * Provides the default set of properties for Regional Lambda backed RestApi * @param scope - the construct to which the RestApi should be attached to. * @param _endpointType - endpoint type for Api Gateway e.g. Regional, Global, Private - * @param _logGroup - CW Log group for Api Gateway access logging + * @param logGroup - CW Log group for Api Gateway access logging */ -export function DefaultRegionalLambdaRestApiProps(_existingLambdaObj: lambda.Function, _logGroup: LogGroup): api.LambdaRestApiProps { - const baseProps: api.RestApiProps = DefaultRestApiProps([api.EndpointType.REGIONAL], _logGroup); +export function DefaultRegionalLambdaRestApiProps(existingLambdaObj: lambda.Function, + logGroup: LogGroup, + includeAuth: boolean = true): api.LambdaRestApiProps { + const baseProps: api.RestApiProps = DefaultRestApiProps([api.EndpointType.REGIONAL], logGroup, includeAuth); const extraProps: api.LambdaRestApiProps = { - handler: _existingLambdaObj, + handler: existingLambdaObj, }; return Object.assign(baseProps, extraProps); diff --git a/source/patterns/@aws-solutions-constructs/core/lib/apigateway-helper.ts b/source/patterns/@aws-solutions-constructs/core/lib/apigateway-helper.ts index 3ba3363dc..ea26649a2 100644 --- a/source/patterns/@aws-solutions-constructs/core/lib/apigateway-helper.ts +++ b/source/patterns/@aws-solutions-constructs/core/lib/apigateway-helper.ts @@ -217,11 +217,13 @@ export interface RegionalLambdaRestApiResponse { * @param apiGatewayProps - (optional) user-specified properties to override the default properties. */ export function RegionalLambdaRestApi(scope: Construct, existingLambdaObj: lambda.Function, - apiGatewayProps?: apigateway.LambdaRestApiProps, logGroupProps?: logs.LogGroupProps): RegionalLambdaRestApiResponse { + apiGatewayProps?: apigateway.LambdaRestApiProps, + logGroupProps?: logs.LogGroupProps, + useDefaultAuth: boolean = true): RegionalLambdaRestApiResponse { // Configure log group for API Gateway AccessLogging const logGroup = buildLogGroup(scope, 'ApiAccessLogGroup', logGroupProps); - const defaultProps = apiDefaults.DefaultRegionalLambdaRestApiProps(existingLambdaObj, logGroup); + const defaultProps = apiDefaults.DefaultRegionalLambdaRestApiProps(existingLambdaObj, logGroup, useDefaultAuth); const configureLambdaRestApiResponse = configureLambdaRestApi(scope, defaultProps, apiGatewayProps); return { api: configureLambdaRestApiResponse.api, role: configureLambdaRestApiResponse.role, group: logGroup}; } diff --git a/source/patterns/@aws-solutions-constructs/core/test/glue-job-helper.test.ts b/source/patterns/@aws-solutions-constructs/core/test/glue-job-helper.test.ts index 545f282af..3504e7fa5 100644 --- a/source/patterns/@aws-solutions-constructs/core/test/glue-job-helper.test.ts +++ b/source/patterns/@aws-solutions-constructs/core/test/glue-job-helper.test.ts @@ -169,7 +169,6 @@ test('Test custom deployment properties', () => { }); const template = Template.fromStack(stack); - defaults.printWarning(`***********${JSON.stringify(template)}`); // check if Glue Job Resource was created correctly template.hasResource('AWS::Glue::Job', { Properties: { diff --git a/source/patterns/@aws-solutions-constructs/core/test/test-helper.ts b/source/patterns/@aws-solutions-constructs/core/test/test-helper.ts index 4ed5c4cd5..fc7065b2a 100644 --- a/source/patterns/@aws-solutions-constructs/core/test/test-helper.ts +++ b/source/patterns/@aws-solutions-constructs/core/test/test-helper.ts @@ -210,25 +210,12 @@ export function CreateTestApi(stack: Stack, id: string): api.LambdaRestApi { addCfnSuppressRules(lambdaFunction, [{ id: "W58", reason: "Test Resource" }]); addCfnSuppressRules(lambdaFunction, [{ id: "W89", reason: "Test Resource" }]); addCfnSuppressRules(lambdaFunction, [{ id: "W92", reason: "Test Resource" }]); - const authFn = new lambda.Function(stack, `${id}AuthFunction`, { - code: lambda.Code.fromAsset(`${__dirname}/lambda`), - runtime: lambda.Runtime.NODEJS_16_X, - handler: ".handler", - }); - addCfnSuppressRules(authFn, [{ id: "W58", reason: "Test Resource" }]); - addCfnSuppressRules(authFn, [{ id: "W89", reason: "Test Resource" }]); - addCfnSuppressRules(authFn, [{ id: "W92", reason: "Test Resource" }]); - - const auth = new api.RequestAuthorizer(stack, `${id}-authorizer`, { - handler: authFn, - identitySources: [api.IdentitySource.header('Authorization')] - }); const restApi = new api.LambdaRestApi(stack, `${id}Api`, { handler: lambdaFunction, defaultMethodOptions: { authorizationType: api.AuthorizationType.CUSTOM, - authorizer: auth + authorizer: CreateApiAuthorizer(stack, `${id}-authorizer`) } }); @@ -251,6 +238,24 @@ export function CreateTestApi(stack: Stack, id: string): api.LambdaRestApi { return restApi; } +export function CreateApiAuthorizer(stack: Stack, id: string): api.IAuthorizer { + const authFn = new lambda.Function(stack, `${id}AuthFunction`, { + code: lambda.Code.fromAsset(`${__dirname}/lambda`), + runtime: lambda.Runtime.NODEJS_16_X, + handler: ".handler", + }); + addCfnSuppressRules(authFn, [{ id: "W58", reason: "Test Resource" }]); + addCfnSuppressRules(authFn, [{ id: "W89", reason: "Test Resource" }]); + addCfnSuppressRules(authFn, [{ id: "W92", reason: "Test Resource" }]); + + const authorizer = new api.RequestAuthorizer(stack, id, { + handler: authFn, + identitySources: [api.IdentitySource.header('Authorization')] + }); + + return authorizer; +} + // Create a short, unique to this stack name // technically this is not 100% OK, as it only uses a portion of the // stack guid - but it's for tests only so if the last segment of 2 stack guids collide someday From a017f274b01c1c5cc1442acf59de7da84fd83245 Mon Sep 17 00:00:00 2001 From: biffgaut <78155736+biffgaut@users.noreply.github.com> Date: Wed, 20 Dec 2023 11:58:20 -0500 Subject: [PATCH 07/14] chore(aws-lambda-sqs): add a test that explicitly creates a cmk key (#1045) * Add a test that creates a CMK * Ensure CMK key is cleaned up when test is torn down --- .../test/integ.lamsqs-useCmk.expected.json | 417 ++++++++++++++++++ .../test/integ.lamsqs-useCmk.ts | 40 ++ 2 files changed, 457 insertions(+) create mode 100644 source/patterns/@aws-solutions-constructs/aws-lambda-sqs/test/integ.lamsqs-useCmk.expected.json create mode 100755 source/patterns/@aws-solutions-constructs/aws-lambda-sqs/test/integ.lamsqs-useCmk.ts diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-sqs/test/integ.lamsqs-useCmk.expected.json b/source/patterns/@aws-solutions-constructs/aws-lambda-sqs/test/integ.lamsqs-useCmk.expected.json new file mode 100644 index 000000000..57b6b68bd --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-sqs/test/integ.lamsqs-useCmk.expected.json @@ -0,0 +1,417 @@ +{ + "Description": "Integration Test for aws-lambda-sqs", + "Resources": { + "testlambdasqsLambdaFunctionServiceRoleC0430CA8": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/lambda/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "LambdaFunctionServiceRolePolicy" + } + ] + } + }, + "testlambdasqsLambdaFunctionServiceRoleDefaultPolicyE3CAD09D": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "sqs:SendMessage", + "sqs:GetQueueAttributes", + "sqs:GetQueueUrl" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "testlambdasqsqueueDD178B7C", + "Arn" + ] + } + }, + { + "Action": [ + "kms:Decrypt", + "kms:Encrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "testlambdasqsEncryptionKeyDAAFCB9F", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "testlambdasqsLambdaFunctionServiceRoleDefaultPolicyE3CAD09D", + "Roles": [ + { + "Ref": "testlambdasqsLambdaFunctionServiceRoleC0430CA8" + } + ] + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W12", + "reason": "Lambda needs the following minimum required permissions to send trace data to X-Ray and access ENIs in a VPC." + } + ] + } + } + }, + "testlambdasqsLambdaFunction28E890A1": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "42887c62b1163d790cdb42902037c9f639feb35681616a16826e400c8d1a4435.zip" + }, + "Environment": { + "Variables": { + "AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1", + "SQS_QUEUE_URL": { + "Ref": "testlambdasqsqueueDD178B7C" + } + } + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "testlambdasqsLambdaFunctionServiceRoleC0430CA8", + "Arn" + ] + }, + "Runtime": "nodejs16.x", + "TracingConfig": { + "Mode": "Active" + } + }, + "DependsOn": [ + "testlambdasqsLambdaFunctionServiceRoleDefaultPolicyE3CAD09D", + "testlambdasqsLambdaFunctionServiceRoleC0430CA8" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "Lambda functions has the required permission to write CloudWatch Logs. It uses custom policy instead of arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole with tighter permissions." + }, + { + "id": "W89", + "reason": "This is not a rule for the general case, just for specific use cases/industries" + }, + { + "id": "W92", + "reason": "Impossible for us to define the correct concurrency for clients" + } + ] + } + } + }, + "testlambdasqsdeadLetterQueueC34BC0BD": { + "Type": "AWS::SQS::Queue", + "Properties": { + "KmsMasterKeyId": "alias/aws/sqs" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "testlambdasqsdeadLetterQueuePolicy270F1626": { + "Type": "AWS::SQS::QueuePolicy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "sqs:DeleteMessage", + "sqs:ReceiveMessage", + "sqs:SendMessage", + "sqs:GetQueueAttributes", + "sqs:RemovePermission", + "sqs:AddPermission", + "sqs:SetQueueAttributes" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": { + "Fn::GetAtt": [ + "testlambdasqsdeadLetterQueueC34BC0BD", + "Arn" + ] + }, + "Sid": "QueueOwnerOnlyAccess" + }, + { + "Action": "SQS:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": { + "Fn::GetAtt": [ + "testlambdasqsdeadLetterQueueC34BC0BD", + "Arn" + ] + }, + "Sid": "HttpsOnly" + } + ], + "Version": "2012-10-17" + }, + "Queues": [ + { + "Ref": "testlambdasqsdeadLetterQueueC34BC0BD" + } + ] + } + }, + "testlambdasqsEncryptionKeyDAAFCB9F": { + "Type": "AWS::KMS::Key", + "Properties": { + "EnableKeyRotation": true, + "KeyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "testlambdasqsqueueDD178B7C": { + "Type": "AWS::SQS::Queue", + "Properties": { + "KmsMasterKeyId": { + "Fn::GetAtt": [ + "testlambdasqsEncryptionKeyDAAFCB9F", + "Arn" + ] + }, + "RedrivePolicy": { + "deadLetterTargetArn": { + "Fn::GetAtt": [ + "testlambdasqsdeadLetterQueueC34BC0BD", + "Arn" + ] + }, + "maxReceiveCount": 15 + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "testlambdasqsqueuePolicy3FC623C5": { + "Type": "AWS::SQS::QueuePolicy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "sqs:DeleteMessage", + "sqs:ReceiveMessage", + "sqs:SendMessage", + "sqs:GetQueueAttributes", + "sqs:RemovePermission", + "sqs:AddPermission", + "sqs:SetQueueAttributes" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": { + "Fn::GetAtt": [ + "testlambdasqsqueueDD178B7C", + "Arn" + ] + }, + "Sid": "QueueOwnerOnlyAccess" + }, + { + "Action": "SQS:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": { + "Fn::GetAtt": [ + "testlambdasqsqueueDD178B7C", + "Arn" + ] + }, + "Sid": "HttpsOnly" + } + ], + "Version": "2012-10-17" + }, + "Queues": [ + { + "Ref": "testlambdasqsqueueDD178B7C" + } + ] + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-lambda-sqs/test/integ.lamsqs-useCmk.ts b/source/patterns/@aws-solutions-constructs/aws-lambda-sqs/test/integ.lamsqs-useCmk.ts new file mode 100755 index 000000000..d24e623ab --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-lambda-sqs/test/integ.lamsqs-useCmk.ts @@ -0,0 +1,40 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +// Imports +import { App, Stack, RemovalPolicy } from "aws-cdk-lib"; +import { LambdaToSqs, LambdaToSqsProps } from "../lib"; +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import { generateIntegStackName } from '@aws-solutions-constructs/core'; + +// Setup +const app = new App(); +const stack = new Stack(app, generateIntegStackName(__filename)); +stack.templateOptions.description = 'Integration Test for aws-lambda-sqs'; + +// Definitions +const props: LambdaToSqsProps = { + lambdaFunctionProps: { + runtime: lambda.Runtime.NODEJS_16_X, + handler: 'index.handler', + code: lambda.Code.fromAsset(`${__dirname}/lambda`) + }, + encryptionKeyProps: { + removalPolicy: RemovalPolicy.DESTROY + } +}; + +new LambdaToSqs(stack, 'test-lambda-sqs', props); + +// Synth +app.synth(); \ No newline at end of file From 012f9e7b6ebd3a717ff120941131a84e803b2922 Mon Sep 17 00:00:00 2001 From: Ryan Hayes Date: Tue, 9 Jan 2024 08:43:03 -0500 Subject: [PATCH 08/14] feat(aws-cloudfront-s3): update construct to use origin access controls; add support for CMK-encrypted buckets (#1038) * feat: upgrade aws-cloudfront-s3 to use oac instead of oai * feat: added testing and documentation notes * merge: alignment with #1037 * fix: version number for aws-cloudfront-s3 custom resource * fix: edits to custom resource package.json file * fix: issue with installing deps for custom resource * feat: create new S3OacOrigin, consolidate custom resources * chore: change bucket variable naming in 'aws-cloudfront-s3/lib/index.ts' * chore: consolidate custom resources, additional cleanup * chore: update resources/package.json file * chore: add resources dependency to aws-cloudfront-s3 package.json file * chore: edit source/package.json to revert workspaces specification * chore: add aws-cdk-lib to resources/package.json * chore: remove carat from aws-cdk-lib dependency * Update package.json * chore: pr updates * chore: pr updates * chore: update integ tests * chore: update package.json * fix: oac naming and resources/package.json * chore: add cfn suppression statements to custom resource providers where applicable * chore: cfn-nag statements for bucket deployments * chore: remove bucketDeployment samples * chore: update imports and integ tests * chore: update integ tests * chore: updated naming for `defaults.CloudFrontDistributionForS3` function * chore: edited comments for s3-oac-origin.ts * chore: minor organizational improvements to import statements * chore: update custom resource description; remove CWLogs policy statement * chore: naming and spelling * feat: add checking for duplicate key policies by sid * feat: re-add support for legacy HttpOrigin configs * chore: eslint * chore: more eslint * chore: cfn-nag * feat: add printWarning for customers using HttpOrigin * fix: remove warning prefix * chore: add comment on default key policy name specification * chore: additional testing, readme updates * feat: refactored props for createCloudFrontDistributionForS3() * chore: print warnings language updates * chore: switch file naming for shared functions in resources/ * chore: update imports in resources/ * feat: overwrite existing key policy, update unit tests * fix: pass id from the top level through to createCloudFrontDistributionForS3 * chore: update integration tests --- .../aws-cloudfront-s3/.eslintignore | 4 +- .../aws-cloudfront-s3/README.md | 13 +- .../aws-cloudfront-s3/lib/index.ts | 80 +- .../aws-cloudfront-s3/package.json | 2 + ...k-provided-as-existingbucket.expected.json | 958 ++++++++++++++++++ ...ted-with-cmk-provided-as-existingbucket.ts | 49 + ...y-provided-as-existingbucket.expected.json | 592 +++++++++++ ...-managed-key-provided-as-existingbucket.ts | 43 + ...fts3-bucket-with-http-origin.expected.json | 559 ++++++++++ .../integ.cfts3-bucket-with-http-origin.ts | 47 + .../integ.cfts3-cmk-encryption.expected.json | 527 ++++++++++ ...-cmk-provided-as-bucket-prop.expected.json | 958 ++++++++++++++++++ ...integ.cfts3-cmk-provided-as-bucket-prop.ts | 45 + .../integ.cfts3-custom-headers.expected.json | 78 +- ...nteg.cfts3-custom-originPath.expected.json | 78 +- ...ustomCloudFrontLoggingBucket.expected.json | 698 +++++++++++++ ...g.cfts3-customLoggingBuckets.expected.json | 78 +- .../integ.cfts3-existing-bucket.expected.json | 78 +- .../integ.cfts3-no-arguments.expected.json | 78 +- ...eg.cfts3-no-security-headers.expected.json | 78 +- .../test/test.cloudfront-s3.test.ts | 177 +++- ...AssetExistingLambdaFunctions.expected.json | 2 +- ...iFromAssetNewLambdaFunctions.expected.json | 2 +- ...-apiFromAssetWithCognitoAuth.expected.json | 2 +- .../@aws-solutions-constructs/core/index.ts | 1 + .../lib/cloudfront-distribution-defaults.ts | 12 +- .../lib/cloudfront-distribution-helper.ts | 60 +- .../core/lib/s3-oac-origin.ts | 88 ++ .../cloudfront-distribution-s3-helper.test.ts | 207 +--- .../resources/.eslintignore | 8 +- .../resources/.gitignore | 5 +- .../resources/index.ts | 1 + .../index.ts | 135 +++ .../resources/lib/key-policy-updater.ts | 89 ++ .../{index.mjs => index.ts} | 6 +- .../resources/lib/template-writer.ts | 23 +- .../resources/lib/utils.ts | 34 + .../resources/package.json | 30 +- ...g.template-writer-from-asset.expected.json | 2 +- ...late-writer-from-large-asset.expected.json | 2 +- .../test/kms-key-policy-updater.test.ts | 245 +++++ .../resources/test/template-writer.test.ts | 2 +- 42 files changed, 5766 insertions(+), 410 deletions(-) create mode 100644 source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-bucket-encrypted-with-cmk-provided-as-existingbucket.expected.json create mode 100644 source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-bucket-encrypted-with-cmk-provided-as-existingbucket.ts create mode 100644 source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-bucket-encrypted-with-managed-key-provided-as-existingbucket.expected.json create mode 100644 source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-bucket-encrypted-with-managed-key-provided-as-existingbucket.ts create mode 100644 source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-bucket-with-http-origin.expected.json create mode 100644 source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-bucket-with-http-origin.ts create mode 100644 source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-cmk-encryption.expected.json create mode 100644 source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-cmk-provided-as-bucket-prop.expected.json create mode 100644 source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-cmk-provided-as-bucket-prop.ts create mode 100644 source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customCloudFrontLoggingBucket.expected.json create mode 100644 source/patterns/@aws-solutions-constructs/core/lib/s3-oac-origin.ts create mode 100644 source/patterns/@aws-solutions-constructs/resources/lib/key-policy-updater-custom-resource/index.ts create mode 100644 source/patterns/@aws-solutions-constructs/resources/lib/key-policy-updater.ts rename source/patterns/@aws-solutions-constructs/resources/lib/template-writer-custom-resource/{index.mjs => index.ts} (93%) create mode 100644 source/patterns/@aws-solutions-constructs/resources/lib/utils.ts create mode 100644 source/patterns/@aws-solutions-constructs/resources/test/kms-key-policy-updater.test.ts diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/.eslintignore b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/.eslintignore index 0819e2e65..d901eca68 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/.eslintignore +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/.eslintignore @@ -1,5 +1,5 @@ -lib/*.js +lib/**/*.js test/*.js *.d.ts coverage -test/lambda/index.js \ No newline at end of file +node_modules diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/README.md b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/README.md index c0f53ff08..fb6a3d907 100755 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/README.md +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/README.md @@ -19,7 +19,7 @@ |![Java Logo](https://docs.aws.amazon.com/cdk/api/latest/img/java32.png) Java|`software.amazon.awsconstructs.services.cloudfronts3`| ## Overview -This AWS Solutions Construct implements an AWS CloudFront fronting an AWS S3 Bucket. +This AWS Solutions Construct provisions an Amazon CloudFront Distribution that serves objects from an AWS S3 Bucket via an Origin Access Control (OAC). Here is a minimal deployable pattern definition: @@ -71,12 +71,13 @@ new CloudFrontToS3(this, "test-cloudfront-s3", new CloudFrontToS3Props.Builder() | **Name** | **Type** | **Description** | |:-------------|:----------------|-----------------| -|cloudFrontWebDistribution|[`cloudfront.Distribution`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_cloudfront.Distribution.html)|Returns an instance of cloudfront.Distribution created by the construct| -|cloudFrontFunction?|[`cloudfront.Function`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_cloudfront.Function.html)|Returns an instance of the Cloudfront function created by the pattern.| -|cloudFrontLoggingBucket|[`s3.Bucket`](https://docs.aws.amazon.com/cdk/api/latest/docs/aws-s3-readme.html)|Returns an instance of the logging bucket for CloudFront Distribution.| -|s3BucketInterface|[`s3.IBucket`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.IBucket.html)|Returns an instance of s3.IBucket created by the construct| -|s3Bucket?|[`s3.Bucket`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.Bucket.html)|Returns an instance of s3.Bucket created by the construct. IMPORTANT: If existingBucketObj was provided in Pattern Construct Props, this property will be `undefined`| +|cloudFrontWebDistribution|[`cloudfront.Distribution`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_cloudfront.Distribution.html)|Returns an instance of cloudfront.Distribution created by the construct.| +|cloudFrontFunction?|[`cloudfront.Function`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_cloudfront.Function.html)|Returns an instance of the Cloudfront function created by the construct.| +|cloudFrontLoggingBucket|[`s3.Bucket`](https://docs.aws.amazon.com/cdk/api/latest/docs/aws-s3-readme.html)|Returns an instance of the logging bucket for the CloudFront Distribution.| +|s3BucketInterface|[`s3.IBucket`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.IBucket.html)|Returns an instance of s3.IBucket created by the construct.| +|s3Bucket?|[`s3.Bucket`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.Bucket.html)|Returns an instance of s3.Bucket created by the construct. IMPORTANT: If `existingBucketObj` was provided in Pattern Construct Props, this property will be `undefined`| |s3LoggingBucket?|[`s3.Bucket`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.Bucket.html)|Returns an instance of s3.Bucket created by the construct as the logging bucket for the primary bucket.| +|originAccessControl?|[`cloudfront.CfnOriginAccessControl`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.cloudfront.CfnOriginAccessControl.html)|Returns an instance of cloudfront.CfnOriginAccessControl created by the construct.| ## Default settings diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/lib/index.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/lib/index.ts index 45776a697..b8e8f1dbe 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/lib/index.ts +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/lib/index.ts @@ -11,11 +11,15 @@ * and limitations under the License. */ +import { Aws } from 'aws-cdk-lib'; import * as cloudfront from 'aws-cdk-lib/aws-cloudfront'; +import * as iam from 'aws-cdk-lib/aws-iam'; +import * as kms from 'aws-cdk-lib/aws-kms'; import * as s3 from 'aws-cdk-lib/aws-s3'; +import * as defaults from '@aws-solutions-constructs/core'; +import * as resources from '@aws-solutions-constructs/resources'; // Note: To ensure CDKv2 compatibility, keep the import statement for Construct separate import { Construct } from 'constructs'; -import * as defaults from '@aws-solutions-constructs/core'; /** * @summary The properties for the CloudFrontToS3 Construct @@ -95,6 +99,7 @@ export class CloudFrontToS3 extends Construct { public readonly s3BucketInterface: s3.IBucket; public readonly s3Bucket?: s3.Bucket; public readonly s3LoggingBucket?: s3.Bucket; + public readonly originAccessControl?: cloudfront.CfnOriginAccessControl; /** * @summary Constructs a new instance of the CloudFrontToS3 class. @@ -114,7 +119,7 @@ export class CloudFrontToS3 extends Construct { defaults.CheckS3Props(props); defaults.CheckCloudFrontProps(props); - let bucket: s3.IBucket; + let originBucket: s3.IBucket; if (!props.existingBucketObj) { const buildS3BucketResponse = defaults.buildS3Bucket(this, { @@ -124,25 +129,68 @@ export class CloudFrontToS3 extends Construct { }); this.s3Bucket = buildS3BucketResponse.bucket; this.s3LoggingBucket = buildS3BucketResponse.loggingBucket; - bucket = this.s3Bucket; + originBucket = this.s3Bucket; } else { - bucket = props.existingBucketObj; + originBucket = props.existingBucketObj; } - this.s3BucketInterface = bucket; + this.s3BucketInterface = originBucket; - const cloudFrontDistributionForS3Response = defaults.CloudFrontDistributionForS3( - this, - this.s3BucketInterface, - props.cloudFrontDistributionProps, - props.insertHttpSecurityHeaders, - props.originPath, - props.cloudFrontLoggingBucketProps, - props.responseHeadersPolicyProps - ); + // Define the CloudFront Distribution + const cloudFrontDistributionForS3Props: defaults.CreateCloudFrontDistributionForS3Props = { + sourceBucket: this.s3BucketInterface, + cloudFrontDistributionProps: props.cloudFrontDistributionProps, + httpSecurityHeaders: props.insertHttpSecurityHeaders, + cloudFrontLoggingBucketProps: props.cloudFrontLoggingBucketProps, + responseHeadersPolicyProps: props.responseHeadersPolicyProps + }; + const cloudFrontDistributionForS3Response = defaults.createCloudFrontDistributionForS3(this, id, cloudFrontDistributionForS3Props); this.cloudFrontWebDistribution = cloudFrontDistributionForS3Response.distribution; this.cloudFrontFunction = cloudFrontDistributionForS3Response.cloudfrontFunction; this.cloudFrontLoggingBucket = cloudFrontDistributionForS3Response.loggingBucket; - } + this.originAccessControl = cloudFrontDistributionForS3Response.originAccessControl; -} + // Attach the OriginAccessControl to the CloudFront Distribution, and remove the OriginAccessIdentity + const l1CloudFrontDistribution = this.cloudFrontWebDistribution.node.defaultChild as cloudfront.CfnDistribution; + l1CloudFrontDistribution.addPropertyOverride('DistributionConfig.Origins.0.OriginAccessControlId', this.originAccessControl?.attrId); + if (props.originPath) { + l1CloudFrontDistribution.addPropertyOverride('DistributionConfig.Origins.0.OriginPath', props.originPath); + } + + // Grant CloudFront permission to get the objects from the s3 bucket origin + originBucket.addToResourcePolicy( + new iam.PolicyStatement({ + effect: iam.Effect.ALLOW, + actions: ['s3:GetObject'], + principals: [new iam.ServicePrincipal('cloudfront.amazonaws.com')], + resources: [originBucket.arnForObjects('*')], + conditions: { + StringEquals: { + 'AWS:SourceArn': `arn:aws:cloudfront::${Aws.ACCOUNT_ID}:distribution/${this.cloudFrontWebDistribution.distributionId}` + } + } + }) + ); + + // We need to create a custom resource to introduce the indirection necessary to avoid + // a circular dependency when granting the CloudFront distribution access to use the + // KMS key to decrypt objects. Without this indirection, it is not possible to reference + // the CloudFront distribution ID in the KMS key policy because - + // * The S3 bucket references the KMS key + // * The CloudFront distribution references the bucket + // * The KMS key references the CloudFront distribution + let encryptionKey: kms.IKey | undefined; + if (props.bucketProps && props.bucketProps.encryptionKey) { + encryptionKey = props.bucketProps.encryptionKey; + } else if (props.existingBucketObj && props.existingBucketObj.encryptionKey) { + encryptionKey = props.existingBucketObj.encryptionKey; + } + + if (encryptionKey) { + resources.createKeyPolicyUpdaterCustomResource(this, { + distribution: this.cloudFrontWebDistribution, + encryptionKey + }); + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/package.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/package.json index dfad84b6d..a0892b15d 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/package.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/package.json @@ -58,6 +58,7 @@ "@aws-cdk/aws-s3": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", "@aws-solutions-constructs/core": "0.0.0", + "@aws-solutions-constructs/resources": "0.0.0", "@aws-cdk/aws-certificatemanager": "0.0.0", "@aws-cdk/aws-cloudfront-origins": "0.0.0", "constructs": "^3.2.0" @@ -85,6 +86,7 @@ "@aws-cdk/aws-cloudfront": "0.0.0", "@aws-cdk/aws-s3": "0.0.0", "@aws-solutions-constructs/core": "0.0.0", + "@aws-solutions-constructs/resources": "0.0.0", "constructs": "^3.2.0", "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/aws-certificatemanager": "0.0.0", diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-bucket-encrypted-with-cmk-provided-as-existingbucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-bucket-encrypted-with-cmk-provided-as-existingbucket.expected.json new file mode 100644 index 000000000..b1b9a934c --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-bucket-encrypted-with-cmk-provided-as-existingbucket.expected.json @@ -0,0 +1,958 @@ +{ + "Description": "Integration Test for aws-cloudfront-s3", + "Resources": { + "cmkKey598B20B2": { + "Type": "AWS::KMS::Key", + "Properties": { + "EnableKeyRotation": true, + "KeyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "existings3bucketencryptedwithcmkS3LoggingBucket2B2DE39B": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W35", + "reason": "This S3 bucket is used as the access logging bucket for another bucket" + } + ] + } + } + }, + "existings3bucketencryptedwithcmkS3LoggingBucketPolicy4A3AC1CB": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "existings3bucketencryptedwithcmkS3LoggingBucket2B2DE39B" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "existings3bucketencryptedwithcmkS3LoggingBucket2B2DE39B", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "existings3bucketencryptedwithcmkS3LoggingBucket2B2DE39B", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "existings3bucketencryptedwithcmkS3BucketCC461491", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "existings3bucketencryptedwithcmkS3LoggingBucket2B2DE39B", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "existings3bucketencryptedwithcmkS3BucketCC461491": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "KMSMasterKeyID": { + "Fn::GetAtt": [ + "cmkKey598B20B2", + "Arn" + ] + }, + "SSEAlgorithm": "aws:kms" + } + } + ] + }, + "LifecycleConfiguration": { + "Rules": [ + { + "NoncurrentVersionTransitions": [ + { + "StorageClass": "GLACIER", + "TransitionInDays": 90 + } + ], + "Status": "Enabled" + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "existings3bucketencryptedwithcmkS3LoggingBucket2B2DE39B" + } + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "existings3bucketencryptedwithcmkS3BucketPolicyA1A37425": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "existings3bucketencryptedwithcmkS3BucketCC461491" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "existings3bucketencryptedwithcmkS3BucketCC461491", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "existings3bucketencryptedwithcmkS3BucketCC461491", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:GetObject", + "Condition": { + "StringEquals": { + "AWS:SourceArn": { + "Fn::Join": [ + "", + [ + "arn:aws:cloudfront::", + { + "Ref": "AWS::AccountId" + }, + ":distribution/", + { + "Ref": "testcloudfronts3cmkencryptionkeyCloudFrontDistribution57C8A907" + } + ] + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "cloudfront.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "existings3bucketencryptedwithcmkS3BucketCC461491", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "F16", + "reason": "Public website bucket policy requires a wildcard principal" + } + ] + } + } + }, + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucketAccessLog8863921C": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W35", + "reason": "This S3 bucket is used as the access logging bucket for another bucket" + } + ] + } + } + }, + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucketAccessLogPolicy8F931BD7": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucketAccessLog8863921C" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucketAccessLog8863921C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucketAccessLog8863921C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucket7C1787CD", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucketAccessLog8863921C", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucket7C1787CD": { + "Type": "AWS::S3::Bucket", + "Properties": { + "AccessControl": "LogDeliveryWrite", + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucketAccessLog8863921C" + } + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucketPolicy5E737735": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucket7C1787CD" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucket7C1787CD", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucket7C1787CD", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfronts3cmkencryptionkeyCloudFrontOac4EFECBD9": { + "Type": "AWS::CloudFront::OriginAccessControl", + "Properties": { + "OriginAccessControlConfig": { + "Description": "Origin access control provisioned by aws-cloudfront-s3", + "Name": { + "Fn::Join": [ + "", + [ + "aws-cloudfront-s3-testn-key-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + ] + }, + "OriginAccessControlOriginType": "s3", + "SigningBehavior": "always", + "SigningProtocol": "sigv4" + } + } + }, + "testcloudfronts3cmkencryptionkeyCloudFrontDistribution57C8A907": { + "Type": "AWS::CloudFront::Distribution", + "Properties": { + "DistributionConfig": { + "DefaultCacheBehavior": { + "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", + "Compress": true, + "TargetOriginId": "cfts3bucketencryptedwithcmkprovidedasexistingbuckettestcloudfronts3cmkencryptionkeyCloudFrontDistributionOrigin128E2E2A5", + "ViewerProtocolPolicy": "redirect-to-https" + }, + "DefaultRootObject": "index.html", + "Enabled": true, + "HttpVersion": "http2", + "IPV6Enabled": true, + "Logging": { + "Bucket": { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucket7C1787CD", + "RegionalDomainName" + ] + } + }, + "Origins": [ + { + "DomainName": { + "Fn::GetAtt": [ + "existings3bucketencryptedwithcmkS3BucketCC461491", + "RegionalDomainName" + ] + }, + "Id": "cfts3bucketencryptedwithcmkprovidedasexistingbuckettestcloudfronts3cmkencryptionkeyCloudFrontDistributionOrigin128E2E2A5", + "OriginAccessControlId": { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyCloudFrontOac4EFECBD9", + "Id" + ] + }, + "S3OriginConfig": {} + } + ] + } + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W70", + "reason": "Since the distribution uses the CloudFront domain name, CloudFront automatically sets the security policy to TLSv1 regardless of the value of MinimumProtocolVersion" + } + ] + } + } + }, + "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateLambdaRoleB7BBA8A2": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Description": "Role to update kms key policy to allow CloudFront access", + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "kms:PutKeyPolicy", + "kms:GetKeyPolicy", + "kms:DescribeKey" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "cmkKey598B20B2", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "KmsPolicy" + } + ] + } + }, + "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateLambdaRoleDefaultPolicy0E93FCDF": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateLambdaRoleDefaultPolicy0E93FCDF", + "Roles": [ + { + "Ref": "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateLambdaRoleB7BBA8A2" + } + ] + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W12", + "reason": "Lambda needs the following minimum required permissions to send trace data to X-Ray and access ENIs in a VPC." + } + ] + } + } + }, + "testcloudfronts3cmkencryptionkeyLambdaFunctionServiceRole85783D1D": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/lambda/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "LambdaFunctionServiceRolePolicy" + } + ] + } + }, + "testcloudfronts3cmkencryptionkeyLambdaFunction4DCD662E": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "4a4b024f310aca2784b69bcb790e9ccaef785e9ad5d1b73624144f88c4465b4f.zip" + }, + "Description": "Custom resource function that updates a provided key policy to allow CloudFront access.", + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateLambdaRoleB7BBA8A2", + "Arn" + ] + }, + "Runtime": "nodejs18.x", + "TracingConfig": { + "Mode": "Active" + } + }, + "DependsOn": [ + "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateLambdaRoleDefaultPolicy0E93FCDF", + "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateLambdaRoleB7BBA8A2" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "Lambda functions has the required permission to write CloudWatch Logs. It uses custom policy instead of arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole with tighter permissions." + }, + { + "id": "W89", + "reason": "This is not a rule for the general case, just for specific use cases/industries" + }, + { + "id": "W92", + "reason": "Impossible for us to define the correct concurrency for clients" + } + ] + } + } + }, + "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateProviderframeworkonEventServiceRole3D4040AD": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateProviderframeworkonEventServiceRoleDefaultPolicy066CD751": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "lambda:InvokeFunction", + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyLambdaFunction4DCD662E", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyLambdaFunction4DCD662E", + "Arn" + ] + }, + ":*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateProviderframeworkonEventServiceRoleDefaultPolicy066CD751", + "Roles": [ + { + "Ref": "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateProviderframeworkonEventServiceRole3D4040AD" + } + ] + } + }, + "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateProviderframeworkonEvent8BCBFC59": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "7382a0addb9f34974a1ea6c6c9b063882af874828f366f5c93b2b7b64db15c94.zip" + }, + "Description": "AWS CDK resource provider framework - onEvent (cfts3-bucket-encrypted-with-cmk-provided-as-existingbucket/test-cloudfront-s3-cmk-encryption-key/KmsKeyPolicyUpdateProvider)", + "Environment": { + "Variables": { + "USER_ON_EVENT_FUNCTION_ARN": { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyLambdaFunction4DCD662E", + "Arn" + ] + } + } + }, + "Handler": "framework.onEvent", + "Role": { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateProviderframeworkonEventServiceRole3D4040AD", + "Arn" + ] + }, + "Runtime": "nodejs18.x", + "Timeout": 900 + }, + "DependsOn": [ + "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateProviderframeworkonEventServiceRoleDefaultPolicy066CD751", + "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateProviderframeworkonEventServiceRole3D4040AD" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "The CDK-provided lambda function that backs their Custom Resource Provider framework has an IAM role with the arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole Managed Policy attached, which grants permission to write to CloudWatch Logs" + }, + { + "id": "W89", + "reason": "The CDK-provided lambda function that backs their Custom Resource Provider framework does not access VPC resources" + }, + { + "id": "W92", + "reason": "The CDK-provided lambda function that backs their Custom Resource Provider framework does not define ReservedConcurrentExecutions" + } + ] + } + } + }, + "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdaterFAFEBF0F": { + "Type": "Custom::KmsKeyPolicyUpdater", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateProviderframeworkonEvent8BCBFC59", + "Arn" + ] + }, + "KmsKeyId": { + "Ref": "cmkKey598B20B2" + }, + "CloudFrontDistributionId": { + "Ref": "testcloudfronts3cmkencryptionkeyCloudFrontDistribution57C8A907" + }, + "AccountId": { + "Ref": "AWS::AccountId" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-bucket-encrypted-with-cmk-provided-as-existingbucket.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-bucket-encrypted-with-cmk-provided-as-existingbucket.ts new file mode 100644 index 000000000..cf034ed24 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-bucket-encrypted-with-cmk-provided-as-existingbucket.ts @@ -0,0 +1,49 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +// Imports +import { App, Stack, RemovalPolicy, aws_kms } from "aws-cdk-lib"; +import { CloudFrontToS3, CloudFrontToS3Props } from "../lib"; +import { buildS3Bucket, generateIntegStackName, suppressAutoDeleteHandlerWarnings } from '@aws-solutions-constructs/core'; +import { BucketEncryption } from "aws-cdk-lib/aws-s3"; + +// Setup +const app = new App(); +const stack = new Stack(app, generateIntegStackName(__filename)); +stack.node.setContext("@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy", true); +stack.templateOptions.description = 'Integration Test for aws-cloudfront-s3'; + +// Definitions +const encryptionKey = new aws_kms.Key(stack, 'cmkKey', { + enableKeyRotation: true, + removalPolicy: RemovalPolicy.DESTROY +}); + +const existingBucketObj = buildS3Bucket(stack, { + bucketProps: { + encryption: BucketEncryption.KMS, + encryptionKey + } +}, 'existing-s3-bucket-encrypted-with-cmk').bucket; + +const props: CloudFrontToS3Props = { + existingBucketObj, + insertHttpSecurityHeaders: false +}; + +new CloudFrontToS3(stack, 'test-cloudfront-s3-cmk-encryption-key', props); + +suppressAutoDeleteHandlerWarnings(stack); + +// Synth +app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-bucket-encrypted-with-managed-key-provided-as-existingbucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-bucket-encrypted-with-managed-key-provided-as-existingbucket.expected.json new file mode 100644 index 000000000..4006ac631 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-bucket-encrypted-with-managed-key-provided-as-existingbucket.expected.json @@ -0,0 +1,592 @@ +{ + "Description": "Integration Test for aws-cloudfront-s3", + "Resources": { + "existings3bucketencryptedwiths3managedkeyS3LoggingBucketF861F6B7": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W35", + "reason": "This S3 bucket is used as the access logging bucket for another bucket" + } + ] + } + } + }, + "existings3bucketencryptedwiths3managedkeyS3LoggingBucketPolicy4358229C": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "existings3bucketencryptedwiths3managedkeyS3LoggingBucketF861F6B7" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "existings3bucketencryptedwiths3managedkeyS3LoggingBucketF861F6B7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "existings3bucketencryptedwiths3managedkeyS3LoggingBucketF861F6B7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "existings3bucketencryptedwiths3managedkeyS3BucketA8C4BE9A", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "existings3bucketencryptedwiths3managedkeyS3LoggingBucketF861F6B7", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "existings3bucketencryptedwiths3managedkeyS3BucketA8C4BE9A": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LifecycleConfiguration": { + "Rules": [ + { + "NoncurrentVersionTransitions": [ + { + "StorageClass": "GLACIER", + "TransitionInDays": 90 + } + ], + "Status": "Enabled" + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "existings3bucketencryptedwiths3managedkeyS3LoggingBucketF861F6B7" + } + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "existings3bucketencryptedwiths3managedkeyS3BucketPolicyFDA85248": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "existings3bucketencryptedwiths3managedkeyS3BucketA8C4BE9A" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "existings3bucketencryptedwiths3managedkeyS3BucketA8C4BE9A", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "existings3bucketencryptedwiths3managedkeyS3BucketA8C4BE9A", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:GetObject", + "Condition": { + "StringEquals": { + "AWS:SourceArn": { + "Fn::Join": [ + "", + [ + "arn:aws:cloudfront::", + { + "Ref": "AWS::AccountId" + }, + ":distribution/", + { + "Ref": "testcloudfronts3managedkeyCloudFrontDistributionE6431C62" + } + ] + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "cloudfront.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "existings3bucketencryptedwiths3managedkeyS3BucketA8C4BE9A", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "F16", + "reason": "Public website bucket policy requires a wildcard principal" + } + ] + } + } + }, + "testcloudfronts3managedkeyCloudfrontLoggingBucketAccessLog09A44955": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W35", + "reason": "This S3 bucket is used as the access logging bucket for another bucket" + } + ] + } + } + }, + "testcloudfronts3managedkeyCloudfrontLoggingBucketAccessLogPolicy08C15592": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfronts3managedkeyCloudfrontLoggingBucketAccessLog09A44955" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3managedkeyCloudfrontLoggingBucketAccessLog09A44955", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3managedkeyCloudfrontLoggingBucketAccessLog09A44955", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "testcloudfronts3managedkeyCloudfrontLoggingBucket4F6525D7", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3managedkeyCloudfrontLoggingBucketAccessLog09A44955", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfronts3managedkeyCloudfrontLoggingBucket4F6525D7": { + "Type": "AWS::S3::Bucket", + "Properties": { + "AccessControl": "LogDeliveryWrite", + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "testcloudfronts3managedkeyCloudfrontLoggingBucketAccessLog09A44955" + } + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "testcloudfronts3managedkeyCloudfrontLoggingBucketPolicy8952C83B": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfronts3managedkeyCloudfrontLoggingBucket4F6525D7" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3managedkeyCloudfrontLoggingBucket4F6525D7", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3managedkeyCloudfrontLoggingBucket4F6525D7", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfronts3managedkeyCloudFrontOac1422B0A1": { + "Type": "AWS::CloudFront::OriginAccessControl", + "Properties": { + "OriginAccessControlConfig": { + "Description": "Origin access control provisioned by aws-cloudfront-s3", + "Name": { + "Fn::Join": [ + "", + [ + "aws-cloudfront-s3-testd-key-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + ] + }, + "OriginAccessControlOriginType": "s3", + "SigningBehavior": "always", + "SigningProtocol": "sigv4" + } + } + }, + "testcloudfronts3managedkeyCloudFrontDistributionE6431C62": { + "Type": "AWS::CloudFront::Distribution", + "Properties": { + "DistributionConfig": { + "DefaultCacheBehavior": { + "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", + "Compress": true, + "TargetOriginId": "cfts3bucketencryptedwithmanagedkeyprovidedasexistingbuckettestcloudfronts3managedkeyCloudFrontDistributionOrigin17C5092B4", + "ViewerProtocolPolicy": "redirect-to-https" + }, + "DefaultRootObject": "index.html", + "Enabled": true, + "HttpVersion": "http2", + "IPV6Enabled": true, + "Logging": { + "Bucket": { + "Fn::GetAtt": [ + "testcloudfronts3managedkeyCloudfrontLoggingBucket4F6525D7", + "RegionalDomainName" + ] + } + }, + "Origins": [ + { + "DomainName": { + "Fn::GetAtt": [ + "existings3bucketencryptedwiths3managedkeyS3BucketA8C4BE9A", + "RegionalDomainName" + ] + }, + "Id": "cfts3bucketencryptedwithmanagedkeyprovidedasexistingbuckettestcloudfronts3managedkeyCloudFrontDistributionOrigin17C5092B4", + "OriginAccessControlId": { + "Fn::GetAtt": [ + "testcloudfronts3managedkeyCloudFrontOac1422B0A1", + "Id" + ] + }, + "S3OriginConfig": {} + } + ] + } + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W70", + "reason": "Since the distribution uses the CloudFront domain name, CloudFront automatically sets the security policy to TLSv1 regardless of the value of MinimumProtocolVersion" + } + ] + } + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-bucket-encrypted-with-managed-key-provided-as-existingbucket.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-bucket-encrypted-with-managed-key-provided-as-existingbucket.ts new file mode 100644 index 000000000..55697bb04 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-bucket-encrypted-with-managed-key-provided-as-existingbucket.ts @@ -0,0 +1,43 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +// Imports +import { App, Stack } from "aws-cdk-lib"; +import { CloudFrontToS3, CloudFrontToS3Props } from "../lib"; +import { buildS3Bucket, generateIntegStackName, suppressAutoDeleteHandlerWarnings } from '@aws-solutions-constructs/core'; +import { BucketEncryption } from "aws-cdk-lib/aws-s3"; + +// Setup +const app = new App(); +const stack = new Stack(app, generateIntegStackName(__filename)); +stack.node.setContext("@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy", true); +stack.templateOptions.description = 'Integration Test for aws-cloudfront-s3'; + +// Definitions +const existingBucketObj = buildS3Bucket(stack, { + bucketProps: { + encryption: BucketEncryption.S3_MANAGED + } +}, 'existing-s3-bucket-encrypted-with-s3-managed-key').bucket; + +const props: CloudFrontToS3Props = { + existingBucketObj, + insertHttpSecurityHeaders: false +}; + +new CloudFrontToS3(stack, 'test-cloudfront-s3-managed-key', props); + +suppressAutoDeleteHandlerWarnings(stack); + +// Synth +app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-bucket-with-http-origin.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-bucket-with-http-origin.expected.json new file mode 100644 index 000000000..7c1650c76 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-bucket-with-http-origin.expected.json @@ -0,0 +1,559 @@ +{ + "Description": "Integration Test for aws-cloudfront-s3", + "Resources": { + "testcloudfronts3legacyhttporiginS3LoggingBucketA3DB7D64": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W35", + "reason": "This S3 bucket is used as the access logging bucket for another bucket" + } + ] + } + } + }, + "testcloudfronts3legacyhttporiginS3LoggingBucketPolicyEC439975": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfronts3legacyhttporiginS3LoggingBucketA3DB7D64" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3legacyhttporiginS3LoggingBucketA3DB7D64", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3legacyhttporiginS3LoggingBucketA3DB7D64", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "testcloudfronts3legacyhttporiginS3Bucket9C7276CA", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3legacyhttporiginS3LoggingBucketA3DB7D64", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfronts3legacyhttporiginS3Bucket9C7276CA": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LifecycleConfiguration": { + "Rules": [ + { + "NoncurrentVersionTransitions": [ + { + "StorageClass": "GLACIER", + "TransitionInDays": 90 + } + ], + "Status": "Enabled" + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "testcloudfronts3legacyhttporiginS3LoggingBucketA3DB7D64" + } + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": false, + "BlockPublicPolicy": false, + "IgnorePublicAcls": false, + "RestrictPublicBuckets": false + }, + "VersioningConfiguration": { + "Status": "Enabled" + }, + "WebsiteConfiguration": { + "IndexDocument": "index.html" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "testcloudfronts3legacyhttporiginS3BucketPolicy7529C63D": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfronts3legacyhttporiginS3Bucket9C7276CA" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:GetObject", + "Effect": "Allow", + "Principal": { + "AWS": "*" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3legacyhttporiginS3Bucket9C7276CA", + "Arn" + ] + }, + "/*" + ] + ] + } + }, + { + "Action": "s3:GetObject", + "Condition": { + "StringEquals": { + "AWS:SourceArn": { + "Fn::Join": [ + "", + [ + "arn:aws:cloudfront::", + { + "Ref": "AWS::AccountId" + }, + ":distribution/", + { + "Ref": "testcloudfronts3legacyhttporiginCloudFrontDistributionAF04EDAB" + } + ] + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "cloudfront.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3legacyhttporiginS3Bucket9C7276CA", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "F16", + "reason": "Public website bucket policy requires a wildcard principal" + } + ] + } + } + }, + "testcloudfronts3legacyhttporiginCloudfrontLoggingBucketAccessLog3AE680E5": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W35", + "reason": "This S3 bucket is used as the access logging bucket for another bucket" + } + ] + } + } + }, + "testcloudfronts3legacyhttporiginCloudfrontLoggingBucketAccessLogPolicyD1441BF6": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfronts3legacyhttporiginCloudfrontLoggingBucketAccessLog3AE680E5" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3legacyhttporiginCloudfrontLoggingBucketAccessLog3AE680E5", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3legacyhttporiginCloudfrontLoggingBucketAccessLog3AE680E5", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "testcloudfronts3legacyhttporiginCloudfrontLoggingBucketC643C2EE", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3legacyhttporiginCloudfrontLoggingBucketAccessLog3AE680E5", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfronts3legacyhttporiginCloudfrontLoggingBucketC643C2EE": { + "Type": "AWS::S3::Bucket", + "Properties": { + "AccessControl": "LogDeliveryWrite", + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "testcloudfronts3legacyhttporiginCloudfrontLoggingBucketAccessLog3AE680E5" + } + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "testcloudfronts3legacyhttporiginCloudfrontLoggingBucketPolicyF48109AD": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfronts3legacyhttporiginCloudfrontLoggingBucketC643C2EE" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3legacyhttporiginCloudfrontLoggingBucketC643C2EE", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3legacyhttporiginCloudfrontLoggingBucketC643C2EE", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfronts3legacyhttporiginCloudFrontDistributionAF04EDAB": { + "Type": "AWS::CloudFront::Distribution", + "Properties": { + "DistributionConfig": { + "DefaultCacheBehavior": { + "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", + "Compress": true, + "TargetOriginId": "cfts3bucketwithhttporigintestcloudfronts3legacyhttporiginCloudFrontDistributionOrigin15A81BB36", + "ViewerProtocolPolicy": "redirect-to-https" + }, + "DefaultRootObject": "index.html", + "Enabled": true, + "HttpVersion": "http2", + "IPV6Enabled": true, + "Logging": { + "Bucket": { + "Fn::GetAtt": [ + "testcloudfronts3legacyhttporiginCloudfrontLoggingBucketC643C2EE", + "RegionalDomainName" + ] + } + }, + "Origins": [ + { + "CustomOriginConfig": { + "OriginProtocolPolicy": "http-only", + "OriginSSLProtocols": [ + "TLSv1.2" + ] + }, + "DomainName": { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Fn::GetAtt": [ + "testcloudfronts3legacyhttporiginS3Bucket9C7276CA", + "WebsiteURL" + ] + } + ] + } + ] + }, + "Id": "cfts3bucketwithhttporigintestcloudfronts3legacyhttporiginCloudFrontDistributionOrigin15A81BB36" + } + ] + } + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W70", + "reason": "Since the distribution uses the CloudFront domain name, CloudFront automatically sets the security policy to TLSv1 regardless of the value of MinimumProtocolVersion" + } + ] + } + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-bucket-with-http-origin.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-bucket-with-http-origin.ts new file mode 100644 index 000000000..941ac31f4 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-bucket-with-http-origin.ts @@ -0,0 +1,47 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +// Imports +import { App, Stack } from "aws-cdk-lib"; +import { CloudFrontToS3, CloudFrontToS3Props } from "../lib"; +import { generateIntegStackName, suppressAutoDeleteHandlerWarnings } from '@aws-solutions-constructs/core'; + +// Setup +const app = new App(); +const stack = new Stack(app, generateIntegStackName(__filename)); +stack.node.setContext("@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy", true); +stack.templateOptions.description = 'Integration Test for aws-cloudfront-s3'; + +// Definitions +const blockPublicAccess = false; +const testProps: CloudFrontToS3Props = { + bucketProps: { + enforceSSL: false, + publicReadAccess: true, + blockPublicAccess: { + blockPublicAcls: blockPublicAccess, + restrictPublicBuckets: blockPublicAccess, + blockPublicPolicy: blockPublicAccess, + ignorePublicAcls: blockPublicAccess + }, + websiteIndexDocument: "index.html" + }, + insertHttpSecurityHeaders: false +}; + +new CloudFrontToS3(stack, 'test-cloudfront-s3-legacy-http-origin', testProps); + +suppressAutoDeleteHandlerWarnings(stack); + +// Synth +app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-cmk-encryption.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-cmk-encryption.expected.json new file mode 100644 index 000000000..4524134d9 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-cmk-encryption.expected.json @@ -0,0 +1,527 @@ +{ + "Description": "Integration Test for aws-cloudfront-s3", + "Resources": { + "cmkKey598B20B2": { + "Type": "AWS::KMS::Key", + "Properties": { + "EnableKeyRotation": true, + "KeyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "testcloudfronts3cmkencryptionkeyS3LoggingBucket5CE52209": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W35", + "reason": "This S3 bucket is used as the access logging bucket for another bucket" + } + ] + } + } + }, + "testcloudfronts3cmkencryptionkeyS3LoggingBucketPolicyF38CB6DD": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfronts3cmkencryptionkeyS3LoggingBucket5CE52209" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyS3LoggingBucket5CE52209", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyS3LoggingBucket5CE52209", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyS3Bucket0E74E5D2", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyS3LoggingBucket5CE52209", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfronts3cmkencryptionkeyS3Bucket0E74E5D2": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LifecycleConfiguration": { + "Rules": [ + { + "NoncurrentVersionTransitions": [ + { + "StorageClass": "GLACIER", + "TransitionInDays": 90 + } + ], + "Status": "Enabled" + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "testcloudfronts3cmkencryptionkeyS3LoggingBucket5CE52209" + } + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "testcloudfronts3cmkencryptionkeyS3BucketPolicy5DF57B01": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfronts3cmkencryptionkeyS3Bucket0E74E5D2" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyS3Bucket0E74E5D2", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyS3Bucket0E74E5D2", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:GetObject", + "Effect": "Allow", + "Principal": { + "CanonicalUser": { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyCloudFrontDistributionOrigin1S3OriginA6F11959", + "S3CanonicalUserId" + ] + } + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyS3Bucket0E74E5D2", + "Arn" + ] + }, + "/*" + ] + ] + } + }, + { + "Action": "s3:GetObject", + "Condition": { + "StringEquals": { + "AWS:SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":cloudfront::", + { + "Ref": "AWS::AccountId" + }, + ":distribution/", + { + "Ref": "testcloudfronts3cmkencryptionkeyCloudFrontDistribution57C8A907" + } + ] + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "cloudfront.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyS3Bucket0E74E5D2", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "F16", + "reason": "Public website bucket policy requires a wildcard principal" + } + ] + } + } + }, + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucket7C1787CD": { + "Type": "AWS::S3::Bucket", + "Properties": { + "AccessControl": "LogDeliveryWrite", + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W35", + "reason": "This S3 bucket is used as the access logging bucket for CloudFront Distribution" + } + ] + } + } + }, + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucketPolicy5E737735": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucket7C1787CD" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucket7C1787CD", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucket7C1787CD", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfronts3cmkencryptionkeyCloudFrontDistributionOrigin1S3OriginA6F11959": { + "Type": "AWS::CloudFront::CloudFrontOriginAccessIdentity", + "Properties": { + "CloudFrontOriginAccessIdentityConfig": { + "Comment": "Identity for cfts3cmkencryptiontestcloudfronts3cmkencryptionkeyCloudFrontDistributionOrigin10F84B155" + } + } + }, + "testcloudfronts3cmkencryptionkeyCloudFrontDistribution57C8A907": { + "Type": "AWS::CloudFront::Distribution", + "Properties": { + "DistributionConfig": { + "DefaultCacheBehavior": { + "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", + "Compress": true, + "TargetOriginId": "cfts3cmkencryptiontestcloudfronts3cmkencryptionkeyCloudFrontDistributionOrigin10F84B155", + "ViewerProtocolPolicy": "redirect-to-https" + }, + "DefaultRootObject": "index.html", + "Enabled": true, + "HttpVersion": "http2", + "IPV6Enabled": true, + "Logging": { + "Bucket": { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucket7C1787CD", + "RegionalDomainName" + ] + } + }, + "Origins": [ + { + "DomainName": { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyS3Bucket0E74E5D2", + "RegionalDomainName" + ] + }, + "Id": "cfts3cmkencryptiontestcloudfronts3cmkencryptionkeyCloudFrontDistributionOrigin10F84B155", + "OriginAccessControlId": { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyCloudFrontOac4EFECBD9", + "Id" + ] + }, + "S3OriginConfig": { + "OriginAccessIdentity": "" + } + } + ] + } + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W70", + "reason": "Since the distribution uses the CloudFront domain name, CloudFront automatically sets the security policy to TLSv1 regardless of the value of MinimumProtocolVersion" + } + ] + } + } + }, + "testcloudfronts3cmkencryptionkeyCloudFrontOac4EFECBD9": { + "Type": "AWS::CloudFront::OriginAccessControl", + "Properties": { + "OriginAccessControlConfig": { + "Name": "cloudfront-default-oac-18c3b751f02", + "OriginAccessControlOriginType": "s3", + "SigningBehavior": "always", + "SigningProtocol": "sigv4" + } + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-cmk-provided-as-bucket-prop.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-cmk-provided-as-bucket-prop.expected.json new file mode 100644 index 000000000..3e1959e94 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-cmk-provided-as-bucket-prop.expected.json @@ -0,0 +1,958 @@ +{ + "Description": "Integration Test for aws-cloudfront-s3", + "Resources": { + "cmkKey598B20B2": { + "Type": "AWS::KMS::Key", + "Properties": { + "EnableKeyRotation": true, + "KeyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "testcloudfronts3cmkencryptionkeyS3LoggingBucket5CE52209": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W35", + "reason": "This S3 bucket is used as the access logging bucket for another bucket" + } + ] + } + } + }, + "testcloudfronts3cmkencryptionkeyS3LoggingBucketPolicyF38CB6DD": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfronts3cmkencryptionkeyS3LoggingBucket5CE52209" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyS3LoggingBucket5CE52209", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyS3LoggingBucket5CE52209", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyS3Bucket0E74E5D2", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyS3LoggingBucket5CE52209", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfronts3cmkencryptionkeyS3Bucket0E74E5D2": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "KMSMasterKeyID": { + "Fn::GetAtt": [ + "cmkKey598B20B2", + "Arn" + ] + }, + "SSEAlgorithm": "aws:kms" + } + } + ] + }, + "LifecycleConfiguration": { + "Rules": [ + { + "NoncurrentVersionTransitions": [ + { + "StorageClass": "GLACIER", + "TransitionInDays": 90 + } + ], + "Status": "Enabled" + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "testcloudfronts3cmkencryptionkeyS3LoggingBucket5CE52209" + } + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "testcloudfronts3cmkencryptionkeyS3BucketPolicy5DF57B01": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfronts3cmkencryptionkeyS3Bucket0E74E5D2" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyS3Bucket0E74E5D2", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyS3Bucket0E74E5D2", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:GetObject", + "Condition": { + "StringEquals": { + "AWS:SourceArn": { + "Fn::Join": [ + "", + [ + "arn:aws:cloudfront::", + { + "Ref": "AWS::AccountId" + }, + ":distribution/", + { + "Ref": "testcloudfronts3cmkencryptionkeyCloudFrontDistribution57C8A907" + } + ] + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "cloudfront.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyS3Bucket0E74E5D2", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "F16", + "reason": "Public website bucket policy requires a wildcard principal" + } + ] + } + } + }, + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucketAccessLog8863921C": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W35", + "reason": "This S3 bucket is used as the access logging bucket for another bucket" + } + ] + } + } + }, + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucketAccessLogPolicy8F931BD7": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucketAccessLog8863921C" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucketAccessLog8863921C", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucketAccessLog8863921C", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucket7C1787CD", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucketAccessLog8863921C", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucket7C1787CD": { + "Type": "AWS::S3::Bucket", + "Properties": { + "AccessControl": "LogDeliveryWrite", + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucketAccessLog8863921C" + } + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucketPolicy5E737735": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucket7C1787CD" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucket7C1787CD", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucket7C1787CD", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfronts3cmkencryptionkeyCloudFrontOac4EFECBD9": { + "Type": "AWS::CloudFront::OriginAccessControl", + "Properties": { + "OriginAccessControlConfig": { + "Description": "Origin access control provisioned by aws-cloudfront-s3", + "Name": { + "Fn::Join": [ + "", + [ + "aws-cloudfront-s3-testn-key-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + ] + }, + "OriginAccessControlOriginType": "s3", + "SigningBehavior": "always", + "SigningProtocol": "sigv4" + } + } + }, + "testcloudfronts3cmkencryptionkeyCloudFrontDistribution57C8A907": { + "Type": "AWS::CloudFront::Distribution", + "Properties": { + "DistributionConfig": { + "DefaultCacheBehavior": { + "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", + "Compress": true, + "TargetOriginId": "cfts3cmkprovidedasbucketproptestcloudfronts3cmkencryptionkeyCloudFrontDistributionOrigin1FA4541E3", + "ViewerProtocolPolicy": "redirect-to-https" + }, + "DefaultRootObject": "index.html", + "Enabled": true, + "HttpVersion": "http2", + "IPV6Enabled": true, + "Logging": { + "Bucket": { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyCloudfrontLoggingBucket7C1787CD", + "RegionalDomainName" + ] + } + }, + "Origins": [ + { + "DomainName": { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyS3Bucket0E74E5D2", + "RegionalDomainName" + ] + }, + "Id": "cfts3cmkprovidedasbucketproptestcloudfronts3cmkencryptionkeyCloudFrontDistributionOrigin1FA4541E3", + "OriginAccessControlId": { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyCloudFrontOac4EFECBD9", + "Id" + ] + }, + "S3OriginConfig": {} + } + ] + } + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W70", + "reason": "Since the distribution uses the CloudFront domain name, CloudFront automatically sets the security policy to TLSv1 regardless of the value of MinimumProtocolVersion" + } + ] + } + } + }, + "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateLambdaRoleB7BBA8A2": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Description": "Role to update kms key policy to allow CloudFront access", + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "kms:PutKeyPolicy", + "kms:GetKeyPolicy", + "kms:DescribeKey" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "cmkKey598B20B2", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "KmsPolicy" + } + ] + } + }, + "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateLambdaRoleDefaultPolicy0E93FCDF": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateLambdaRoleDefaultPolicy0E93FCDF", + "Roles": [ + { + "Ref": "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateLambdaRoleB7BBA8A2" + } + ] + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W12", + "reason": "Lambda needs the following minimum required permissions to send trace data to X-Ray and access ENIs in a VPC." + } + ] + } + } + }, + "testcloudfronts3cmkencryptionkeyLambdaFunctionServiceRole85783D1D": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/lambda/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "LambdaFunctionServiceRolePolicy" + } + ] + } + }, + "testcloudfronts3cmkencryptionkeyLambdaFunction4DCD662E": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "4a4b024f310aca2784b69bcb790e9ccaef785e9ad5d1b73624144f88c4465b4f.zip" + }, + "Description": "Custom resource function that updates a provided key policy to allow CloudFront access.", + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateLambdaRoleB7BBA8A2", + "Arn" + ] + }, + "Runtime": "nodejs18.x", + "TracingConfig": { + "Mode": "Active" + } + }, + "DependsOn": [ + "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateLambdaRoleDefaultPolicy0E93FCDF", + "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateLambdaRoleB7BBA8A2" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "Lambda functions has the required permission to write CloudWatch Logs. It uses custom policy instead of arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole with tighter permissions." + }, + { + "id": "W89", + "reason": "This is not a rule for the general case, just for specific use cases/industries" + }, + { + "id": "W92", + "reason": "Impossible for us to define the correct concurrency for clients" + } + ] + } + } + }, + "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateProviderframeworkonEventServiceRole3D4040AD": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateProviderframeworkonEventServiceRoleDefaultPolicy066CD751": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "lambda:InvokeFunction", + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyLambdaFunction4DCD662E", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyLambdaFunction4DCD662E", + "Arn" + ] + }, + ":*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateProviderframeworkonEventServiceRoleDefaultPolicy066CD751", + "Roles": [ + { + "Ref": "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateProviderframeworkonEventServiceRole3D4040AD" + } + ] + } + }, + "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateProviderframeworkonEvent8BCBFC59": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "7382a0addb9f34974a1ea6c6c9b063882af874828f366f5c93b2b7b64db15c94.zip" + }, + "Description": "AWS CDK resource provider framework - onEvent (cfts3-cmk-provided-as-bucket-prop/test-cloudfront-s3-cmk-encryption-key/KmsKeyPolicyUpdateProvider)", + "Environment": { + "Variables": { + "USER_ON_EVENT_FUNCTION_ARN": { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyLambdaFunction4DCD662E", + "Arn" + ] + } + } + }, + "Handler": "framework.onEvent", + "Role": { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateProviderframeworkonEventServiceRole3D4040AD", + "Arn" + ] + }, + "Runtime": "nodejs18.x", + "Timeout": 900 + }, + "DependsOn": [ + "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateProviderframeworkonEventServiceRoleDefaultPolicy066CD751", + "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateProviderframeworkonEventServiceRole3D4040AD" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "The CDK-provided lambda function that backs their Custom Resource Provider framework has an IAM role with the arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole Managed Policy attached, which grants permission to write to CloudWatch Logs" + }, + { + "id": "W89", + "reason": "The CDK-provided lambda function that backs their Custom Resource Provider framework does not access VPC resources" + }, + { + "id": "W92", + "reason": "The CDK-provided lambda function that backs their Custom Resource Provider framework does not define ReservedConcurrentExecutions" + } + ] + } + } + }, + "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdaterFAFEBF0F": { + "Type": "Custom::KmsKeyPolicyUpdater", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "testcloudfronts3cmkencryptionkeyKmsKeyPolicyUpdateProviderframeworkonEvent8BCBFC59", + "Arn" + ] + }, + "KmsKeyId": { + "Ref": "cmkKey598B20B2" + }, + "CloudFrontDistributionId": { + "Ref": "testcloudfronts3cmkencryptionkeyCloudFrontDistribution57C8A907" + }, + "AccountId": { + "Ref": "AWS::AccountId" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-cmk-provided-as-bucket-prop.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-cmk-provided-as-bucket-prop.ts new file mode 100644 index 000000000..741ecb832 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-cmk-provided-as-bucket-prop.ts @@ -0,0 +1,45 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +// Imports +import { App, Stack, RemovalPolicy, aws_kms } from "aws-cdk-lib"; +import { CloudFrontToS3, CloudFrontToS3Props } from "../lib"; +import { generateIntegStackName, suppressAutoDeleteHandlerWarnings } from '@aws-solutions-constructs/core'; +import { BucketEncryption } from "aws-cdk-lib/aws-s3"; + +// Setup +const app = new App(); +const stack = new Stack(app, generateIntegStackName(__filename)); +stack.templateOptions.description = 'Integration Test for aws-cloudfront-s3'; + +// Definitions +const encryptionKey = new aws_kms.Key(stack, 'cmkKey', { + enableKeyRotation: true, + removalPolicy: RemovalPolicy.DESTROY +}); + +const props: CloudFrontToS3Props = { + bucketProps: { + enforceSSL: true, + encryption: BucketEncryption.KMS, + encryptionKey + }, + insertHttpSecurityHeaders: false +}; + +new CloudFrontToS3(stack, 'test-cloudfront-s3-cmk-encryption-key', props); + +suppressAutoDeleteHandlerWarnings(stack); + +// Synth +app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-headers.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-headers.expected.json index 6cb970be3..89d7ec9ce 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-headers.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-headers.expected.json @@ -347,14 +347,28 @@ }, { "Action": "s3:GetObject", + "Condition": { + "StringEquals": { + "AWS:SourceArn": { + "Fn::Join": [ + "", + [ + "arn:aws:cloudfront::", + { + "Ref": "AWS::AccountId" + }, + ":distribution/", + { + "Ref": "testcloudfronts3CloudFrontDistribution0565DEE8" + } + ] + ] + } + } + }, "Effect": "Allow", "Principal": { - "CanonicalUser": { - "Fn::GetAtt": [ - "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058", - "S3CanonicalUserId" - ] - } + "Service": "cloudfront.amazonaws.com" }, "Resource": { "Fn::Join": [ @@ -755,11 +769,35 @@ "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, - "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058": { - "Type": "AWS::CloudFront::CloudFrontOriginAccessIdentity", + "testcloudfronts3CloudFrontOac7A951AA6": { + "Type": "AWS::CloudFront::OriginAccessControl", "Properties": { - "CloudFrontOriginAccessIdentityConfig": { - "Comment": "Identity for cfts3customheaderstestcloudfronts3CloudFrontDistributionOrigin126E0E496" + "OriginAccessControlConfig": { + "Description": "Origin access control provisioned by aws-cloudfront-s3", + "Name": { + "Fn::Join": [ + "", + [ + "aws-cloudfront-s3-testnt-s3-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + ] + }, + "OriginAccessControlOriginType": "s3", + "SigningBehavior": "always", + "SigningProtocol": "sigv4" } } }, @@ -805,19 +843,13 @@ ] }, "Id": "cfts3customheaderstestcloudfronts3CloudFrontDistributionOrigin126E0E496", - "S3OriginConfig": { - "OriginAccessIdentity": { - "Fn::Join": [ - "", - [ - "origin-access-identity/cloudfront/", - { - "Ref": "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058" - } - ] - ] - } - } + "OriginAccessControlId": { + "Fn::GetAtt": [ + "testcloudfronts3CloudFrontOac7A951AA6", + "Id" + ] + }, + "S3OriginConfig": {} } ] } diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-originPath.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-originPath.expected.json index b9458440d..493da6600 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-originPath.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-custom-originPath.expected.json @@ -315,14 +315,28 @@ }, { "Action": "s3:GetObject", + "Condition": { + "StringEquals": { + "AWS:SourceArn": { + "Fn::Join": [ + "", + [ + "arn:aws:cloudfront::", + { + "Ref": "AWS::AccountId" + }, + ":distribution/", + { + "Ref": "testcloudfronts3CloudFrontDistribution0565DEE8" + } + ] + ] + } + } + }, "Effect": "Allow", "Principal": { - "CanonicalUser": { - "Fn::GetAtt": [ - "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058", - "S3CanonicalUserId" - ] - } + "Service": "cloudfront.amazonaws.com" }, "Resource": { "Fn::Join": [ @@ -723,11 +737,35 @@ "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, - "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058": { - "Type": "AWS::CloudFront::CloudFrontOriginAccessIdentity", + "testcloudfronts3CloudFrontOac7A951AA6": { + "Type": "AWS::CloudFront::OriginAccessControl", "Properties": { - "CloudFrontOriginAccessIdentityConfig": { - "Comment": "Identity for cfts3customoriginPathtestcloudfronts3CloudFrontDistributionOrigin1AE2DDD7C" + "OriginAccessControlConfig": { + "Description": "Origin access control provisioned by aws-cloudfront-s3", + "Name": { + "Fn::Join": [ + "", + [ + "aws-cloudfront-s3-testnt-s3-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + ] + }, + "OriginAccessControlOriginType": "s3", + "SigningBehavior": "always", + "SigningProtocol": "sigv4" } } }, @@ -773,20 +811,14 @@ ] }, "Id": "cfts3customoriginPathtestcloudfronts3CloudFrontDistributionOrigin1AE2DDD7C", + "OriginAccessControlId": { + "Fn::GetAtt": [ + "testcloudfronts3CloudFrontOac7A951AA6", + "Id" + ] + }, "OriginPath": "/testPath", - "S3OriginConfig": { - "OriginAccessIdentity": { - "Fn::Join": [ - "", - [ - "origin-access-identity/cloudfront/", - { - "Ref": "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058" - } - ] - ] - } - } + "S3OriginConfig": {} } ] } diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customCloudFrontLoggingBucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customCloudFrontLoggingBucket.expected.json new file mode 100644 index 000000000..27ac41fde --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customCloudFrontLoggingBucket.expected.json @@ -0,0 +1,698 @@ +{ + "Description": "Integration Test for aws-cloudfront-s3 custom CloudFront Logging Bubkcet", + "Resources": { + "testcloudfronts3S3LoggingBucket90D239DD": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W35", + "reason": "This S3 bucket is used as the access logging bucket for another bucket" + } + ] + } + } + }, + "testcloudfronts3S3LoggingBucketPolicy529D4CFF": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfronts3S3LoggingBucket90D239DD" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3S3LoggingBucket90D239DD", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3S3LoggingBucket90D239DD", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "testcloudfronts3S3BucketE0C5F76E", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3S3LoggingBucket90D239DD", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfronts3S3BucketE0C5F76E": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "LifecycleConfiguration": { + "Rules": [ + { + "NoncurrentVersionTransitions": [ + { + "StorageClass": "GLACIER", + "TransitionInDays": 90 + } + ], + "Status": "Enabled" + } + ] + }, + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "testcloudfronts3S3LoggingBucket90D239DD" + } + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "testcloudfronts3S3BucketPolicy250F1F61": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfronts3S3BucketE0C5F76E" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3S3BucketE0C5F76E", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3S3BucketE0C5F76E", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3S3BucketE0C5F76E", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3S3BucketE0C5F76E", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:GetObject", + "Condition": { + "StringEquals": { + "AWS:SourceArn": { + "Fn::Join": [ + "", + [ + "arn:aws:cloudfront::", + { + "Ref": "AWS::AccountId" + }, + ":distribution/", + { + "Ref": "testcloudfronts3CloudFrontDistribution0565DEE8" + } + ] + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "cloudfront.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3S3BucketE0C5F76E", + "Arn" + ] + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "F16", + "reason": "Public website bucket policy requires a wildcard principal" + } + ] + } + } + }, + "testcloudfronts3S3BucketAutoDeleteObjectsCustomResourceA13DD8F7": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "testcloudfronts3S3BucketE0C5F76E" + } + }, + "DependsOn": [ + "testcloudfronts3S3BucketPolicy250F1F61" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "testcloudfronts3SetHttpSecurityHeaders6C5A1E69": { + "Type": "AWS::CloudFront::Function", + "Properties": { + "AutoPublish": true, + "FunctionCode": "function handler(event) { var response = event.response; var headers = response.headers; headers['strict-transport-security'] = { value: 'max-age=63072000; includeSubdomains; preload'}; headers['content-security-policy'] = { value: \"default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'\"}; headers['x-content-type-options'] = { value: 'nosniff'}; headers['x-frame-options'] = {value: 'DENY'}; headers['x-xss-protection'] = {value: '1; mode=block'}; return response; }", + "FunctionConfig": { + "Comment": "SetHttpSecurityHeadersc853f5cf48adabb9680b666a0c549e9b779fe54127", + "Runtime": "cloudfront-js-1.0" + }, + "Name": "SetHttpSecurityHeadersc853f5cf48adabb9680b666a0c549e9b779fe54127" + } + }, + "testcloudfronts3CloudfrontLoggingBucket985C0FE8": { + "Type": "AWS::S3::Bucket", + "Properties": { + "AccessControl": "LogDeliveryWrite", + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "ObjectWriter" + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ], + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W35", + "reason": "This S3 bucket is used as the access logging bucket for CloudFront Distribution" + } + ] + } + } + }, + "testcloudfronts3CloudfrontLoggingBucketPolicyDF55851B": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "testcloudfronts3CloudfrontLoggingBucket985C0FE8" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucket985C0FE8", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucket985C0FE8", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucket985C0FE8", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucket985C0FE8", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "testcloudfronts3CloudfrontLoggingBucketAutoDeleteObjectsCustomResource19604D88": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "testcloudfronts3CloudfrontLoggingBucket985C0FE8" + } + }, + "DependsOn": [ + "testcloudfronts3CloudfrontLoggingBucketPolicyDF55851B" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "testcloudfronts3CloudFrontOac7A951AA6": { + "Type": "AWS::CloudFront::OriginAccessControl", + "Properties": { + "OriginAccessControlConfig": { + "Name": { + "Fn::Join": [ + "", + [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + ] + }, + "OriginAccessControlOriginType": "s3", + "SigningBehavior": "always", + "SigningProtocol": "sigv4" + } + } + }, + "testcloudfronts3CloudFrontDistribution0565DEE8": { + "Type": "AWS::CloudFront::Distribution", + "Properties": { + "DistributionConfig": { + "DefaultCacheBehavior": { + "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", + "Compress": true, + "FunctionAssociations": [ + { + "EventType": "viewer-response", + "FunctionARN": { + "Fn::GetAtt": [ + "testcloudfronts3SetHttpSecurityHeaders6C5A1E69", + "FunctionARN" + ] + } + } + ], + "TargetOriginId": "cfts3customCloudFrontLoggingBuckettestcloudfronts3CloudFrontDistributionOrigin18A4ECB64", + "ViewerProtocolPolicy": "redirect-to-https" + }, + "DefaultRootObject": "index.html", + "Enabled": true, + "HttpVersion": "http2", + "IPV6Enabled": true, + "Logging": { + "Bucket": { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucket985C0FE8", + "RegionalDomainName" + ] + } + }, + "Origins": [ + { + "DomainName": { + "Fn::GetAtt": [ + "testcloudfronts3S3BucketE0C5F76E", + "RegionalDomainName" + ] + }, + "Id": "cfts3customCloudFrontLoggingBuckettestcloudfronts3CloudFrontDistributionOrigin18A4ECB64", + "OriginAccessControlId": { + "Fn::GetAtt": [ + "testcloudfronts3CloudFrontOac7A951AA6", + "Id" + ] + }, + "S3OriginConfig": {} + } + ] + } + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W70", + "reason": "Since the distribution uses the CloudFront domain name, CloudFront automatically sets the security policy to TLSv1 regardless of the value of MinimumProtocolVersion" + } + ] + } + } + }, + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ] + } + }, + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "b7f33614a69548d6bafe224d751a7ef238cde19097415e553fe8b63a4c8fd8a6.zip" + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + }, + "Runtime": "nodejs18.x", + "Description": { + "Fn::Join": [ + "", + [ + "Lambda function for auto-deleting objects in ", + { + "Ref": "testcloudfronts3S3BucketE0C5F76E" + }, + " S3 bucket." + ] + ] + } + }, + "DependsOn": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "CDK generated custom resource" + }, + { + "id": "W89", + "reason": "CDK generated custom resource" + }, + { + "id": "W92", + "reason": "CDK generated custom resource" + } + ] + } + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customLoggingBuckets.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customLoggingBuckets.expected.json index f250de061..2c9f327e8 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customLoggingBuckets.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-customLoggingBuckets.expected.json @@ -327,14 +327,28 @@ }, { "Action": "s3:GetObject", + "Condition": { + "StringEquals": { + "AWS:SourceArn": { + "Fn::Join": [ + "", + [ + "arn:aws:cloudfront::", + { + "Ref": "AWS::AccountId" + }, + ":distribution/", + { + "Ref": "testcloudfronts3CloudFrontDistribution0565DEE8" + } + ] + ] + } + } + }, "Effect": "Allow", "Principal": { - "CanonicalUser": { - "Fn::GetAtt": [ - "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058", - "S3CanonicalUserId" - ] - } + "Service": "cloudfront.amazonaws.com" }, "Resource": { "Fn::Join": [ @@ -761,11 +775,35 @@ "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, - "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058": { - "Type": "AWS::CloudFront::CloudFrontOriginAccessIdentity", + "testcloudfronts3CloudFrontOac7A951AA6": { + "Type": "AWS::CloudFront::OriginAccessControl", "Properties": { - "CloudFrontOriginAccessIdentityConfig": { - "Comment": "Identity for cfts3customLoggingBucketstestcloudfronts3CloudFrontDistributionOrigin1BBEA7E26" + "OriginAccessControlConfig": { + "Description": "Origin access control provisioned by aws-cloudfront-s3", + "Name": { + "Fn::Join": [ + "", + [ + "aws-cloudfront-s3-testnt-s3-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + ] + }, + "OriginAccessControlOriginType": "s3", + "SigningBehavior": "always", + "SigningProtocol": "sigv4" } } }, @@ -811,19 +849,13 @@ ] }, "Id": "cfts3customLoggingBucketstestcloudfronts3CloudFrontDistributionOrigin1BBEA7E26", - "S3OriginConfig": { - "OriginAccessIdentity": { - "Fn::Join": [ - "", - [ - "origin-access-identity/cloudfront/", - { - "Ref": "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058" - } - ] - ] - } - } + "OriginAccessControlId": { + "Fn::GetAtt": [ + "testcloudfronts3CloudFrontOac7A951AA6", + "Id" + ] + }, + "S3OriginConfig": {} } ] } diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-existing-bucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-existing-bucket.expected.json index 00d8308b4..5286672a9 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-existing-bucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-existing-bucket.expected.json @@ -365,14 +365,28 @@ }, { "Action": "s3:GetObject", + "Condition": { + "StringEquals": { + "AWS:SourceArn": { + "Fn::Join": [ + "", + [ + "arn:aws:cloudfront::", + { + "Ref": "AWS::AccountId" + }, + ":distribution/", + { + "Ref": "testcloudfronts3CloudFrontDistribution0565DEE8" + } + ] + ] + } + } + }, "Effect": "Allow", "Principal": { - "CanonicalUser": { - "Fn::GetAtt": [ - "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058", - "S3CanonicalUserId" - ] - } + "Service": "cloudfront.amazonaws.com" }, "Resource": { "Fn::Join": [ @@ -799,11 +813,35 @@ "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, - "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058": { - "Type": "AWS::CloudFront::CloudFrontOriginAccessIdentity", + "testcloudfronts3CloudFrontOac7A951AA6": { + "Type": "AWS::CloudFront::OriginAccessControl", "Properties": { - "CloudFrontOriginAccessIdentityConfig": { - "Comment": "Identity for cfts3existingbuckettestcloudfronts3CloudFrontDistributionOrigin10617473F" + "OriginAccessControlConfig": { + "Description": "Origin access control provisioned by aws-cloudfront-s3", + "Name": { + "Fn::Join": [ + "", + [ + "aws-cloudfront-s3-testnt-s3-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + ] + }, + "OriginAccessControlOriginType": "s3", + "SigningBehavior": "always", + "SigningProtocol": "sigv4" } } }, @@ -860,19 +898,13 @@ ] }, "Id": "cfts3existingbuckettestcloudfronts3CloudFrontDistributionOrigin10617473F", - "S3OriginConfig": { - "OriginAccessIdentity": { - "Fn::Join": [ - "", - [ - "origin-access-identity/cloudfront/", - { - "Ref": "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058" - } - ] - ] - } - } + "OriginAccessControlId": { + "Fn::GetAtt": [ + "testcloudfronts3CloudFrontOac7A951AA6", + "Id" + ] + }, + "S3OriginConfig": {} }, { "DomainName": { diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-arguments.expected.json index ffe9fb145..c5944c813 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-arguments.expected.json @@ -325,14 +325,28 @@ }, { "Action": "s3:GetObject", + "Condition": { + "StringEquals": { + "AWS:SourceArn": { + "Fn::Join": [ + "", + [ + "arn:aws:cloudfront::", + { + "Ref": "AWS::AccountId" + }, + ":distribution/", + { + "Ref": "testcloudfronts3CloudFrontDistribution0565DEE8" + } + ] + ] + } + } + }, "Effect": "Allow", "Principal": { - "CanonicalUser": { - "Fn::GetAtt": [ - "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058", - "S3CanonicalUserId" - ] - } + "Service": "cloudfront.amazonaws.com" }, "Resource": { "Fn::Join": [ @@ -733,11 +747,35 @@ "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, - "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058": { - "Type": "AWS::CloudFront::CloudFrontOriginAccessIdentity", + "testcloudfronts3CloudFrontOac7A951AA6": { + "Type": "AWS::CloudFront::OriginAccessControl", "Properties": { - "CloudFrontOriginAccessIdentityConfig": { - "Comment": "Identity for cfts3noargumentstestcloudfronts3CloudFrontDistributionOrigin1203032D1" + "OriginAccessControlConfig": { + "Description": "Origin access control provisioned by aws-cloudfront-s3", + "Name": { + "Fn::Join": [ + "", + [ + "aws-cloudfront-s3-testnt-s3-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + ] + }, + "OriginAccessControlOriginType": "s3", + "SigningBehavior": "always", + "SigningProtocol": "sigv4" } } }, @@ -783,19 +821,13 @@ ] }, "Id": "cfts3noargumentstestcloudfronts3CloudFrontDistributionOrigin1203032D1", - "S3OriginConfig": { - "OriginAccessIdentity": { - "Fn::Join": [ - "", - [ - "origin-access-identity/cloudfront/", - { - "Ref": "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058" - } - ] - ] - } - } + "OriginAccessControlId": { + "Fn::GetAtt": [ + "testcloudfronts3CloudFrontOac7A951AA6", + "Id" + ] + }, + "S3OriginConfig": {} } ] } diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-security-headers.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-security-headers.expected.json index 5299de8f5..b6829049b 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-security-headers.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.cfts3-no-security-headers.expected.json @@ -315,14 +315,28 @@ }, { "Action": "s3:GetObject", + "Condition": { + "StringEquals": { + "AWS:SourceArn": { + "Fn::Join": [ + "", + [ + "arn:aws:cloudfront::", + { + "Ref": "AWS::AccountId" + }, + ":distribution/", + { + "Ref": "testcloudfronts3nosecurityheadersCloudFrontDistribution3BC8CDED" + } + ] + ] + } + } + }, "Effect": "Allow", "Principal": { - "CanonicalUser": { - "Fn::GetAtt": [ - "testcloudfronts3nosecurityheadersCloudFrontDistributionOrigin1S3Origin38CFDB89", - "S3CanonicalUserId" - ] - } + "Service": "cloudfront.amazonaws.com" }, "Resource": { "Fn::Join": [ @@ -711,11 +725,35 @@ "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, - "testcloudfronts3nosecurityheadersCloudFrontDistributionOrigin1S3Origin38CFDB89": { - "Type": "AWS::CloudFront::CloudFrontOriginAccessIdentity", + "testcloudfronts3nosecurityheadersCloudFrontOac7954FB73": { + "Type": "AWS::CloudFront::OriginAccessControl", "Properties": { - "CloudFrontOriginAccessIdentityConfig": { - "Comment": "Identity for cfts3nosecurityheaderstestcloudfronts3nosecurityheadersCloudFrontDistributionOrigin1A0125E27" + "OriginAccessControlConfig": { + "Description": "Origin access control provisioned by aws-cloudfront-s3", + "Name": { + "Fn::Join": [ + "", + [ + "aws-cloudfront-s3-testaders-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + ] + }, + "OriginAccessControlOriginType": "s3", + "SigningBehavior": "always", + "SigningProtocol": "sigv4" } } }, @@ -750,19 +788,13 @@ ] }, "Id": "cfts3nosecurityheaderstestcloudfronts3nosecurityheadersCloudFrontDistributionOrigin1A0125E27", - "S3OriginConfig": { - "OriginAccessIdentity": { - "Fn::Join": [ - "", - [ - "origin-access-identity/cloudfront/", - { - "Ref": "testcloudfronts3nosecurityheadersCloudFrontDistributionOrigin1S3Origin38CFDB89" - } - ] - ] - } - } + "OriginAccessControlId": { + "Fn::GetAtt": [ + "testcloudfronts3nosecurityheadersCloudFrontOac7954FB73", + "Id" + ] + }, + "S3OriginConfig": {} } ] } diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/test.cloudfront-s3.test.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/test.cloudfront-s3.test.ts index 70438b604..1928d8701 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/test.cloudfront-s3.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/test.cloudfront-s3.test.ts @@ -18,6 +18,8 @@ import {Duration, RemovalPolicy, Stack} from "aws-cdk-lib"; import {CloudFrontToS3, CloudFrontToS3Props} from "../lib"; import * as acm from 'aws-cdk-lib/aws-certificatemanager'; import * as defaults from '@aws-solutions-constructs/core'; +import { Key } from "aws-cdk-lib/aws-kms"; +import * as origins from 'aws-cdk-lib/aws-cloudfront-origins'; function deploy(stack: cdk.Stack, props?: CloudFrontToS3Props) { return new CloudFrontToS3(stack, 'test-cloudfront-s3', { @@ -113,19 +115,6 @@ test('check existing bucket', () => { template.hasResourceProperties("AWS::S3::Bucket", { BucketName: "my-bucket" }); - - template.hasResource("AWS::S3::BucketPolicy", { - Metadata: { - cfn_nag: { - rules_to_suppress: [ - { - id: "F16", - reason: "Public website bucket policy requires a wildcard principal" - } - ] - } - } - }); }); test('check exception for Missing existingObj from props for deploy = false', () => { @@ -195,19 +184,8 @@ test("Test existingBucketObj", () => { ] }, Id: "existingIBucketCloudFrontDistributionOrigin1D5849125", - S3OriginConfig: { - OriginAccessIdentity: { - "Fn::Join": [ - "", - [ - "origin-access-identity/cloudfront/", - { - Ref: "existingIBucketCloudFrontDistributionOrigin1S3OriginDDDB1606" - } - ] - ] - } - } + OriginAccessControlId: { "Fn::GetAtt": [ "existingIBucketCloudFrontOacEB42E98F", "Id" ] }, + S3OriginConfig: { } } ] } @@ -464,3 +442,150 @@ test("Confirm CheckCloudFrontProps is being called", () => { }); }).toThrowError('responseHeadersPolicyProps.securityHeadersBehavior can only be passed if httpSecurityHeaders is set to `false`.'); }); + +test("Custom resource is provisioned if encryption key is provided as bucketProp", () => { + const stack = new cdk.Stack(); + const encryptionKey = new Key(stack, 'cmkKey', { + enableKeyRotation: true, + removalPolicy: RemovalPolicy.DESTROY + }); + deploy(stack, { + bucketProps: { + encryptionKey, + encryption: s3.BucketEncryption.KMS + } + }); + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::Lambda::Function', { + Role: { + "Fn::GetAtt": [ "testcloudfronts3KmsKeyPolicyUpdateLambdaRole08D4BED2", "Arn" ] + } + }); +}); + +test("Custom resource is provisioned if CMK was used to encrypt an existing bucket", () => { + const stack = new cdk.Stack(); + const encryptionKey = new Key(stack, 'cmkKey', { + enableKeyRotation: true, + removalPolicy: RemovalPolicy.DESTROY + }); + const existingBucketObj = defaults.buildS3Bucket(stack, { + bucketProps: { + encryption: s3.BucketEncryption.KMS, + encryptionKey + } + }, 'existing-s3-bucket-encrypted-with-cmk').bucket; + new CloudFrontToS3(stack, 'test-cloudfront-s3', { + existingBucketObj + }); + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::Lambda::Function', { + Role: { + "Fn::GetAtt": [ "testcloudfronts3KmsKeyPolicyUpdateLambdaRole08D4BED2", "Arn" ] + } + }); +}); + +test("Custom resource is not provisioned if encryption key is not provided as bucketProp", () => { + const stack = new cdk.Stack(); + deploy(stack); + const template = Template.fromStack(stack); + template.resourceCountIs('AWS::Lambda::Function', 0); +}); + +test("Custom resource is not provisioned if CMK was not used to encrypt an existing bucket", () => { + const stack = new cdk.Stack(); + const existingBucketObj = defaults.buildS3Bucket(stack, {}, 'existing-s3-bucket-encrypted-with-cmk').bucket; + new CloudFrontToS3(stack, 'test-cloudfront-s3', { + existingBucketObj + }); + const template = Template.fromStack(stack); + template.resourceCountIs('AWS::Lambda::Function', 0); +}); + +test("HttpOrigin is provisioned if a static website bucket is used", () => { + const stack = new cdk.Stack(); + const blockPublicAccess = false; + const props: CloudFrontToS3Props = { + bucketProps: { + enforceSSL: false, + publicReadAccess: true, // <-- required for isWebsite + blockPublicAccess: { + blockPublicAcls: blockPublicAccess, + restrictPublicBuckets: blockPublicAccess, + blockPublicPolicy: blockPublicAccess, + ignorePublicAcls: blockPublicAccess + }, + websiteIndexDocument: "index.html" // <-- required for isWebsite + }, + insertHttpSecurityHeaders: false + }; + const construct = new CloudFrontToS3(stack, 'test-cloudfront-s3', props); + const template = Template.fromStack(stack); + // Assert resources + template.resourceCountIs('AWS::CloudFront::OriginAccessControl', 0); + template.hasResourceProperties('AWS::CloudFront::Distribution', { + DistributionConfig: { + Origins: [ + { + CustomOriginConfig: { + OriginProtocolPolicy: "http-only" + } + } + ] + } + }); + template.resourceCountIs('AWS::CloudFront::OriginAccessIdentity', 0); + // Assert pattern properties (output props) + expect(construct.originAccessControl).toBe(undefined); +}); + +test("OAC is provisioned in all other cases", () => { + const stack = new cdk.Stack(); + const construct = new CloudFrontToS3(stack, 'test-cloudfront-s3', {}); + const template = Template.fromStack(stack); + // Assert resources + template.resourceCountIs('AWS::CloudFront::OriginAccessControl', 1); + template.resourceCountIs('AWS::CloudFront::OriginAccessIdentity', 0); + // Assert pattern properties (output props) + expect(construct.originAccessControl).not.toBe(undefined); +}); + +test("If a customer provides their own httpOrigin, or other origin type, use that one", () => { + const stack = new cdk.Stack(); + const blockPublicAccess = false; + const props: CloudFrontToS3Props = { + bucketProps: { + enforceSSL: false, + publicReadAccess: true, // <-- required for isWebsite + blockPublicAccess: { + blockPublicAcls: blockPublicAccess, + restrictPublicBuckets: blockPublicAccess, + blockPublicPolicy: blockPublicAccess, + ignorePublicAcls: blockPublicAccess + }, + websiteIndexDocument: "index.html" // <-- required for isWebsite + }, + insertHttpSecurityHeaders: false, + cloudFrontDistributionProps: { + defaultBehavior: { + origin: new origins.HttpOrigin('example.com', { + originId: 'custom-http-origin-for-testing' + }) + } + } + }; + new CloudFrontToS3(stack, 'test-cloudfront-s3', props); + const template = Template.fromStack(stack); + // Assert resources + template.hasResourceProperties('AWS::CloudFront::Distribution', { + DistributionConfig: { + Origins: [ + { + DomainName: "example.com", + Id: "custom-http-origin-for-testing" + } + ] + } + }); +}); diff --git a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromAssetExistingLambdaFunctions.expected.json b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromAssetExistingLambdaFunctions.expected.json index 0778d1a11..5eb2680dd 100644 --- a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromAssetExistingLambdaFunctions.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromAssetExistingLambdaFunctions.expected.json @@ -550,7 +550,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "96f7d2d4f45b57a586d030966d31e06bc622b5f20258ca38d605fef38e6b9a17.zip" + "S3Key": "b1223f33e5124fdec3dc7988012c02ed49e285da9c1b8bcd2ce2d2c7c5d09ee7.zip" }, "Handler": "index.handler", "MemorySize": 1024, diff --git a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromAssetNewLambdaFunctions.expected.json b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromAssetNewLambdaFunctions.expected.json index e3332f80b..312fd4aa5 100644 --- a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromAssetNewLambdaFunctions.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromAssetNewLambdaFunctions.expected.json @@ -549,7 +549,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "96f7d2d4f45b57a586d030966d31e06bc622b5f20258ca38d605fef38e6b9a17.zip" + "S3Key": "b1223f33e5124fdec3dc7988012c02ed49e285da9c1b8bcd2ce2d2c7c5d09ee7.zip" }, "Handler": "index.handler", "MemorySize": 1024, diff --git a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromAssetWithCognitoAuth.expected.json b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromAssetWithCognitoAuth.expected.json index baa7e97c8..bae72716e 100644 --- a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromAssetWithCognitoAuth.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromAssetWithCognitoAuth.expected.json @@ -549,7 +549,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "96f7d2d4f45b57a586d030966d31e06bc622b5f20258ca38d605fef38e6b9a17.zip" + "S3Key": "b1223f33e5124fdec3dc7988012c02ed49e285da9c1b8bcd2ce2d2c7c5d09ee7.zip" }, "Handler": "index.handler", "MemorySize": 1024, diff --git a/source/patterns/@aws-solutions-constructs/core/index.ts b/source/patterns/@aws-solutions-constructs/core/index.ts index 29bc3741f..e208d1ac7 100644 --- a/source/patterns/@aws-solutions-constructs/core/index.ts +++ b/source/patterns/@aws-solutions-constructs/core/index.ts @@ -40,6 +40,7 @@ export * from './lib/secretsmanager-helper'; export * from './lib/security-group-helper'; export * from './lib/sns-defaults'; export * from './lib/sns-helper'; +export * from './lib/s3-oac-origin'; export * from './lib/sqs-defaults'; export * from './lib/sqs-helper'; export * from './lib/cloudfront-distribution-helper'; diff --git a/source/patterns/@aws-solutions-constructs/core/lib/cloudfront-distribution-defaults.ts b/source/patterns/@aws-solutions-constructs/core/lib/cloudfront-distribution-defaults.ts index 266fa604d..bd7cea1bd 100644 --- a/source/patterns/@aws-solutions-constructs/core/lib/cloudfront-distribution-defaults.ts +++ b/source/patterns/@aws-solutions-constructs/core/lib/cloudfront-distribution-defaults.ts @@ -59,27 +59,19 @@ export function DefaultCloudFrontWebDistributionForApiGatewayProps(apiEndPoint: * @internal This is an internal core function and should not be called directly by Solutions Constructs clients. */ export function DefaultCloudFrontWebDistributionForS3Props( - sourceBucket: s3.IBucket, + origin: IOrigin, loggingBucket: s3.Bucket | undefined, setHttpSecurityHeaders: boolean, - originPath?: string, cfFunction?: cloudfront.IFunction, responseHeadersPolicy?: cloudfront.ResponseHeadersPolicy, ): cloudfront.DistributionProps { - let origin: IOrigin; - - if (originPath) { - origin = new origins.S3Origin(sourceBucket, { originPath }); - } else { - origin = new origins.S3Origin(sourceBucket); - } - let defaultBehavior: BehaviorOptions = { origin, viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, ...getFunctionAssociationsProp(setHttpSecurityHeaders, cfFunction) }; + if (responseHeadersPolicy) { defaultBehavior = {...defaultBehavior, responseHeadersPolicy }; } diff --git a/source/patterns/@aws-solutions-constructs/core/lib/cloudfront-distribution-helper.ts b/source/patterns/@aws-solutions-constructs/core/lib/cloudfront-distribution-helper.ts index 9040b0f8f..8e5925f40 100644 --- a/source/patterns/@aws-solutions-constructs/core/lib/cloudfront-distribution-helper.ts +++ b/source/patterns/@aws-solutions-constructs/core/lib/cloudfront-distribution-helper.ts @@ -26,9 +26,10 @@ import { DefaultCloudFrontWebDistributionForApiGatewayProps, DefaultCloudFrontDistributionForMediaStoreProps } from './cloudfront-distribution-defaults'; -import { addCfnSuppressRules, consolidateProps } from './utils'; +import { addCfnSuppressRules, consolidateProps, generatePhysicalName } from './utils'; import { createCloudFrontLoggingBucket } from './s3-bucket-helper'; import { DefaultS3Props } from './s3-bucket-defaults'; +import { S3OacOrigin } from './s3-oac-origin'; // Note: To ensure CDKv2 compatibility, keep the import statement for Construct separate import { Construct } from 'constructs'; @@ -102,43 +103,68 @@ export function CloudFrontDistributionForApiGateway(scope: Construct, return { distribution: cfDistribution, cloudfrontFunction, loggingBucket}; } -export interface CloudFrontDistributionForS3Response { +export interface CreateCloudFrontDistributionForS3Props { + readonly sourceBucket: s3.IBucket, + readonly cloudFrontDistributionProps?: cloudfront.DistributionProps | any, + readonly httpSecurityHeaders?: boolean, + readonly cloudFrontLoggingBucketProps?: s3.BucketProps, + readonly responseHeadersPolicyProps?: cloudfront.ResponseHeadersPolicyProps +} + +export interface CreateCloudFrontDistributionForS3Response { readonly distribution: cloudfront.Distribution, readonly loggingBucket?: s3.Bucket, readonly cloudfrontFunction?: cloudfront.Function, + readonly originAccessControl?: cloudfront.CfnOriginAccessControl, } /** * @internal This is an internal core function and should not be called directly by Solutions Constructs clients. */ -export function CloudFrontDistributionForS3( +export function createCloudFrontDistributionForS3( scope: Construct, - sourceBucket: s3.IBucket, - cloudFrontDistributionProps?: cloudfront.DistributionProps | any, - httpSecurityHeaders: boolean = true, - originPath?: string, - cloudFrontLoggingBucketProps?: s3.BucketProps, - responseHeadersPolicyProps?: cloudfront.ResponseHeadersPolicyProps -): CloudFrontDistributionForS3Response { + id: string, + props: CreateCloudFrontDistributionForS3Props +): CreateCloudFrontDistributionForS3Response { + const httpSecurityHeaders = props.httpSecurityHeaders ?? true; const cloudfrontFunction = getCloudfrontFunction(httpSecurityHeaders, scope); - const loggingBucket = getLoggingBucket(cloudFrontDistributionProps, scope, cloudFrontLoggingBucketProps); + const loggingBucket = getLoggingBucket(props.cloudFrontDistributionProps, scope, props.cloudFrontLoggingBucketProps); - const defaultprops = DefaultCloudFrontWebDistributionForS3Props(sourceBucket, + let originAccessControl; + let originProps = {}; + + if (!props.sourceBucket.isWebsite) { + originAccessControl = new cloudfront.CfnOriginAccessControl(scope, 'CloudFrontOac', { + originAccessControlConfig: { + name: generatePhysicalName('aws-cloudfront-s3-', [id], 64), + originAccessControlOriginType: 's3', + signingBehavior: 'always', + signingProtocol: 'sigv4', + description: 'Origin access control provisioned by aws-cloudfront-s3' + } + }); + originProps = { originAccessControl }; + } + + const origin = new S3OacOrigin(props.sourceBucket, originProps); + + const defaultprops = DefaultCloudFrontWebDistributionForS3Props(origin, loggingBucket, httpSecurityHeaders, - originPath, cloudfrontFunction, - responseHeadersPolicyProps ? new cloudfront.ResponseHeadersPolicy(scope, 'ResponseHeadersPolicy', responseHeadersPolicyProps) : undefined + props.responseHeadersPolicyProps ? + new cloudfront.ResponseHeadersPolicy(scope, 'ResponseHeadersPolicy', props.responseHeadersPolicyProps) : + undefined ); - const cfprops = consolidateProps(defaultprops, cloudFrontDistributionProps); + const cfprops = consolidateProps(defaultprops, props.cloudFrontDistributionProps); // Create the Cloudfront Distribution const cfDistribution = new cloudfront.Distribution(scope, 'CloudFrontDistribution', cfprops); updateSecurityPolicy(cfDistribution); // Extract the CfnBucketPolicy from the sourceBucket - const bucketPolicy = sourceBucket.policy as s3.BucketPolicy; + const bucketPolicy = props.sourceBucket.policy as s3.BucketPolicy; // the lack of a bucketPolicy means the bucket was imported from outside the stack so the lack of cfn_nag suppression is not an issue if (bucketPolicy) { addCfnSuppressRules(bucketPolicy, [ @@ -148,7 +174,7 @@ export function CloudFrontDistributionForS3( } ]); } - return { distribution: cfDistribution, cloudfrontFunction, loggingBucket}; + return { distribution: cfDistribution, cloudfrontFunction, loggingBucket, originAccessControl}; } export interface CloudFrontDistributionForMediaStoreResponse { diff --git a/source/patterns/@aws-solutions-constructs/core/lib/s3-oac-origin.ts b/source/patterns/@aws-solutions-constructs/core/lib/s3-oac-origin.ts new file mode 100644 index 000000000..c75a78767 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/core/lib/s3-oac-origin.ts @@ -0,0 +1,88 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +/* + * The functions found here in the core library are for internal use and can be changed + * or removed outside of a major release. We recommend against calling them directly from client code. + */ + +import * as cloudfront from 'aws-cdk-lib/aws-cloudfront'; +import * as s3 from 'aws-cdk-lib/aws-s3'; +import { HttpOrigin } from 'aws-cdk-lib/aws-cloudfront-origins'; +import { printWarning } from './utils'; +// Note: To ensure CDKv2 compatibility, keep the import statement for Construct separate +import { Construct } from 'constructs'; + +/** + * Properties to use to customize an S3 Origin. + */ +export interface S3OacOriginProps extends cloudfront.OriginProps { + /** + * The origin access control that will be used when calling your S3 bucket. + */ + readonly originAccessControl?: cloudfront.CfnOriginAccessControl; +} + +/** + * A custom implementation of S3Origin that allows an origin access control (OAC) to be used instead of + * an origin access identity (OAI), which is currently the only option supported by default CDK. + */ +export class S3OacOrigin implements cloudfront.IOrigin { + private readonly origin: cloudfront.IOrigin; + + constructor(bucket: s3.IBucket, props: S3OacOriginProps) { + if (bucket.isWebsite) { + // If the bucket is configured for website hosting, set up an HttpOrigin to support legacy clients + printWarning(`Bucket ${bucket.bucketName} is being provided as a source but currently has website hosting enabled. + This requires both the bucket and its objects to be public. AWS strongly recommends against configuring buckets + and objects for public access. As an alternative, we recommend turning off website hosting settings on the bucket, + which will result in an origin access control (OAC) being provisioned through which CloudFront can securely + serve assets from the bucket.`); + this.origin = new HttpOrigin(bucket.bucketWebsiteDomainName, { + protocolPolicy: cloudfront.OriginProtocolPolicy.HTTP_ONLY, // S3 only supports HTTP for website buckets + ...props + }); + } else { + if (!props.originAccessControl) { + throw new Error(`"props.originAccessControl" is undefined. An origin access control must be provided when using a + bucket that does not have website hosting enabled.`); + } + // If else, set up the origin access control + this.origin = new S3OacBucketOrigin(bucket, props.originAccessControl!); + } + } + + public bind(scope: Construct, options: cloudfront.OriginBindOptions): cloudfront.OriginBindConfig { + return this.origin.bind(scope, options); + } +} + +/** + * An origin specific to a S3 bucket (not configured for website hosting). + */ +class S3OacBucketOrigin extends cloudfront.OriginBase { + public originAccessControl!: cloudfront.CfnOriginAccessControl; + + constructor(bucket: s3.IBucket, originAccessControl: cloudfront.CfnOriginAccessControl) { + super(bucket.bucketRegionalDomainName); + this.originAccessControl = originAccessControl; + } + + public bind(scope: Construct, options: cloudfront.OriginBindOptions): cloudfront.OriginBindConfig { + return super.bind(scope, options); + } + + protected renderS3OriginConfig(): cloudfront.CfnDistribution.S3OriginConfigProperty | undefined { + return { }; + } +} diff --git a/source/patterns/@aws-solutions-constructs/core/test/cloudfront-distribution-s3-helper.test.ts b/source/patterns/@aws-solutions-constructs/core/test/cloudfront-distribution-s3-helper.test.ts index 065eb806e..ebdc96213 100644 --- a/source/patterns/@aws-solutions-constructs/core/test/cloudfront-distribution-s3-helper.test.ts +++ b/source/patterns/@aws-solutions-constructs/core/test/cloudfront-distribution-s3-helper.test.ts @@ -15,7 +15,7 @@ import { Template } from 'aws-cdk-lib/assertions'; import { Stack } from 'aws-cdk-lib'; import * as cloudfront from 'aws-cdk-lib/aws-cloudfront'; import * as lambda from 'aws-cdk-lib/aws-lambda'; -import { CloudFrontDistributionForS3 } from '../lib/cloudfront-distribution-helper'; +import { createCloudFrontDistributionForS3 } from '../lib/cloudfront-distribution-helper'; import { buildS3Bucket } from '../lib/s3-bucket-helper'; import { Bucket } from 'aws-cdk-lib/aws-s3'; import * as origins from 'aws-cdk-lib/aws-cloudfront-origins'; @@ -26,7 +26,9 @@ import * as defaults from '../'; test('check bucket policy metadata', () => { const stack = new Stack(); const buildS3BucketResponse = buildS3Bucket(stack, {}); - CloudFrontDistributionForS3(stack, buildS3BucketResponse.bucket); + createCloudFrontDistributionForS3(stack, 'sample-cf-distro', { + sourceBucket: buildS3BucketResponse.bucket + }); const template = Template.fromStack(stack); template.hasResource('AWS::S3::BucketPolicy', { Metadata: { @@ -45,7 +47,9 @@ test('check bucket policy metadata', () => { test('test cloudfront check bucket policy', () => { const stack = new Stack(); const buildS3BucketResponse = buildS3Bucket(stack, {}); - CloudFrontDistributionForS3(stack, buildS3BucketResponse.bucket); + createCloudFrontDistributionForS3(stack, 'sample-cf-distro', { + sourceBucket: buildS3BucketResponse.bucket + }); const template = Template.fromStack(stack); template.hasResourceProperties("AWS::S3::BucketPolicy", { @@ -84,32 +88,6 @@ test('test cloudfront check bucket policy', () => { ] } ] - }, - { - Action: "s3:GetObject", - Effect: "Allow", - Principal: { - CanonicalUser: { - "Fn::GetAtt": [ - "CloudFrontDistributionOrigin1S3Origin3D9CA0E9", - "S3CanonicalUserId" - ] - } - }, - Resource: { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "S3Bucket07682993", - "Arn" - ] - }, - "/*" - ] - ] - } } ], Version: "2012-10-17" @@ -121,7 +99,11 @@ test('test cloudfront with no security headers ', () => { const stack = new Stack(); const buildS3BucketResponse = buildS3Bucket(stack, {}); - CloudFrontDistributionForS3(stack, buildS3BucketResponse.bucket, {}, false); + createCloudFrontDistributionForS3(stack, 'sample-cf-distro', { + sourceBucket: buildS3BucketResponse.bucket, + cloudFrontDistributionProps: {}, + httpSecurityHeaders: false + }); const template = Template.fromStack(stack); template.hasResourceProperties("AWS::CloudFront::Distribution", { @@ -143,31 +125,7 @@ test('test cloudfront with no security headers ', () => { "RegionalDomainName" ] } - }, - Origins: [ - { - DomainName: { - "Fn::GetAtt": [ - "S3Bucket07682993", - "RegionalDomainName" - ] - }, - Id: "CloudFrontDistributionOrigin176EC3A12", - S3OriginConfig: { - OriginAccessIdentity: { - "Fn::Join": [ - "", - [ - "origin-access-identity/cloudfront/", - { - Ref: "CloudFrontDistributionOrigin1S3Origin3D9CA0E9" - } - ] - ] - } - } - } - ] + } } }); }); @@ -182,7 +140,10 @@ test('test cloudfront override cloudfront logging bucket ', () => { logBucket }; - CloudFrontDistributionForS3(stack, buildS3BucketResponse.bucket, myprops); + createCloudFrontDistributionForS3(stack, 'sample-cf-distro', { + sourceBucket: buildS3BucketResponse.bucket, + cloudFrontDistributionProps: myprops + }); const template = Template.fromStack(stack); template.hasResourceProperties("AWS::CloudFront::Distribution", { @@ -215,31 +176,7 @@ test('test cloudfront override cloudfront logging bucket ', () => { "RegionalDomainName" ] } - }, - Origins: [ - { - DomainName: { - "Fn::GetAtt": [ - "S3Bucket07682993", - "RegionalDomainName" - ] - }, - Id: "CloudFrontDistributionOrigin176EC3A12", - S3OriginConfig: { - OriginAccessIdentity: { - "Fn::Join": [ - "", - [ - "origin-access-identity/cloudfront/", - { - Ref: "CloudFrontDistributionOrigin1S3Origin3D9CA0E9" - } - ] - ] - } - } - } - ] + } } }); }); @@ -256,7 +193,10 @@ test('test cloudfront override properties', () => { }, }; - CloudFrontDistributionForS3(stack, buildS3BucketResponse.bucket, props); + createCloudFrontDistributionForS3(stack, 'sample-cf-distro', { + sourceBucket: buildS3BucketResponse.bucket, + cloudFrontDistributionProps: props + }); const template = Template.fromStack(stack); template.hasResourceProperties("AWS::CloudFront::Distribution", { @@ -342,14 +282,17 @@ test('test override cloudfront with custom cloudfront function', () => { code: cloudfront.FunctionCode.fromInline("exports.handler = (event, context, callback) => {}") }); - CloudFrontDistributionForS3(stack, buildS3BucketResponse.bucket, { - defaultBehavior: { - functionAssociations: [ - { - eventType: cloudfront.FunctionEventType.VIEWER_RESPONSE, - function: cloudfrontFunction - } - ], + createCloudFrontDistributionForS3(stack, 'sample-cf-distro', { + sourceBucket: buildS3BucketResponse.bucket, + cloudFrontDistributionProps: { + defaultBehavior: { + functionAssociations: [ + { + eventType: cloudfront.FunctionEventType.VIEWER_RESPONSE, + function: cloudfrontFunction + } + ], + } } }); @@ -384,31 +327,7 @@ test('test override cloudfront with custom cloudfront function', () => { "RegionalDomainName" ] } - }, - Origins: [ - { - DomainName: { - "Fn::GetAtt": [ - "S3Bucket07682993", - "RegionalDomainName" - ] - }, - Id: "CloudFrontDistributionOrigin176EC3A12", - S3OriginConfig: { - OriginAccessIdentity: { - "Fn::Join": [ - "", - [ - "origin-access-identity/cloudfront/", - { - Ref: "CloudFrontDistributionOrigin1S3Origin3D9CA0E9" - } - ] - ] - } - } - } - ] + } } }); }); @@ -429,18 +348,21 @@ test('test override cloudfront replace custom lambda@edge', () => { lambda: handler, }); - CloudFrontDistributionForS3(stack, buildS3BucketResponse.bucket, { - defaultBehavior: { - edgeLambdas: [ - { - eventType: LambdaEdgeEventType.VIEWER_REQUEST, - includeBody: false, - functionVersion: handlerVersion, - } - ] - } - }, - false); + createCloudFrontDistributionForS3(stack, 'sample-cf-distro', { + sourceBucket: buildS3BucketResponse.bucket, + cloudFrontDistributionProps: { + defaultBehavior: { + edgeLambdas: [ + { + eventType: LambdaEdgeEventType.VIEWER_REQUEST, + includeBody: false, + functionVersion: handlerVersion, + } + ] + } + }, + httpSecurityHeaders: false + }); const template = Template.fromStack(stack); template.hasResourceProperties("AWS::CloudFront::Distribution", { @@ -471,31 +393,7 @@ test('test override cloudfront replace custom lambda@edge', () => { "RegionalDomainName" ] } - }, - Origins: [ - { - DomainName: { - "Fn::GetAtt": [ - "S3Bucket07682993", - "RegionalDomainName" - ] - }, - Id: "CloudFrontDistributionOrigin176EC3A12", - S3OriginConfig: { - OriginAccessIdentity: { - "Fn::Join": [ - "", - [ - "origin-access-identity/cloudfront/", - { - Ref: "CloudFrontDistributionOrigin1S3Origin3D9CA0E9" - } - ] - ] - } - } - } - ] + } } }); }); @@ -510,7 +408,10 @@ test('test cloudfront override cloudfront custom domain names ', () => { certificate }; - CloudFrontDistributionForS3(stack, buildS3BucketResponse.bucket, myprops); + createCloudFrontDistributionForS3(stack, 'sample-cf-distro', { + sourceBucket: buildS3BucketResponse.bucket, + cloudFrontDistributionProps: myprops + }); const template = Template.fromStack(stack); template.hasResourceProperties("AWS::CloudFront::Distribution", { diff --git a/source/patterns/@aws-solutions-constructs/resources/.eslintignore b/source/patterns/@aws-solutions-constructs/resources/.eslintignore index 06bf256b8..7c6ee37af 100644 --- a/source/patterns/@aws-solutions-constructs/resources/.eslintignore +++ b/source/patterns/@aws-solutions-constructs/resources/.eslintignore @@ -1,5 +1,3 @@ -index.js -lib/*.js -test/*.js -*.d.ts -coverage \ No newline at end of file +**/*.js +**/*.d.ts +coverage diff --git a/source/patterns/@aws-solutions-constructs/resources/.gitignore b/source/patterns/@aws-solutions-constructs/resources/.gitignore index f4b17fa0b..96da1f07d 100644 --- a/source/patterns/@aws-solutions-constructs/resources/.gitignore +++ b/source/patterns/@aws-solutions-constructs/resources/.gitignore @@ -1,9 +1,8 @@ index.js -lib/*.js -test/*.js !test/lambda*/* *.js.map -*.d.ts +**/*.d.ts +**/*.js node_modules *.generated.ts dist diff --git a/source/patterns/@aws-solutions-constructs/resources/index.ts b/source/patterns/@aws-solutions-constructs/resources/index.ts index 46e157f5e..65b49677e 100644 --- a/source/patterns/@aws-solutions-constructs/resources/index.ts +++ b/source/patterns/@aws-solutions-constructs/resources/index.ts @@ -11,4 +11,5 @@ * and limitations under the License. */ +export * from './lib/key-policy-updater'; export * from './lib/template-writer'; \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/resources/lib/key-policy-updater-custom-resource/index.ts b/source/patterns/@aws-solutions-constructs/resources/lib/key-policy-updater-custom-resource/index.ts new file mode 100644 index 000000000..a14c5aa2a --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/resources/lib/key-policy-updater-custom-resource/index.ts @@ -0,0 +1,135 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import { KMSClient, GetKeyPolicyCommand, DescribeKeyCommand, PutKeyPolicyCommand, KeyManagerType } from "@aws-sdk/client-kms"; + +const kmsClient = new KMSClient(); + +export const handler = async (event: any, context: any) => { + + let status = 'SUCCESS'; + let responseData = {}; + + if (event.RequestType === 'Create' || event.RequestType === 'Update') { + + try { + const kmsKeyId = event.ResourceProperties.KmsKeyId; + const cloudFrontDistributionId = event.ResourceProperties.CloudFrontDistributionId; + const accountId = event.ResourceProperties.AccountId; + const region = process.env.AWS_REGION; + + const describeKeyCommandResponse = await kmsClient.send(new DescribeKeyCommand({ + KeyId: kmsKeyId + })); + + if (describeKeyCommandResponse.KeyMetadata?.KeyManager === KeyManagerType.AWS) { + return { + Status: 'SUCCESS', + Reason: 'An AWS managed key was provided, no action needed from the custom resource, exiting now.', + PhysicalResourceId: event.PhysicalResourceId ?? context.logStreamName, + StackId: event.StackId, + RequestId: event.RequestId, + LogicalResourceId: event.LogicalResourceId, + Data: 'An AWS managed key was provided, no action needed from the custom resource, exiting now.', + }; + } + + // The PolicyName is specified as "default" below because that is the only valid name as + // written in the documentation for @aws-sdk/client-kms.GetKeyPolicyCommandInput: + // https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-client-kms/Interface/GetKeyPolicyCommandInput/ + const getKeyPolicyCommandResponse = await kmsClient.send(new GetKeyPolicyCommand({ + KeyId: kmsKeyId, + PolicyName: 'default' + })); + + if (!getKeyPolicyCommandResponse.Policy) { + return { + Status: 'FAILED', + Reason: 'An error occurred while retrieving the key policy.', + PhysicalResourceId: event.PhysicalResourceId ?? context.logStreamName, + StackId: event.StackId, + RequestId: event.RequestId, + LogicalResourceId: event.LogicalResourceId, + Data: 'An error occurred while retrieving the key policy.', + }; + } + + // Define the updated key policy to allow CloudFront access + const keyPolicy = JSON.parse(getKeyPolicyCommandResponse?.Policy); + const keyPolicyStatement = { + Sid: 'Grant-CloudFront-Distribution-Key-Usage', + Effect: 'Allow', + Principal: { + Service: 'cloudfront.amazonaws.com', + }, + Action: [ + 'kms:Decrypt', + 'kms:Encrypt', + 'kms:GenerateDataKey*', + 'kms:ReEncrypt*' + ], + Resource: `arn:aws:kms:${region}:${accountId}:key/${kmsKeyId}`, + Condition: { + StringEquals: { + 'AWS:SourceArn': `arn:aws:cloudfront::${accountId}:distribution/${cloudFrontDistributionId}` + } + } + }; + const updatedKeyPolicy = updateKeyPolicy(keyPolicy, keyPolicyStatement); + + await kmsClient.send(new PutKeyPolicyCommand({ + KeyId: kmsKeyId, + Policy: JSON.stringify(updatedKeyPolicy), + PolicyName: 'default' + })); + } catch (err) { + status = 'FAILED'; + responseData = { + Error: JSON.stringify(err) + }; + } + } + + return { + Status: status, + Reason: JSON.stringify(responseData), + PhysicalResourceId: event.PhysicalResourceId ?? context.logStreamName, + StackId: event.StackId, + RequestId: event.RequestId, + LogicalResourceId: event.LogicalResourceId, + Data: responseData, + }; +}; + +/** + * Updates a provided key policy with a provided key policy statement. First checks whether the provided key policy statement + * already exists. If an existing key policy is found with a matching sid, the provided key policy will overwrite the existing + * key policy. If no matching key policy is found, the provided key policy will be appended onto the array of policy statements. + * @param keyPolicy - the JSON.parse'd result of the otherwise stringified key policy. + * @param keyPolicyStatement - the key policy statement to be added to the key policy. + * @returns keyPolicy - the updated key policy. + */ +export const updateKeyPolicy = (keyPolicy: any, keyPolicyStatement: any) => { + // Check to see if a duplicate key policy exists by matching on the sid. This is to prevent duplicate key policies + // from being added/updated in response to a stack being updated one or more times after initial creation. + const existingKeyPolicyIndex = keyPolicy.Statement.findIndex((statement: any) => statement.Sid === keyPolicyStatement.Sid); + // If a match is found, overwrite the key policy statement... + // Otherwise, push the new key policy to the array of statements + if (existingKeyPolicyIndex > -1) { + keyPolicy.Statement[existingKeyPolicyIndex] = keyPolicyStatement; + } else { + keyPolicy.Statement.push(keyPolicyStatement); + } + // Return the result + return keyPolicy; +}; diff --git a/source/patterns/@aws-solutions-constructs/resources/lib/key-policy-updater.ts b/source/patterns/@aws-solutions-constructs/resources/lib/key-policy-updater.ts new file mode 100644 index 000000000..1aed93f94 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/resources/lib/key-policy-updater.ts @@ -0,0 +1,89 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import * as cdk from "aws-cdk-lib"; +import * as iam from 'aws-cdk-lib/aws-iam'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import { Aws, CustomResource } from 'aws-cdk-lib'; +import { Provider } from "aws-cdk-lib/custom-resources"; +import { buildLambdaFunction } from "@aws-solutions-constructs/core"; +import { IKey } from "aws-cdk-lib/aws-kms"; +import { Distribution } from "aws-cdk-lib/aws-cloudfront"; +import { addCfnSuppressRulesForCustomResourceProvider } from "./utils"; +// Note: To ensure CDKv2 compatibility, keep the import statement for Construct separate +import { Construct } from 'constructs'; + +export interface CreateKeyPolicyUpdaterResponse { + readonly lambdaFunction: lambda.Function; + readonly customResource: CustomResource; +} + +export interface KeyPolicyUpdaterProps { + readonly encryptionKey: IKey; + readonly distribution: Distribution; + readonly timeout?: cdk.Duration; + readonly memorySize?: number; +} + +export function createKeyPolicyUpdaterCustomResource( + scope: Construct, + props: KeyPolicyUpdaterProps +): CreateKeyPolicyUpdaterResponse { + + const lambdaFunction = buildLambdaFunction(scope, { + lambdaFunctionProps: { + runtime: lambda.Runtime.NODEJS_18_X, + handler: 'index.handler', + description: 'Custom resource function that updates a provided key policy to allow CloudFront access.', + timeout: props.timeout, + memorySize: props.memorySize, + code: lambda.Code.fromAsset(`${__dirname}/key-policy-updater-custom-resource`), + role: new iam.Role(scope, 'KmsKeyPolicyUpdateLambdaRole', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'), + description: 'Role to update kms key policy to allow CloudFront access', + inlinePolicies: { + KmsPolicy: new iam.PolicyDocument({ + statements: [ + new iam.PolicyStatement({ + actions: ['kms:PutKeyPolicy', 'kms:GetKeyPolicy', 'kms:DescribeKey'], + effect: iam.Effect.ALLOW, + resources: [ props.encryptionKey.keyArn ] + }) + ] + }) + } + }) + } + }); + + const kmsKeyPolicyUpdateProvider = new Provider(scope, 'KmsKeyPolicyUpdateProvider', { + onEventHandler: lambdaFunction + }); + + addCfnSuppressRulesForCustomResourceProvider(kmsKeyPolicyUpdateProvider); + + const customResource = new CustomResource(scope, 'KmsKeyPolicyUpdater', { + resourceType: 'Custom::KmsKeyPolicyUpdater', + serviceToken: kmsKeyPolicyUpdateProvider.serviceToken, + properties: { + KmsKeyId: props.encryptionKey.keyId, + CloudFrontDistributionId: props.distribution.distributionId, + AccountId: Aws.ACCOUNT_ID + }, + }); + + return { + lambdaFunction, + customResource + }; +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/resources/lib/template-writer-custom-resource/index.mjs b/source/patterns/@aws-solutions-constructs/resources/lib/template-writer-custom-resource/index.ts similarity index 93% rename from source/patterns/@aws-solutions-constructs/resources/lib/template-writer-custom-resource/index.mjs rename to source/patterns/@aws-solutions-constructs/resources/lib/template-writer-custom-resource/index.ts index 3e5be7940..7d82dbe4a 100644 --- a/source/patterns/@aws-solutions-constructs/resources/lib/template-writer-custom-resource/index.mjs +++ b/source/patterns/@aws-solutions-constructs/resources/lib/template-writer-custom-resource/index.ts @@ -16,7 +16,7 @@ import * as crypto from 'crypto'; const s3Client = new S3Client({ region: process.env.REGION }); -export const handler = async (event, context) => { +export const handler = async (event: any, context: any) => { let status = 'SUCCESS'; let responseData = {}; @@ -37,9 +37,9 @@ export const handler = async (event, context) => { let template = await getObjectResponse.Body?.transformToString(); - templateValues.forEach(templateValue => { + templateValues.forEach((templateValue: any) => { template = template?.replace( - new RegExp(templateValue.id, 'g'), + new RegExp(templateValue.id, 'g'), templateValue.value ); }); diff --git a/source/patterns/@aws-solutions-constructs/resources/lib/template-writer.ts b/source/patterns/@aws-solutions-constructs/resources/lib/template-writer.ts index c899e6b08..0d691d114 100644 --- a/source/patterns/@aws-solutions-constructs/resources/lib/template-writer.ts +++ b/source/patterns/@aws-solutions-constructs/resources/lib/template-writer.ts @@ -11,7 +11,6 @@ * and limitations under the License. */ -import { Construct } from "constructs"; import * as cdk from "aws-cdk-lib"; import * as iam from 'aws-cdk-lib/aws-iam'; import * as lambda from 'aws-cdk-lib/aws-lambda'; @@ -20,7 +19,10 @@ import { Aws, CustomResource } from 'aws-cdk-lib'; import { Asset } from "aws-cdk-lib/aws-s3-assets"; import * as path from 'path'; import { Provider } from "aws-cdk-lib/custom-resources"; -import { addCfnSuppressRules, buildLambdaFunction } from "@aws-solutions-constructs/core"; +import { buildLambdaFunction } from "@aws-solutions-constructs/core"; +import { addCfnSuppressRulesForCustomResourceProvider } from "./utils"; +// Note: To ensure CDKv2 compatibility, keep the import statement for Construct separate +import { Construct } from 'constructs'; /** * The TemplateValue interface defines the id-value pair that will @@ -143,22 +145,7 @@ export function createTemplateWriterCustomResource( onEventHandler: templateWriterLambda }); - const providerFrameworkFunction = templateWriterProvider.node.children[0].node.findChild('Resource') as lambda.CfnFunction; - - addCfnSuppressRules(providerFrameworkFunction, [ - { - id: 'W58', - reason: `The CDK-provided lambda function that backs their Custom Resource Provider framework has an IAM role with the arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole Managed Policy attached, which grants permission to write to CloudWatch Logs` - }, - { - id: 'W89', - reason: `The CDK-provided lambda function that backs their Custom Resource Provider framework does not access VPC resources` - }, - { - id: 'W92', - reason: `The CDK-provided lambda function that backs their Custom Resource Provider framework does not define ReservedConcurrentExecutions` - } - ]); + addCfnSuppressRulesForCustomResourceProvider(templateWriterProvider); const customResource = new CustomResource(scope, `${id}TemplateWriterCustomResource`, { resourceType: 'Custom::TemplateWriter', diff --git a/source/patterns/@aws-solutions-constructs/resources/lib/utils.ts b/source/patterns/@aws-solutions-constructs/resources/lib/utils.ts new file mode 100644 index 000000000..107644d61 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/resources/lib/utils.ts @@ -0,0 +1,34 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import { Provider } from "aws-cdk-lib/custom-resources"; +import { addCfnSuppressRules } from "@aws-solutions-constructs/core"; + +export const addCfnSuppressRulesForCustomResourceProvider = (provider: Provider) => { + const providerFrameworkFunction = provider.node.children[0].node.findChild('Resource') as lambda.CfnFunction; + addCfnSuppressRules(providerFrameworkFunction, [ + { + id: 'W58', + reason: `The CDK-provided lambda function that backs their Custom Resource Provider framework has an IAM role with the arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole Managed Policy attached, which grants permission to write to CloudWatch Logs` + }, + { + id: 'W89', + reason: `The CDK-provided lambda function that backs their Custom Resource Provider framework does not access VPC resources` + }, + { + id: 'W92', + reason: `The CDK-provided lambda function that backs their Custom Resource Provider framework does not define ReservedConcurrentExecutions` + } + ]); +}; diff --git a/source/patterns/@aws-solutions-constructs/resources/package.json b/source/patterns/@aws-solutions-constructs/resources/package.json index 137ee67b8..24890b332 100644 --- a/source/patterns/@aws-solutions-constructs/resources/package.json +++ b/source/patterns/@aws-solutions-constructs/resources/package.json @@ -52,13 +52,20 @@ } }, "dependencies": { - "@aws-solutions-constructs/core": "0.0.0" + "@aws-cdk/aws-cloudfront": "0.0.0", + "@aws-cdk/aws-iam": "0.0.0", + "@aws-cdk/aws-kms": "0.0.0", + "@aws-cdk/aws-lambda": "0.0.0", + "@aws-cdk/core": "0.0.0", + "@aws-cdk/custom-resources": "0.0.0", + "@aws-sdk/client-kms": "^3.478.0", + "@aws-sdk/client-s3": "^3.478.0", + "@aws-solutions-constructs/core": "0.0.0", + "aws-sdk-client-mock": "^3.0.0" }, "devDependencies": { "@types/jest": "^27.4.0", - "@types/node": "^10.3.0", - "aws-cdk-lib": "0.0.0", - "constructs": "^10.0.0" + "@types/node": "^10.3.0" }, "jest": { "moduleFileExtensions": [ @@ -74,9 +81,18 @@ ] ] }, + "bundledDependencies": [ + "@aws-sdk/client-kms", + "@aws-sdk/client-s3", + "aws-sdk-client-mock" + ], "peerDependencies": { - "@aws-solutions-constructs/core": "0.0.0", - "aws-cdk-lib": "^0.0.0", - "constructs": "^10.0.0" + "@aws-cdk/aws-cloudfront": "0.0.0", + "@aws-cdk/aws-iam": "0.0.0", + "@aws-cdk/aws-kms": "0.0.0", + "@aws-cdk/aws-lambda": "0.0.0", + "@aws-cdk/core": "0.0.0", + "@aws-cdk/custom-resources": "0.0.0", + "@aws-solutions-constructs/core": "0.0.0" } } \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/resources/test/integ.template-writer-from-asset.expected.json b/source/patterns/@aws-solutions-constructs/resources/test/integ.template-writer-from-asset.expected.json index 7e40d2de2..64d46773c 100644 --- a/source/patterns/@aws-solutions-constructs/resources/test/integ.template-writer-from-asset.expected.json +++ b/source/patterns/@aws-solutions-constructs/resources/test/integ.template-writer-from-asset.expected.json @@ -209,7 +209,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "96f7d2d4f45b57a586d030966d31e06bc622b5f20258ca38d605fef38e6b9a17.zip" + "S3Key": "b1223f33e5124fdec3dc7988012c02ed49e285da9c1b8bcd2ce2d2c7c5d09ee7.zip" }, "Handler": "index.handler", "Role": { diff --git a/source/patterns/@aws-solutions-constructs/resources/test/integ.template-writer-from-large-asset.expected.json b/source/patterns/@aws-solutions-constructs/resources/test/integ.template-writer-from-large-asset.expected.json index e7903d42f..f9d13348c 100644 --- a/source/patterns/@aws-solutions-constructs/resources/test/integ.template-writer-from-large-asset.expected.json +++ b/source/patterns/@aws-solutions-constructs/resources/test/integ.template-writer-from-large-asset.expected.json @@ -209,7 +209,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "96f7d2d4f45b57a586d030966d31e06bc622b5f20258ca38d605fef38e6b9a17.zip" + "S3Key": "b1223f33e5124fdec3dc7988012c02ed49e285da9c1b8bcd2ce2d2c7c5d09ee7.zip" }, "Handler": "index.handler", "MemorySize": 1024, diff --git a/source/patterns/@aws-solutions-constructs/resources/test/kms-key-policy-updater.test.ts b/source/patterns/@aws-solutions-constructs/resources/test/kms-key-policy-updater.test.ts new file mode 100644 index 000000000..a4ace058f --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/resources/test/kms-key-policy-updater.test.ts @@ -0,0 +1,245 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import { mockClient } from "aws-sdk-client-mock"; +import { KMSClient, DescribeKeyCommand, KeyManagerType, GetKeyPolicyCommand, PutKeyPolicyCommand } from "@aws-sdk/client-kms"; +import { handler, updateKeyPolicy } from "../lib/key-policy-updater-custom-resource"; + +const kmsMock = mockClient(KMSClient); + +beforeEach(() => { + kmsMock.reset(); +}); + +it('Should exit if an AWS managed key is provided; return a success code but give the exact reason', async () => { + // Mocks + kmsMock.on(DescribeKeyCommand).resolves({ + KeyMetadata: { + KeyId: 'sample-key-id', + KeyManager: KeyManagerType.AWS + } + }); + // Arrange + const e = { + RequestType: 'Create', + ResourceProperties: { + CloudFrontDistributionId: 'sample-cf-distro-id', + AccountId: '111122223333' + } + }; + const context = { + // ... + }; + // Act + const res = await handler(e, context); + // Assert + expect(res.Status).toBe('SUCCESS'); + expect(res.Data).toBe('An AWS managed key was provided, no action needed from the custom resource, exiting now.'); +}); + +it('Should return an error if the key policy is returned as undefined', async () => { + // Mocks + kmsMock.on(DescribeKeyCommand).resolves({ + KeyMetadata: { + KeyId: 'sample-key-id', + KeyManager: KeyManagerType.CUSTOMER + } + }); + kmsMock.on(GetKeyPolicyCommand).resolves({ + Policy: undefined + }); + // Arrange + const e = { + RequestType: 'Update', + ResourceProperties: { + CloudFrontDistributionId: 'sample-cf-distro-id', + AccountId: '111122223333' + } + }; + const context = { + // ... + }; + // Act + const res = await handler(e, context); + // Assert + expect(res.Status).toBe('FAILED'); +}); + +it('Should update the key policy if the proper params are given', async () => { + // Mocks + kmsMock.on(DescribeKeyCommand).resolves({ + KeyMetadata: { + KeyId: 'sample-key-id', + KeyManager: KeyManagerType.CUSTOMER + } + }); + kmsMock.on(GetKeyPolicyCommand).resolves({ + Policy: `{\n + \"Version\" : \"2012-10-17\",\n + \"Id\" : \"sample-key-id\",\n + \"Statement\" : [ {\n + \"Sid\" : \"Grant-CloudFront-Distribution-Key-Usage\",\n + \"Effect\" : \"Allow\",\n + \"Principal\" : {\n + \"AWS\" : \"arn:aws:iam::111122223333:root\"\n + },\n + \"Action\" : \"kms:*\",\n + \"Resource\" : \"*\"\n + } ]\n + }` + }); + // Arrange + const e = { + RequestType: 'Update', + ResourceProperties: { + CloudFrontDistributionId: 'sample-cf-distro-id', + AccountId: '111122223333' + } + }; + const context = { + // ... + }; + // Act + const res = await handler(e, context); + // Assert + expect(res.Status).toBe('SUCCESS'); +}); + +it('Should fail if an error occurs with putting the new key policy, all other inputs valid', async () => { + // Mocks + kmsMock.on(DescribeKeyCommand).resolves({ + KeyMetadata: { + KeyId: 'sample-key-id', + KeyManager: KeyManagerType.CUSTOMER + } + }); + kmsMock.on(GetKeyPolicyCommand).resolves({ + Policy: `{\n + \"Version\" : \"2012-10-17\",\n + \"Id\" : \"key-default-1\",\n + \"Statement\" : [ {\n + \"Sid\" : \"Grant-CloudFront-Distribution-Key-Usage\",\n + \"Effect\" : \"Allow\",\n + \"Principal\" : {\n + \"AWS\" : \"arn:aws:iam::111122223333:root\"\n + },\n + \"Action\" : \"kms:*\",\n + \"Resource\" : \"*\"\n + } ]\n + }` + }); + kmsMock.on(PutKeyPolicyCommand).rejects(); + const e = { + RequestType: 'Update', + ResourceProperties: { + CloudFrontDistributionId: 'sample-cf-distro-id', + AccountId: '111122223333' + } + }; + const context = { + // ... + }; + // Act + const res = await handler(e, context); + // Assert + expect(res.Status).toBe('FAILED'); +}); + +it('Should fail if the key policy has already been applied in a previous stack update or similar event (custom resource response)', async () => { + // Mocks + kmsMock.on(DescribeKeyCommand).resolves({ + KeyMetadata: { + KeyId: 'sample-key-id', + KeyManager: KeyManagerType.CUSTOMER + } + }); + kmsMock.on(GetKeyPolicyCommand).resolves({ + Policy: `{\n + \"Version\" : \"2012-10-17\",\n + \"Id\" : \"key-default-1\",\n + \"Statement\" : [ {\n + \"Sid\" : \"Grant-CloudFront-Distribution-Key-Usage\",\n + \"Effect\" : \"Allow\",\n + \"Principal\" : {\n + \"AWS\" : \"arn:aws:iam::111122223333:root\"\n + },\n + \"Action\" : \"kms:*\",\n + \"Resource\" : \"*\"\n + } ]\n + }` + }); + const e = { + RequestType: 'Update', + ResourceProperties: { + CloudFrontDistributionId: 'sample-cf-distro-id', + AccountId: '111122223333' + } + }; + const context = { + // ... + }; + // Act + const res = await handler(e, context); + // Assert + expect(res.Status).toBe('SUCCESS'); +}); + +it('updateKeyPolicy() should overwrite an existing key policy statement that matches on the sid', async () => { + // Arrange + const keyPolicy = { + Version: "2012-10-17", + Id: "key-default-1", + Statement: [ + { + Sid: 'Grant-CloudFront-Distribution-Key-Usage', + Effect: "Allow" + }, + { + Sid: 'Some-Other-Key-Policy-Statement', + Effect: "Allow" + } + ] + }; + const keyPolicyStatement = { + Sid: 'Grant-CloudFront-Distribution-Key-Usage', + Effect: "Deny" + }; + // Act + const res = updateKeyPolicy(keyPolicy, keyPolicyStatement); + // Assert + expect(res.Statement[0].Sid).toBe('Grant-CloudFront-Distribution-Key-Usage'); + expect(res.Statement[0].Effect).toBe('Deny'); +}); + +it('updateKeyPolicy() should add the key policy statement if one with matching sid does not already exist', async () => { + // Arrange + const keyPolicy = { + Version: "2012-10-17", + Id: "key-default-1", + Statement: [ + { + Sid: 'Some-Other-Key-Policy-Statement', + Effect: "Allow" + } + ] + }; + const keyPolicyStatement = { + Sid: 'Grant-CloudFront-Distribution-Key-Usage', + Effect: "Deny" + }; + // Act + const res = updateKeyPolicy(keyPolicy, keyPolicyStatement); + // Assert + expect(res.Statement[1].Sid).toBe('Grant-CloudFront-Distribution-Key-Usage'); + expect(res.Statement[1].Effect).toBe('Deny'); +}); diff --git a/source/patterns/@aws-solutions-constructs/resources/test/template-writer.test.ts b/source/patterns/@aws-solutions-constructs/resources/test/template-writer.test.ts index 3ec2af365..0fdd59f87 100644 --- a/source/patterns/@aws-solutions-constructs/resources/test/template-writer.test.ts +++ b/source/patterns/@aws-solutions-constructs/resources/test/template-writer.test.ts @@ -13,9 +13,9 @@ import { Duration, Stack } from 'aws-cdk-lib'; import { Template } from 'aws-cdk-lib/assertions'; -import { TemplateValue, createTemplateWriterCustomResource } from '../lib/template-writer'; import { Asset } from 'aws-cdk-lib/aws-s3-assets'; import * as path from 'path'; +import { TemplateValue, createTemplateWriterCustomResource } from '../lib/template-writer'; test('TemplateWriter sets properties correctly', () => { const stack = new Stack(); From 9a3f2b87e19836245e2fec661612445f5f88f05f Mon Sep 17 00:00:00 2001 From: AWS Solutions Constructs Automation Date: Tue, 9 Jan 2024 13:49:10 +0000 Subject: [PATCH 09/14] chore(release): 2.48.0 --- CHANGELOG.md | 9 +++++++++ source/lerna.json | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d22307084..e21057f2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [2.48.0](https://github.com/awslabs/aws-solutions-constructs/compare/v2.47.0...v2.48.0) (2024-01-09) + + +### Features + +* **aws-cloudfront-apigateway-lambda:** require explicit authentication type ([#1044](https://github.com/awslabs/aws-solutions-constructs/issues/1044)) ([720dec5](https://github.com/awslabs/aws-solutions-constructs/commit/720dec500a728a3c57832b7e479ee8eca1f08056)) +* **aws-cloudfront-s3:** update construct to use origin access controls; add support for CMK-encrypted buckets ([#1038](https://github.com/awslabs/aws-solutions-constructs/issues/1038)) ([012f9e7](https://github.com/awslabs/aws-solutions-constructs/commit/012f9e7b6ebd3a717ff120941131a84e803b2922)), closes [#1037](https://github.com/awslabs/aws-solutions-constructs/issues/1037) +* **cloudfront constructs:** add s3 access logging to cloudfront access log buckets by default ([#1042](https://github.com/awslabs/aws-solutions-constructs/issues/1042)) ([51ec028](https://github.com/awslabs/aws-solutions-constructs/commit/51ec028ebd4763965671483e74924e3b8e328337)) + ## [2.47.0](https://github.com/awslabs/aws-solutions-constructs/compare/v2.46.0...v2.47.0) (2023-12-01) Built on CDK v2.111.0 diff --git a/source/lerna.json b/source/lerna.json index c89f6b0b0..f64d6cd04 100644 --- a/source/lerna.json +++ b/source/lerna.json @@ -6,5 +6,5 @@ "./patterns/@aws-solutions-constructs/*" ], "rejectCycles": "true", - "version": "2.47.0" + "version": "2.48.0" } From 1ea9c3d0e43b9f0d9a695fdc38aceb34936e44ce Mon Sep 17 00:00:00 2001 From: biffgaut Date: Tue, 9 Jan 2024 09:06:57 -0500 Subject: [PATCH 10/14] chore(changelog): Updated CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e21057f2f..ed27a3f50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,12 @@ All notable changes to this project will be documented in this file. See [standa ## [2.48.0](https://github.com/awslabs/aws-solutions-constructs/compare/v2.47.0...v2.48.0) (2024-01-09) +### ⚠ BREAKING CHANGES + +* **aws-cloudfront-apigateway-lambda:** require explicit authentication type ([#1044](https://github.com/awslabs/aws-solutions-constructs/issues/1044)) ([720dec5](https://github.com/awslabs/aws-solutions-constructs/commit/720dec500a728a3c57832b7e479ee8eca1f08056)) ### Features -* **aws-cloudfront-apigateway-lambda:** require explicit authentication type ([#1044](https://github.com/awslabs/aws-solutions-constructs/issues/1044)) ([720dec5](https://github.com/awslabs/aws-solutions-constructs/commit/720dec500a728a3c57832b7e479ee8eca1f08056)) * **aws-cloudfront-s3:** update construct to use origin access controls; add support for CMK-encrypted buckets ([#1038](https://github.com/awslabs/aws-solutions-constructs/issues/1038)) ([012f9e7](https://github.com/awslabs/aws-solutions-constructs/commit/012f9e7b6ebd3a717ff120941131a84e803b2922)), closes [#1037](https://github.com/awslabs/aws-solutions-constructs/issues/1037) * **cloudfront constructs:** add s3 access logging to cloudfront access log buckets by default ([#1042](https://github.com/awslabs/aws-solutions-constructs/issues/1042)) ([51ec028](https://github.com/awslabs/aws-solutions-constructs/commit/51ec028ebd4763965671483e74924e3b8e328337)) From 573df48137ee22b5ddf37d4fa99512a9a78ef5f2 Mon Sep 17 00:00:00 2001 From: biffgaut Date: Tue, 9 Jan 2024 09:07:04 -0500 Subject: [PATCH 11/14] chore(version): Upgraded CDK --- deployment/v2/align-version.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/v2/align-version.js b/deployment/v2/align-version.js index 3765c4045..fe57f9e10 100755 --- a/deployment/v2/align-version.js +++ b/deployment/v2/align-version.js @@ -10,7 +10,7 @@ const findVersion = process.argv[2]; const replaceVersion = process.argv[3]; // these versions need to be sourced from a config file -const awsCdkLibVersion = '2.111.0'; +const awsCdkLibVersion = '2.118.0'; const constructsVersion = '10.0.0'; const MODULE_EXEMPTIONS = new Set([ '@aws-cdk/cloudformation-diff', From 2a51cf067844409873f6999f4fd47d02d34608d6 Mon Sep 17 00:00:00 2001 From: biffgaut Date: Tue, 9 Jan 2024 09:18:40 -0500 Subject: [PATCH 12/14] Adjust CDK version but stay compatible --- deployment/v2/align-version.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/v2/align-version.js b/deployment/v2/align-version.js index fe57f9e10..a012a0b91 100755 --- a/deployment/v2/align-version.js +++ b/deployment/v2/align-version.js @@ -10,7 +10,7 @@ const findVersion = process.argv[2]; const replaceVersion = process.argv[3]; // these versions need to be sourced from a config file -const awsCdkLibVersion = '2.118.0'; +const awsCdkLibVersion = '2.114.0'; const constructsVersion = '10.0.0'; const MODULE_EXEMPTIONS = new Set([ '@aws-cdk/cloudformation-diff', From e2fd8ffb6ddf5e1c6d0d199232957279dadc63bb Mon Sep 17 00:00:00 2001 From: biffgaut Date: Tue, 9 Jan 2024 10:09:47 -0500 Subject: [PATCH 13/14] Reset to original CDK version to release --- deployment/v2/align-version.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/v2/align-version.js b/deployment/v2/align-version.js index a012a0b91..3765c4045 100755 --- a/deployment/v2/align-version.js +++ b/deployment/v2/align-version.js @@ -10,7 +10,7 @@ const findVersion = process.argv[2]; const replaceVersion = process.argv[3]; // these versions need to be sourced from a config file -const awsCdkLibVersion = '2.114.0'; +const awsCdkLibVersion = '2.111.0'; const constructsVersion = '10.0.0'; const MODULE_EXEMPTIONS = new Set([ '@aws-cdk/cloudformation-diff', From 1349e1e81576701a56866a23ae41a9d46ca214b1 Mon Sep 17 00:00:00 2001 From: biffgaut Date: Tue, 9 Jan 2024 10:39:16 -0500 Subject: [PATCH 14/14] Udpate CDK build version --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed27a3f50..4418dc350 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file. See [standa ## [2.48.0](https://github.com/awslabs/aws-solutions-constructs/compare/v2.47.0...v2.48.0) (2024-01-09) +Built on CDK v2.111.0 + ### ⚠ BREAKING CHANGES * **aws-cloudfront-apigateway-lambda:** require explicit authentication type ([#1044](https://github.com/awslabs/aws-solutions-constructs/issues/1044)) ([720dec5](https://github.com/awslabs/aws-solutions-constructs/commit/720dec500a728a3c57832b7e479ee8eca1f08056))