Example #1
0
 def rest_api(self, event_streams, event_replayer):
     rest_api = _api_gtw.RestApi(self, "{}RestApi".format(self.stack_id))
     rest_api.add_usage_plan("RestApiUsagePlan",
                             api_key=_api_gtw.ApiKey(self, "TestApiKey"),
                             api_stages=[
                                 _api_gtw.UsagePlanPerApiStage(
                                     api=rest_api,
                                     stage=rest_api.deployment_stage)
                             ])
     api_role = _iam.Role(
         self,
         "RestApiRole",
         assumed_by=_iam.ServicePrincipal('apigateway.amazonaws.com'))
     api_role.add_to_policy(
         _iam.PolicyStatement(
             actions=['firehose:PutRecord'],
             resources=[stream.attr_arn for stream in event_streams]))
     for stream in event_streams:
         stream_resource = rest_api.root.add_resource(
             path_part=stream.delivery_stream_name.lower(), )
         stream_resource.add_method(
             'POST',
             api_key_required=True,
             integration=_api_gtw.Integration(
                 type=_api_gtw.IntegrationType.AWS,
                 uri=
                 "arn:aws:apigateway:eu-west-1:firehose:action/PutRecord",
                 integration_http_method='POST',
                 options=_api_gtw.IntegrationOptions(
                     credentials_role=api_role,
                     passthrough_behavior=_api_gtw.PassthroughBehavior.
                     NEVER,
                     request_parameters={
                         'integration.request.header.Content-Type':
                         "'application/x-amz-json-1.1'"
                     },
                     request_templates={
                         'application/json':
                         json.dumps({
                             "DeliveryStreamName":
                             stream.delivery_stream_name,
                             "Record": {
                                 "Data": "$util.base64Encode($input.body)"
                             }
                         })
                     },
                     integration_responses=[
                         _api_gtw.IntegrationResponse(status_code="200")
                     ])),
             method_responses=[_api_gtw.MethodResponse(status_code="200")])
         replay = stream_resource.add_resource(path_part='replay')
         replay.add_method(
             http_method='POST',
             integration=_api_gtw.LambdaIntegration(event_replayer),
             method_responses=[
                 _api_gtw.MethodResponse(status_code="202"),
                 _api_gtw.MethodResponse(status_code="400")
             ])
Example #2
0
    def __init__(self, scope: core.Construct, construct_id: str,
                 apigw_role: _iam.Role, eventBus: _events.EventBus, **kwargs):
        super().__init__(scope, construct_id, **kwargs)

        integrationOptions = \
            _apigw.IntegrationOptions(
                credentials_role=apigw_role,
                request_parameters={
                    "integration.request.header.X-Amz-Target": "'AWSEvents.PutEvents'",
                    "integration.request.header.Content-Type": "'application/x-amz-json-1.1'",
                },
                request_templates={
                    "application/json":'#set($language=$input.params(\'language\'))\n{"Entries": [{"Source": "com.amazon.alexa.$language", "Detail": ' + \
                                       '"$util.escapeJavaScript($input.body)",' + \
                                       ' "Resources": ["resource1", "resource2"], "DetailType": "myDetailType", "EventBusName": "' + eventBus.event_bus_name + '"}]}'
                },
                integration_responses=[_apigw.IntegrationResponse(
                    status_code="200",
                    response_templates={"application/json": ""},
                )]
            )

        # Integration API Gateway with EventBridge
        integrationEventBridge = _apigw.Integration(
            type=_apigw.IntegrationType("AWS"),
            integration_http_method="POST",
            options=integrationOptions,
            uri=
            f"arn:aws:apigateway:{os.environ['CDK_DEFAULT_REGION']}:events:path//"
        )

        myApi = _apigw.RestApi(self, construct_id)
        # myApi.root.add_method("POST",
        #     integrationEventBridge,
        #     method_responses=[
        #         _apigw.MethodResponse(
        #             status_code="200"
        #         )
        #     ]
        # )

        languageResource = myApi.root.add_resource("{language}")
        languageResource.add_method(
            "POST",
            integrationEventBridge,
            method_responses=[_apigw.MethodResponse(status_code="200")],
            request_models={"application/json": self.getModel(myApi)},
            request_validator=_apigw.RequestValidator(
                self,
                "myValidator",
                rest_api=myApi,
                validate_request_body=True))
Example #3
0
    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        ###
        # Let's create our own Event Bus for this rather than using default
        ###
        bus = events.EventBus(self,
                              'DestinedEventBus',
                              event_bus_name='the-destined-lambda')

        ###
        # Destinations need invoked Asynchronously so let's use SNS
        ###
        topic = sns.Topic(self,
                          'theDestinedLambdaTopic',
                          display_name='The Destined Lambda CDK Pattern Topic')

        ###
        # Lambda configured with success and failure destinations
        # Note the actual lambda has no EventBridge code inside it
        ###
        destined_lambda = _lambda.Function(
            self,
            "destinedLambda",
            runtime=_lambda.Runtime.NODEJS_12_X,
            handler="destinedLambda.handler",
            code=_lambda.Code.from_asset("lambda_fns"),
            retry_attempts=0,
            on_success=destinations.EventBridgeDestination(event_bus=bus),
            on_failure=destinations.EventBridgeDestination(event_bus=bus))
        topic.add_subscription(
            subscriptions.LambdaSubscription(destined_lambda))

        ###
        # This is a lambda that will be called by onSuccess for destinedLambda
        # It simply prints the event it receives to the cloudwatch logs
        ###
        success_lambda = _lambda.Function(
            self,
            "successLambda",
            runtime=_lambda.Runtime.NODEJS_12_X,
            handler="success.handler",
            code=_lambda.Code.from_asset("lambda_fns"),
            timeout=core.Duration.seconds(3))
        ###
        # EventBridge Rule to send events to our success lambda
        # Notice how we can still do event filtering based on the json payload returned by the destined lambda
        ###
        success_rule = events.Rule(
            self,
            'successRule',
            event_bus=bus,
            description=
            'all success events are caught here and logged centrally',
            event_pattern=events.EventPattern(
                detail={
                    "requestContext": {
                        "condition": ["Success"]
                    },
                    "responsePayload": {
                        "source": ["cdkpatterns.the-destined-lambda"],
                        "action": ["message"]
                    }
                }))
        success_rule.add_target(targets.LambdaFunction(success_lambda))

        ###
        # This is a lambda that will be called by onFailure for destinedLambda
        # It simply prints the event it receives to the cloudwatch logs.
        # Notice how it includes the message that came into destined lambda to make it fail so you have
        # everything you need to do retries or manually investigate
        ###
        failure_lambda = _lambda.Function(
            self,
            "failureLambda",
            runtime=_lambda.Runtime.NODEJS_12_X,
            handler="failure.handler",
            code=_lambda.Code.from_asset("lambda_fns"),
            timeout=core.Duration.seconds(3))

        ###
        # EventBridge Rule to send events to our failure lambda
        ###
        failure_rule = events.Rule(
            self,
            'failureRule',
            event_bus=bus,
            description=
            'all failure events are caught here and logged centrally',
            event_pattern=events.EventPattern(
                detail={"responsePayload": {
                    "errorType": ["Error"]
                }}))
        failure_rule.add_target(targets.LambdaFunction(failure_lambda))

        ###
        # API Gateway Creation
        # This is complicated because it transforms the incoming json payload into a query string url
        # this url is used to post the payload to sns without a lambda inbetween
        ###

        gateway = api_gw.RestApi(
            self,
            'theDestinedLambdaAPI',
            deploy_options=api_gw.StageOptions(
                metrics_enabled=True,
                logging_level=api_gw.MethodLoggingLevel.INFO,
                data_trace_enabled=True,
                stage_name='prod'))
        # Give our gateway permissions to interact with SNS
        api_gw_sns_role = iam.Role(
            self,
            'ApiGatewaySNSRole',
            assumed_by=iam.ServicePrincipal('apigateway.amazonaws.com'))
        topic.grant_publish(api_gw_sns_role)

        # shortening the lines of later code
        schema = api_gw.JsonSchema
        schema_type = api_gw.JsonSchemaType

        # Because this isn't a proxy integration, we need to define our response model
        response_model = gateway.add_model(
            'ResponseModel',
            content_type='application/json',
            model_name='ResponseModel',
            schema=schema(
                schema=api_gw.JsonSchemaVersion.DRAFT4,
                title='pollResponse',
                type=schema_type.OBJECT,
                properties={'message': schema(type=schema_type.STRING)}))

        error_response_model = gateway.add_model(
            'ErrorResponseModel',
            content_type='application/json',
            model_name='ErrorResponseModel',
            schema=schema(schema=api_gw.JsonSchemaVersion.DRAFT4,
                          title='errorResponse',
                          type=schema_type.OBJECT,
                          properties={
                              'state': schema(type=schema_type.STRING),
                              'message': schema(type=schema_type.STRING)
                          }))

        request_template = "Action=Publish&" + \
                           "TargetArn=$util.urlEncode('" + topic.topic_arn + "')&" + \
                           "Message=please $input.params().querystring.get('mode')&" + \
                           "Version=2010-03-31"

        # This is the VTL to transform the error response
        error_template = {
            "state": 'error',
            "message": "$util.escapeJavaScript($input.path('$.errorMessage'))"
        }
        error_template_string = json.dumps(error_template,
                                           separators=(',', ':'))

        # This is how our gateway chooses what response to send based on selection_pattern
        integration_options = api_gw.IntegrationOptions(
            credentials_role=api_gw_sns_role,
            request_parameters={
                'integration.request.header.Content-Type':
                "'application/x-www-form-urlencoded'"
            },
            request_templates={"application/json": request_template},
            passthrough_behavior=api_gw.PassthroughBehavior.NEVER,
            integration_responses=[
                api_gw.IntegrationResponse(
                    status_code='200',
                    response_templates={
                        "application/json":
                        json.dumps({"message": 'Message added to SNS topic'})
                    }),
                api_gw.IntegrationResponse(
                    selection_pattern="^\[Error\].*",
                    status_code='400',
                    response_templates={
                        "application/json": error_template_string
                    },
                    response_parameters={
                        'method.response.header.Content-Type':
                        "'application/json'",
                        'method.response.header.Access-Control-Allow-Origin':
                        "'*'",
                        'method.response.header.Access-Control-Allow-Credentials':
                        "'true'"
                    })
            ])

        # Add an SendEvent endpoint onto the gateway
        gateway.root.add_resource('SendEvent') \
            .add_method('GET', api_gw.Integration(type=api_gw.IntegrationType.AWS,
                                                  integration_http_method='POST',
                                                  uri='arn:aws:apigateway:us-east-1:sns:path//',
                                                  options=integration_options
                                                  ),
                        method_responses=[
                            api_gw.MethodResponse(status_code='200',
                                                  response_parameters={
                                                      'method.response.header.Content-Type': True,
                                                      'method.response.header.Access-Control-Allow-Origin': True,
                                                      'method.response.header.Access-Control-Allow-Credentials': True
                                                  },
                                                  response_models={
                                                      'application/json': response_model
                                                  }),
                            api_gw.MethodResponse(status_code='400',
                                                  response_parameters={
                                                      'method.response.header.Content-Type': True,
                                                      'method.response.header.Access-Control-Allow-Origin': True,
                                                      'method.response.header.Access-Control-Allow-Credentials': True
                                                  },
                                                  response_models={
                                                      'application/json': error_response_model
                                                  }),
                        ]
                        )
