Example #1
0
    def setup_cloudfront(self):
        """Setup CloudFront
            / is pointing to S3
            /api is pointing to Load Balancer

        Returns
        -------
        aws_cloudfront.Distribution that redirecting traffic to ALB

        """

        return cf.Distribution(
            self,
            'CloudFront',
            default_behavior=cf.BehaviorOptions(
                origin=origins.S3Origin(self.web_bucket)),
            additional_behaviors={
                '/api/*':
                cf.BehaviorOptions(
                    origin=origins.LoadBalancerV2Origin(
                        self.service.load_balancer,
                        protocol_policy=cf.OriginProtocolPolicy.HTTP_ONLY,
                    ),
                    cache_policy=cf.CachePolicy.CACHING_DISABLED,
                    allowed_methods=cf.AllowedMethods.ALLOW_ALL,
                )
            })
    def __init__(self, scope: core.Construct, construct_id: str,
                 **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        # The code that defines your stack goes here
        bucket = s3.Bucket(
            self,
            'cloudfronttestbucket',
            bucket_name="testcloudfrontbuckettoday",
            public_read_access=False,
            removal_policy=core.RemovalPolicy.DESTROY,
        )

        s3identity = cloudfront.OriginAccessIdentity(
            self, 'accessidentity', comment="access-identity-")

        s3Origin = origins.S3Origin(bucket=bucket,
                                    origin_access_identity=s3identity)

        current_behavior = cloudfront.BehaviorOptions(
            origin=s3Origin,
            viewer_protocol_policy=cloudfront.ViewerProtocolPolicy.
            REDIRECT_TO_HTTPS)

        skull_distribution = cloudfront.Distribution(
            self, 'skulldistibution', default_behavior=current_behavior)

        bucket.grant_read(identity=s3identity.grant_principal)
Example #3
0
    def S3_FRONTENDDEPLOY(self):
        bucket = s3.Bucket(
            self,
            "FrontendBucket",
            removal_policy=core.RemovalPolicy.DESTROY,
            versioned=True,
        )

        distribution = cloudfront.Distribution(
            self,
            "FrontendDistribution",
            # TODO: The domain and cert info should be env vars
            domain_names=["www.seanfischer.io"],
            certificate=certificates.Certificate.from_certificate_arn(
                self,
                "DomainCertificateEast1",
                "arn:aws:acm:us-east-1:261392311630:certificate/859dc9d1-7a5f-4474-bcad-bcba1607a5ed",
            ),
            default_root_object="index.html",
            default_behavior=cloudfront.BehaviorOptions(
                origin=origins.S3Origin(bucket, ),
                viewer_protocol_policy=cloudfront.ViewerProtocolPolicy.
                REDIRECT_TO_HTTPS,
            ),
        )

        s3deploy.BucketDeployment(
            self,
            "FrontendS3Deployment",
            sources=[s3deploy.Source.asset("./src")],
            destination_bucket=bucket,
            distribution=distribution,
        )

        return bucket
Example #4
0
    def create_cloudfront_for_user_pool(self, tag: str) -> str:
        """
        Creates a CloudFront distribution to sit in front of the Cognito User Pool, to test the
        SDK's ability to specify custom endpoints for a User Pool. Customers may specify custom
        endpoints to (for example) protect unauthorized calls from DDOS attacks that cause accounts
        to be throttled. While that would generally be done by adding a Web Application Firewall in
        front of a CloudFront distribution, we will simply set up a straight-up proxy endpoint to
        ensure that calls are passed through as expected.

        In the future, we may extend this stack and the associated integ tests with an Edge Lambda
        function to inject AppClientSecret after passing through the CloudFront distribution, but
        that is not strictly germane to the simple ability to specify a custom endpoint.

        :return: the CloudFront Origin to be used for the value of the `Endpoint` in the custom
        config
        """
        origin = aws_cloudfront_origins.HttpOrigin(
            f"cognito-idp.{self.region}.amazonaws.com")
        distribution = aws_cloudfront.Distribution(
            self,
            f"cloudfront_userpool_{tag}",
            default_behavior=aws_cloudfront.BehaviorOptions(
                allowed_methods=aws_cloudfront.AllowedMethods.ALLOW_ALL,
                origin=origin),
        )

        return distribution.domain_name
Example #5
0
    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        lambda_edge = _lambda.Function(
            self,
            'LambdaEdge',
            runtime=_lambda.Runtime.PYTHON_3_7,
            handler='index.handler',
            code=_lambda.Code.from_asset('lambda'),
        )

        #create an S3 bucket used as origin for Cloudfront, not used but origin is a required field
        hosting_bucket = s3.Bucket(self, "MyHostingBucket")

        my_distribution = cloudfront.Distribution(
            self,
            "MyDistribution",
            default_behavior=cloudfront.BehaviorOptions(
                origin=origins.S3Origin(hosting_bucket),
                edge_lambdas=[
                    cloudfront.EdgeLambda(
                        function_version=lambda_edge.current_version,
                        event_type=cloudfront.LambdaEdgeEventType.
                        ORIGIN_REQUEST)
                ]),
            comment='Dynamic content generation using Lambda@Edge')

        CfnOutput(self,
                  "DomainName",
                  value=my_distribution.domain_name,
                  export_name='DomainName',
                  description='CloudFront Domain Name')
Example #6
0
    def __init__(self, app: core.App, cfn_name: str, stack_env):
        super().__init__(scope=app, id=f"{cfn_name}-{stack_env}")

        s3_bucket = s3.Bucket(scope=self,
                              id=f"{cfn_name}-{stack_env}-bucket",
                              website_index_document="index.html")

        # upload files in `./html` to the bucket defined above
        _ = s3_deploy.BucketDeployment(
            scope=self,
            id=f"{cfn_name}-{stack_env}-deployments",
            sources=[s3_deploy.Source.asset("./html")],
            destination_bucket=s3_bucket,
            destination_key_prefix=self.ORIGIN_PATH)

        # set S3 as origin
        s3_origin = origin.S3Origin(bucket=s3_bucket,
                                    origin_path=self.ORIGIN_PATH)
        # create CloudFront Distribution
        _ = cloud_front.Distribution(
            scope=self,
            id=f"{cfn_name}-{stack_env}",
            default_behavior=cloud_front.BehaviorOptions(
                # may warning here, for type mismatched.
                origin=s3_origin))
Example #7
0
 def create_cloud_front(self):
     static_bucket = self.create_s3('static_files', type='s3')
     distribution = cloudfront.Distribution(
         self,
         "distribution",
         default_behavior=cloudfront.BehaviorOptions(
             allowed_methods=cloudfront.AllowedMethods.ALLOW_ALL,
             origin=cloudfront.Origin.from_bucket(static_bucket)))
     # NEED CERTIFICATE, but I don't have one
     # distribution = cloudfront.CloudFrontWebDistribution(
     #     self, "AnAmazingWebsiteProbably",
     #     origin_configs=[cloudfront.SourceConfiguration(
     #         s3_origin_source=cloudfront.S3OriginConfig(
     #             s3_bucket_source=static_bucket),
     #         behaviors=[cloudfront.Behavior(is_default_behavior=True)]
     #     )],
     #     viewer_certificate=cloudfront.ViewerCertificate.from_iam_certificate(
     #         self.CERTIFICATE_ID,
     #         aliases=["example.com"],
     #         security_policy=cloudfront.SecurityPolicyProtocol.TLS_V1,
     #         # default
     #         ssl_method=cloudfront.SSLMethod.SNI
     #     )
     # )
     return distribution
Example #8
0
 def _create_cloudfront_distribution(self):
     """Create a cloudfront distribution with a private bucket as the origin"""
     self.distribution = cloudfront.Distribution(
         self,
         "cloudfront_distribution",
         default_behavior=cloudfront.BehaviorOptions(
             origin=origins.S3Origin(self.bucket),
             viewer_protocol_policy=cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
         ),
         domain_names=[self._site_domain_name],
         certificate=self.certificate,
         default_root_object="index.html",
     )
Example #9
0
  def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
    super().__init__(scope, construct_id, **kwargs)

    s3_bucket_name = cdk.CfnParameter(self, 'S3BucketForStaticContents',
      type='String',
      description='s3 bucket that the site contents are deployed to'
    )

    site_bucket = s3.Bucket.from_bucket_name(self, 'S3BucketForStaticSite', s3_bucket_name.value_as_string)

    cloudfrontOAI = cloudfront.OriginAccessIdentity(self, 'CloudFrontOAI',
      comment="Allows CloudFront to reach the bucket: {name}".format(name=s3_bucket_name.value_as_string)
    );
    cloudfrontOAI.apply_removal_policy(cdk.RemovalPolicy.DESTROY)

    #XXX: Add policy document the existing s3 bucket
    #XXX: https://stackoverflow.com/questions/60087302/how-to-add-resource-policy-to-existing-s3-bucket-with-cdk-in-javascript
    site_bucket_policy_statement = aws_iam.PolicyStatement(**{
      'actions': ['s3:GetObject'],
      'resources': [site_bucket.arn_for_objects('*')],
      'principals': [aws_iam.CanonicalUserPrincipal(cloudfrontOAI.cloud_front_origin_access_identity_s3_canonical_user_id)]
    })

    s3.CfnBucketPolicy(self, 'SiteBucketPolicy',
      bucket=site_bucket.bucket_name,
      policy_document=aws_iam.PolicyDocument(statements=[site_bucket_policy_statement])
    )

    distribution = cloudfront.Distribution(self, "myDist",
      default_behavior=cloudfront.BehaviorOptions(
        origin=cf_origins.S3Origin(bucket=site_bucket, origin_access_identity=cloudfrontOAI)
      ),
      error_responses=[
        #XXX: If you have accessed root page of cloudfront url (i.e. https://your-domain.cloudfront.net/),
        #XXX: 403:Forbidden error might occur. In order to prevent this error,
        #XXX: configure 403:Forbidden error response page to be 'index.html'
        cloudfront.ErrorResponse(http_status=403, response_http_status=200,
          response_page_path='/index.html', ttl=cdk.Duration.seconds(10)),
        #XXX: Configure 404:NotFound error response page to be 'error.html'
        cloudfront.ErrorResponse(http_status=404, response_http_status=404,
          response_page_path='/error.html', ttl=cdk.Duration.seconds(10))
      ]
    )

    cdk.CfnOutput(self, 'StackName', value=self.stack_name, export_name='StackName')
    cdk.CfnOutput(self, 'SiteBucket', value=site_bucket.bucket_name, export_name='SiteBucket')
    cdk.CfnOutput(self, 'DistributionId', value=distribution.distribution_id, export_name='DistributionId')
    cdk.CfnOutput(self, 'DistributionDomainName', value=distribution.distribution_domain_name, export_name='DistributionDomainName')
    cdk.CfnOutput(self, 'CloudFrontOriginAccessId', value=cloudfrontOAI.cloud_front_origin_access_identity_s3_canonical_user_id, export_name='CloudFrontOAI')
Example #10
0
    def __init__(self, scope: core.Construct, construct_id: str,
                 s3_construct: S3Construct, **kwargs) -> None:

        super().__init__(scope, construct_id, **kwargs)

        self.distribution = aws_cloudfront.Distribution(
            self,
            "release_artifacts_cloudfront",
            default_behavior=aws_cloudfront.BehaviorOptions(
                origin=aws_cloudfront_origins.S3Origin(s3_construct.bucket),
                viewer_protocol_policy=aws_cloudfront.ViewerProtocolPolicy.
                HTTPS_ONLY,
            ),
        )

        # We do not want someone to accidentally delete the
        # CloudFront distribution, since that would break
        # publicly-available distribution mechanisms. Set
        # the removal policy to RETAIN so that deleting
        # the `cloudformation` stack does not remove the
        # CloudFront resource.
        self.distribution.apply_removal_policy(core.RemovalPolicy.RETAIN)
Example #11
0
    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        #create the Cloudfront Function used to change the uri
        cf_function = cloudfront.Function(
            self,
            "Function",
            code=cloudfront.FunctionCode.from_file(
                file_path="cff/ab_testing.js"))

        #the S3 bucket where the html files will be hosted
        hosting_bucket = s3.Bucket(self, "MyHostingBucket")

        #copy the html files at deployment to the hosting bucket
        s3_deployment.BucketDeployment(
            self,
            "myDeployment",
            sources=[s3_deployment.Source.asset("website")],
            destination_bucket=hosting_bucket,
        )

        #create a cloudfront distribution and associate the cloudfront Function to the Distribution
        my_distribution = cloudfront.Distribution(
            self,
            "MyDistribution",
            default_behavior=cloudfront.BehaviorOptions(
                origin=origins.S3Origin(hosting_bucket),
                function_associations=[
                    cloudfront.FunctionAssociation(
                        function=cf_function,
                        event_type=cloudfront.FunctionEventType.VIEWER_REQUEST)
                ]))

        CfnOutput(self,
                  "DomainName",
                  value=my_distribution.domain_name,
                  export_name='DomainName',
                  description='CloudFront Domain Name')
Example #12
0
    def __init__(self, app: core.App, id: str) -> None:
        super().__init__(app, id)

        with open("config.json") as f:
            self.config = json.load(f)
        assert (
            "SECRET_KEY"
            in self.config), "Need random SECRET_KEY specified in config.json"
        assert (
            "CERTIFICATE_ARN"
            in self.config), "Need CERTIFICATE_ARN specified in config.json"

        self.lambda_dir = "assets/lambda"
        os.makedirs(os.path.join(self.lambda_dir, "templates", "generated"),
                    exist_ok=True)

        r = requests.get(
            "https://api.github.com/repos/sumpfork/dominiontabs/releases")
        changelog = r.json()
        changelog = [{
            "url":
            ch["html_url"],
            "date":
            dt.datetime.strptime(ch["published_at"][:10], "%Y-%m-%d").date(),
            "name":
            ch["name"],
            "tag":
            ch["tag_name"],
            "description":
            ch["body"],
        } for ch in changelog]

        env = Environment(loader=FileSystemLoader("templates"),
                          autoescape=select_autoescape(["html"]))
        t = env.get_template("changelog.html.j2")
        generated_template_path = os.path.join(self.lambda_dir, "templates",
                                               "generated")
        shutil.rmtree(generated_template_path)
        os.mkdir(generated_template_path)

        with open(
                os.path.join(generated_template_path, "changelog.html"),
                "w",
        ) as f:
            f.write(t.render(changelog=changelog))

        static_website_bucket = s3.Bucket(
            self,
            "Dominion Divider Generator Site",
        )

        cf_static_dist = cloudfront.Distribution(
            self,
            "StaticCloudfrontDist",
            default_behavior=cloudfront.BehaviorOptions(
                origin=cloudfront_origins.S3Origin(static_website_bucket)),
        )

        s3_deployment.BucketDeployment(
            self,
            "Static Files Deployment",
            sources=[s3_deployment.Source.asset("./static")],
            destination_bucket=static_website_bucket,
            destination_key_prefix="static",
        )

        flask_app = lambda_python.PythonFunction(
            self,
            "DominionDividersFlaskApp",
            entry=self.lambda_dir,
            index="lambda-handlers.py",
            handler="apig_wsgi_handler",
            environment={
                "STATIC_WEB_URL": f"https://{cf_static_dist.domain_name}",
                "FLASK_SECRET_KEY": self.config["SECRET_KEY"],
                "GA_CONFIG": self.config.get("GA_CONFIG", ""),
            },
            timeout=core.Duration.seconds(60),
            memory_size=512,
            runtime=lambda_.Runtime.PYTHON_3_8,
        )
        api = apig.LambdaRestApi(
            self,
            "bgtools-api",
            handler=flask_app,
            binary_media_types=["*/*"],
            minimum_compression_size=10e4,
            deploy_options={
                "method_options": {
                    "/*/*":
                    apig.MethodDeploymentOptions(throttling_rate_limit=10,
                                                 throttling_burst_limit=20)
                }
            },
        )
        cloudfront.Distribution(
            self,
            "BGToolsCloudfrontDist",
            default_behavior=cloudfront.BehaviorOptions(
                origin=cloudfront_origins.HttpOrigin(
                    core.Fn.select(2, core.Fn.split("/", api.url)),
                    origin_path=core.Fn.join(
                        "",
                        ["/",
                         core.Fn.select(3, core.Fn.split("/", api.url))]),
                ),
                origin_request_policy=cloudfront.OriginRequestPolicy(
                    self,
                    "OriginRequestPolicy",
                    cookie_behavior=cloudfront.OriginRequestCookieBehavior.all(
                    ),
                ),
                allowed_methods=cloudfront.AllowedMethods.ALLOW_ALL,
            ),
            domain_names=["domdiv.bgtools.net"],
            certificate=acm.Certificate.from_certificate_arn(
                self,
                "cert",
                self.config["CERTIFICATE_ARN"],
            ),
        )

        dashboard = aws_cloudwatch.Dashboard(
            self,
            f"bgtools-dashboard",
            dashboard_name=f"bgtools-prod",
            start="-P1D",
            period_override=aws_cloudwatch.PeriodOverride.INHERIT,
        )
        dashboard.add_widgets(
            aws_cloudwatch.GraphWidget(
                title="API Gateway Counts",
                width=6,
                height=6,
                left=[
                    aws_cloudwatch.Metric(
                        namespace="AWS/ApiGateway",
                        metric_name="5XXError",
                        dimensions={
                            "ApiName": "bgtools-api",
                            "Stage": api.deployment_stage.stage_name,
                        },
                        period=core.Duration.minutes(amount=30),
                        statistic="Sum",
                        color="#d62728",
                    ),
                    aws_cloudwatch.Metric(
                        namespace="AWS/ApiGateway",
                        metric_name="4XXError",
                        dimensions={
                            "ApiName": "bgtools-api",
                            "Stage": api.deployment_stage.stage_name,
                        },
                        period=core.Duration.minutes(amount=30),
                        statistic="Sum",
                        color="#8c564b",
                    ),
                    aws_cloudwatch.Metric(
                        namespace="AWS/ApiGateway",
                        metric_name="Count",
                        dimensions={
                            "ApiName": "bgtools-api",
                            "Stage": api.deployment_stage.stage_name,
                        },
                        period=core.Duration.minutes(amount=30),
                        statistic="Sum",
                        color="#2ca02c",
                    ),
                ],
            ),
            aws_cloudwatch.GraphWidget(
                title="API Gateway Latencies",
                width=6,
                height=6,
                left=[
                    aws_cloudwatch.Metric(
                        namespace="AWS/ApiGateway",
                        metric_name="Latency",
                        dimensions={
                            "ApiName": "bgtools-api",
                            "Stage": api.deployment_stage.stage_name,
                        },
                        period=core.Duration.minutes(amount=30),
                        statistic="Average",
                    ),
                    aws_cloudwatch.Metric(
                        namespace="AWS/ApiGateway",
                        metric_name="IntegrationLatency",
                        dimensions={
                            "ApiName": "bgtools-api",
                            "Stage": api.deployment_stage.stage_name,
                        },
                        period=core.Duration.minutes(amount=30),
                        statistic="Average",
                    ),
                ],
            ),
        )
Example #13
0
    def __init__(self, scope: core.Construct, construct_id: str,
                 **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        # Domain name to redirect
        domain_name = core.CfnParameter(
            self,
            "domainName",
            type="String",
            description="Domain name to redirect",
        )

        # Here we use a specific certificate from parameter values
        cert_arn = core.CfnParameter(
            self,
            "certArn",
            type="String",
            description=
            "Certificate ARN of for the redirection (has to be in us-east-1",
        )

        image_tag = core.CfnParameter(
            self,
            "imageTag",
            type="String",
            description="Image tag to deploy as container",
        )

        processed_bucket_name = core.CfnParameter(
            self,
            "processedBucket",
            type="String",
            description="Name of the S3 bucket holding the processed data",
        )

        cookie_secret = core.CfnParameter(
            self,
            "cookieSecret",
            type="String",
            description=
            "The secret value to encrypt the login cookies by the dashboard.",
        )
        # End: Input variables

        # Create VPC and Fargate Cluster
        # NOTE: Limit AZs to avoid reaching resource quotas
        vpc = ec2.Vpc(self, "DashboardVPC", max_azs=2)

        cluster = ecs.Cluster(self,
                              "DashboardCluster",
                              vpc=vpc,
                              container_insights=True)

        repository = ecr.Repository(self,
                                    "DashboardRepository",
                                    repository_name="nccid-dashboard")
        secret = secretsmanager.Secret(self,
                                       "DashboardSecret",
                                       secret_name="nccid-dashboard-secrets")
        service_secret = ecs.Secret.from_secrets_manager(secret)

        fargate_service = ecs_patterns.NetworkLoadBalancedFargateService(
            self,
            "DashboardService",
            cluster=cluster,
            task_image_options={
                "image":
                ecs.ContainerImage.from_ecr_repository(
                    repository=repository, tag=image_tag.value_as_string),
                "secrets": [service_secret],
                "environment": {
                    "AWS_PROCESSED_BUCKET":
                    processed_bucket_name.value_as_string,
                    "COOKIE_SECRET_KEY": cookie_secret.value_as_string,
                    "DASHBOARD_DOMAIN": domain_name.value_as_string,
                },
            },
            platform_version=ecs.FargatePlatformVersion.VERSION1_4,
            # See values for these entries at https://docs.aws.amazon.com/cdk/api/latest/python/aws_cdk.aws_ecs_patterns/NetworkLoadBalancedFargateService.html#networkloadbalancedfargateservice
            cpu=256,  # .25 vCPU
            memory_limit_mib=512,  # 0.5 GB
        )

        processed_bucket = s3.Bucket.from_bucket_name(
            self,
            id="ProcessedBucket",
            bucket_name=processed_bucket_name.value_as_string,
        )
        processed_bucket.grant_read(fargate_service.task_definition.task_role)

        fargate_service.service.connections.security_groups[
            0].add_ingress_rule(
                peer=ec2.Peer.ipv4(vpc.vpc_cidr_block),
                connection=ec2.Port.tcp(80),
                description="Allow HTTP inbound from VPC",
            )

        cert = _acm.Certificate.from_certificate_arn(self, "cert",
                                                     cert_arn.value_as_string)

        origin = _origins.HttpOrigin(
            domain_name=fargate_service.load_balancer.load_balancer_dns_name,
            protocol_policy=_cloudfront.OriginProtocolPolicy.HTTP_ONLY,
        )
        behaviour = _cloudfront.BehaviorOptions(
            origin=origin,
            allowed_methods=_cloudfront.AllowedMethods.ALLOW_ALL,
            cache_policy=_cloudfront.CachePolicy.CACHING_DISABLED,
            origin_request_policy=_cloudfront.OriginRequestPolicy.ALL_VIEWER,
            viewer_protocol_policy=_cloudfront.ViewerProtocolPolicy.
            REDIRECT_TO_HTTPS,
        )

        distribution = _cloudfront.Distribution(
            self,
            "nccid-dasboard-dist",
            default_behavior=behaviour,
            certificate=cert,
            domain_names=[domain_name.value_as_string],
        )
        # Explicit dependency setup
        distribution.node.add_dependency(fargate_service.load_balancer)

        core.CfnOutput(
            self,
            "nccidCloudfrontDistribution",
            value=distribution.distribution_domain_name,
            description="Cloudfront domain to set the CNAME for.",
        )
Example #14
0
    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)
        self.current_dir = os.path.dirname(__file__)

        self.website_bucket = s3.Bucket(
            self,
            "qs-embed-bucket",
            bucket_name=f'quicksight-embed-{core.Aws.ACCOUNT_ID}',
            block_public_access=s3.BlockPublicAccess.BLOCK_ALL)

        self.quicksight_embed_lambda_role = iam.Role(
            self,
            'quicksight-embed-lambda-role',
            description='Role for the Quicksight dashboard embed Lambdas',
            role_name='quicksight-embed-lambda-role',
            max_session_duration=core.Duration.seconds(3600),
            assumed_by=iam.ServicePrincipal('lambda.amazonaws.com'),
            inline_policies={
                'AllowAccess':
                iam.PolicyDocument(statements=[
                    iam.PolicyStatement(
                        effect=iam.Effect.ALLOW,
                        actions=[
                            'logs:CreateLogGroup', 'logs:CreateLogStream',
                            'logs:PutLogEvents'
                        ],
                        resources=[
                            f'arn:aws:logs:{core.Aws.REGION}:{core.Aws.ACCOUNT_ID}:*'
                        ]),
                    iam.PolicyStatement(
                        effect=iam.Effect.ALLOW,
                        actions=["secrets:GetSecretValue"],
                        resources=[
                            f"arn:aws:secretsmanager:{core.Aws.REGION}:{core.Aws.ACCOUNT_ID}:secret:*"
                        ]),
                    iam.PolicyStatement(effect=iam.Effect.ALLOW,
                                        actions=[
                                            "quicksight:GetDashboardEmbedUrl",
                                            "quicksight:GetAuthCode"
                                        ],
                                        resources=["*"])
                ])
            })

        self.quicksight_migration_lambda = _lambda.Function(
            self,
            'quicksight-migration-lambda',
            handler='quicksight_embed.lambda_handler',
            runtime=_lambda.Runtime.PYTHON_3_8,
            code=_lambda.Code.from_asset(
                os.path.join(self.current_dir, '../lambda/quicksight_embed/')),
            function_name='quicksight_embed_lambda',
            role=self.quicksight_embed_lambda_role,
            timeout=core.Duration.minutes(3),
            memory_size=512,
            environment={
                'DASHBOARD_ID':
                'CHANGEME_DASHBOARD_ID',
                'QUICKSIGHT_USER_ARN':
                f'arn:aws:quicksight:us-east-1:{core.Aws.ACCOUNT_ID}:user/default/quicksight-migration-user'
            })

        self.apigw_lambda = ApiGatewayToLambda(
            self,
            "ApiGatewayToLambdaQSEmbed",
            existing_lambda_obj=self.quicksight_migration_lambda,
            api_gateway_props=apigw.LambdaRestApiProps(
                rest_api_name="quicksight-embed",
                handler=self.quicksight_migration_lambda,
                deploy=True,
                proxy=False,
                default_method_options=apigw.MethodOptions(
                    authorization_type=apigw.AuthorizationType.NONE),
                policy=iam.PolicyDocument(statements=[
                    iam.PolicyStatement(effect=iam.Effect.ALLOW,
                                        actions=['execute-api:Invoke'],
                                        resources=["execute-api:/prod/*"],
                                        principals=[iam.ArnPrincipal("*")])
                ])))

        self.embedurl = self.apigw_lambda.api_gateway.root.add_resource(
            "embedurl")
        self.embedurl.add_method(
            "GET",
            method_responses=[{
                'statusCode': '200',
                'responseParameters': {
                    'method.response.header.Access-Control-Allow-Headers':
                    True,
                    'method.response.header.Access-Control-Allow-Methods':
                    True,
                    'method.response.header.Access-Control-Allow-Origin': True
                }
            }],
            integration=apigw.LambdaIntegration(
                self.quicksight_migration_lambda,
                proxy=False,
                integration_responses=[{
                    'statusCode': '200',
                    'responseTemplates': {
                        "application/json": ""
                    },
                    'responseParameters': {
                        'method.response.header.Access-Control-Allow-Headers':
                        "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'",
                        'method.response.header.Access-Control-Allow-Origin':
                        "'*'",
                        'method.response.header.Access-Control-Allow-Methods':
                        "'GET'"
                    }
                }]))

        self.embedurl.add_method(
            'OPTIONS',
            apigw.MockIntegration(integration_responses=[{
                'statusCode': '200',
                'responseParameters': {
                    'method.response.header.Access-Control-Allow-Headers':
                    "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'",
                    'method.response.header.Access-Control-Allow-Origin':
                    "'*'",
                    'method.response.header.Access-Control-Allow-Methods':
                    "'GET,OPTIONS'"
                }
            }],
                                  passthrough_behavior=apigw.
                                  PassthroughBehavior.WHEN_NO_MATCH,
                                  request_templates={
                                      "application/json":
                                      "{\"statusCode\":200}"
                                  }),
            method_responses=[{
                'statusCode': '200',
                'responseParameters': {
                    'method.response.header.Access-Control-Allow-Headers':
                    True,
                    'method.response.header.Access-Control-Allow-Methods':
                    True,
                    'method.response.header.Access-Control-Allow-Origin': True
                }
            }])

        # Cloudfront Distribution for authentication
        self.embed_auth_lambda_role = iam.Role(
            self,
            'embed-auth-lambda-role',
            description=
            'Role for the Quicksight dashboard embed authentication Lambda',
            role_name='embed-auth-lambda-role',
            max_session_duration=core.Duration.seconds(3600),
            assumed_by=iam.ServicePrincipal('lambda.amazonaws.com'),
            inline_policies={
                'AllowAccess':
                iam.PolicyDocument(statements=[
                    iam.PolicyStatement(
                        effect=iam.Effect.ALLOW,
                        actions=[
                            'logs:CreateLogGroup', 'logs:CreateLogStream',
                            'logs:PutLogEvents'
                        ],
                        resources=[
                            f'arn:aws:logs:{core.Aws.REGION}:{core.Aws.ACCOUNT_ID}:*'
                        ])
                ])
            })

        self.embed_auth_lambda = _lambda.Function(
            self,
            'embed-auth-lambda',
            handler='index.handler',
            description=
            "A Lambda@Edge function for QuickSight embed authentication via CloudFront Distribution",
            runtime=_lambda.Runtime.NODEJS_14_X,
            code=_lambda.Code.from_asset(
                os.path.join(self.current_dir, '../lambda/embed_auth/')),
            function_name='embed_auth_lambda',
            role=self.embed_auth_lambda_role,
            timeout=core.Duration.seconds(5),
            memory_size=128)

        self.embed_auth_dist = cloudfront.Distribution(
            self,
            "embed-auth-dist",
            enabled=True,
            default_root_object="index.html",
            default_behavior=cloudfront.BehaviorOptions(
                origin=origins.S3Origin(self.website_bucket),
                allowed_methods=cloudfront.AllowedMethods.ALLOW_GET_HEAD,
                edge_lambdas=[{
                    "functionVersion": self.embed_auth_lambda.current_version,
                    "eventType": cloudfront.LambdaEdgeEventType.VIEWER_REQUEST,
                    "includeBody": True
                }]))

        core.CfnOutput(self,
                       "EmbedAPIGatewayURL",
                       value=self.apigw_lambda.api_gateway.url + "embedurl?",
                       description="Embed API GW URL")

        core.CfnOutput(self,
                       "EmbedCloudFrontURL",
                       value="https://" +
                       self.embed_auth_dist.distribution_domain_name,
                       description="CloudFront Distribution URL")
Example #15
0
    def __init__(self, scope: core.Construct, construct_id: str,
                 **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        # Input variables

        # Domain name to redirect
        domain_name = core.CfnParameter(
            self,
            "domainName",
            type="String",
            description="Domain name to redirect",
        )

        # Here we use a specific certificate from parameter values
        cert_arn = core.CfnParameter(
            self,
            "certArn",
            type="String",
            description=
            "Certificate ARN of for the redirection (has to be in us-east-1",
        )
        # End: Input variables

        # Infra setup

        redirect_fn = _lambda.Function(
            self,
            "NCCIDRedirectLambda",
            handler="lambda-handler.handler",
            runtime=_lambda.Runtime.PYTHON_3_8,
            code=_lambda.Code.asset("lambda"),
        )

        redirect_integration = _apigw2int.LambdaProxyIntegration(
            handler=redirect_fn)

        cert = _acm.Certificate.from_certificate_arn(self, "cert",
                                                     cert_arn.value_as_string)

        http_api = _apigw2.HttpApi(
            self,
            "nccid-redirect",
            api_name="nccid-redirect",
            description="A redirection gateway.",
        )

        http_api.add_routes(path="/",
                            methods=[_apigw2.HttpMethod.GET],
                            integration=redirect_integration)

        # Change https address into just domain name (while keeping the Cloudformation variables in)
        origin_target = http_api.url.replace("https://", "",
                                             1).replace("/", "")
        origin = _origins.HttpOrigin(domain_name=origin_target)
        behaviour = _cloudfront.BehaviorOptions(origin=origin)

        distribution = _cloudfront.Distribution(
            self,
            "nccid-redirect-dist",
            default_behavior=behaviour,
            certificate=cert,
            domain_names=[domain_name.value_as_string],
        )
        # Explicit dependency is required between the API gateway and Cloudfront distribution
        distribution.node.add_dependency(http_api)

        # Outputs
        distribution_domain = core.CfnOutput(  # noqa:  F841
            self,
            "nccidRedirectDomain",
            value=distribution.distribution_domain_name,
            description=
            "The Cloudfront domain to add to the CNAME records for the redirected domain",
        )
Example #16
0
def create_static_site(scope: InfraStack, hosted_zone: route53.HostedZone):
    bucket = s3.Bucket(
        scope,
        id=scope.context.construct_id("s3"),
        bucket_name=f'{scope.context.construct_id("s3")}-bucket',
        versioned=True,
        block_public_access=s3.BlockPublicAccess(
            restrict_public_buckets=True,
            block_public_policy=True,
            block_public_acls=True,
        ),
        removal_policy=RemovalPolicy.RETAIN,
        auto_delete_objects=False,
        lifecycle_rules=[
            s3.LifecycleRule(noncurrent_version_expiration=Duration.days(7))
        ],
    )

    prod_domain_name = "gingerbeans.tech"
    lower_domain_name = f"{scope.context.env_name}.gingerbeans.tech"
    domain_name = (prod_domain_name if scope.context.env_name == PRODUCTION
                   else lower_domain_name)

    my_certificate = acm.DnsValidatedCertificate(
        scope,
        scope.context.construct_id("certificate"),
        domain_name=domain_name,
        hosted_zone=hosted_zone,
        region="us-east-1",
    )

    origin_access_identity = cloudfront.OriginAccessIdentity(
        scope, scope.context.construct_id("cfOriginAccessIdentity"))
    cf_s3_access = iam.PolicyStatement()
    cf_s3_access.add_actions("s3:GetBucket*")
    cf_s3_access.add_actions("s3:GetObject*")
    cf_s3_access.add_actions("s3:List*")
    cf_s3_access.add_resources(bucket.bucket_arn)
    cf_s3_access.add_resources(f"{bucket.bucket_arn}/*")
    cf_s3_access.add_canonical_user_principal(
        origin_access_identity.
        cloud_front_origin_access_identity_s3_canonical_user_id)
    bucket.add_to_resource_policy(cf_s3_access)

    distro = cloudfront.Distribution(
        scope,
        scope.context.construct_id("dist"),
        default_behavior=cloudfront.BehaviorOptions(origin=origins.S3Origin(
            bucket=bucket, origin_access_identity=origin_access_identity)),
        default_root_object="index.html",
        domain_names=[domain_name],
        certificate=my_certificate,
    )

    route53.AaaaRecord(
        scope,
        scope.context.construct_id("AAAA"),
        record_name=domain_name,
        target=route53.RecordTarget.from_alias(
            targets.CloudFrontTarget(distro)),
        zone=hosted_zone,
    )

    route53.ARecord(
        scope,
        scope.context.construct_id("A"),
        record_name=domain_name,
        target=route53.RecordTarget.from_alias(
            targets.CloudFrontTarget(distro)),
        zone=hosted_zone,
    )

    s3_deployment.BucketDeployment(
        scope,
        scope.context.construct_id("s3_deployment"),
        sources=[s3_deployment.Source.asset("../gb-ui/build")],
        destination_key_prefix="/",
        destination_bucket=bucket,
        distribution=distro,
        distribution_paths=["/*"],
    )