示例#1
0
    def __init__(self,
                 scope: core.Construct,
                 id: str,
                 stack_name: str,
                 task_definition_cpu: int,
                 task_definition_memory_limit_mib: int,
                 docker_image_name: str,
                 container_port: int,
                 desired_container_count: int,
                 private_subnets: Sequence[aws_ec2.Subnet] = None,
                 public_subnets: Sequence[aws_ec2.Subnet] = None,
                 private_security_group: aws_ec2.SecurityGroup = None,
                 public_security_group: aws_ec2.SecurityGroup = None,
                 vpc: aws_ec2.Vpc = None,
                 fargate_cluster: aws_ecs.Cluster = None,
                 authorizer_lambda_arn: str = None,
                 authorizer_lambda_role_arn: str = None,
                 **kwargs):
        super().__init__(scope, id, **kwargs)

        # Role
        self.role = aws_iam.Role(
            self,
            'Role',
            assumed_by=aws_iam.ServicePrincipal(service='ecs.amazonaws.com'),
            managed_policies=[
                aws_iam.ManagedPolicy.from_aws_managed_policy_name(
                    managed_policy_name=
                    'service-role/AmazonECSTaskExecutionRolePolicy')
            ],
            inline_policies={
                id:
                aws_iam.PolicyDocument(statements=[
                    aws_iam.PolicyStatement(
                        effect=aws_iam.Effect.ALLOW,
                        actions=[
                            'kms:Encrypt',
                            'kms:Decrypt',
                            'kms:ReEncrypt*',
                            'kms:GenerateDataKey*',
                            'kms:DescribeKey',
                            'ec2:CreateNetworkInterface',
                            'ec2:DescribeNetworkInterfaces',
                            'ec2:DeleteNetworkInterface',
                            # Remaining actions from https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/quickref-ecs.html
                            'elasticloadbalancing:DeregisterInstancesFromLoadBalancer',
                            'elasticloadbalancing:DeregisterTargets',
                            'elasticloadbalancing:Describe*',
                            'elasticloadbalancing:RegisterInstancesWithLoadBalancer',
                            'elasticloadbalancing:RegisterTargets',
                            'ec2:Describe*',
                            'ec2:AuthorizeSecurityGroupIngress'
                        ],
                        resources=['*'])
                ])
            })
        self.role.assume_role_policy.add_statements(
            aws_iam.PolicyStatement(
                actions=['sts:AssumeRole'],
                principals=[
                    aws_iam.ServicePrincipal(service='ecs-tasks.amazonaws.com')
                ]))

        # Set Defaults if parameters are None
        if vpc is None:
            vpc = aws_ec2.Vpc(self, 'Vpc')

        if private_subnets is None:
            private_subnets = vpc.private_subnets

        if public_subnets is None:
            public_subnets = vpc.public_subnets

        if public_security_group is None:
            public_security_group = aws_ec2.SecurityGroup(
                self, 'PublicSecurityGroup', vpc=vpc, allow_all_outbound=True)
            # Allow inbound HTTP traffic
            public_security_group.add_ingress_rule(
                peer=aws_ec2.Peer.ipv4(cidr_ip='0.0.0.0/0'),
                connection=aws_ec2.Port.tcp(port=80))
            # Allow inbound HTTPS traffic
            public_security_group.add_ingress_rule(
                peer=aws_ec2.Peer.ipv4(cidr_ip='0.0.0.0/0'),
                connection=aws_ec2.Port.tcp(port=443))

        if private_security_group is None:
            private_security_group = aws_ec2.SecurityGroup(
                self, 'PrivateSecurityGroup', vpc=vpc, allow_all_outbound=True)

            public_subnet_cidr_blocks = Utils.get_subnet_cidr_blocks(
                public_subnets)

            # Create an ingress rule for each of the NLB's subnet's CIDR ranges and add the rules to the ECS service's
            # security group.  This will allow requests from the NLB to go into the ECS service.  This allow inbound
            # traffic from public subnets.
            for cidr_block in public_subnet_cidr_blocks:
                private_security_group.add_ingress_rule(
                    peer=aws_ec2.Peer.ipv4(cidr_ip=cidr_block),
                    connection=aws_ec2.Port.tcp(port=container_port))

        if fargate_cluster is None:
            fargate_cluster = aws_ecs.Cluster(
                self,
                'FargateCluster',
            )

        task_def = aws_ecs.FargateTaskDefinition(
            self,
            'TaskDefinition',
            cpu=task_definition_cpu,
            memory_limit_mib=task_definition_memory_limit_mib,
            task_role=self.role,
            execution_role=self.role)

        container = aws_ecs.ContainerDefinition(
            self,
            'Container',
            image=aws_ecs.ContainerImage.from_registry(name=docker_image_name),
            task_definition=task_def,
            logging=aws_ecs.AwsLogDriver(stream_prefix='/ecs'))
        container.add_port_mappings(
            aws_ecs.PortMapping(container_port=container_port,
                                protocol=aws_ec2.Protocol.TCP))

        ecs_service = aws_ecs.FargateService(
            self,
            'FargateService',
            cluster=fargate_cluster,
            task_definition=task_def,
            vpc_subnets=aws_ec2.SubnetSelection(subnets=private_subnets),
            security_group=private_security_group,
            desired_count=desired_container_count)

        target_group = aws_elasticloadbalancingv2.NetworkTargetGroup(
            self,
            'TargetGroup',
            port=80,  # Health check occurs over HTTP
            health_check=aws_elasticloadbalancingv2.HealthCheck(
                protocol=aws_elasticloadbalancingv2.Protocol.TCP),
            targets=[ecs_service],
            vpc=vpc)

        nlb = aws_elasticloadbalancingv2.NetworkLoadBalancer(
            self,
            'NetworkLoadBalancer',
            vpc=vpc,
            internet_facing=False,
            vpc_subnets=aws_ec2.SubnetSelection(subnets=public_subnets),
        )
        nlb.add_listener(
            id='Listener',
            port=80,  # HTTP listener
            default_target_groups=[target_group])

        # nlb.log_access_logs(  # todo:  add this later when you have time to research the correct bucket policy.
        #     bucket=aws_s3.Bucket(
        #         self, 'LoadBalancerLogBucket',
        #         bucket_name='load-balancer-logs',
        #         public_read_access=False,
        #         block_public_access=aws_s3.BlockPublicAccess(
        #             block_public_policy=True,
        #             restrict_public_buckets=True
        #         )
        #     )
        # )

        # Dependencies
        ecs_service.node.add_dependency(nlb)

        # API Gateway
        rest_api = aws_apigateway.RestApi(self, stack_name)
        resource = rest_api.root.add_resource(
            path_part='{proxy+}',
            default_method_options=aws_apigateway.MethodOptions(
                request_parameters={'method.request.path.proxy': True}))

        token_authorizer = None
        if authorizer_lambda_arn and authorizer_lambda_role_arn:
            token_authorizer = aws_apigateway.TokenAuthorizer(  #todo: make this a parameter?
                self,
                'JwtTokenAuthorizer',
                results_cache_ttl=core.Duration.minutes(5),
                identity_source='method.request.header.Authorization',
                assume_role=aws_iam.Role.from_role_arn(
                    self,
                    'AuthorizerLambdaInvokationRole',
                    role_arn=authorizer_lambda_role_arn),
                handler=aws_lambda.Function.from_function_arn(
                    self,
                    'AuthorizerLambda',
                    function_arn=authorizer_lambda_arn))

        resource.add_method(
            http_method='ANY',
            authorization_type=aws_apigateway.AuthorizationType.CUSTOM,
            authorizer=token_authorizer,
            integration=aws_apigateway.HttpIntegration(
                url=f'http://{nlb.load_balancer_dns_name}/{{proxy}}',
                http_method='ANY',
                proxy=True,
                options=aws_apigateway.IntegrationOptions(
                    request_parameters={
                        'integration.request.path.proxy':
                        'method.request.path.proxy'
                    },
                    connection_type=aws_apigateway.ConnectionType.VPC_LINK,
                    vpc_link=aws_apigateway.VpcLink(
                        self,
                        'VpcLink',
                        description=
                        f'API Gateway VPC Link to internal NLB for {stack_name}',
                        vpc_link_name=stack_name,
                        targets=[nlb]))))