Example #4
0
    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)
        api_name='voncv'
        cert_arn='arn:aws:acm:us-east-1:921279086507:certificate/1cdc5fa6-0978-4c3c-96fa-ced4188b4fd0'
        # Example automatically generated. See https://github.com/aws/jsii/issues/826
        cert = acm.Certificate.from_certificate_arn(self,"cert",cert_arn)
        domain_name='voncv.sema4.com'
        domain_obj=api.DomainNameOptions(certificate=cert, domain_name=domain_name)
        #domain_name_obj=api.DefaultDomainMappingOptions( domain_name=domain_obj1, mapping_key=None)
        integration_uri='http://voncwesprod.sema4.com:3000'


        #classaws_cdk.aws_apigateway.HttpIntegration(url, *, http_method=None, options=None, proxy=None)

        #base_api=api.HttpApi(self, 'rootapi', api_name=api_name, cors_preflight=None, create_default_stage=None, default_domain_mapping=domain_name_obj)
        #                     , default_integration=None)


        base_api = api.RestApi(self, "RestApi",rest_api_name=api_name,domain_name=domain_obj,
                           deploy=False
                           )


        integration_response_200=api.IntegrationResponse(status_code='200')
        integration_response_400=api.IntegrationResponse(status_code='400',selection_pattern='400')
        integration_response_401=api.IntegrationResponse(status_code='401',selection_pattern='401')
        integration_response_404=api.IntegrationResponse(status_code='400',selection_pattern='404')
        integration_response_405=api.IntegrationResponse(status_code='401',selection_pattern='405')
        integration_response_500=api.IntegrationResponse(status_code='401',selection_pattern='500')

        integration_response_all=[integration_response_200,
                                  integration_response_400,
                                  integration_response_401,
                                  integration_response_404,
                                  integration_response_405,
                                  integration_response_500
                                  ]
        integration_options=api.IntegrationOptions(integration_responses=integration_response_all)
        #method_reponse=api.IntegrationOptions(integration_responses=integration_response_all)
        mr=[
            api.MethodResponse(status_code='200'),
            api.MethodResponse(status_code='400'),
            api.MethodResponse(status_code='401'),
            api.MethodResponse(status_code='404'),
            api.MethodResponse(status_code='405'),
            api.MethodResponse(status_code='500')
                      ]







        integration=api.Integration(type=api.IntegrationType.HTTP,integration_http_method='POST',uri=integration_uri,options=integration_options)



        control = base_api.root.add_resource('control')
        control_get=control.add_method('POST',integration)
        control_controlid=control.add_resource('{controlid}')
        control_controlid_post=control_controlid.add_method('POST',integration)
        control_controlid_sample=control_controlid.add_resource('sample')
        control_controlid_sample_sampleid=control_controlid_sample.add_resource('sampleid')
        control_controlid_sample_sampleid_get=control_controlid_sample_sampleid.add_method('GET',integration,method_responses=mr)
        control_controlid_sample_sampleid_pipelineoutput=control_controlid_sample_sampleid.add_resource('pipeline_output')
        control_controlid_sample_sampleid_pipelineoutput_post=control_controlid_sample_sampleid_pipelineoutput.add_method('POST',integration)


        getalllpatients = base_api.root.add_resource('getallpatients')
        getalllpatients_get=getalllpatients.add_method('GET',integration)


        patient = base_api.root.add_resource('patient')
        patient_post = patient.add_method('GET',integration)
        patient_get = patient.add_method('POST',integration)

        patientid=patient.add_resource('patientid')
        patient_put=patientid.add_method('PUT',integration)
        patient_get=patientid.add_method('GET',integration)

        patient_case=patientid.add_resource('case')
        patient_case_post=patient_case.add_method('POST',integration)

        patient_case_caseid=patient_case.add_resource('caseid')
        patient_case_caseid_get= patient_case_caseid.add_method('GET',integration)
        patient_case_caseid_put= patient_case_caseid.add_method('PUT',integration)
        patient_case_caseid_close=patient_case_caseid.add_resource('close')
        patient_case_caseid_close_post=patient_case_caseid_close.add_method('PUT',integration)
        patient_case_caseid_curate=patient_case_caseid.add_resource('curate')
        patient_case_caseid_curate_post=patient_case_caseid_curate.add_method('POST',integration)
        patient_case_caseid_fail=patient_case_caseid.add_resource('fail')
        patient_case_caseid_fail_post=patient_case_caseid_fail.add_method('POST',integration)
        patient_case_caseid_reopen=patient_case_caseid.add_resource('reopen')
        patient_case_caseid_reopen_post=patient_case_caseid_reopen.add_method('POST',integration)
        patient_case_caseid_sample=patient_case_caseid.add_resource('sample')
        patient_case_caseid_sample_sampleid=patient_case_caseid_sample.add_resource('sampleid')
        patient_case_caseid_sample_sampleid_get=patient_case_caseid_sample_sampleid.add_method('GET',integration)
        patient_case_caseid_sample_sampleid_fail=patient_case_caseid_sample_sampleid.add_resource('fail')
        patient_case_caseid_sample_sampleid_fail_post=patient_case_caseid_sample_sampleid_fail.add_method('POST',integration)
        patient_case_caseid_sample_sampleid_pipelineoutput=patient_case_caseid_sample_sampleid.add_resource('pipeline-output')
        patient_case_caseid_sample_sampleid_pipelineoutput_post=patient_case_caseid_sample_sampleid_pipelineoutput.add_method('POST',integration)
        patient_case_caseid_variant=patient_case_caseid.add_resource('variant')
        patient_case_caseid_variant_post=patient_case_caseid.add_method('POST',integration)
