Example #1
0
    def __init__(self, scope: core.Construct, id: str,
                 handler: lambda_.Function, context: InfraContext,
                 **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        self.rest_api = a.LambdaRestApi(self,
                                        id,
                                        options=a.RestApiProps(),
                                        handler=handler,
                                        proxy=True,
                                        description='Frontend proxy for ' +
                                        handler.function_name)
    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        lambda_repository = aws_codecommit.Repository(
            self,
            "QuestionsLambdaRepository",
            repository_name="MythicalMysfits-QuestionsLambdaRepository",
        )

        core.CfnOutput(
            self,
            "questionsRepositoryCloneUrlHTTP",
            value=lambda_repository.repository_clone_url_http,
            description="Questions Lambda Repository Clone URL HTTP",
        )
        core.CfnOutput(
            self,
            "questionsRepositoryCloneUrlSSH",
            value=lambda_repository.repository_clone_url_ssh,
            description="Questions Lambda Repository Clone URL SSH",
        )

        table = aws_dynamodb.Table(
            self,
            "Table",
            table_name="MysfitsQuestionsTable",
            partition_key=aws_dynamodb.Attribute(
                name="QuestionId", type=aws_dynamodb.AttributeType.STRING),
            stream=aws_dynamodb.StreamViewType.NEW_IMAGE,
        )

        lambda_function_policy_statement_ddb = aws_iam.PolicyStatement()
        lambda_function_policy_statement_ddb.add_actions("dynamodb:PutItem")
        lambda_function_policy_statement_ddb.add_resources(table.table_arn)

        lambda_function_policy_statement_xray = aws_iam.PolicyStatement()
        lambda_function_policy_statement_xray.add_actions(
            "xray:PutTraceSegments",
            "xray:PutTelemetryRecords",
            "xray:GetSamplingRules",
            "xray:GetSamplingTargets",
            "xray:GetSamplingStatisticSummaries",
        )
        lambda_function_policy_statement_xray.add_all_resources()

        mysfits_post_question = aws_lambda.Function(
            self,
            "PostQuestionFunction",
            handler="mysfitsPostQuestion.postQuestion",
            runtime=aws_lambda.Runtime.PYTHON_3_6,
            description=
            "A microservice Lambda function that receives a new question submitted to the MythicalMysfits website from a user and inserts it into a DynamoDB database table.",
            memory_size=128,
            code=aws_lambda.Code.asset(
                os.path.join("..", "..", "lambda-questions",
                             "PostQuestionsService")),
            timeout=core.Duration.seconds(30),
            initial_policy=[
                lambda_function_policy_statement_ddb,
                lambda_function_policy_statement_xray,
            ],
            tracing=aws_lambda.Tracing.ACTIVE,
        )

        topic = aws_sns.Topic(
            self,
            "Topic",
            display_name="MythicalMysfitsQuestionsTopic",
            topic_name="MythicalMysfitsQuestionsTopic",
        )
        topic.add_subscription(subs.EmailSubscription(os.environ["SNS_EMAIL"]))

        post_question_lamdaa_function_policy_statement_sns = aws_iam.PolicyStatement(
        )
        post_question_lamdaa_function_policy_statement_sns.add_actions(
            "sns:Publish")
        post_question_lamdaa_function_policy_statement_sns.add_resources(
            topic.topic_arn)

        mysfits_process_question_stream = aws_lambda.Function(
            self,
            "ProcessQuestionStreamFunction",
            handler="mysfitsProcessStream.processStream",
            runtime=aws_lambda.Runtime.PYTHON_3_6,
            description=
            "An AWS Lambda function that will process all new questions posted to mythical mysfits and notify the site administrator of the question that was asked.",
            memory_size=128,
            code=aws_lambda.Code.asset(
                os.path.join("..", "..", "lambda-questions",
                             "ProcessQuestionsStream")),
            timeout=core.Duration.seconds(30),
            initial_policy=[
                post_question_lamdaa_function_policy_statement_sns,
                lambda_function_policy_statement_xray,
            ],
            tracing=aws_lambda.Tracing.ACTIVE,
            environment={"SNS_TOPIC_ARN": topic.topic_arn},
            events=[
                event.DynamoEventSource(
                    table,
                    starting_position=aws_lambda.StartingPosition.TRIM_HORIZON,
                    batch_size=1,
                )
            ],
        )

        questions_api_role = aws_iam.Role(
            self,
            "QuestionsApiRole",
            assumed_by=aws_iam.ServicePrincipal("apigateway.amazonaws.com"),
        )

        api_policy = aws_iam.PolicyStatement()
        api_policy.add_actions("lambda:InvokeFunction")
        api_policy.add_resources(mysfits_post_question.function_arn)
        aws_iam.Policy(
            self,
            "QuestionsApiPolicy",
            policy_name="questions_api_policy",
            statements=[api_policy],
            roles=[questions_api_role],
        )

        questions_integration = aws_apigateway.LambdaIntegration(
            mysfits_post_question,
            credentials_role=questions_api_role,
            integration_responses=[
                aws_apigateway.IntegrationResponse(
                    status_code="200",
                    response_templates={
                        "application/json": '{"status": "OK"}'
                    },
                )
            ],
        )

        api = aws_apigateway.LambdaRestApi(
            self,
            "APIEndpoint",
            handler=mysfits_post_question,
            options=aws_apigateway.RestApiProps(
                rest_api_name="Questions API Server"),
            proxy=False,
        )

        questions_method = api.root.add_resource("questions")
        questions_method.add_method(
            "POST",
            questions_integration,
            method_responses=[
                aws_apigateway.MethodResponse(status_code="200")
            ],
            authorization_type=aws_apigateway.AuthorizationType.NONE,
        )

        questions_method.add_method(
            "OPTIONS",
            aws_apigateway.MockIntegration(
                integration_responses=[
                    aws_apigateway.IntegrationResponse(
                        status_code="200",
                        response_parameters={
                            "method.response.header.Access-Control-Allow-Headers":
                            "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Amz-User-Agent'",
                            "method.response.header.Access-Control-Allow-Origin":
                            "'*'",
                            "method.response.header.Access-Control-Allow-Credentials":
                            "'false'",
                            "method.response.header.Access-Control-Allow-Methods":
                            "'OPTIONS,GET,PUT,POST,DELETE'",
                        },
                    )
                ],
                passthrough_behavior=aws_apigateway.PassthroughBehavior.NEVER,
                request_templates={"application/json": '{"statusCode": 200}'},
            ),
            method_responses=[
                aws_apigateway.MethodResponse(
                    status_code="200",
                    response_parameters={
                        "method.response.header.Access-Control-Allow-Headers":
                        True,
                        "method.response.header.Access-Control-Allow-Methods":
                        True,
                        "method.response.header.Access-Control-Allow-Credentials":
                        True,
                        "method.response.header.Access-Control-Allow-Origin":
                        True,
                    },
                )
            ],
        )
Example #3
0
    def __init__(self,
                 scope: core.Construct,
                 id: str,
                 resources: FsiSharedResources,
                 subnet_group_name: str = 'Default',
                 **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        # Configure the container resources...
        self.repo = assets.DockerImageAsset(self,
                                            'Repo',
                                            directory='src/fsi/earnings',
                                            file='Dockerfile')

        code = lambda_.DockerImageCode.from_ecr(
            repository=self.repo.repository,
            tag=self.repo.image_uri.split(':')[-1])

        # Configure security policies...
        role = iam.Role(
            self,
            'Role',
            assumed_by=iam.ServicePrincipal(service='lambda'),
            description='HomeNet-{}-Fsi-EarningsReport'.format(
                resources.landing_zone.zone_name),
            role_name='fsi-earnings@homenet.{}.{}'.format(
                resources.landing_zone.zone_name,
                core.Stack.of(self).region).lower(),
            managed_policies=[
                iam.ManagedPolicy.from_aws_managed_policy_name(
                    managed_policy_name=
                    'service-role/AWSLambdaVPCAccessExecutionRole'),
            ])

        # Grant any permissions...
        self.earnings_table = d.Table(
            self,
            'EarningCalendar',
            table_name='FsiCoreSvc-EarningsCalendar',
            billing_mode=d.BillingMode.PAY_PER_REQUEST,
            partition_key=d.Attribute(name='PartitionKey',
                                      type=d.AttributeType.STRING),
            sort_key=d.Attribute(name='SortKey', type=d.AttributeType.STRING),
            time_to_live_attribute='Expiration',
            point_in_time_recovery=True,
            server_side_encryption=True)
        self.earnings_table.grant_read_write_data(role)

        # Define any variables for the function
        self.function_env = {
            'CACHE_TABLE': self.earnings_table.table_name,
        }

        # Create the backing webapi compute ...
        self.function = lambda_.DockerImageFunction(
            self,
            'Function',
            code=code,
            role=role,
            function_name='HomeNet-{}-Fsi-{}'.format(
                resources.landing_zone.zone_name, FsiEarningsGateway.__name__),
            description='Python Lambda function for ' +
            FsiEarningsGateway.__name__,
            timeout=core.Duration.seconds(30),
            tracing=lambda_.Tracing.ACTIVE,
            vpc=resources.landing_zone.vpc,
            log_retention=logs.RetentionDays.FIVE_DAYS,
            memory_size=128,
            allow_all_outbound=True,
            vpc_subnets=ec2.SubnetSelection(
                subnet_group_name=subnet_group_name),
            security_groups=[resources.landing_zone.security_group],
            environment=self.function_env,
        )

        # Bind APIG to Lambda compute...
        self.frontend_proxy = a.LambdaRestApi(
            self,
            'ApiGateway',
            proxy=True,
            handler=self.function,
            options=a.RestApiProps(
                description='Hosts the Earnings Calendar Services via ' +
                self.function.function_name,
                domain_name=a.DomainNameOptions(
                    domain_name='earnings.trader.fsi',
                    certificate=Certificate.from_certificate_arn(
                        self,
                        'Certificate',
                        certificate_arn=
                        'arn:aws:acm:us-east-2:581361757134:certificate/4e3235f7-49a1-42a5-a671-f2449b45f72d'
                    ),
                    security_policy=a.SecurityPolicy.TLS_1_0),
                policy=iam.PolicyDocument(statements=[
                    iam.PolicyStatement(effect=iam.Effect.ALLOW,
                                        actions=['execute-api:Invoke'],
                                        principals=[iam.AnyPrincipal()],
                                        resources=['*'],
                                        conditions={
                                            'IpAddress': {
                                                'aws:SourceIp': [
                                                    '10.0.0.0/8',
                                                    '192.168.0.0/16',
                                                    '72.90.160.65/32'
                                                ]
                                            }
                                        })
                ]),
                endpoint_configuration=a.EndpointConfiguration(
                    types=[a.EndpointType.REGIONAL], )))

        # Register Dns Name
        r53.ARecord(self,
                    'AliasRecord',
                    zone=resources.trader_dns_zone,
                    record_name='earnings.%s' %
                    resources.trader_dns_zone.zone_name,
                    target=r53.RecordTarget.from_alias(
                        dns_targets.ApiGateway(self.frontend_proxy)))
Example #4
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,
            ))