示例#2
0
    def __init__(self, scope: cdk.Construct, construct_id: str, context,
                 resource: apigw.Resource, user_pool: cognito.UserPool,
                 user_client: cognito.UserPoolClient,
                 user_pool_parameter: ssm.StringParameter,
                 launcher_network_config_parameter: ssm.StringParameter,
                 launcher_task_definition: GrantingTaskDefinition,
                 launcher_cluster: ecs.Cluster, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        server_domain = self.node.try_get_context('server_domain')

        servers_table = db.Table.from_table_name(self, 'servers-dynamo',
                                                 context['servers_table_name'])
        servers_key = kms.Key.from_key_arn(
            self, 'ServersKey',
            f'arn:aws:kms:{context["region"]}:{context["account_id"]}:key/{context["kms_key_id"]}'
        )

        class ServerLambda(PythonFunction):
            def __init__(self, scope, construct_id, handler: str):
                super().__init__(
                    scope,
                    construct_id,
                    entry='servers_lambda',
                    index='views.py',
                    handler=handler,
                    runtime=_lambda.Runtime.PYTHON_3_8,
                    timeout=cdk.Duration.seconds(30),
                    environment={
                        'SERVER_DOMAIN':
                        server_domain,
                        'DYNAMODB_SERVERS_TABLE_NAME':
                        servers_table.table_name,
                        'LAUNCHER_NETWORK_CONFIG_PARAMETER':
                        launcher_network_config_parameter.parameter_name,
                        'LAUNCHER_TASK_ARN':
                        launcher_task_definition.task_definition_arn,
                        'CLUSTER_ARN':
                        launcher_cluster.cluster_arn
                    })
                servers_table.grant_read_write_data(self)
                servers_key.grant_encrypt_decrypt(self)
                launcher_network_config_parameter.grant_read(self)
                launcher_task_definition.grant_run(self)

        servers_lambda = ServerLambda(self, 'ServersLambda', handler='servers')
        server_lambda = ServerLambda(self, 'ServerLambda', handler='server')

        user_pool_parameter_lambda = PythonFunction(
            self,
            'UserPoolParameterLambda',
            entry='parameter_lambda',
            index='main.py',
            handler='main',
            runtime=_lambda.Runtime.PYTHON_3_8,
            timeout=cdk.Duration.seconds(30),
            environment={
                'USER_POOL_PARAMETER': user_pool_parameter.parameter_name
            })
        user_pool_parameter.grant_read(user_pool_parameter_lambda)

        user_pool_authorizer = apigw.CognitoUserPoolsAuthorizer(
            self, 'UserPoolAuthorizer', cognito_user_pools=[user_pool])

        # /api
        api = resource.add_resource('api')

        # /api/user_pool
        parameter = api.add_resource(
            'user_pool',
            default_integration=apigw.LambdaIntegration(
                user_pool_parameter_lambda),
            default_method_options=apigw.MethodOptions(
                authorization_type=apigw.AuthorizationType.NONE))
        parameter.add_method('GET')

        # /api/servers
        servers = api.add_resource(
            'servers',
            default_integration=apigw.LambdaIntegration(servers_lambda),
            default_method_options=apigw.MethodOptions(
                authorizer=user_pool_authorizer))
        servers.add_method('GET')
        servers.add_method('PUT')

        # /api/servers/{hostname}
        server = servers.add_resource(
            '{hostname}',
            default_integration=apigw.LambdaIntegration(server_lambda),
            default_method_options=apigw.MethodOptions(
                authorizer=user_pool_authorizer))
        server.add_method('GET')
        server.add_method('PUT')
        server.add_method('POST')
示例#3
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}',
            website_index_document="index.html",
            public_read_access=True)

        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=["sts:AssumeRole", "iam:ListRoles"],
                        resources=[
                            "arn:aws:iam::*:role/quicksight-migration-*-assume-role"
                        ]),
                    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:*",
                                        ],
                                        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':
                '938b365e-c001-4723-9a27-029654da7531',
                '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),
                default_cors_preflight_options=apigw.CorsOptions(
                    allow_origins=apigw.Cors.ALL_ORIGINS,
                    allow_methods=apigw.Cors.ALL_METHODS,
                    allow_headers=[
                        'Access-Control-Allow-Origin',
                        'Access-Control-Allow-Headers', 'Content-Type'
                    ]),
                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")
    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        # API Gateway needs to have resource policy granting FHIR Works on AWS lambda
        # execute permissions. Lambda function ARN will be passed during deployment as CDK context variable
        # FHIR Works lambda will need to have policy attached to its execution role
        # allowing it to invoke API
        # From --context resource-router-lambda-role="arn:aws:iam::123456789012:role/rolename"
        imported_resource_router_lambda_role = self.node.try_get_context(
            "resource-router-lambda-role"
        )
        # Amazon ECS on AWS Fargate container implementing connection manager
        # will be launched into a VPC that needs to have private and public subnets
        # and NAT gateway or instance
        # From --context vpc-id="vpc-123456"
        vpc_id = self.node.try_get_context("vpc-id")

        # The following parameters specify name of the HL7 server
        # that will be receiving transformed HL7v2 messages and TCP port
        # that it will be listening on
        # From --context hl7-server-name="hl7.example.com"
        # From --context hl7-port="2575"
        hl7_server_name = self.node.try_get_context("hl7-server-name")
        hl7_port = self.node.try_get_context("hl7-port")

        # In this proof of concept source of data for read interactions
        # is S3 bucket where mock HL7 server stores processed HL7 messages
        # From --context test-server-output-bucket-name="DOC-EXAMPLE-BUCKET"
        test_server_output_bucket_name = self.node.try_get_context(
            "test-server-output-bucket-name"
        )

        # SQS queue
        # Custom transform lambda communicates with Connectivity Manager using this SQS queue
        queue = sqs.Queue(
            self, f"{COMPONENT_PREFIX}Queue", encryption=sqs.QueueEncryption.KMS_MANAGED
        )

        # S3 Bucket to retrieve HL7v2 messages in proof of concept deployment
        test_server_output_bucket = s3.Bucket.from_bucket_name(
            self, f"{COMPONENT_PREFIX}OutputBucket", test_server_output_bucket_name
        )

        # Transform Lambda
        # Reference implementation of Custom Transform component of Transform Execution Environment

        transform_lambda = lambda_.Function(
            self,
            f"{COMPONENT_PREFIX}TransformLambda",
            handler="transform.handler",
            runtime=lambda_.Runtime.PYTHON_3_8,
            code=lambda_.Code.from_asset(
                path.join(dirname, "../../lambda"),
                bundling={
                    "image": lambda_.Runtime.PYTHON_3_8.bundling_docker_image,
                    "command": [
                        "bash",
                        "-c",
                        " && ".join(
                            [
                                "pip install --no-cache-dir -r requirements.txt -t /asset-output",
                                "(tar -c --exclude-from=exclude.lst -f - .)|(cd /asset-output; tar -xf -)",
                            ]
                        ),
                    ],
                },
            ),
            timeout=core.Duration.seconds(60),
            environment=dict(
                SQS_QUEUE=queue.queue_url,
                # The following parameter is optional
                S3_BUCKET_NAME=test_server_output_bucket_name,
            ),
        )
        queue.grant_send_messages(transform_lambda)

        # API Gateway with Lambda construct (using https://aws.amazon.com/solutions/constructs/patterns)
        # Reference implementation of Custom Transform component of Transform Execution Environment

        api_lambda = apigw_lambda.ApiGatewayToLambda(
            self,
            "ApiGw",
            existing_lambda_obj=transform_lambda,
            api_gateway_props=apigw.LambdaRestApiProps(
                handler=transform_lambda,
                proxy=False,
                rest_api_name=f"{COMPONENT_PREFIX_DASHES}-api",
                endpoint_export_name=f"{COMPONENT_PREFIX}ApiEndPoint",
                description=f"{COMPONENT_PREFIX} APIGW with Transform Lambda (FHIR to HL7v2)",
                default_method_options=apigw.MethodOptions(
                    authorization_type=apigw.AuthorizationType.IAM,
                ),
                policy=iam.PolicyDocument(
                    statements=[
                        iam.PolicyStatement(
                            actions=["execute-api:Invoke"],
                            effect=iam.Effect.ALLOW,
                            principals=[
                                iam.ArnPrincipal(imported_resource_router_lambda_role),
                            ],
                            resources=["execute-api:/*/*/*"],
                        )
                    ]
                ),
            ),
        )
        rest_api = api_lambda.api_gateway
        persistence = rest_api.root.add_resource("persistence")
        resource_type = persistence.add_resource("{resource_type}")
        resource_type.add_method("POST")
        resource_id = resource_type.add_resource("{id}")
        resource_id.add_method("GET")
        resource_id.add_method("PUT")
        resource_id.add_method("DELETE")

        # ECS Fargate Container (HL7v2 sender)
        # This container implements Connectivity Manager component
        # of Transform Execution Environment

        vpc = ec2.Vpc.from_lookup(self, "DefaultVpc", vpc_id=vpc_id)

        cluster = ecs.Cluster(self, f"{COMPONENT_PREFIX}Cluster", vpc=vpc)

        ecs_patterns.QueueProcessingFargateService(
            self,
            f"{COMPONENT_PREFIX}Service",
            cluster=cluster,
            image=ecs.ContainerImage.from_asset(path.join(dirname, "../../container")),
            queue=queue,
            desired_task_count=1,
            log_driver=ecs.LogDriver.aws_logs(
                stream_prefix=f"{COMPONENT_PREFIX}HL7Client",
                log_retention=logs.RetentionDays.ONE_DAY,
            ),
            environment=dict(
                SERVER_NAME=hl7_server_name,
                PORT_NUMBER=hl7_port,
            ),
        )

        # The following permission grants are needed to support
        # read interactions with integration transform
        test_server_output_bucket.grant_read(transform_lambda)

        transform_lambda.add_to_role_policy(
            iam.PolicyStatement(
                actions=["s3:ListBucket"],
                effect=iam.Effect.ALLOW,
                resources=[test_server_output_bucket.bucket_arn],
            )
        )
        transform_lambda.add_to_role_policy(
            iam.PolicyStatement(
                actions=["s3:GetObject"],
                effect=iam.Effect.ALLOW,
                resources=[test_server_output_bucket.arn_for_objects("*")],
            )
        )

        # CloudFormation Stack outputs
        # The following outputs needed to configure FHIR Works on AWS API interface
        core.CfnOutput(
            self,
            "TransformApiRootUrl",
            value=rest_api.url,
            export_name="TransformApiRootUrl",
        )
        core.CfnOutput(
            self,
            "TransformApiRegion",
            value=self.region,
            export_name="TransformApiRegion",
        )
        core.CfnOutput(
            self,
            "TransformApiAccountId",
            value=self.account,
            export_name="TransformApiAccountId",
        )
