diff --git a/.gitignore b/.gitignore index a2a2b7d57..dc301d328 100644 --- a/.gitignore +++ b/.gitignore @@ -36,4 +36,4 @@ workshop/data/* workshop/datagenerator/* workshop/requirements.txt workshop/1-Personalization/generate_interactions_personalize.py -workshop/1-Personalization/generate_interactions_personalize_offers.py \ No newline at end of file +workshop/1-Personalization/generate_interactions_personalize_offers.py diff --git a/aws/cloudformation-templates/base/_template.yaml b/aws/cloudformation-templates/base/_template.yaml index db14264d6..805f24649 100644 --- a/aws/cloudformation-templates/base/_template.yaml +++ b/aws/cloudformation-templates/base/_template.yaml @@ -319,6 +319,12 @@ Resources: Parameters: Uid: !Sub ${ParentStackName}-${AWS::Region} + # SelfSignedCertACM + SelfSignedCertACM: + Type: AWS::CloudFormation::Stack + Properties: + TemplateURL: !Sub https://s3.amazonaws.com/${ResourceBucket}/${ResourceBucketRelativePath}cloudformation-templates/base/acm-cert.yaml + Outputs: UserPoolId: Description: Authentication Cognito User Pool Id. @@ -451,3 +457,7 @@ Outputs: EvidentlyProjectName: Description: Evidently project name Value: !GetAtt Evidently.Outputs.EvidentlyProjectName + + ACMCertARN: + Description: selfsigned cert ARN + Value: !GetAtt SelfSignedCertACM.Outputs.ACMimportCertArn \ No newline at end of file diff --git a/aws/cloudformation-templates/base/acm-cert.yaml b/aws/cloudformation-templates/base/acm-cert.yaml new file mode 100644 index 000000000..4340e682d --- /dev/null +++ b/aws/cloudformation-templates/base/acm-cert.yaml @@ -0,0 +1,153 @@ +AWSTemplateFormatVersion: 2010-09-09 + +Description: > + This template deploys the SSL/TLS selfsigned certificate for Retail Demo Store ELBs to ACM + Author: Ronak Shah + +Resources: + ACMARNParameter: + Type: AWS::SSM::Parameter + Properties: + Name: /retaildemostore/acm-arn + Type: String + Value: Dummy + Description: Retail Demo Store ACM Arn + + ACMImportCertLambdaFunLogGroup: + Type: AWS::Logs::LogGroup + Properties: + LogGroupName: !Sub /aws/lambda/${ACMimportCertLambdaFunction} + RetentionInDays: 3 + + ACMimportCertLambdaFunction: + Type: AWS::Lambda::Function + DependsOn: ACMARNParameter + Properties: + Description: 'Retail Demo Store acm-import-certificate function that returns ARN for imported certificate' + Code: + ZipFile: | + import boto3 + import cfnresponse + from botocore.exceptions import ClientError + + response_data = {} + + acm_client = boto3.client('acm') + ssm_client = boto3.client('ssm') + + def handler(event, context): + response_status = cfnresponse.SUCCESS + acmarn_param_name = '/retaildemostore/acm-arn'; + + try: + + if event['RequestType'] == 'Create': + + # Get Self-Signed Certificate from retail-demo-store-code S3 bucket. + + s3 = boto3.resource('s3') + obj = s3.Object('retail-demo-store-code', 'keys/test.cert') + certifictate_pem = obj.get()['Body'].read() + + obj = s3.Object('retail-demo-store-code', 'keys/test.key') + private_key_pem = obj.get()['Body'].read() + + my_response = acm_client.import_certificate( + Certificate=certifictate_pem, + PrivateKey=private_key_pem, + Tags=[ + { + 'Key': 'ACM', + 'Value': 'retailDemoStore' + }, + ] + ) + + # Overwrite Certificate ARN value in SSM Parameter + acmarn_parameter = ssm_client.put_parameter( + Name=acmarn_param_name, + Value=my_response['CertificateArn'], + Type='String', + Overwrite=True) + + response_data['certificate_arn'] = my_response['CertificateArn'] + response_data['Message'] = "Resource creation succeeded" + elif event['RequestType'] == 'Update': + response_data['Message'] = "Resource update succeeded" + elif event['RequestType'] == 'Delete': + # Delete the cert from ACM, assumes all attachments are already removed. + + # Retrieve ACM ARN from Parameter store + acmarn_parameter = ssm_client.get_parameter(Name=acmarn_param_name) + + # Delete ACM + my_response = acm_client.delete_certificate( + CertificateArn=acmarn_parameter['Parameter']['Value'] + ) + + response_data['Message'] = "Resource deletion succeeded" + except ClientError as e: + print("Error: " + str(e)) + response_status = cfnresponse.FAILED + response_data['Message'] = "Resource {} failed: {}".format(event['RequestType'], e) + cfnresponse.send(event, context, response_status, response_data) + Handler: index.handler + Runtime: python3.9 + Timeout: 120 + Role: !GetAtt ACMimportCertLambdaExecutionRole.Arn + + ACMimportCertLambdaExecutionRole: + Type: 'AWS::IAM::Role' + Properties: + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: + - lambda.amazonaws.com + Action: + - 'sts:AssumeRole' + Path: / + Policies: + - PolicyName: CustomPolicy + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - logs:CreateLogStream + - logs:DescribeLogStreams + - logs:PutLogEvents + Resource: + - !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:*' + - Effect: Allow + Action: + - ssm:GetParameter + - ssm:PutParameter + Resource: + - !Sub 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/retaildemostore/acm-arn' + - Effect: Allow + Action: + - acm:ImportCertificate + - acm:DeleteCertificate + - acm:AddTagsToCertificate + Resource: + - !Sub 'arn:aws:acm:${AWS::Region}:${AWS::AccountId}:certificate/*' + - Effect: Allow + Action: + - s3:GetObject + Resource: 'arn:aws:s3:::retail-demo-store-code/keys/*' + ACMimportCertLambdaFunctionExecution: + Type: Custom::CustomLambdaACMCert + Version: "1.0" + Properties: + ServiceToken: !GetAtt ACMimportCertLambdaFunction.Arn + +Outputs: + ACMimportCertLambdaFunctionArn: + Description: Lambda function ARN for ACM self-signed cert function + Value: !GetAtt ACMimportCertLambdaFunction.Arn + ACMimportCertArn: + Description: ACM self signed cert Arn to use in ELB listener + Value: !GetAtt ACMimportCertLambdaFunctionExecution.certificate_arn diff --git a/aws/cloudformation-templates/services/_template.yaml b/aws/cloudformation-templates/services/_template.yaml index ff67c60d7..d7d0591e3 100644 --- a/aws/cloudformation-templates/services/_template.yaml +++ b/aws/cloudformation-templates/services/_template.yaml @@ -128,6 +128,10 @@ Parameters: Type: String Description: Evidently project name + ACMCertARN: + Type: String + Description: Self signed cert ARN + Resources: ProductsService: @@ -164,6 +168,7 @@ Resources: ImageRootUrl: !Ref ImageRootUrl Uid: !Sub ${ParentStackName}-${AWS::Region} EvidentlyProjectName: !Ref EvidentlyProjectName + ACMCertARN: !Ref ACMCertARN UsersService: Type: AWS::CloudFormation::Stack @@ -197,6 +202,7 @@ Resources: ImageRootUrl: !Ref ImageRootUrl Uid: !Sub ${ParentStackName}-${AWS::Region} EvidentlyProjectName: !Ref EvidentlyProjectName + ACMCertARN: !Ref ACMCertARN CartsService: Type: AWS::CloudFormation::Stack @@ -229,6 +235,7 @@ Resources: ImageRootUrl: !Ref ImageRootUrl Uid: !Sub ${ParentStackName}-${AWS::Region} EvidentlyProjectName: !Ref EvidentlyProjectName + ACMCertARN: !Ref ACMCertARN OrdersService: Type: AWS::CloudFormation::Stack @@ -261,6 +268,7 @@ Resources: ImageRootUrl: !Ref ImageRootUrl Uid: !Sub ${ParentStackName}-${AWS::Region} EvidentlyProjectName: !Ref EvidentlyProjectName + ACMCertARN: !Ref ACMCertARN SearchService: Type: AWS::CloudFormation::Stack @@ -294,6 +302,7 @@ Resources: ImageRootUrl: !Ref ImageRootUrl Uid: !Sub ${ParentStackName}-${AWS::Region} EvidentlyProjectName: !Ref EvidentlyProjectName + ACMCertARN: !Ref ACMCertARN LocationService: Type: AWS::CloudFormation::Stack @@ -327,6 +336,7 @@ Resources: ImageRootUrl: !Ref ImageRootUrl Uid: !Sub ${ParentStackName}-${AWS::Region} EvidentlyProjectName: !Ref EvidentlyProjectName + ACMCertARN: !Ref ACMCertARN OffersService: Type: AWS::CloudFormation::Stack @@ -360,6 +370,7 @@ Resources: ImageRootUrl: !Ref ImageRootUrl Uid: !Sub ${ParentStackName}-${AWS::Region} EvidentlyProjectName: !Ref EvidentlyProjectName + ACMCertARN: !Ref ACMCertARN RecommendationsService: Type: AWS::CloudFormation::Stack @@ -395,6 +406,7 @@ Resources: ImageRootUrl: !Ref ImageRootUrl Uid: !Sub ${ParentStackName}-${AWS::Region} EvidentlyProjectName: !Ref EvidentlyProjectName + ACMCertARN: !Ref ACMCertARN VideosService: Type: AWS::CloudFormation::Stack @@ -429,6 +441,7 @@ Resources: ImageRootUrl: !Ref ImageRootUrl Uid: !Sub ${ParentStackName}-${AWS::Region} EvidentlyProjectName: !Ref EvidentlyProjectName + ACMCertARN: !Ref ACMCertARN # Pinpoint personalized messaging customization PinpointPersonalize: diff --git a/aws/cloudformation-templates/services/service/_template.yaml b/aws/cloudformation-templates/services/service/_template.yaml index bec4f7269..c31c92995 100644 --- a/aws/cloudformation-templates/services/service/_template.yaml +++ b/aws/cloudformation-templates/services/service/_template.yaml @@ -214,6 +214,10 @@ Parameters: Type: String Description: Evidently project name + ACMCertARN: + Type: String + Description: Self signed cert ARN + Resources: Loadbalancer: @@ -224,6 +228,7 @@ Resources: ServiceName: !Ref ServiceName Subnets: !Ref Subnets VpcId: !Ref VpcId + ACMCertARN: !Ref ACMCertARN Pipeline: Type: AWS::CloudFormation::Stack diff --git a/aws/cloudformation-templates/services/service/loadbalancer.yaml b/aws/cloudformation-templates/services/service/loadbalancer.yaml index e4a8870b8..bce936725 100644 --- a/aws/cloudformation-templates/services/service/loadbalancer.yaml +++ b/aws/cloudformation-templates/services/service/loadbalancer.yaml @@ -14,6 +14,9 @@ Parameters: VpcId: Type: String + ACMCertARN: + Type: String + Resources: SecurityGroup: Type: "AWS::EC2::SecurityGroup" @@ -51,6 +54,18 @@ Resources: - Type: forward TargetGroupArn: !Ref TargetGroup + LoadBalancerHTTPSListener: + Type: AWS::ElasticLoadBalancingV2::Listener + Properties: + LoadBalancerArn: !Ref LoadBalancer + Port: 443 + Protocol: HTTPS + Certificates: + - CertificateArn: !Ref ACMCertARN + DefaultActions: + - Type: forward + TargetGroupArn: !Ref TargetGroup + # Listener Rules ListenerRule: Type: AWS::ElasticLoadBalancingV2::ListenerRule @@ -65,6 +80,19 @@ Resources: - TargetGroupArn: !Ref TargetGroup Type: forward + ListenerHTTPSRule: + Type: AWS::ElasticLoadBalancingV2::ListenerRule + Properties: + ListenerArn: !Ref LoadBalancerHTTPSListener + Priority: 2 + Conditions: + - Field: path-pattern + Values: + - / + Actions: + - TargetGroupArn: !Ref TargetGroup + Type: forward + # Target Groups TargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup diff --git a/aws/cloudformation-templates/template.yaml b/aws/cloudformation-templates/template.yaml index de8fabdfb..daf8a979b 100644 --- a/aws/cloudformation-templates/template.yaml +++ b/aws/cloudformation-templates/template.yaml @@ -563,6 +563,7 @@ Resources: - RootURL: !GetAtt Base.Outputs.WebUICDNURL ParentStackName: !Ref AWS::StackName EvidentlyProjectName: !GetAtt Base.Outputs.EvidentlyProjectName + ACMCertARN: !GetAtt Base.Outputs.ACMCertARN # Web UI Pipeline WebUIPipeline: @@ -873,6 +874,10 @@ Outputs: Description: ARN for the CloudWatch Evidently project Value: !GetAtt Base.Outputs.EvidentlyProjectName + ACMCertARN: + Description: ARN for selfsigned cert for ELB + Value: !GetAtt Base.Outputs.ACMCertARN + ExportEnvVarScript: Description: A script to export all required environment variable for running docker-compose locally, but connecting to Cloud resources. Replace the existing .env file with this output. Value: !Sub |