Example #5
0
    def __init__(self,
                 scope: core.Construct,
                 id: str,
                 infra: RtspBaseResourcesConstruct,
                 subnet_group_name: str = 'Default',
                 **kwargs) -> None:
        super().__init__(scope, id, **kwargs)
        core.Tags.of(self).add(key='Source', value=PhotosApiConstruct.__name__)

        # Configure the container resources...
        self.repo = assets.DockerImageAsset(self,
                                            'Repo',
                                            directory='src/rtsp/photo-api',
                                            file='Dockerfile')

        code = lambda_.DockerImageCode.from_ecr(
            repository=self.repo.repository,
            tag=self.repo.image_uri.split(':')[-1])

        # Configure security policies...
        role = iam.Role(
            self,
            'Role',
            assumed_by=iam.ServicePrincipal(service='lambda'),
            description='HomeNet-{}-PhotoApi'.format(
                infra.landing_zone.zone_name),
            role_name='rtsp-photoapi@homenet.{}.{}'.format(
                infra.landing_zone.zone_name,
                core.Stack.of(self).region),
            managed_policies=[
                iam.ManagedPolicy.from_aws_managed_policy_name(
                    managed_policy_name=
                    'service-role/AWSLambdaVPCAccessExecutionRole'),
                iam.ManagedPolicy.from_aws_managed_policy_name(
                    managed_policy_name='AmazonS3ReadOnlyAccess')
            ])

        infra.bucket.grant_read(role)
        infra.face_table.grant_read_write_data(role)

        # Define any variables for the function
        self.function_env = {
            'FACE_TABLE': infra.face_table.table_name,
            'REGION': core.Stack.of(self).region,
        }

        # Create the backing webapi compute ...
        self.function = lambda_.DockerImageFunction(
            self,
            'Function',
            code=code,
            role=role,
            function_name='HomeNet-PhotoApi',
            description='Python Lambda function for ' +
            PhotosApiConstruct.__name__,
            timeout=core.Duration.seconds(30),
            tracing=lambda_.Tracing.ACTIVE,
            vpc=infra.landing_zone.vpc,
            log_retention=RetentionDays.FIVE_DAYS,
            memory_size=128,
            allow_all_outbound=True,
            vpc_subnets=ec2.SubnetSelection(
                subnet_group_name=subnet_group_name),
            security_groups=[infra.security_group],
            environment=self.function_env,
        )

        # Bind APIG to Lambda compute...
        # Calls need to use https://photos-api.virtual.world
        self.frontend_proxy = a.LambdaRestApi(
            self,
            'ApiGateway',
            proxy=True,
            handler=self.function,
            options=a.RestApiProps(
                description='Photo-Api proxy for ' +
                self.function.function_name,
                binary_media_types=['image/png', 'image/jpg', 'image/bmp'],
                domain_name=a.DomainNameOptions(
                    domain_name='photos-api.virtual.world',
                    certificate=Certificate.from_certificate_arn(
                        self,
                        'Certificate',
                        certificate_arn=
                        'arn:aws:acm:us-east-1:581361757134:certificate/c91263e7-882e-441d-aa2f-717074aed6d0'
                    ),
                    security_policy=a.SecurityPolicy.TLS_1_0),
                policy=iam.PolicyDocument(statements=[
                    iam.PolicyStatement(effect=iam.Effect.ALLOW,
                                        actions=['execute-api:Invoke'],
                                        principals=[iam.AnyPrincipal()],
                                        resources=['*'],
                                        conditions={
                                            'IpAddress': {
                                                'aws:SourceIp': [
                                                    '10.0.0.0/8',
                                                    '192.168.0.0/16',
                                                    '72.90.160.65/32'
                                                ]
                                            }
                                        })
                ]),
                endpoint_configuration=a.EndpointConfiguration(
                    types=[a.EndpointType.REGIONAL],
                    #vpc_endpoints=[
                    #  infra.landing_zone.vpc_endpoints.interfaces['execute-api']
                    #]
                )))
    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'
                }))