示例#5
0
    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        my_api = apigw.RestApi(
            self,
            "SageMakerAPI",
            default_cors_preflight_options=apigw.CorsOptions(
                allow_origins=["*"], allow_headers=["*"], allow_methods=["*"]))
        endpoint_name = self.node.try_get_context("endpoint_name")

        options = apigw.IntegrationOptions(
            credentials_role=iam.Role(
                self,
                "SMInvoke",
                assumed_by=iam.ServicePrincipal("apigateway"),
                inline_policies={
                    "FullSageMaker":
                    iam.PolicyDocument(statements=[
                        iam.PolicyStatement(actions=["*"], resources=["*"])
                    ])
                }),
            integration_responses=[
                apigw.IntegrationResponse(
                    status_code="200",
                    response_templates={"application/json": ""},
                    response_parameters={
                        "method.response.header.Access-Control-Allow-Headers":
                        "'*'",
                        "method.response.header.Access-Control-Allow-Methods":
                        "'*'",
                        "method.response.header.Access-Control-Allow-Origin":
                        "'*'"
                    })
            ])

        integration = apigw.Integration(
            type=apigw.IntegrationType.AWS,
            uri=
            "arn:aws:apigateway:us-east-1:runtime.sagemaker:path/endpoints/{}/invocations"
            .format(self.node.try_get_context("endpoint_name")),
            integration_http_method="POST",
            options=options)

        apigw.Method(
            self,
            "PostRoot",
            http_method="POST",
            resource=my_api.root,
            integration=integration,
            options=apigw.MethodOptions(method_responses=[
                apigw.MethodResponse(
                    status_code="200",
                    response_parameters={
                        "method.response.header.Access-Control-Allow-Methods":
                        True,
                        "method.response.header.Access-Control-Allow-Headers":
                        True,
                        "method.response.header.Access-Control-Allow-Origin":
                        True
                    })
            ]))