Example #5
0
    def __init__(self, scope: core.Construct, id: str, stack_log_level: str,
                 back_end_api_name: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        # Create Serverless Event Processor using Lambda):
        # Read Lambda Code):
        try:
            with open(
                    "api_with_stage_variables/stacks/back_end/lambda_src/serverless_greeter.py",
                    mode="r") as f:
                greeter_fn_code = f.read()
        except OSError as e:
            print("Unable to read Lambda Function Code")
            raise e

        greeter_fn = _lambda.Function(
            self,
            "secureGreeterFn",
            function_name=f"greeter_fn_{id}",
            runtime=_lambda.Runtime.PYTHON_3_7,
            handler="index.lambda_handler",
            code=_lambda.InlineCode(greeter_fn_code),
            timeout=core.Duration.seconds(15),
            reserved_concurrent_executions=20,
            environment={
                "LOG_LEVEL": f"{stack_log_level}",
                "Environment": "Production",
                "ANDON_CORD_PULLED": "False",
                "RANDOM_SLEEP_ENABLED": "False"
            },
            description=
            "A simple greeter function, which responds with a timestamp")
        greeter_fn_version = greeter_fn.latest_version
        greeter_fn_version_alias = _lambda.Alias(
            self,
            "greeterFnMystiqueAutomationAlias",
            alias_name="MystiqueAutomation",
            version=greeter_fn_version)

        greeter_fn_dev_alias = _lambda.Alias(self,
                                             "greeterFnDevAlias",
                                             alias_name="dev",
                                             version=greeter_fn_version)

        greeter_fn_test_alias = _lambda.Alias(self,
                                              "greeterFnTestAlias",
                                              alias_name="test",
                                              version=greeter_fn_version)

        greeter_fn_prod_alias = _lambda.Alias(self,
                                              "greeterFnProdAlias",
                                              alias_name="prod",
                                              version=greeter_fn_version)

        # Create Custom Loggroup
        # /aws/lambda/function-name
        greeter_fn_lg = _logs.LogGroup(
            self,
            "greeterFnLoggroup",
            log_group_name=f"/aws/lambda/{greeter_fn.function_name}",
            retention=_logs.RetentionDays.ONE_WEEK,
            removal_policy=core.RemovalPolicy.DESTROY)
        # %%

        # Create API Gateway
        wa_api = _apigw.RestApi(
            self,
            "backEnd01Api",
            rest_api_name=f"{back_end_api_name}",
            # deploy_options=wa_api_dev_stage_options,
            retain_deployments=False,
            deploy=False,
            endpoint_types=[_apigw.EndpointType.EDGE],
            description=
            f"{GlobalArgs.OWNER}: API Best Practices. Stage Variables for APIs. This stack deploys an API and integrates with Lambda using Stage Variables."
        )

        wa_api_logs = _logs.LogGroup(
            self,
            "waApiLogs",
            log_group_name=f"/aws/apigateway/{back_end_api_name}/access_logs",
            removal_policy=core.RemovalPolicy.DESTROY,
            retention=_logs.RetentionDays.ONE_DAY)

        ######################################
        ##    CONFIG FOR API STAGE : DEV    ##
        ######################################
        dev_wa_api_deploy = _apigw.Deployment(
            self,
            "devDeploy",
            api=wa_api,
            description=f"{GlobalArgs.OWNER}: Deployment of 'dev' Api Stage",
            retain_deployments=False)

        dev_api_stage = _apigw.Stage(
            self,
            "devStage",
            deployment=dev_wa_api_deploy,
            stage_name="miztiik-dev",
            throttling_rate_limit=10,
            throttling_burst_limit=100,
            # Log full requests/responses data
            data_trace_enabled=True,
            # Enable Detailed CloudWatch Metrics
            metrics_enabled=True,
            logging_level=_apigw.MethodLoggingLevel.INFO,
            access_log_destination=_apigw.LogGroupLogDestination(wa_api_logs),
            variables={"lambdaAlias": "dev"})

        # wa_api.deployment_stage = dev_api_stage

        test_wa_api_deploy = _apigw.Deployment(
            self,
            "testDeploy",
            api=wa_api,
            description=f"{GlobalArgs.OWNER}: Deployment of 'test' Api Stage",
            retain_deployments=False)
        test_api_stage = _apigw.Stage(
            self,
            "testStage",
            deployment=test_wa_api_deploy,
            stage_name="miztiik-test",
            throttling_rate_limit=10,
            throttling_burst_limit=100,
            # Log full requests/responses data
            data_trace_enabled=True,
            # Enable Detailed CloudWatch Metrics
            metrics_enabled=True,
            logging_level=_apigw.MethodLoggingLevel.INFO,
            access_log_destination=_apigw.LogGroupLogDestination(wa_api_logs),
            variables={"lambdaAlias": "test"})

        wa_api.deployment_stage = test_api_stage

        prod_wa_api_deploy = _apigw.Deployment(
            self,
            "prodDeploy",
            api=wa_api,
            description=f"{GlobalArgs.OWNER}: Deployment of 'prod' Api Stage",
            retain_deployments=False)
        prod_api_stage = _apigw.Stage(
            self,
            "prodStage",
            deployment=prod_wa_api_deploy,
            stage_name="miztiik-prod",
            throttling_rate_limit=10,
            throttling_burst_limit=100,
            # Log full requests/responses data
            data_trace_enabled=True,
            # Enable Detailed CloudWatch Metrics
            metrics_enabled=True,
            logging_level=_apigw.MethodLoggingLevel.INFO,
            access_log_destination=_apigw.LogGroupLogDestination(wa_api_logs),
            variables={"lambdaAlias": "prod"})

        prod_api_stage.node.add_dependency(test_api_stage)

        wa_api.deployment_stage = prod_api_stage

        wa_api_res = wa_api.root.add_resource("wa-api")
        greeter = wa_api_res.add_resource("greeter")

        backend_stage_uri = (f"arn:aws:apigateway:"
                             f"{core.Aws.REGION}"
                             f":lambda:path/2015-03-31/functions/"
                             f"{greeter_fn.function_arn}"
                             f":"
                             f"${{stageVariables.lambdaAlias}}"
                             f"/invocations")

        greeter_method_get = greeter.add_method(
            http_method="GET",
            request_parameters={
                "method.request.header.InvocationType": True,
                "method.request.path.mystique": True
            },
            # integration=_apigw.LambdaIntegration(
            #     # handler=greeter_fn,
            #     handler=backend_stage_uri,
            #     proxy=True
            # )
            integration=_apigw.Integration(
                type=_apigw.IntegrationType.AWS_PROXY,
                integration_http_method="GET",
                uri=backend_stage_uri))

        # "arn:aws:apigateway:eu-west-2:lambda:path/2015-03-31/functions/arn:aws:lambda:eu-west-2:614517326458:function:${stageVariables.preflightFunction}/invocations",
        # https://lambda.us-east-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-east-1:230023004178:function:greeter_fn_well-architected-api:${stageVariable.lambdaAlias}/invocations

        # We need to manually add permissions for the stages to invoke
        # CDK By default adds permission only for the last .deployment_stage
        #####################################################################
        ##  CDK BUG: CONDITIONS DOES NOT TAKE EFFECT IN SERVICE PRINCIPAL  ##
        #####################################################################
        """
        greeter_fn.grant_invoke(
            _iam.ServicePrincipal(
                service="apigateway.amazonaws.com",
                conditions={
                    "ArnLike": {"aws:SourceArn": "this-does-not-work"}
                }
            )
        )
        """

        greeter_fn.add_permission(
            "allowStageInvocation",
            principal=_iam.ServicePrincipal("apigateway.amazonaws.com"),
            # source_arn=f"arn:aws:execute-api:{core.Aws.REGION}:{core.Aws.ACCOUNT_ID}:{wa_api.rest_api_id}/{dev_api_stage.stage_name}/{greeter_method_get.resource.path}"
            source_arn=greeter_method_get.method_arn.replace(
                wa_api.deployment_stage.stage_name, "*"),
            action="lambda:InvokeFunction",
        )

        greeter_fn_dev_alias.add_permission(
            "greeter_fn_devAliasApiPerms",
            principal=_iam.ServicePrincipal("apigateway.amazonaws.com"),
            action="lambda:InvokeFunction",
            source_arn=greeter_method_get.method_arn.replace(
                wa_api.deployment_stage.stage_name, "*"),
        )
        greeter_fn_test_alias.add_permission(
            "greeter_fn_testAliasApiPerms",
            principal=_iam.ServicePrincipal("apigateway.amazonaws.com"),
            action="lambda:InvokeFunction",
            source_arn=greeter_method_get.method_arn.replace(
                wa_api.deployment_stage.stage_name, "*"),
        )
        greeter_fn_prod_alias.add_permission(
            "greeter_fn_prodAliasApiPerms",
            principal=_iam.ServicePrincipal("apigateway.amazonaws.com"),
            action="lambda:InvokeFunction",
            source_arn=greeter_method_get.method_arn.replace(
                wa_api.deployment_stage.stage_name, "*"),
        )

        # Outputs
        output_0 = core.CfnOutput(
            self,
            "AutomationFrom",
            value=f"{GlobalArgs.SOURCE_INFO}",
            description=
            "To know more about this automation stack, check out our github page."
        )

        output_1 = core.CfnOutput(
            self,
            "WaApiUrl",
            value=f"{greeter.url}",
            description=
            "Use an utility like curl from the same VPC as the API to invoke it."
        )
    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        # DynamoDB Table
        # Streaming is enabled to send the whole new object down the pipe
        table = dynamo_db.Table(self,
                                "TheDynamoStreamer",
                                partition_key=dynamo_db.Attribute(
                                    name="message",
                                    type=dynamo_db.AttributeType.STRING),
                                stream=dynamo_db.StreamViewType.NEW_IMAGE)

        # defines an AWS  Lambda resource
        subscriber_lambda = _lambda.Function(
            self,
            "DynamoLambdaHandler",
            runtime=_lambda.Runtime.PYTHON_3_8,
            handler="lambda.handler",
            code=_lambda.Code.from_asset("lambda_fns/subscribe"))

        subscriber_lambda.add_event_source(
            _event.DynamoEventSource(
                table=table,
                starting_position=_lambda.StartingPosition.LATEST))

        # API Gateway Creation
        gateway = api_gw.RestApi(
            self,
            'DynamoStreamerAPI',
            deploy_options=api_gw.StageOptions(
                metrics_enabled=True,
                logging_level=api_gw.MethodLoggingLevel.INFO,
                data_trace_enabled=True,
                stage_name='prod'))

        # Give our gateway permissions to interact with dynamodb
        api_gw_dynamo_role = iam.Role(
            self,
            'DefaultLambdaHanderRole',
            assumed_by=iam.ServicePrincipal('apigateway.amazonaws.com'))
        table.grant_read_write_data(api_gw_dynamo_role)

        # shortening the lines of later code
        schema = api_gw.JsonSchema
        schema_type = api_gw.JsonSchemaType

        # Because this isn't a proxy integration, we need to define our response model
        response_model = gateway.add_model(
            'ResponseModel',
            content_type='application/json',
            model_name='ResponseModel',
            schema=schema(
                schema=api_gw.JsonSchemaVersion.DRAFT4,
                title='pollResponse',
                type=schema_type.OBJECT,
                properties={'message': schema(type=schema_type.STRING)}))

        error_response_model = gateway.add_model(
            'ErrorResponseModel',
            content_type='application/json',
            model_name='ErrorResponseModel',
            schema=schema(schema=api_gw.JsonSchemaVersion.DRAFT4,
                          title='errorResponse',
                          type=schema_type.OBJECT,
                          properties={
                              'state': schema(type=schema_type.STRING),
                              'message': schema(type=schema_type.STRING)
                          }))

        # This is the VTL to transform our incoming JSON to a Dynamo Insert Query
        request_template = {
            "TableName": table.table_name,
            "Item": {
                "message": {
                    "S": "$input.path('$.message')"
                }
            }
        }
        request_template_string = json.dumps(request_template,
                                             separators=(',', ':'))

        # This is the VTL to transform the error response
        error_template = {
            "state": 'error',
            "message": "$util.escapeJavaScript($input.path('$.errorMessage'))"
        }
        error_template_string = json.dumps(error_template,
                                           separators=(',', ':'))

        # This is how our gateway chooses what response to send based on selection_pattern
        integration_options = api_gw.IntegrationOptions(
            credentials_role=api_gw_dynamo_role,
            request_templates={"application/json": request_template_string},
            passthrough_behavior=api_gw.PassthroughBehavior.NEVER,
            integration_responses=[
                api_gw.IntegrationResponse(
                    status_code='200',
                    response_templates={
                        "application/json":
                        json.dumps({"message": 'item added to db'})
                    }),
                api_gw.IntegrationResponse(
                    selection_pattern="^\[BadRequest\].*",
                    status_code='400',
                    response_templates={
                        "application/json": error_template_string
                    },
                    response_parameters={
                        'method.response.header.Content-Type':
                        "'application/json'",
                        'method.response.header.Access-Control-Allow-Origin':
                        "'*'",
                        'method.response.header.Access-Control-Allow-Credentials':
                        "'true'"
                    })
            ])

        # Add an InsertItem endpoint onto the gateway
        gateway.root.add_resource('InsertItem') \
            .add_method('POST', api_gw.Integration(type=api_gw.IntegrationType.AWS,
                                                   integration_http_method='POST',
                                                   uri='arn:aws:apigateway:us-east-1:dynamodb:action/PutItem',
                                                   options=integration_options
                                                   ),
                        method_responses=[
                            api_gw.MethodResponse(status_code='200',
                                                  response_parameters={
                                                      'method.response.header.Content-Type': True,
                                                      'method.response.header.Access-Control-Allow-Origin': True,
                                                      'method.response.header.Access-Control-Allow-Credentials': True
                                                  },
                                                  response_models={
                                                      'application/json': response_model
                                                  }),
                            api_gw.MethodResponse(status_code='400',
                                                  response_parameters={
                                                      'method.response.header.Content-Type': True,
                                                      'method.response.header.Access-Control-Allow-Origin': True,
                                                      'method.response.header.Access-Control-Allow-Credentials': True
                                                  },
                                                  response_models={
                                                      'application/json': error_response_model
                                                  }),
                        ]
                        )
Example #7
0
    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

## Create S3 Bucket. We'll populate it seperately. 
        bucket_name="{}-s3-ecs-apigw-test".format(core.Aws.ACCOUNT_ID)
        bucket = s3.Bucket(self, "s3-ecs-apigw-test",
            bucket_name=bucket_name,
            versioned=True,
            public_read_access=False
            )

## Create ECS  Cluster, Taks and Service
### Create the VPC for the demo
        vpc = ec2.Vpc(self, "MyVpc", max_azs=3)

### Create the ECS Cluster
        cluster = ecs.Cluster(self,
            "ecs-apigw-test-cluster",
            cluster_name="ecs-apigw-test-cluster",
            container_insights=True,
            vpc=vpc)
### Using the Network LoadBalance Fargate patterm, this wills create the container definition, the task definition, the service and the Network load balancer for it. 
        ecs_deploy = ecsp.NetworkLoadBalancedFargateService(self, 
            "ecs-apigw-test",
            cluster=cluster,
            cpu=512,
            desired_count=2,
            public_load_balancer=False,
            memory_limit_mib=2048,
            task_image_options=ecsp.NetworkLoadBalancedTaskImageOptions(
                image=ecs.ContainerImage.from_registry("strm/helloworld-http")
            ),
            health_check_grace_period=core.Duration.minutes(5)
            )
### Have to add an Ingress rule to allow traffic through. This applies the CIDR of the VPC. 
        ecs_deploy.service.connections.security_groups[0].add_ingress_rule(
            ec2.Peer.ipv4(vpc.vpc_cidr_block),
            ec2.Port(
                protocol=ec2.Protocol.TCP,
                string_representation="",
                from_port=80,
                to_port=80
            )
        )
## Create API Gateway resources

### Create the VPC Link to the Network Load Balancer
        vpc_link =apigw.VpcLink(self, 
            "ecs-test-vpc-link",
            targets = [ecs_deploy.load_balancer])
### Create the API
        api = apigw.RestApi(self, "ecs-s3-test-api",
                  rest_api_name="ECS S3 Test API",
                  description="Test API for distributing traffic to S3 and ECS",
                  binary_media_types=["image/png"])
### Create the execution role for the API methods. 
        rest_api_role = iam.Role(
            self,
            "RestAPIRole",
            assumed_by=iam.ServicePrincipal("apigateway.amazonaws.com"),
            managed_policies=[iam.ManagedPolicy.from_aws_managed_policy_name("AmazonS3ReadOnlyAccess")])

### Generic Method Response that can be used with each API method
        method_response = apigw.MethodResponse(status_code="200",
            response_parameters={"method.response.header.Content-Type": True})

### Root URI
        root_integration_response = apigw.IntegrationResponse(
            status_code="200",
            response_templates={"text/html": "$input.path('$')"},
            response_parameters={"method.response.header.Content-Type": "'text/html'"})
        root_integration_options = apigw.IntegrationOptions(
            credentials_role=rest_api_role,
            integration_responses=[root_integration_response],
            request_templates={"application/json": "Action=SendMessage&MessageBody=$input.body"},
            passthrough_behavior=apigw.PassthroughBehavior.NEVER,
            request_parameters={
                "integration.request.header.Content-Type": "'application/x-www-form-urlencoded'"})
        root_resource_s3_integration = apigw.AwsIntegration(
            service="s3",
            integration_http_method="GET",
            subdomain=bucket_name,
            path="index.html",
            options=root_integration_options)

 
        root_method = api.root.add_method("GET",
            root_resource_s3_integration,
            method_responses=[method_response])


### API URI
        api_integration = apigw.Integration(
            type=apigw.IntegrationType.HTTP_PROXY,
            uri="http://{}".format(ecs_deploy.load_balancer.load_balancer_dns_name),
            integration_http_method="GET",
            options={
                "connection_type": apigw.ConnectionType.VPC_LINK,
                "vpc_link": vpc_link
            }
        )
        apis = api.root.add_resource("apis")
        apii = apis.add_resource("{api}")
        # apis = api.root.add_resource("apis")
        apii_get = apii.add_method("GET",
            api_integration,
            method_responses=[method_response],
            request_parameters={
                "method.request.path.api": True,})

## Add Images URI
        image_integration_response = apigw.IntegrationResponse(
            status_code="200",
            content_handling=apigw.ContentHandling.CONVERT_TO_BINARY,
            # response_templates={"text/html": "$input.path('$')"},
            response_parameters={"method.response.header.Content-Type": "'image/png'"})
        image_integration_options = apigw.IntegrationOptions(
            credentials_role=rest_api_role,
            integration_responses=[image_integration_response],
            request_templates={"application/json": "Action=SendMessage&MessageBody=$input.body"},
            passthrough_behavior=apigw.PassthroughBehavior.NEVER,
            request_parameters={
                "integration.request.header.Content-Type": "'application/x-www-form-urlencoded'",
                "integration.request.path.image": "method.request.path.image"})
        images_resource_s3_integration = apigw.AwsIntegration(
            service="s3",
            integration_http_method="GET",
            subdomain=bucket_name,
            path="images/{image}",
            options=image_integration_options)
        images_resource = api.root.add_resource("images")
        image_resource = images_resource.add_resource("{image}")
        images_get = image_resource.add_method("GET",
            images_resource_s3_integration,
            method_responses=[method_response],
            request_parameters={
                "method.request.path.image": True,})

## Fall Through
        folder = api.root.add_resource("{folder}")
        item = folder.add_resource("{item}")
        integration_response = apigw.IntegrationResponse(
            status_code="200",
            response_templates={"text/html": "$input.path('$')"},
            response_parameters={"method.response.header.Content-Type": "'text/html'"})
        s3_proxy_integration_options = apigw.IntegrationOptions(
            credentials_role=rest_api_role,
            integration_responses=[integration_response],
            request_templates={"application/json": "Action=SendMessage&MessageBody=$input.body"},
            passthrough_behavior=apigw.PassthroughBehavior.NEVER,
            request_parameters={
                "integration.request.header.Content-Type": "'application/x-www-form-urlencoded'",
                "integration.request.path.item": "method.request.path.item",
                "integration.request.path.folder": "method.request.path.folder"})
        s3_proxy_resource_s3_integration = apigw.AwsIntegration(
            service="s3",
            integration_http_method="GET",
            subdomain=bucket_name,
            path="{folder}/{item}",
            options=s3_proxy_integration_options)
        item_get = item.add_method("GET",
            s3_proxy_resource_s3_integration,
            method_responses=[method_response],
            request_parameters={
                "method.request.path.item": True,
                "method.request.path.folder": True
            }
            )
Example #8
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
                    })
            ]))
    def __init__(self, scope: core.Construct, construct_id: str,
                 **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        # Create two VPCs - one to host our private website, the other to act as a client
        website_vpc = Vpc(
            self,
            "WEBSITEVPC",
            cidr="10.0.0.0/16",
        )
        client_vpc = Vpc(
            self,
            "ClientVPC",
            cidr="10.1.0.0/16",
        )

        # Create a bastion host in the client API which will act like our client workstation
        bastion = BastionHostLinux(
            self,
            "WEBClient",
            vpc=client_vpc,
            instance_name='my-bastion',
            instance_type=InstanceType('t3.micro'),
            machine_image=AmazonLinuxImage(),
            subnet_selection=SubnetSelection(subnet_type=SubnetType.PRIVATE),
            security_group=SecurityGroup(
                scope=self,
                id='bastion-sg',
                security_group_name='bastion-sg',
                description=
                'Security group for the bastion, no inbound open because we should access'
                ' to the bastion via AWS SSM',
                vpc=client_vpc,
                allow_all_outbound=True))

        # Set up a VPC peering connection between client and API VPCs, and adjust
        # the routing table to allow connections back and forth
        VpcPeeringHelper(self, 'Peering', website_vpc, client_vpc)

        # Create VPC endpoints for API gateway
        vpc_endpoint = InterfaceVpcEndpoint(
            self,
            'APIGWVpcEndpoint',
            vpc=website_vpc,
            service=InterfaceVpcEndpointAwsService.APIGATEWAY,
            private_dns_enabled=True,
        )
        vpc_endpoint.connections.allow_from(bastion, Port.tcp(443))
        endpoint_id = vpc_endpoint.vpc_endpoint_id

        api_policy = iam.PolicyDocument(statements=[
            iam.PolicyStatement(principals=[iam.AnyPrincipal()],
                                actions=['execute-api:Invoke'],
                                resources=['execute-api:/*'],
                                effect=iam.Effect.DENY,
                                conditions={
                                    "StringNotEquals": {
                                        "aws:SourceVpce": endpoint_id
                                    }
                                }),
            iam.PolicyStatement(principals=[iam.AnyPrincipal()],
                                actions=['execute-api:Invoke'],
                                resources=['execute-api:/*'],
                                effect=iam.Effect.ALLOW)
        ])

        # Create an s3 bucket to hold the content
        content_bucket = s3.Bucket(self,
                                   "ContentBucket",
                                   removal_policy=core.RemovalPolicy.DESTROY)

        # Upload our static content to the bucket
        s3dep.BucketDeployment(self,
                               "DeployWithInvalidation",
                               sources=[s3dep.Source.asset('website')],
                               destination_bucket=content_bucket)

        # Create a private API GW in the API VPC
        api = apigw.RestApi(self,
                            'PrivateS3Api',
                            endpoint_configuration=apigw.EndpointConfiguration(
                                types=[apigw.EndpointType.PRIVATE],
                                vpc_endpoints=[vpc_endpoint]),
                            policy=api_policy)

        # Create a role to allow API GW to access our S3 bucket contents
        role = iam.Role(
            self,
            "Role",
            assumed_by=iam.ServicePrincipal("apigateway.amazonaws.com"))
        role.add_to_policy(
            iam.PolicyStatement(effect=iam.Effect.ALLOW,
                                resources=[
                                    content_bucket.bucket_arn,
                                    content_bucket.bucket_arn + '/*'
                                ],
                                actions=["s3:Get*"]))

        # Create a proxy resource that captures all non-root resource requests
        resource = api.root.add_resource("{proxy+}")
        # Create an integration with S3
        resource_integration = apigw.Integration(
            type=apigw.IntegrationType.AWS,
            integration_http_method='GET',
            options=apigw.IntegrationOptions(
                request_parameters=
                {  # map the proxy parameter so we can pass the request path
                    "integration.request.path.proxy":
                    "method.request.path.proxy"
                },
                integration_responses=[
                    apigw.IntegrationResponse(
                        status_code='200',
                        response_parameters=
                        {  # map the content type of the S3 object back to the HTTP response
                            "method.response.header.Content-Type":
                            "integration.response.header.Content-Type"
                        })
                ],
                credentials_role=role),
            # reference the bucket content we want to retrieve
            uri='arn:aws:apigateway:eu-west-1:s3:path/%s/{proxy}' %
            (content_bucket.bucket_name))
        # handle the GET request and map it to our new integration
        resource.add_method(
            "GET",
            resource_integration,
            method_responses=[
                apigw.MethodResponse(status_code='200',
                                     response_parameters={
                                         "method.response.header.Content-Type":
                                         False
                                     })
            ],
            request_parameters={"method.request.path.proxy": True})
        # Handle requests to the root of our site
        # Create another integration with S3 - this time with no proxy parameter
        resource_integration = apigw.Integration(
            type=apigw.IntegrationType.AWS,
            integration_http_method='GET',
            options=apigw.IntegrationOptions(
                integration_responses=[
                    apigw.IntegrationResponse(
                        status_code='200',
                        response_parameters=
                        {  # map the content type of the S3 object back to the HTTP response
                            "method.response.header.Content-Type":
                            "integration.response.header.Content-Type"
                        })
                ],
                credentials_role=role),
            # reference the bucket content we want to retrieve
            uri='arn:aws:apigateway:eu-west-1:s3:path/%s/index.html' %
            (content_bucket.bucket_name))
        # handle the GET request and map it to our new integration
        api.root.add_method("GET",
                            resource_integration,
                            method_responses=[
                                apigw.MethodResponse(
                                    status_code='200',
                                    response_parameters={
                                        "method.response.header.Content-Type":
                                        False
                                    })
                            ])
Example #10
0
    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        ###
        # SNS Topic Creation
        # Our API Gateway posts messages directly to this
        ###
        topic = sns.Topic(self,
                          'theBigFanTopic',
                          display_name='The Big Fan CDK Pattern Topic')

        ###
        # SQS Subscribers creation for our SNS Topic
        # 2 subscribers, one for messages with a status of created one for any other message
        ###

        # Status:created SNS Subscriber Queue
        created_status_queue = sqs.Queue(
            self,
            'BigFanTopicStatusCreatedSubscriberQueue',
            visibility_timeout=core.Duration.seconds(300),
            queue_name='BigFanTopicStatusCreatedSubscriberQueue')

        # Only send messages to our created_status_queue with a status of created
        created_filter = sns.SubscriptionFilter.string_filter(
            whitelist=['created'])
        topic.add_subscription(
            subscriptions.SqsSubscription(
                created_status_queue,
                raw_message_delivery=True,
                filter_policy={'status': created_filter}))

        # Any other status SNS Subscriber Queue
        other_status_queue = sqs.Queue(
            self,
            'BigFanTopicAnyOtherStatusSubscriberQueue',
            visibility_timeout=core.Duration.seconds(300),
            queue_name='BigFanTopicAnyOtherStatusSubscriberQueue')

        # Only send messages to our other_status_queue that do not have a status of created
        other_filter = sns.SubscriptionFilter.string_filter(
            blacklist=['created'])
        topic.add_subscription(
            subscriptions.SqsSubscription(
                other_status_queue,
                raw_message_delivery=True,
                filter_policy={'status': other_filter}))

        ###
        # Creation of Lambdas that subscribe to above SQS queues
        ###

        # Created status queue lambda
        sqs_created_status_subscriber = _lambda.Function(
            self,
            "SQSCreatedStatusSubscribeLambdaHandler",
            runtime=_lambda.Runtime.NODEJS_12_X,
            handler="createdStatus.handler",
            code=_lambda.Code.from_asset("lambda_fns/subscribe"))
        created_status_queue.grant_consume_messages(
            sqs_created_status_subscriber)
        sqs_created_status_subscriber.add_event_source(
            _event.SqsEventSource(created_status_queue))

        # Any other status queue lambda
        sqs_other_status_subscriber = _lambda.Function(
            self,
            "SQSAnyOtherStatusSubscribeLambdaHandler",
            runtime=_lambda.Runtime.NODEJS_12_X,
            handler="anyOtherStatus.handler",
            code=_lambda.Code.from_asset("lambda_fns/subscribe"))
        other_status_queue.grant_consume_messages(sqs_other_status_subscriber)
        sqs_other_status_subscriber.add_event_source(
            _event.SqsEventSource(other_status_queue))

        ###
        # API Gateway Creation
        # This is complicated because it transforms the incoming json payload into a query string url
        # this url is used to post the payload to sns without a lambda inbetween
        ###

        gateway = api_gw.RestApi(
            self,
            'theBigFanAPI',
            deploy_options=api_gw.StageOptions(
                metrics_enabled=True,
                logging_level=api_gw.MethodLoggingLevel.INFO,
                data_trace_enabled=True,
                stage_name='prod'))

        # Give our gateway permissions to interact with SNS
        api_gw_sns_role = iam.Role(
            self,
            'DefaultLambdaHanderRole',
            assumed_by=iam.ServicePrincipal('apigateway.amazonaws.com'))
        topic.grant_publish(api_gw_sns_role)

        # shortening the lines of later code
        schema = api_gw.JsonSchema
        schema_type = api_gw.JsonSchemaType

        # Because this isn't a proxy integration, we need to define our response model
        response_model = gateway.add_model(
            'ResponseModel',
            content_type='application/json',
            model_name='ResponseModel',
            schema=schema(
                schema=api_gw.JsonSchemaVersion.DRAFT4,
                title='pollResponse',
                type=schema_type.OBJECT,
                properties={'message': schema(type=schema_type.STRING)}))

        error_response_model = gateway.add_model(
            'ErrorResponseModel',
            content_type='application/json',
            model_name='ErrorResponseModel',
            schema=schema(schema=api_gw.JsonSchemaVersion.DRAFT4,
                          title='errorResponse',
                          type=schema_type.OBJECT,
                          properties={
                              'state': schema(type=schema_type.STRING),
                              'message': schema(type=schema_type.STRING)
                          }))

        request_template = "Action=Publish&" + \
                           "TargetArn=$util.urlEncode('" + topic.topic_arn + "')&" + \
                           "Message=$util.urlEncode($input.path('$.message'))&" + \
                           "Version=2010-03-31&" + \
                           "MessageAttributes.entry.1.Name=status&" + \
                           "MessageAttributes.entry.1.Value.DataType=String&" + \
                           "MessageAttributes.entry.1.Value.StringValue=$util.urlEncode($input.path('$.status'))"

        # This is the VTL to transform the error response
        error_template = {
            "state": 'error',
            "message": "$util.escapeJavaScript($input.path('$.errorMessage'))"
        }
        error_template_string = json.dumps(error_template,
                                           separators=(',', ':'))

        # This is how our gateway chooses what response to send based on selection_pattern
        integration_options = api_gw.IntegrationOptions(
            credentials_role=api_gw_sns_role,
            request_parameters={
                'integration.request.header.Content-Type':
                "'application/x-www-form-urlencoded'"
            },
            request_templates={"application/json": request_template},
            passthrough_behavior=api_gw.PassthroughBehavior.NEVER,
            integration_responses=[
                api_gw.IntegrationResponse(
                    status_code='200',
                    response_templates={
                        "application/json":
                        json.dumps({"message": 'message added to topic'})
                    }),
                api_gw.IntegrationResponse(
                    selection_pattern="^\[Error\].*",
                    status_code='400',
                    response_templates={
                        "application/json": error_template_string
                    },
                    response_parameters={
                        'method.response.header.Content-Type':
                        "'application/json'",
                        'method.response.header.Access-Control-Allow-Origin':
                        "'*'",
                        'method.response.header.Access-Control-Allow-Credentials':
                        "'true'"
                    })
            ])

        # Add an SendEvent endpoint onto the gateway
        gateway.root.add_resource('SendEvent') \
            .add_method('POST', api_gw.Integration(type=api_gw.IntegrationType.AWS,
                                                   integration_http_method='POST',
                                                   uri='arn:aws:apigateway:us-east-1:sns:path//',
                                                   options=integration_options
                                                   ),
                        method_responses=[
                            api_gw.MethodResponse(status_code='200',
                                                  response_parameters={
                                                      'method.response.header.Content-Type': True,
                                                      'method.response.header.Access-Control-Allow-Origin': True,
                                                      'method.response.header.Access-Control-Allow-Credentials': True
                                                  },
                                                  response_models={
                                                      'application/json': response_model
                                                  }),
                            api_gw.MethodResponse(status_code='400',
                                                  response_parameters={
                                                      'method.response.header.Content-Type': True,
                                                      'method.response.header.Access-Control-Allow-Origin': True,
                                                      'method.response.header.Access-Control-Allow-Credentials': True
                                                  },
                                                  response_models={
                                                      'application/json': error_response_model
                                                  }),
                        ]
                        )
    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        ###
        # SNS Topic Creation
        # Our API Gateway posts messages directly to this
        ###
        topic = sns.Topic(self,
                          'TheXRayTracerSnsFanOutTopic',
                          display_name='The XRay Tracer Fan Out Topic')
        self.sns_topic_arn = topic.topic_arn

        ###
        # API Gateway Creation
        # This is complicated because it is a direct SNS integration through VTL not a proxy integration
        # Tracing is enabled for X-Ray
        ###

        gateway = api_gw.RestApi(
            self,
            'xrayTracerAPI',
            deploy_options=api_gw.StageOptions(
                metrics_enabled=True,
                logging_level=api_gw.MethodLoggingLevel.INFO,
                data_trace_enabled=True,
                tracing_enabled=True,
                stage_name='prod'))

        # Give our gateway permissions to interact with SNS
        api_gw_sns_role = iam.Role(
            self,
            'ApiGatewaySNSRole',
            assumed_by=iam.ServicePrincipal('apigateway.amazonaws.com'))
        topic.grant_publish(api_gw_sns_role)

        # shortening the lines of later code
        schema = api_gw.JsonSchema
        schema_type = api_gw.JsonSchemaType

        # Because this isn't a proxy integration, we need to define our response model
        response_model = gateway.add_model(
            'ResponseModel',
            content_type='application/json',
            model_name='ResponseModel',
            schema=schema(
                schema=api_gw.JsonSchemaVersion.DRAFT4,
                title='pollResponse',
                type=schema_type.OBJECT,
                properties={'message': schema(type=schema_type.STRING)}))

        error_response_model = gateway.add_model(
            'ErrorResponseModel',
            content_type='application/json',
            model_name='ErrorResponseModel',
            schema=schema(schema=api_gw.JsonSchemaVersion.DRAFT4,
                          title='errorResponse',
                          type=schema_type.OBJECT,
                          properties={
                              'state': schema(type=schema_type.STRING),
                              'message': schema(type=schema_type.STRING)
                          }))

        request_template = "Action=Publish&" + \
                           "TargetArn=$util.urlEncode('" + topic.topic_arn + "')&" + \
                           "Message=$util.urlEncode($context.path)&" + \
                           "Version=2010-03-31"

        # This is the VTL to transform the error response
        error_template = {
            "state": 'error',
            "message": "$util.escapeJavaScript($input.path('$.errorMessage'))"
        }
        error_template_string = json.dumps(error_template,
                                           separators=(',', ':'))

        # This is how our gateway chooses what response to send based on selection_pattern
        integration_options = api_gw.IntegrationOptions(
            credentials_role=api_gw_sns_role,
            request_parameters={
                'integration.request.header.Content-Type':
                "'application/x-www-form-urlencoded'"
            },
            request_templates={"application/json": request_template},
            passthrough_behavior=api_gw.PassthroughBehavior.NEVER,
            integration_responses=[
                api_gw.IntegrationResponse(
                    status_code='200',
                    response_templates={
                        "application/json":
                        json.dumps({"message": 'message added to topic'})
                    }),
                api_gw.IntegrationResponse(
                    selection_pattern="^\[Error\].*",
                    status_code='400',
                    response_templates={
                        "application/json": error_template_string
                    },
                    response_parameters={
                        'method.response.header.Content-Type':
                        "'application/json'",
                        'method.response.header.Access-Control-Allow-Origin':
                        "'*'",
                        'method.response.header.Access-Control-Allow-Credentials':
                        "'true'"
                    })
            ])

        method_responses = [
            api_gw.MethodResponse(
                status_code='200',
                response_parameters={
                    'method.response.header.Content-Type': True,
                    'method.response.header.Access-Control-Allow-Origin': True,
                    'method.response.header.Access-Control-Allow-Credentials':
                    True
                },
                response_models={'application/json': response_model}),
            api_gw.MethodResponse(
                status_code='400',
                response_parameters={
                    'method.response.header.Content-Type': True,
                    'method.response.header.Access-Control-Allow-Origin': True,
                    'method.response.header.Access-Control-Allow-Credentials':
                    True
                },
                response_models={'application/json': error_response_model}),
        ]

        # Add a / endpoint onto the gateway
        gateway.root \
            .add_method('GET', api_gw.Integration(type=api_gw.IntegrationType.AWS,
                                                  integration_http_method='POST',
                                                  uri='arn:aws:apigateway:us-east-1:sns:path//',
                                                  options=integration_options
                                                  ),
                        method_responses=method_responses
                        )

        # Add a {proxy+} endpoint onto the gateway
        gateway.root.add_resource('{proxy+}') \
            .add_method('GET', api_gw.Integration(type=api_gw.IntegrationType.AWS,
                                                  integration_http_method='POST',
                                                  uri='arn:aws:apigateway:us-east-1:sns:path//',
                                                  options=integration_options
                                                  ),
                        method_responses=method_responses
                        )
Example #12
0
    def __init__(self, scope: cdk.Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        # Create a Lambda function
        # Code in ./src directory
        lambda_fn = lambda_.Function(
            self, "MyFunction",
            runtime=lambda_.Runtime.PYTHON_3_9,
            handler="index.handler",
            code=lambda_.Code.from_asset(os.path.join(DIRNAME, "src"))
        )
        #Input parameter for custom domain name created for API Gateway, ex: apis.example.com
        custom_domain_name = cdk.CfnParameter(self, "customdomainname", type="String",
        description="The custom domain name for AWS REST API Gateway")

        #Input parameter for ACM certificate arn
        certificate_arn = cdk.CfnParameter(self, "certificatearn", type="String",
        description="The ACM certificate ARN for custom domain name")

        #Input parameter for s3 bucket name that has truststore pem file
        truststore_bucket = cdk.CfnParameter(self, "truststorebucket", type="String",
        description="The S3 trustsore uri for custom domain name")

        #Input parameter for public hosted zone id
        public_zone_id = cdk.CfnParameter(self, "publiczoneid", type="String",
        description="The public hosted zone id to create a record for custom domain name")

        #Input parameter for public hosted zone id
        public_zone_name = cdk.CfnParameter(self, "publiczonename", type="String",
        description="The public hosted zone name to create a record for custom domain name")

        # Create the Rest API
        rest_api = apigateway.RestApi(
            self, "TLSRestAPI",
            endpoint_types=[apigateway.EndpointType.REGIONAL],
            description="RestAPI Gateway to verify mutual TLS",
            deploy=False,
            retain_deployments=False,
            disable_execute_api_endpoint=True
        )

        # Create the custom domain
        api_domain = apigateway.DomainName(
            self, "MyDomainName",
            domain_name=custom_domain_name.value_as_string,
            certificate=acm.Certificate.from_certificate_arn(self, "cert", certificate_arn.value_as_string),
            mtls=apigateway.MTLSConfig(
                bucket=s3.Bucket.from_bucket_name(self, "Bucket", truststore_bucket.value_as_string),
                key="/truststore.pem"
            ),
            security_policy=apigateway.SecurityPolicy.TLS_1_2
        )

        # Create API deployment
        deployment = apigateway.Deployment(
            self, "Deployment",
            api=rest_api,
            retain_deployments=False
        )

        # Create prod Stage for deployment
        stage = apigateway.Stage(
            self, "prod",
            deployment=deployment,
        )

        # Create API mapping to custom domain
        base_path_mapping = apigateway.BasePathMapping(self, "MyBasePathMapping",
            domain_name=api_domain,
            rest_api=rest_api,
            stage=stage
        )

        #Get public hosted zone object
        public_zone = route53.HostedZone.from_hosted_zone_attributes(self, "publiczone", hosted_zone_id=public_zone_id.value_as_string,zone_name=public_zone_name.value_as_string)

        #Create A record for custom domain name in public hosted zone
        #Record name will be taken from custom domain name, ex: for apis.example.com, record name will be apis
        record = route53.ARecord(self, 'MyAliasRecord',
            zone=public_zone,
            record_name=cdk.Fn.select(0,cdk.Fn.split('.',custom_domain_name.value_as_string)),
            target=route53.RecordTarget.from_alias(targets.ApiGatewayDomain(api_domain))
        )

        rest_api.deployment_stage = stage

        # Create URI for lambda function
        stage_uri = f"arn:aws:apigateway:{cdk.Aws.REGION}:lambda:path/2015-03-31/functions/{lambda_fn.function_arn}/invocations"

        # Create Lambda Integration
        integration = apigateway.Integration(
            type=apigateway.IntegrationType.AWS_PROXY,
            integration_http_method="POST",
            uri=stage_uri
        )

        # Create APIGW Method
        method = rest_api.root.add_method("GET", integration)

        # Add Lambda permissions
        lambda_fn.add_permission(
            "lambdaPermission",
            action="lambda:InvokeFunction",
            principal=iam.ServicePrincipal("apigateway.amazonaws.com"),
            source_arn=method.method_arn.replace(
                rest_api.deployment_stage.stage_name, "*"
            )
        )

        # OUTPUTS
        cdk.CfnOutput(self, "LambdaFunction", export_name="MyLambdaFunction", value=lambda_fn.function_arn)
        cdk.CfnOutput(self, "ApigwId", export_name="MyAPIGWID", value=rest_api.rest_api_id)
        cdk.CfnOutput(self, "CustomDomainName", export_name="MyCustomDomain", value=api_domain.domain_name)
        cdk.CfnOutput(self, "CustomDomainHostedZone", export_name="MyCustomeZone", value=api_domain.domain_name_alias_hosted_zone_id)
        cdk.CfnOutput(self, "CustomDomainAlias", export_name="MyCustomAlias", value=api_domain.domain_name_alias_domain_name)
Example #13
0
    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        # Create a Lambda function
        # Code in ./src directory
        lambda_fn = lambda_.Function(self,
                                     "MyFunction",
                                     runtime=lambda_.Runtime.PYTHON_3_9,
                                     handler="index.handler",
                                     code=lambda_.Code.from_asset(
                                         os.path.join(DIRNAME, "src")))

        # Function version
        version = lambda_fn.current_version

        # Create function alias
        alias = lambda_.Alias(self,
                              "LambdaAlias",
                              alias_name="Prod",
                              version=version)

        # Create the Rest API
        rest_api = apigateway.RestApi(
            self,
            "RestApi",
            endpoint_types=[apigateway.EndpointType.REGIONAL],
            deploy=False,
            retain_deployments=False)

        # Create initial deployment
        deployment = apigateway.Deployment(self,
                                           "Deployment",
                                           api=rest_api,
                                           retain_deployments=False)

        # Create prod Stage
        stage = apigateway.Stage(self,
                                 "prod",
                                 deployment=deployment,
                                 variables={"lambdaAlias": "Prod"})

        rest_api.deployment_stage = stage

        # Create URI for lambda alias
        stage_uri = f"arn:aws:apigateway:{Aws.REGION}:lambda:path/2015-03-31/functions/{lambda_fn.function_arn}:${{stageVariables.lambdaAlias}}/invocations"

        # Create Lambda Integration
        integration = apigateway.Integration(
            type=apigateway.IntegrationType.AWS_PROXY,
            integration_http_method="POST",
            uri=stage_uri)

        # Create APIGW Method
        method = rest_api.root.add_method("GET", integration)

        # Add Lambda permissions
        lambda_fn.add_permission(
            "lambdaPermission",
            action="lambda:InvokeFunction",
            principal=iam.ServicePrincipal("apigateway.amazonaws.com"),
            source_arn=method.method_arn.replace(
                rest_api.deployment_stage.stage_name, "*"))

        # Add permissions for Prod alias
        alias.add_permission(
            "aliasPermission",
            action="lambda:InvokeFunction",
            principal=iam.ServicePrincipal("apigateway.amazonaws.com"),
            source_arn=method.method_arn.replace(
                rest_api.deployment_stage.stage_name, "*"))

        # OUTPUTS
        CfnOutput(self,
                  "LambdaFunction",
                  export_name="MyLambdaFunction",
                  value=lambda_fn.function_arn)
        CfnOutput(self,
                  "ApigwId",
                  export_name="MyAPIGWID",
                  value=rest_api.rest_api_id)
        CfnOutput(self,
                  "MethodArn",
                  export_name="MyMethodArn",
                  value=method.method_arn)
        CfnOutput(self,
                  "StageName",
                  export_name="MyStageName",
                  value=rest_api.deployment_stage.stage_name)