Example #7
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)))
  def __init__(self, scope: core.Construct, id: str, resources:FsiSharedResources, subnet_group_name:str='Default', **kwargs) -> None:
    super().__init__(scope, id, **kwargs)
    
    # Configure the container resources...
    self.repo = assets.DockerImageAsset(self,'Repo',
      directory='src/fsi/account-linking',
      file='Dockerfile')

    code = lambda_.DockerImageCode.from_ecr(
        repository=self.repo.repository,
        tag=self.repo.image_uri.split(':')[-1])

    # Configure security policies...
    role = iam.Role(self,'Role',
      assumed_by=iam.ServicePrincipal(service='lambda'),
      description='HomeNet-{}-Fsi-AccountLinking'.format(resources.landing_zone.zone_name),
      role_name='fsi-accountlinking@homenet.{}.{}'.format(
        resources.landing_zone.zone_name,
        core.Stack.of(self).region),
      managed_policies=[
        iam.ManagedPolicy.from_aws_managed_policy_name(
          managed_policy_name='service-role/AWSLambdaVPCAccessExecutionRole'),        
      ])

    # Grant any permissions...
    resources.tda_secret.grant_write(role)

    # Define any variables for the function
    self.function_env = {
      'REGION': core.Stack.of(self).region,
      'TDA_SECRET_ID': resources.tda_secret.secret_arn,
      'TDA_REDIRECT_URI':  ssm.StringParameter.from_string_parameter_name(self,'TDA_REDIRECT_URI',
        string_parameter_name='/HomeNet/Amertitrade/redirect_uri').string_value,
      'TDA_CLIENT_ID': ssm.StringParameter.from_string_parameter_name(self, 'TDA_CLIENT_ID',
        string_parameter_name='/HomeNet/Ameritrade/client_id').string_value
    }

    # Create the backing webapi compute ...
    self.function = lambda_.DockerImageFunction(self,'Function',
      code = code,
      role= role,
      function_name='HomeNet-{}-Fsi-{}'.format(
        resources.landing_zone.zone_name,
        FsiAmeritradeAuthGateway.__name__),
      description='Python Lambda function for '+FsiAmeritradeAuthGateway.__name__,
      timeout= core.Duration.seconds(30),
      tracing= lambda_.Tracing.ACTIVE,
      vpc= resources.landing_zone.vpc,
      log_retention= logs.RetentionDays.FIVE_DAYS,
      memory_size=128,
      allow_all_outbound=True,
      vpc_subnets=ec2.SubnetSelection(subnet_group_name=subnet_group_name),
      security_groups=[resources.landing_zone.security_group],
      environment=self.function_env,
    )

    # Bind APIG to Lambda compute...
    self.frontend_proxy =  a.LambdaRestApi(self,'ApiGateway',
      proxy=True,
      handler=self.function,
      options=a.RestApiProps(
        description='Hosts the Ameritrade Auth Callback  via '+self.function.function_name,
        domain_name= a.DomainNameOptions(
          domain_name='auth.trader.fsi',
          certificate=Certificate.from_certificate_arn(self,'Certificate',
           certificate_arn= 'arn:aws:acm:us-east-2:581361757134:certificate/0d1fc756-ebd6-4660-83a8-814c0976a8c2'),
          security_policy= a.SecurityPolicy.TLS_1_0),
        policy= iam.PolicyDocument(
          statements=[
            iam.PolicyStatement(
              effect= iam.Effect.ALLOW,
              actions=['execute-api:Invoke'],
              principals=[iam.AnyPrincipal()],
              resources=['*'],
              conditions={
                'IpAddress':{
                  'aws:SourceIp': ['10.0.0.0/8','192.168.0.0/16','72.90.160.65/32']
                }
              }
            )
          ]
        ),
        endpoint_configuration= a.EndpointConfiguration(
          types = [ a.EndpointType.REGIONAL],
        )
      ))

    # Register Dns Name
    r53.ARecord(self,'AliasRecord',
      zone=resources.trader_dns_zone,
      record_name='auth.%s' % resources.trader_dns_zone.zone_name,
      target= r53.RecordTarget.from_alias(dns_targets.ApiGateway(self.frontend_proxy)))