示例#6
0
    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)
        self.current_dir = os.path.dirname(__file__)

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

        self.quicksight_migration_lambda_role = iam.Role(
            self,
            'quicksight-migration-lambda-role',
            description='Role for the Quicksight dashboard migration Lambdas',
            role_name='quicksight-migration-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=["sts:AssumeRole", "iam:ListRoles"],
                        resources=[
                            "arn:aws:iam::*:role/quicksight-migration-*-assume-role"
                        ]),
                    iam.PolicyStatement(
                        effect=iam.Effect.ALLOW,
                        actions=["s3:PutObject", "s3:ListBucket"],
                        resources=[
                            self.bucket.bucket_arn,
                            f"{self.bucket.bucket_arn}/*"
                        ]),
                    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:*",
                                        ],
                                        resources=["*"])
                ])
            })

        # API Gateway to SQS
        self.apigw_sqs = ApiGatewayToSqs(
            self,
            "ApiGatewayToSQSqsMigration",
            allow_create_operation=True,
            allow_read_operation=False,
            allow_delete_operation=False,
            api_gateway_props=apigw.RestApiProps(
                rest_api_name="quicksight-migration-sqs",
                deploy=True,
                default_method_options=apigw.MethodOptions(
                    authorization_type=apigw.AuthorizationType.NONE),
                default_cors_preflight_options=apigw.CorsOptions(
                    allow_origins=apigw.Cors.ALL_ORIGINS,
                    allow_methods=apigw.Cors.ALL_METHODS,
                    allow_headers=[
                        'Access-Control-Allow-Origin',
                        'Access-Control-Allow-Headers', 'Content-Type'
                    ]),
                policy=iam.PolicyDocument(statements=[
                    iam.PolicyStatement(effect=iam.Effect.ALLOW,
                                        actions=['execute-api:Invoke'],
                                        resources=["execute-api:/prod/*"],
                                        principals=[iam.ArnPrincipal("*")])
                ])),
            queue_props=sqs.QueueProps(
                queue_name="quicksight-migration-sqs",
                visibility_timeout=core.Duration.minutes(15)))

        self.quicksight_migration_lambda = _lambda.Function(
            self,
            'quicksight-migration-lambda',
            handler='quicksight_migration.lambda_function.lambda_handler',
            runtime=_lambda.Runtime.PYTHON_3_8,
            code=_lambda.Code.from_asset(
                os.path.join(self.current_dir,
                             '../lambda/quicksight_migration/')),
            function_name='quicksight_migration_lambda',
            role=self.quicksight_migration_lambda_role,
            timeout=core.Duration.minutes(15),
            memory_size=1024,
            environment={
                'BUCKET_NAME': self.bucket.bucket_name,
                'S3_KEY': 'None',
                'INFRA_CONFIG_PARAM': '/infra/config',
                'SQS_URL': self.apigw_sqs.sqs_queue.queue_url
            })

        self.quicksight_migration_lambda.add_event_source(
            event_sources.SqsEventSource(
                enabled=True,
                queue=self.apigw_sqs.sqs_queue,
            ))
示例#7
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")
    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)
        self.current_dir = os.path.dirname(__file__)

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

        self.quicksight_migration_lambda_role = iam.Role(
            self,
            'quicksight-migration-lambda-role',
            description='Role for the Quicksight dashboard migration Lambdas',
            role_name='quicksight-migration-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=["sts:AssumeRole", "iam:ListRoles"],
                        resources=[
                            "arn:aws:iam::*:role/quicksight-migration-*-assume-role"
                        ]),
                    iam.PolicyStatement(
                        effect=iam.Effect.ALLOW,
                        actions=["s3:PutObject", "s3:ListBucket"],
                        resources=[
                            self.bucket.bucket_arn,
                            f"{self.bucket.bucket_arn}/*"
                        ]),
                    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:*",
                                        ],
                                        resources=["*"])
                ])
            })

        self.apigw_lambda = ApiGatewayToLambda(
            self,
            "ApiGatewayToLambdaQSMigration",
            api_gateway_props=apigw.RestApiProps(
                rest_api_name="quicksight-migration",
                default_method_options=apigw.MethodOptions(
                    authorization_type=apigw.AuthorizationType.IAM)),
            lambda_function_props=_lambda.FunctionProps(
                handler='lambda_handler.lambda_handler',
                runtime=_lambda.Runtime.PYTHON_3_8,
                code=_lambda.Code.from_asset(
                    os.path.join(self.current_dir,
                                 '../lambda/quicksight_migration/')),
                function_name='quicksight_migration',
                role=self.quicksight_migration_lambda_role,
                timeout=core.Duration.minutes(15),
                memory_size=512,
                environment={
                    'BUCKET_NAME': self.bucket.bucket_name,
                    'S3_KEY': 'sales/manifest.json',
                    'INFRA_CONFIG_PARAM': '/infra/config'
                }))
示例#9
0
    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        bundling_options = core.BundlingOptions(
            image=_lambda.Runtime.PYTHON_3_7.bundling_docker_image,
            command=[
                'bash',
                '-c',
                'pip install -r requirements.txt -t /asset-output && rsync -r . /asset-output',
            ])
        self.hello_lambda_source_code = _lambda.Code.from_asset(
            'lambda', bundling=bundling_options)

        self.hit_lambda_source_code = _lambda.Code.from_asset(
            'hit_lambda', bundling=bundling_options)

        self.hello_func = _lambda.Function(
            self,
            'HelloHandler',
            runtime=_lambda.Runtime.PYTHON_3_7,
            handler='hello.handler',
            code=self.hello_lambda_source_code,
            environment={'testkey': 'testvalue'})

        self.hit_func = _lambda.Function(
            self,
            'HitHandler',
            runtime=_lambda.Runtime.PYTHON_3_7,
            handler='hitcounter.handler',
            code=self.hit_lambda_source_code,
            environment={
                'DOWNSTREAM_FUNCTION_NAME': self.hello_func.function_name
            },
            initial_policy=[
                iam.PolicyStatement(
                    actions=[
                        "dynamodb:PutItem", "dynamodb:DescribeTable",
                        "dynamodb:UpdateItem"
                    ],
                    resources=["arn:aws:dynamodb:*:*:table/Hits"])
            ])

        self.hit_counter = lambda_ddb.LambdaToDynamoDB(
            self,
            'LambdaToDynamoDB',
            deploy_lambda=False,
            existing_lambda_obj=self.hit_func,
            dynamo_table_props=ddb.TableProps(
                table_name='Hits',
                partition_key={
                    'name': 'path',
                    'type': ddb.AttributeType.STRING
                },
                removal_policy=core.RemovalPolicy.DESTROY))

        self.hello_func.grant_invoke(self.hit_counter.lambda_function)

        # The code that defines your stack goes here
        apigw_lambda.ApiGatewayToLambda(
            self,
            'ApiGatewayToLambda',
            deploy_lambda=False,
            existing_lambda_obj=self.hit_counter.lambda_function,
            api_gateway_props=apigw.RestApiProps(
                default_method_options=apigw.MethodOptions(
                    authorization_type=apigw.AuthorizationType.NONE)))