Esempio n. 1
0
    def __init__(self, scope: core.Construct, construct_id: str,
                 **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        self.user_pool = cognito.UserPool(
            self,
            'UserPool',
            auto_verify=cognito.AutoVerifiedAttrs(email=True),
            self_sign_up_enabled=True,
            user_pool_name='MysfitsUserPool')

        user_pool_client = cognito.UserPoolClient(
            self,
            'UserPoolClient',
            user_pool=self.user_pool,
            user_pool_client_name='MysfitsUserPoolClient')

        core.CfnOutput(self,
                       'CognitoUserPool',
                       description='The Cognito User Pool',
                       value=self.user_pool.user_pool_id)
        # self.user_pool_id = core.CfnOutput(
        #     self, "CognitoUserPool",
        #     description='The Cognito User Pool',
        #     value=user_pool.user_pool_id,
        #     export_name="user-pool-id"
        # ).import_value

        core.CfnOutput(self,
                       'CognitoUserPoolClient',
                       description='The Cognito User Pool Client',
                       value=user_pool_client.user_pool_client_id)
Esempio n. 2
0
    def _create_userpool(self):
        user_pool = cognito.UserPool(
            self,
            "movio",
            account_recovery=cognito.AccountRecovery.EMAIL_ONLY,
            auto_verify=cognito.AutoVerifiedAttrs(email=True, phone=False),
            mfa=cognito.Mfa.OFF,
            mfa_second_factor=cognito.MfaSecondFactor(otp=True, sms=False),
            self_sign_up_enabled=False,
            sign_in_aliases=cognito.SignInAliases(email=True, username=True),
            standard_attributes=cognito.StandardAttributes(
                email=cognito.StandardAttribute(mutable=False,
                                                required=True), ),
            user_invitation=cognito.UserInvitationConfig(
                email_subject="Moshan email verification",
                email_body=
                "Thanks for signing up to moshan! Your username is {username} and temporary password is {####}\nYou can now login at https://moshan.tv",
            ),
            user_verification=cognito.UserVerificationConfig(
                email_subject="Moshan email verification",
                email_body=
                "Thanks for signing up to moshan! Verify your account by clicking on {##Verify Email##}",
                email_style=cognito.VerificationEmailStyle.LINK),
        )

        user_pool.add_client(
            "moshan",
            auth_flows=cognito.AuthFlow(refresh_token=True),
            o_auth=cognito.OAuthSettings(
                flows=cognito.OAuthFlows(authorization_code_grant=True),
                callback_urls=[
                    "https://moshan.tv/callback.html",
                    "https://beta.moshan.tv/callback.html"
                ],
                scopes=[
                    cognito.OAuthScope.EMAIL, cognito.OAuthScope.OPENID,
                    cognito.OAuthScope.PROFILE
                ]),
            prevent_user_existence_errors=True,
        )

        cert = Certificate.from_certificate_arn(self, "domainCert",
                                                self.cert_arn)
        user_pool.add_domain("CognitoDomain",
                             custom_domain=cognito.CustomDomainOptions(
                                 domain_name=self.domain_name,
                                 certificate=cert))
Esempio n. 3
0
 def _create_user_pool(self) -> cognito.UserPool:
     pool = cognito.UserPool(
         scope=self,
         id="orbit-user-pool",
         account_recovery=cognito.AccountRecovery.EMAIL_ONLY,
         auto_verify=cognito.AutoVerifiedAttrs(email=True, phone=False),
         custom_attributes=None,
         email_settings=None,
         lambda_triggers=None,
         mfa=cognito.Mfa.OFF,
         mfa_second_factor=None,
         password_policy=cognito.PasswordPolicy(
             min_length=8,
             require_digits=True,
             require_lowercase=True,
             require_symbols=True,
             require_uppercase=True,
             temp_password_validity=Duration.days(5),
         ),
         self_sign_up_enabled=False,
         sign_in_aliases=cognito.SignInAliases(email=True,
                                               phone=False,
                                               preferred_username=False,
                                               username=True),
         sign_in_case_sensitive=True,
         sms_role=None,
         sms_role_external_id=None,
         standard_attributes=cognito.StandardAttributes(
             email=cognito.StandardAttribute(required=True, mutable=True)),
         user_invitation=cognito.UserInvitationConfig(
             email_subject="Invite to join Orbit Workbench!",
             email_body=
             "Hello, you have been invited to join Orbit Workbench!<br/><br/>"
             "Username: {username}<br/>"
             "Temporary password: {####}<br/><br/>"
             "Regards",
         ),
         user_pool_name=f"orbit-{self.env_name}-user-pool",
     )
     pool.apply_removal_policy(policy=core.RemovalPolicy.DESTROY)
     pool.add_domain(
         id="orbit-user-pool-domain",
         cognito_domain=cognito.CognitoDomainOptions(
             domain_prefix=f"orbit-{self.context.account_id}-{self.env_name}"
         ),
     )
     return pool
Esempio n. 4
0
    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        with open("stack/config.yml", 'r') as stream:
            configs = yaml.safe_load(stream)

        ### S3 core
        images_S3_bucket = _s3.Bucket(self, "ICS_IMAGES")

        images_S3_bucket.add_cors_rule(
            allowed_methods=[_s3.HttpMethods.POST],
            allowed_origins=["*"] # add API gateway web resource URL
        )

        ### SQS core
        image_deadletter_queue = _sqs.Queue(self, "ICS_IMAGES_DEADLETTER_QUEUE")
        image_queue = _sqs.Queue(self, "ICS_IMAGES_QUEUE",
            dead_letter_queue={
                "max_receive_count": configs["DeadLetterQueue"]["MaxReceiveCount"],
                "queue": image_deadletter_queue
            })

        ### api gateway core
        api_gateway = RestApi(self, 'ICS_API_GATEWAY', rest_api_name='ImageContentSearchApiGateway')
        api_gateway_resource = api_gateway.root.add_resource(configs["ProjectName"])
        api_gateway_landing_page_resource = api_gateway_resource.add_resource('web')
        api_gateway_get_signedurl_resource = api_gateway_resource.add_resource('signedUrl')
        api_gateway_image_search_resource = api_gateway_resource.add_resource('search')

        ### landing page function
        get_landing_page_function = Function(self, "ICS_GET_LANDING_PAGE",
            function_name="ICS_GET_LANDING_PAGE",
            runtime=Runtime.PYTHON_3_7,
            handler="main.handler",
            code=Code.asset("./src/landingPage"))

        get_landing_page_integration = LambdaIntegration(
            get_landing_page_function, 
            proxy=True, 
            integration_responses=[{
                'statusCode': '200',
               'responseParameters': {
                   'method.response.header.Access-Control-Allow-Origin': "'*'",
                }
            }])

        api_gateway_landing_page_resource.add_method('GET', get_landing_page_integration,
            method_responses=[{
                'statusCode': '200',
                'responseParameters': {
                    'method.response.header.Access-Control-Allow-Origin': True,
                }
            }])

        ### cognito
        required_attribute = _cognito.StandardAttribute(required=True)

        users_pool = _cognito.UserPool(self, "ICS_USERS_POOL",
            auto_verify=_cognito.AutoVerifiedAttrs(email=True), #required for self sign-up
            standard_attributes=_cognito.StandardAttributes(email=required_attribute), #required for self sign-up
            self_sign_up_enabled=configs["Cognito"]["SelfSignUp"])

        user_pool_app_client = _cognito.CfnUserPoolClient(self, "ICS_USERS_POOL_APP_CLIENT", 
            supported_identity_providers=["COGNITO"],
            allowed_o_auth_flows=["implicit"],
            allowed_o_auth_scopes=configs["Cognito"]["AllowedOAuthScopes"],
            user_pool_id=users_pool.user_pool_id,
            callback_ur_ls=[api_gateway_landing_page_resource.url],
            allowed_o_auth_flows_user_pool_client=True,
            explicit_auth_flows=["ALLOW_REFRESH_TOKEN_AUTH"])

        user_pool_domain = _cognito.UserPoolDomain(self, "ICS_USERS_POOL_DOMAIN", 
            user_pool=users_pool, 
            cognito_domain=_cognito.CognitoDomainOptions(domain_prefix=configs["Cognito"]["DomainPrefix"]))

        ### get signed URL function
        get_signedurl_function = Function(self, "ICS_GET_SIGNED_URL",
            function_name="ICS_GET_SIGNED_URL",
            environment={
                "ICS_IMAGES_BUCKET": images_S3_bucket.bucket_name,
                "DEFAULT_SIGNEDURL_EXPIRY_SECONDS": configs["Functions"]["DefaultSignedUrlExpirySeconds"]
            },
            runtime=Runtime.PYTHON_3_7,
            handler="main.handler",
            code=Code.asset("./src/getSignedUrl"))

        get_signedurl_integration = LambdaIntegration(
            get_signedurl_function, 
            proxy=True, 
            integration_responses=[{
                'statusCode': '200',
               'responseParameters': {
                   'method.response.header.Access-Control-Allow-Origin': "'*'",
                }
            }])

        api_gateway_get_signedurl_authorizer = CfnAuthorizer(self, "ICS_API_GATEWAY_GET_SIGNED_URL_AUTHORIZER",
            rest_api_id=api_gateway_get_signedurl_resource.rest_api.rest_api_id,
            name="ICS_API_GATEWAY_GET_SIGNED_URL_AUTHORIZER",
            type="COGNITO_USER_POOLS",
            identity_source="method.request.header.Authorization",
            provider_arns=[users_pool.user_pool_arn])

        api_gateway_get_signedurl_resource.add_method('GET', get_signedurl_integration,
            authorization_type=AuthorizationType.COGNITO,
            method_responses=[{
                'statusCode': '200',
                'responseParameters': {
                    'method.response.header.Access-Control-Allow-Origin': True,
                }
            }]
            ).node.find_child('Resource').add_property_override('AuthorizerId', api_gateway_get_signedurl_authorizer.ref)

        images_S3_bucket.grant_put(get_signedurl_function, objects_key_pattern="new/*")

        ### image massage function
        image_massage_function = Function(self, "ICS_IMAGE_MASSAGE",
            function_name="ICS_IMAGE_MASSAGE",
            timeout=core.Duration.seconds(6),
            runtime=Runtime.PYTHON_3_7,
            environment={"ICS_IMAGE_MASSAGE": image_queue.queue_name},
            handler="main.handler",
            code=Code.asset("./src/imageMassage"))

        images_S3_bucket.grant_write(image_massage_function, "processed/*")
        images_S3_bucket.grant_delete(image_massage_function, "new/*")
        images_S3_bucket.grant_read(image_massage_function, "new/*")
        
        new_image_added_notification = _s3notification.LambdaDestination(image_massage_function)

        images_S3_bucket.add_event_notification(_s3.EventType.OBJECT_CREATED, 
            new_image_added_notification, 
            _s3.NotificationKeyFilter(prefix="new/")
            )

        image_queue.grant_send_messages(image_massage_function)

        ### image analyzer function
        image_analyzer_function = Function(self, "ICS_IMAGE_ANALYSIS",
            function_name="ICS_IMAGE_ANALYSIS",
            runtime=Runtime.PYTHON_3_7,
            timeout=core.Duration.seconds(10),
            environment={
                "ICS_IMAGES_BUCKET": images_S3_bucket.bucket_name,
                "DEFAULT_MAX_CALL_ATTEMPTS": configs["Functions"]["DefaultMaxApiCallAttempts"],
                "REGION": core.Aws.REGION,
                },
            handler="main.handler",
            code=Code.asset("./src/imageAnalysis")) 

        image_analyzer_function.add_event_source(_lambda_event_source.SqsEventSource(queue=image_queue, batch_size=10))
        image_queue.grant_consume_messages(image_massage_function)

        lambda_rekognition_access = _iam.PolicyStatement(
            effect=_iam.Effect.ALLOW, 
            actions=["rekognition:DetectLabels", "rekognition:DetectModerationLabels"],
            resources=["*"]                    
        )

        image_analyzer_function.add_to_role_policy(lambda_rekognition_access)
        images_S3_bucket.grant_read(image_analyzer_function, "processed/*")

        ### API gateway finalizing
        self.add_cors_options(api_gateway_get_signedurl_resource)
        self.add_cors_options(api_gateway_landing_page_resource)
        self.add_cors_options(api_gateway_image_search_resource)

        ### database 
        database_secret = _secrets_manager.Secret(self, "ICS_DATABASE_SECRET",
            secret_name="rds-db-credentials/image-content-search-rds-secret",
            generate_secret_string=_secrets_manager.SecretStringGenerator(
                generate_string_key='password',
                secret_string_template='{"username": "******"}',
                exclude_punctuation=True,
                exclude_characters='/@\" \\\'',
                require_each_included_type=True
            )
        )

        database = _rds.CfnDBCluster(self, "ICS_DATABASE",
            engine=_rds.DatabaseClusterEngine.aurora_mysql(version=_rds.AuroraMysqlEngineVersion.VER_5_7_12).engine_type,
            engine_mode="serverless",
            database_name=configs["Database"]["Name"],
            enable_http_endpoint=True,
            deletion_protection=configs["Database"]["DeletionProtection"],
            master_username=database_secret.secret_value_from_json("username").to_string(),
            master_user_password=database_secret.secret_value_from_json("password").to_string(),
            scaling_configuration=_rds.CfnDBCluster.ScalingConfigurationProperty(
                auto_pause=configs["Database"]["Scaling"]["AutoPause"],
                min_capacity=configs["Database"]["Scaling"]["Min"],
                max_capacity=configs["Database"]["Scaling"]["Max"],
                seconds_until_auto_pause=configs["Database"]["Scaling"]["SecondsToAutoPause"]
            ),
        )

        database_cluster_arn = "arn:aws:rds:{}:{}:cluster:{}".format(core.Aws.REGION, core.Aws.ACCOUNT_ID, database.ref)
   
        secret_target = _secrets_manager.CfnSecretTargetAttachment(self,"ICS_DATABASE_SECRET_TARGET",
            target_type="AWS::RDS::DBCluster",
            target_id=database.ref,
            secret_id=database_secret.secret_arn
        )

        secret_target.node.add_dependency(database)

        ### database function
        image_data_function_role = _iam.Role(self, "ICS_IMAGE_DATA_FUNCTION_ROLE",
            role_name="ICS_IMAGE_DATA_FUNCTION_ROLE",
            assumed_by=_iam.ServicePrincipal("lambda.amazonaws.com"),
            managed_policies=[
                _iam.ManagedPolicy.from_aws_managed_policy_name("service-role/AWSLambdaVPCAccessExecutionRole"),
                _iam.ManagedPolicy.from_aws_managed_policy_name("service-role/AWSLambdaBasicExecutionRole"),
                _iam.ManagedPolicy.from_aws_managed_policy_name("AmazonRDSDataFullAccess")
            ]
        )
        
        image_data_function = Function(self, "ICS_IMAGE_DATA",
            function_name="ICS_IMAGE_DATA",
            runtime=Runtime.PYTHON_3_7,
            timeout=core.Duration.seconds(5),
            role=image_data_function_role,
            environment={
                "DEFAULT_MAX_CALL_ATTEMPTS": configs["Functions"]["DefaultMaxApiCallAttempts"],
                "CLUSTER_ARN": database_cluster_arn,
                "CREDENTIALS_ARN": database_secret.secret_arn,
                "DB_NAME": database.database_name,
                "REGION": core.Aws.REGION
                },
            handler="main.handler",
            code=Code.asset("./src/imageData")
        ) 

        image_search_integration = LambdaIntegration(
            image_data_function, 
            proxy=True, 
            integration_responses=[{
                'statusCode': '200',
               'responseParameters': {
                   'method.response.header.Access-Control-Allow-Origin': "'*'",
                }
            }])

        api_gateway_image_search_authorizer = CfnAuthorizer(self, "ICS_API_GATEWAY_IMAGE_SEARCH_AUTHORIZER",
            rest_api_id=api_gateway_image_search_resource.rest_api.rest_api_id,
            name="ICS_API_GATEWAY_IMAGE_SEARCH_AUTHORIZER",
            type="COGNITO_USER_POOLS", 
            identity_source="method.request.header.Authorization",
            provider_arns=[users_pool.user_pool_arn])

        api_gateway_image_search_resource.add_method('POST', image_search_integration,
            authorization_type=AuthorizationType.COGNITO,
            method_responses=[{
                'statusCode': '200',
                'responseParameters': {
                    'method.response.header.Access-Control-Allow-Origin': True,
                }
            }]
            ).node.find_child('Resource').add_property_override('AuthorizerId', api_gateway_image_search_authorizer.ref)


        lambda_access_search = _iam.PolicyStatement(
            effect=_iam.Effect.ALLOW, 
            actions=["translate:TranslateText"],
            resources=["*"]            
        ) 

        image_data_function.add_to_role_policy(lambda_access_search)

        ### custom resource
        lambda_provider = Provider(self, 'ICS_IMAGE_DATA_PROVIDER', 
            on_event_handler=image_data_function
        )

        core.CustomResource(self, 'ICS_IMAGE_DATA_RESOURCE', 
            service_token=lambda_provider.service_token,
            pascal_case_properties=False,
            resource_type="Custom::SchemaCreation",
            properties={
                "source": "Cloudformation"
            }
        )

        ### event bridge
        event_bus = _events.EventBus(self, "ICS_IMAGE_CONTENT_BUS")

        event_rule = _events.Rule(self, "ICS_IMAGE_CONTENT_RULE",
            rule_name="ICS_IMAGE_CONTENT_RULE",
            description="The event from image analyzer to store the data",
            event_bus=event_bus,
            event_pattern=_events.EventPattern(resources=[image_analyzer_function.function_arn]),
        )

        event_rule.add_target(_event_targets.LambdaFunction(image_data_function))

        event_bus.grant_put_events(image_analyzer_function)
        image_analyzer_function.add_environment("EVENT_BUS", event_bus.event_bus_name)

        ### outputs
        core.CfnOutput(self, 'CognitoHostedUILogin',
            value='https://{}.auth.{}.amazoncognito.com/login?client_id={}&response_type=token&scope={}&redirect_uri={}'.format(user_pool_domain.domain_name, core.Aws.REGION, user_pool_app_client.ref, '+'.join(user_pool_app_client.allowed_o_auth_scopes), api_gateway_landing_page_resource.url),
            description='The Cognito Hosted UI Login Page'
        )
    def __init__(self, scope: core.Construct, construct_id: str,
                 **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        # -----------------------------------
        #           Cognito User Pool
        # -----------------------------------
        userpool = cognito.UserPool(
            self,
            "ServerlessTodoUserPool",
            user_pool_name="ServerlessTodoUserPool",
            sign_in_aliases=cognito.SignInAliases(username=True, email=True),
            password_policy=cognito.PasswordPolicy(
                min_length=6,
                require_digits=True,
                require_lowercase=True,
                require_symbols=True,
                require_uppercase=True,
                temp_password_validity=core.Duration.days(7)),
            auto_verify=cognito.AutoVerifiedAttrs(email=True),
            standard_attributes=cognito.StandardAttributes(
                email=cognito.StandardAttribute(mutable=True, required=True),
                family_name=cognito.StandardAttribute(mutable=True,
                                                      required=True),
                given_name=cognito.StandardAttribute(mutable=True,
                                                     required=True)))
        user_pool_client = userpool.add_client(
            "UserPoolClient",
            auth_flows=cognito.AuthFlow(admin_user_password=True))

        # -----------------------------------
        #           dynamodb
        # -----------------------------------
        dynamodbTable = dynamodb.Table(
            self,
            "TaskTable",
            partition_key=dynamodb.Attribute(
                name="id", type=dynamodb.AttributeType.STRING),
            sort_key=dynamodb.Attribute(name="meta",
                                        type=dynamodb.AttributeType.STRING),
            billing_mode=dynamodb.BillingMode.PAY_PER_REQUEST,
            point_in_time_recovery=True,
            server_side_encryption=True)
        dynamodbTable.add_global_secondary_index(
            partition_key=dynamodb.Attribute(
                name="meta", type=dynamodb.AttributeType.STRING),
            sort_key=dynamodb.Attribute(name="id",
                                        type=dynamodb.AttributeType.STRING),
            index_name="meta-id-index")
        dynamodbTable.add_global_secondary_index(
            partition_key=dynamodb.Attribute(
                name="owner", type=dynamodb.AttributeType.STRING),
            sort_key=dynamodb.Attribute(name="meta",
                                        type=dynamodb.AttributeType.STRING),
            index_name="owner-meta-index")

        # -----------------------------------
        #             apigateway
        # -----------------------------------
        acm_arn = self.node.try_get_context('acm_arn')
        domain_name = self.node.try_get_context("domain_name")
        hosted_zone = self.node.try_get_context("hosted_zone")

        api_policy = iam.PolicyDocument(
            statements=iam.PolicyStatement(actions=["lambda:InvokeFunction"], )
            .add_resources("arn:aws:lambda:{}:{}:function:*".format(
                self.region, self.account)))

        if acm_arn and domain_name and hosted_zone:

            api = apigw.RestApi(
                self,
                'API',
                domain_name=apigw.DomainNameOptions(
                    certificate=acm.Certificate.from_certificate_arn(
                        self, 'ApiCertificate', acm_arn),
                    domain_name=domain_name,
                    endpoint_type=apigw.EndpointType.REGIONAL),
                deploy_options=apigw.StageOptions(metrics_enabled=True),
                policy=api_policy,
                rest_api_name="Serverless TODO API",
                endpoint_types=[apigw.EndpointType.REGIONAL],
                default_cors_preflight_options=apigw.CorsOptions(
                    allow_origins=apigw.Cors.
                    ALL_ORIGINS,  # TODO: Temporary for development
                    allow_headers=[
                        "Content-Type", "X-Amz-Date", "Authorization",
                        "X-Api-Key", "X-Amz-Security-Token", "X-Tracing-Id",
                        "x-jeffy-correlation-id", "x-amzn-trace-id"
                    ],
                    allow_methods=apigw.Cors.ALL_METHODS,
                    allow_credentials=True))
            route53.CfnRecordSet(
                self,
                "apiDomainRecord",
                name=domain_name,
                type="A",
                alias_target={
                    "dnsName":
                    api.domain_name.domain_name_alias_domain_name,
                    "hostedZoneId":
                    api.domain_name.domain_name_alias_hosted_zone_id
                },
                hosted_zone_id=hosted_zone,
            )
        else:
            api = apigw.RestApi(
                self,
                'API',
                deploy_options=apigw.StageOptions(metrics_enabled=True),
                policy=api_policy,
                rest_api_name="Serverless TODO API",
                endpoint_types=[apigw.EndpointType.REGIONAL],
                default_cors_preflight_options=apigw.CorsOptions(
                    allow_origins=apigw.Cors.
                    ALL_ORIGINS,  # TODO: Temporary for development
                    allow_headers=[
                        "Content-Type", "X-Amz-Date", "Authorization",
                        "X-Api-Key", "X-Amz-Security-Token", "X-Tracing-Id",
                        "x-jeffy-correlation-id", "x-amzn-trace-id"
                    ],
                    allow_methods=apigw.Cors.ALL_METHODS,
                    allow_credentials=True))

        cognito_authorizer = apigw.CognitoUserPoolsAuthorizer(
            self,
            "CognitoAuthorizer",
            cognito_user_pools=[userpool],
            authorizer_name='todo_cognito_authorizer',
            identity_source='method.request.header.Authorization',
            results_cache_ttl=core.Duration.minutes(60))

        api_role = iam.Role(self,
                            "ApiRole",
                            assumed_by=iam.ServicePrincipal(
                                service="apigateway.amazonaws.com"))
        api_statement = iam.PolicyStatement(
            actions=["lambda:InvokeFunction"],
            resources=[
                "arn:aws:lambda:{}:{}:function:*".format(
                    self.region, self.account)
            ])
        api_role.add_to_policy(api_statement)

        # -----------------------------------
        #      lambda common configure
        # -----------------------------------
        env = {
            "TABLE_NAME": dynamodbTable.table_name,
            "USER_POOL_ID": userpool.user_pool_id,
            "USER_POOL_NAME": userpool.user_pool_provider_name,
            "CLIENT_ID": user_pool_client.user_pool_client_id
        }

        # -----------------------------------
        #           get handler
        # -----------------------------------
        get_resource_base_name = "getTaskFunction"
        get_task_func = lambda_.Function(
            self,
            get_resource_base_name,
            code=lambda_.Code.from_asset(
                'function/src/task',
                bundling=core.BundlingOptions(
                    image=lambda_.Runtime.PYTHON_3_8.bundling_docker_image,
                    command=[
                        'bash', '-c',
                        'pip install -r requirements.txt -t /asset-output && cp -a . /asset-output'
                    ],
                )),
            handler="get.lambda_handler",
            runtime=lambda_.Runtime.PYTHON_3_8,
            environment=env,
            tracing=lambda_.Tracing.ACTIVE,
            timeout=core.Duration.seconds(29),
            memory_size=512)

        get_task_func.add_to_role_policy(statement=iam.PolicyStatement(
            actions=['dynamodb:*'],
            resources=[
                dynamodbTable.table_arn, dynamodbTable.table_arn + '/*'
            ]))
        logs.LogGroup(self,
                      get_resource_base_name + 'LogGroup',
                      log_group_name='/aws/lambda/' +
                      get_task_func.function_name,
                      retention=logs.RetentionDays.TWO_WEEKS)

        task_path = api.root.add_resource("task")
        task_id_path = task_path.add_resource("{task_id}")
        get_task_integration = apigw.LambdaIntegration(
            get_task_func, credentials_role=api_role)
        task_id_path.add_method(
            "GET",
            integration=get_task_integration,
            authorization_type=apigw.AuthorizationType.COGNITO,
            authorizer=cognito_authorizer,
        )

        # -----------------------------------
        #           create handler
        # -----------------------------------
        create_resource_base_name = "createTaskFunction"
        create_task_func = lambda_.Function(
            self,
            create_resource_base_name,
            code=lambda_.Code.from_asset(
                'function/src/task',
                bundling=core.BundlingOptions(
                    image=lambda_.Runtime.PYTHON_3_8.bundling_docker_image,
                    command=[
                        'bash', '-c',
                        'pip install -r requirements.txt -t /asset-output && cp -a . /asset-output'
                    ],
                )),
            handler="create.lambda_handler",
            runtime=lambda_.Runtime.PYTHON_3_8,
            environment=env,
            tracing=lambda_.Tracing.ACTIVE,
            timeout=core.Duration.seconds(29),
            memory_size=512)

        create_task_func.add_to_role_policy(statement=iam.PolicyStatement(
            actions=['dynamodb:*'],
            resources=[
                dynamodbTable.table_arn, dynamodbTable.table_arn + '/*'
            ]))
        logs.LogGroup(self,
                      create_resource_base_name + 'LogGroup',
                      log_group_name='/aws/lambda/' +
                      create_task_func.function_name,
                      retention=logs.RetentionDays.TWO_WEEKS)

        create_task_integration = apigw.LambdaIntegration(
            create_task_func, credentials_role=api_role)
        task_path.add_method(
            "POST",
            integration=create_task_integration,
            authorization_type=apigw.AuthorizationType.COGNITO,
            authorizer=cognito_authorizer,
        )

        # -----------------------------------
        #           update handler
        # -----------------------------------
        update_resource_base_name = "updateTaskFunction"
        update_task_func = lambda_.Function(
            self,
            update_resource_base_name,
            code=lambda_.Code.from_asset(
                'function/src/task',
                bundling=core.BundlingOptions(
                    image=lambda_.Runtime.PYTHON_3_8.bundling_docker_image,
                    command=[
                        'bash', '-c',
                        'pip install -r requirements.txt -t /asset-output && cp -a . /asset-output'
                    ],
                )),
            handler="update.lambda_handler",
            runtime=lambda_.Runtime.PYTHON_3_8,
            environment=env,
            tracing=lambda_.Tracing.ACTIVE,
            timeout=core.Duration.seconds(29),
            memory_size=512)

        update_task_func.add_to_role_policy(statement=iam.PolicyStatement(
            actions=['dynamodb:*'],
            resources=[
                dynamodbTable.table_arn, dynamodbTable.table_arn + '/*'
            ]))
        logs.LogGroup(self,
                      update_resource_base_name + 'LogGroup',
                      log_group_name='/aws/lambda/' +
                      update_task_func.function_name,
                      retention=logs.RetentionDays.TWO_WEEKS)

        update_task_integration = apigw.LambdaIntegration(
            update_task_func, credentials_role=api_role)
        task_id_path.add_method(
            "POST",
            integration=update_task_integration,
            authorization_type=apigw.AuthorizationType.COGNITO,
            authorizer=cognito_authorizer,
        )

        # -----------------------------------
        #           delete handler
        # -----------------------------------
        delete_resource_base_name = "deleteTaskFunction"
        delete_task_func = lambda_.Function(
            self,
            delete_resource_base_name,
            code=lambda_.Code.from_asset(
                'function/src/task',
                bundling=core.BundlingOptions(
                    image=lambda_.Runtime.PYTHON_3_8.bundling_docker_image,
                    command=[
                        'bash', '-c',
                        'pip install -r requirements.txt -t /asset-output && cp -a . /asset-output'
                    ],
                )),
            handler="delete.lambda_handler",
            runtime=lambda_.Runtime.PYTHON_3_8,
            environment=env,
            tracing=lambda_.Tracing.ACTIVE,
            timeout=core.Duration.seconds(29),
            memory_size=512)

        delete_task_func.add_to_role_policy(statement=iam.PolicyStatement(
            actions=['dynamodb:*'],
            resources=[
                dynamodbTable.table_arn, dynamodbTable.table_arn + '/*'
            ]))
        logs.LogGroup(self,
                      delete_resource_base_name + 'LogGroup',
                      log_group_name='/aws/lambda/' +
                      delete_task_func.function_name,
                      retention=logs.RetentionDays.TWO_WEEKS)

        delete_task_integration = apigw.LambdaIntegration(
            delete_task_func, credentials_role=api_role)
        task_id_path.add_method(
            "DELETE",
            integration=delete_task_integration,
            authorization_type=apigw.AuthorizationType.COGNITO,
            authorizer=cognito_authorizer,
        )

        # -----------------------------------
        #           search handler
        # -----------------------------------
        search_resource_base_name = "searchTaskFunction"
        search_task_func = lambda_.Function(
            self,
            search_resource_base_name,
            code=lambda_.Code.from_asset(
                'function/src/task',
                bundling=core.BundlingOptions(
                    image=lambda_.Runtime.PYTHON_3_8.bundling_docker_image,
                    command=[
                        'bash', '-c',
                        'pip install -r requirements.txt -t /asset-output && cp -a . /asset-output'
                    ],
                )),
            handler="search.lambda_handler",
            runtime=lambda_.Runtime.PYTHON_3_8,
            environment=env,
            tracing=lambda_.Tracing.ACTIVE,
            timeout=core.Duration.seconds(29),
            memory_size=512)

        search_task_func.add_to_role_policy(statement=iam.PolicyStatement(
            actions=['dynamodb:*'],
            resources=[
                dynamodbTable.table_arn, dynamodbTable.table_arn + '/*'
            ]))
        logs.LogGroup(self,
                      search_resource_base_name + 'LogGroup',
                      log_group_name='/aws/lambda/' +
                      search_task_func.function_name,
                      retention=logs.RetentionDays.TWO_WEEKS)

        search_task_integration = apigw.LambdaIntegration(
            search_task_func, credentials_role=api_role)
        tasks_path = api.root.add_resource("tasks")
        tasks_path.add_method(
            "GET",
            integration=search_task_integration,
            authorization_type=apigw.AuthorizationType.COGNITO,
            authorizer=cognito_authorizer,
        )

        # -----------------------------------
        #           login handler
        # -----------------------------------
        login_resource_base_name = "loginFunction"
        login_task_func = lambda_.Function(
            self,
            login_resource_base_name,
            code=lambda_.Code.from_asset(
                'function/src/user',
                bundling=core.BundlingOptions(
                    image=lambda_.Runtime.PYTHON_3_8.bundling_docker_image,
                    command=[
                        'bash', '-c',
                        'pip install -r requirements.txt -t /asset-output && cp -a . /asset-output'
                    ],
                )),
            handler="login.lambda_handler",
            runtime=lambda_.Runtime.PYTHON_3_8,
            environment=env,
            tracing=lambda_.Tracing.ACTIVE,
            timeout=core.Duration.seconds(29),
            memory_size=512)

        login_task_func.add_to_role_policy(statement=iam.PolicyStatement(
            actions=['cognito-idp:AdminInitiateAuth'],
            resources=[userpool.user_pool_arn]))
        logs.LogGroup(self,
                      login_resource_base_name + 'LogGroup',
                      log_group_name='/aws/lambda/' +
                      login_task_func.function_name,
                      retention=logs.RetentionDays.TWO_WEEKS)

        login_task_integration = apigw.LambdaIntegration(login_task_func)
        auth_path = api.root.add_resource("auth")
        auth_login_path = auth_path.add_resource("login")
        auth_login_path.add_method("POST", integration=login_task_integration)
Esempio n. 6
0
    def __init__(self, scope: cdk.Construct, construct_id: str,
                 **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)
        # Image Bucket
        image_bucket = s3.Bucket(self,
                                 IMG_BUCKET_NAME,
                                 removal_policy=cdk.RemovalPolicy.DESTROY)
        cdk.CfnOutput(self, "imageBucket", value=image_bucket.bucket_name)

        image_bucket.add_cors_rule(
            allowed_methods=[s3.HttpMethods.GET, s3.HttpMethods.PUT],
            allowed_origins=["*"],
            allowed_headers=["*"],
            max_age=3000,
        )

        # Thumbnail Bucket
        resized_image_bucket = s3.Bucket(
            self,
            RESIZED_IMG_BUCKET_NAME,
            removal_policy=cdk.RemovalPolicy.DESTROY)
        cdk.CfnOutput(self,
                      "resizedBucket",
                      value=resized_image_bucket.bucket_name)

        resized_image_bucket.add_cors_rule(
            allowed_methods=[s3.HttpMethods.GET, s3.HttpMethods.PUT],
            allowed_origins=["*"],
            allowed_headers=["*"],
            max_age=3000,
        )
        # S3 Static bucket for website code
        web_bucket = s3.Bucket(
            self,
            WEBSITE_BUCKET_NAME,
            website_index_document="index.html",
            website_error_document="index.html",
            removal_policy=cdk.RemovalPolicy.DESTROY,
            # uncomment this and delete the policy statement below to allow public access to our
            # static website
            # public_read_access=true
        )

        web_policy_statement = iam.PolicyStatement(
            actions=["s3:GetObject"],
            resources=[web_bucket.arn_for_objects("*")],
            principals=[iam.AnyPrincipal()],
            conditions={"IpAddress": {
                "aws:SourceIp": ["139.138.203.36"]
            }},
        )

        web_bucket.add_to_resource_policy(web_policy_statement)

        cdk.CfnOutput(self,
                      "bucketURL",
                      value=web_bucket.bucket_website_domain_name)

        # Deploy site contents to S3 Bucket
        s3_dep.BucketDeployment(
            self,
            "DeployWebsite",
            sources=[s3_dep.Source.asset("./public")],
            destination_bucket=web_bucket,
        )

        # DynamoDB to store image labels
        partition_key = dynamodb.Attribute(name="image",
                                           type=dynamodb.AttributeType.STRING)
        table = dynamodb.Table(
            self,
            "ImageLabels",
            partition_key=partition_key,
            removal_policy=cdk.RemovalPolicy.DESTROY,
        )
        cdk.CfnOutput(self, "ddbTable", value=table.table_name)

        # Lambda layer for Pillow library
        layer = lb.LayerVersion(
            self,
            "pil",
            code=lb.Code.from_asset("reklayer"),
            compatible_runtimes=[lb.Runtime.PYTHON_3_7],
            license="Apache-2.0",
            description=
            "A layer to enable the PIL library in our Rekognition Lambda",
        )

        # Lambda function
        rek_fn = lb.Function(
            self,
            "rekognitionFunction",
            code=lb.Code.from_asset("rekognitionFunction"),
            runtime=lb.Runtime.PYTHON_3_7,
            handler="index.handler",
            timeout=cdk.Duration.seconds(30),
            memory_size=1024,
            layers=[layer],
            environment={
                "TABLE": table.table_name,
                "BUCKET": image_bucket.bucket_name,
                "THUMBBUCKET": resized_image_bucket.bucket_name,
            },
        )

        image_bucket.grant_read(rek_fn)
        resized_image_bucket.grant_write(rek_fn)
        table.grant_write_data(rek_fn)

        rek_fn.add_to_role_policy(
            iam.PolicyStatement(effect=iam.Effect.ALLOW,
                                actions=["rekognition:DetectLabels"],
                                resources=["*"]))

        # Lambda for Synchronous front end
        serviceFn = lb.Function(
            self,
            "serviceFunction",
            code=lb.Code.from_asset("servicelambda"),
            runtime=lb.Runtime.PYTHON_3_7,
            handler="index.handler",
            environment={
                "TABLE": table.table_name,
                "BUCKET": image_bucket.bucket_name,
                "RESIZEDBUCKET": resized_image_bucket.bucket_name,
            },
        )

        image_bucket.grant_write(serviceFn)
        resized_image_bucket.grant_write(serviceFn)
        table.grant_read_write_data(serviceFn)

        # Cognito User Pool Auth
        auto_verified_attrs = cognito.AutoVerifiedAttrs(email=True)
        sign_in_aliases = cognito.SignInAliases(email=True, username=True)
        user_pool = cognito.UserPool(
            self,
            "UserPool",
            self_sign_up_enabled=True,
            auto_verify=auto_verified_attrs,
            sign_in_aliases=sign_in_aliases,
        )

        user_pool_client = cognito.UserPoolClient(self,
                                                  "UserPoolClient",
                                                  user_pool=user_pool,
                                                  generate_secret=False)

        identity_pool = cognito.CfnIdentityPool(
            self,
            "ImageRekognitionIdentityPool",
            allow_unauthenticated_identities=False,
            cognito_identity_providers=[{
                "clientId":
                user_pool_client.user_pool_client_id,
                "providerName":
                user_pool.user_pool_provider_name,
            }],
        )

        # API Gateway
        cors_options = apigw.CorsOptions(allow_origins=apigw.Cors.ALL_ORIGINS,
                                         allow_methods=apigw.Cors.ALL_METHODS)
        api = apigw.LambdaRestApi(
            self,
            "imageAPI",
            default_cors_preflight_options=cors_options,
            handler=serviceFn,
            proxy=False,
        )

        auth = apigw.CfnAuthorizer(
            self,
            "ApiGatewayAuthorizer",
            name="customer-authorizer",
            identity_source="method.request.header.Authorization",
            provider_arns=[user_pool.user_pool_arn],
            rest_api_id=api.rest_api_id,
            # type=apigw.AuthorizationType.COGNITO,
            type="COGNITO_USER_POOLS",
        )

        assumed_by = iam.FederatedPrincipal(
            "cognito-identity.amazon.com",
            conditions={
                "StringEquals": {
                    "cognito-identity.amazonaws.com:aud": identity_pool.ref
                },
                "ForAnyValue:StringLike": {
                    "cognito-identity.amazonaws.com:amr": "authenticated"
                },
            },
            assume_role_action="sts:AssumeRoleWithWebIdentity",
        )
        authenticated_role = iam.Role(
            self,
            "ImageRekognitionAuthenticatedRole",
            assumed_by=assumed_by,
        )
        # IAM policy granting users permission to get and put their pictures
        policy_statement = iam.PolicyStatement(
            actions=["s3:GetObject", "s3:PutObject"],
            effect=iam.Effect.ALLOW,
            resources=[
                image_bucket.bucket_arn +
                "/private/${cognito-identity.amazonaws.com:sub}/*",
                image_bucket.bucket_arn +
                "/private/${cognito-identity.amazonaws.com:sub}/",
                resized_image_bucket.bucket_arn +
                "/private/${cognito-identity.amazonaws.com:sub}/*",
                resized_image_bucket.bucket_arn +
                "/private/${cognito-identity.amazonaws.com:sub}/",
            ],
        )

        # IAM policy granting users permission to list their pictures
        list_policy_statement = iam.PolicyStatement(
            actions=["s3:ListBucket"],
            effect=iam.Effect.ALLOW,
            resources=[
                image_bucket.bucket_arn, resized_image_bucket.bucket_arn
            ],
            conditions={
                "StringLike": {
                    "s3:prefix":
                    ["private/${cognito-identity.amazonaws.com:sub}/*"]
                }
            },
        )

        authenticated_role.add_to_policy(policy_statement)
        authenticated_role.add_to_policy(list_policy_statement)

        # Attach role to our Identity Pool
        cognito.CfnIdentityPoolRoleAttachment(
            self,
            "IdentityPoolRoleAttachment",
            identity_pool_id=identity_pool.ref,
            roles={"authenticated": authenticated_role.role_arn},
        )

        # Get some outputs from cognito
        cdk.CfnOutput(self, "UserPoolId", value=user_pool.user_pool_id)
        cdk.CfnOutput(self,
                      "AppClientId",
                      value=user_pool_client.user_pool_client_id)
        cdk.CfnOutput(self, "IdentityPoolId", value=identity_pool.ref)

        # New Amazon API Gateway with AWS Lambda Integration
        success_response = apigw.IntegrationResponse(
            status_code="200",
            response_parameters={
                "method.response.header.Access-Control-Allow-Origin": "'*'"
            },
        )
        error_response = apigw.IntegrationResponse(
            selection_pattern="(\n|.)+",
            status_code="500",
            response_parameters={
                "method.response.header.Access-Control-Allow-Origin": "'*'"
            },
        )

        request_template = json.dumps({
            "action":
            "$util.escapeJavaScript($input.params('action'))",
            "key":
            "$util.escapeJavaScript($input.params('key'))",
        })

        lambda_integration = apigw.LambdaIntegration(
            serviceFn,
            proxy=False,
            request_parameters={
                "integration.request.querystring.action":
                "method.request.querystring.action",
                "integration.request.querystring.key":
                "method.request.querystring.key",
            },
            request_templates={"application/json": request_template},
            passthrough_behavior=apigw.PassthroughBehavior.WHEN_NO_TEMPLATES,
            integration_responses=[success_response, error_response],
        )

        imageAPI = api.root.add_resource("images")

        success_resp = apigw.MethodResponse(
            status_code="200",
            response_parameters={
                "method.response.header.Access-Control-Allow-Origin": True
            },
        )
        error_resp = apigw.MethodResponse(
            status_code="500",
            response_parameters={
                "method.response.header.Access-Control-Allow-Origin": True
            },
        )

        # GET /images
        get_method = imageAPI.add_method(
            "GET",
            lambda_integration,
            authorization_type=apigw.AuthorizationType.COGNITO,
            request_parameters={
                "method.request.querystring.action": True,
                "method.request.querystring.key": True,
            },
            method_responses=[success_resp, error_resp],
        )
        # DELETE /images
        delete_method = imageAPI.add_method(
            "DELETE",
            lambda_integration,
            authorization_type=apigw.AuthorizationType.COGNITO,
            request_parameters={
                "method.request.querystring.action": True,
                "method.request.querystring.key": True,
            },
            method_responses=[success_resp, error_resp],
        )

        # Override the authorizer id because it doesn't work when defininting it as a param
        # in add_method
        get_method_resource = get_method.node.find_child("Resource")
        get_method_resource.add_property_override("AuthorizerId", auth.ref)
        delete_method_resource = delete_method.node.find_child("Resource")
        delete_method_resource.add_property_override("AuthorizerId", auth.ref)

        # Building SQS queue and DeadLetter Queue
        dl_queue = sqs.Queue(
            self,
            "ImageDLQueue",
            queue_name="ImageDLQueue",
        )

        dl_queue_opts = sqs.DeadLetterQueue(max_receive_count=2,
                                            queue=dl_queue)

        queue = sqs.Queue(
            self,
            "ImageQueue",
            queue_name="ImageQueue",
            visibility_timeout=cdk.Duration.seconds(30),
            receive_message_wait_time=cdk.Duration.seconds(20),
            dead_letter_queue=dl_queue_opts,
        )

        # S3 Bucket Create Notification to SQS
        # Whenever an image is uploaded add it to the queue

        image_bucket.add_object_created_notification(
            s3n.SqsDestination(queue),
            s3.NotificationKeyFilter(prefix="private/"))
Esempio n. 7
0
    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        # S3: Create a Bucket for Unicorn Pursuit web page, and grant public read:
        bucket = s3.Bucket(
            self,
            "www.unicornpursuit.com",
            bucket_name="www.unicornpursuit.com",
            access_control=s3.BucketAccessControl.PUBLIC_READ,
        )

        # Grant public read access to the bucket
        bucket.grant_public_access()

        # DynamoDB: Create Table for Project Info (ID, Owner, Content, Photo and Votes)
        voting = ddb.CfnTable(
            self,
            "UnicornDynamoDBVoting",
            table_name="UnicornDynamoDBVoting",
            key_schema=[
                ddb.CfnTable.KeySchemaProperty(attribute_name="id",
                                               key_type="HASH"),
                ddb.CfnTable.KeySchemaProperty(attribute_name="owner",
                                               key_type="RANGE"),
            ],

            # In the new DynamoDB, you can't create AttDefProperty for non-key attributes.
            attribute_definitions=[
                ddb.CfnTable.AttributeDefinitionProperty(attribute_name="id",
                                                         attribute_type="N"),
                ddb.CfnTable.AttributeDefinitionProperty(
                    attribute_name="owner", attribute_type="S"),
            ],
            provisioned_throughput=ddb.CfnTable.ProvisionedThroughputProperty(
                read_capacity_units=5, write_capacity_units=5))

        # Grant RW writes for Unicorn App in Fargate and relevant Lambdas invoked from API Gateway
        # voting.grant_read_write_data(user)

        # Second DynamoDB table called "users" for storing who voted for whom
        # Example: [email protected] gave 5 points to project 323, 4 points to 111 etc.
        users = ddb.Table(self,
                          "UnicornDynamoDBUsers",
                          table_name="UnicornDynamoDBUsers",
                          partition_key=ddb.Attribute(
                              name="owner", type=ddb.AttributeType.STRING))

        # Cognito: Create User Pool
        userpool = cognito.UserPool(
            self,
            "CognitoUnicornUserPool",
            user_pool_name="CognitoUnicornUserPool",
            self_sign_up_enabled=True,

            ## Require username or email for users to sign in
            sign_in_aliases=cognito.SignInAliases(
                username=False,
                email=True,
            ),
            # Require users to give their full name when signing up
            required_attributes=cognito.RequiredAttributes(fullname=True,
                                                           email=True,
                                                           phone_number=True),
            # Verify new sign ups using email
            auto_verify=cognito.AutoVerifiedAttrs(
                email=False,
                phone=True,
            ),
            # Configure OTP Settings ()
            user_verification=cognito.UserVerificationConfig(
                sms_message=
                "Hey Unicorn Hunter, welcome to Unicorn Pursuit! Your OTP is {####}",
            ),
            # Set up required password policy
            password_policy=cognito.PasswordPolicy(
                min_length=12,
                require_symbols=True,
                require_lowercase=True,
                require_uppercase=True,
                require_digits=True,
            ))

        ## Cognito: Create App Client & create Authentication Flow with User and Password
        client = userpool.add_client(
            "UnicornAppClient",
            user_pool_client_name="UnicornAppClient",
            generate_secret=False,

            ## We'll allow both Flows, Implicit and Authorization Code, and decide in the app which to use.
            auth_flows=cognito.AuthFlow(admin_user_password=False,
                                        custom=False,
                                        refresh_token=True,
                                        user_password=True,
                                        user_srp=False),
        )
Esempio n. 8
0
    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        # Let's start with creating an IAM Service Role, later to be assumed by our ECS Fargate Container
        # After creating any resource, we'll be attaching IAM policies to this role using the `fargate_role`.
        fargate_role = iam.Role(
            self,
            "ecsTaskExecutionRole",
            role_name="ecsTaskExecutionRole",
            assumed_by=iam.ServicePrincipal("ecs-tasks.amazonaws.com"),
            description="Custom Role assumed by ECS Fargate (container)"
        )

        # S3: Create a Bucket for Unicorn Pursuit web page, and grant public read:
        bucket = s3.Bucket(self, "www.unicornpursuit.com",
                        bucket_name="www.unicornpursuit.com",
                        access_control=s3.BucketAccessControl.PUBLIC_READ,
                        )

        # Grant public read access to the bucket
        bucket.grant_public_access()


        # Grant S3 Read/Write access to our Fargate Container
        fargate_role.add_to_policy(statement=iam.PolicyStatement(
            resources=[bucket.bucket_arn],
            actions=["s3:*"]
        ))

        # DynamoDB: Create Table for Project Info (ID, Owner, Content, Photo and Votes)
        voting_ddb = ddb.CfnTable(
            self, "UnicornDynamoDBVoting",
            table_name="UnicornDynamoDBVoting",
            key_schema=[
                ddb.CfnTable.KeySchemaProperty(attribute_name="id",key_type="HASH"),
                ddb.CfnTable.KeySchemaProperty(attribute_name="owner",key_type="RANGE"),
            ],
            
        # In the new DynamoDB, you can't create AttDefProperty for non-key attributes.
            attribute_definitions=[
                ddb.CfnTable.AttributeDefinitionProperty(attribute_name="id",attribute_type="N"),
                ddb.CfnTable.AttributeDefinitionProperty(attribute_name="owner",attribute_type="S"),
            ],
            provisioned_throughput=ddb.CfnTable.ProvisionedThroughputProperty(
                read_capacity_units=5,
                write_capacity_units=5
            )
        )
        

        # Second DynamoDB table called "users" for storing who voted for whom
        # Example: [email protected] gave 5 points to project 323, 4 points to 111 etc.
        users_ddb = ddb.Table(
            self, "UnicornDynamoDBUsers",
            table_name="UnicornDynamoDBUsers",
            partition_key=ddb.Attribute(
                name="Owner",
                type=ddb.AttributeType.STRING
            )
        )

        # Grant RW writes for Unicorn App in Fargate
        fargate_role.add_to_policy(statement=iam.PolicyStatement(
            resources=[voting_ddb.attr_arn,
            users_ddb.table_arn],
            actions=["dynamodb:*"]
        ))

        # Cognito: Create User Pool
        userpool = cognito.UserPool(
            self, "CognitoUnicornUserPool",
            user_pool_name="CognitoUnicornUserPool",
            self_sign_up_enabled=True,
            
            ## Require username or email for users to sign in
            sign_in_aliases=cognito.SignInAliases(
                username=False,
                email=True,
            ),
            # Require users to give their full name when signing up
            required_attributes=cognito.RequiredAttributes(
                fullname=True,
                email=True,
                phone_number=True
            ),
            # Verify new sign ups using email
            auto_verify=cognito.AutoVerifiedAttrs(
                email=False,
                phone=True,
            ),
            # Configure OTP Settings ()
            user_verification=cognito.UserVerificationConfig(
                sms_message="Hey Unicorn Hunter, welcome to Unicorn Pursuit! Your OTP is {####}",
            ),
            # Set up required password policy
            password_policy=cognito.PasswordPolicy(
                min_length=12,
                require_symbols=True,
                require_lowercase=True,
                require_uppercase=True,
                require_digits=True,
            )
        )

        ## Cognito: Create App Client & create Authentication Flow with User and Password
        userpool.add_client(
            "UnicornAppClient",
            user_pool_client_name="UnicornAppClient",
            generate_secret=False,
            
            ## We'll allow both Flows, Implicit and Authorization Code, and decide in the app which to use.
            auth_flows=cognito.AuthFlow(
                admin_user_password=False,
                custom=False,
                refresh_token=True,
                user_password=True,
                user_srp=False
                ),
        )

        # Grant Cognito Access to Fargate. Include SSM, so Client App ID can be retrived.
        fargate_role.add_to_policy(statement=iam.PolicyStatement(
            resources=["*"],
            actions=["ssm:*"]
        ))

        fargate_role.add_to_policy(statement=iam.PolicyStatement(
            resources=[userpool.user_pool_arn],
            actions=["cognito-identity:*","cognito-idp:*","cognito-sync:*"]
        ))

        ## Fargate: Create ECS:Fargate with ECR uploaded image
        vpc = ec2.Vpc(self, "UnicornVPC", max_azs=2, nat_gateways=None)

        """   VPC with optimal NAT GW usage
        vpc_lowcost = ec2.Vpc(self, "LowCostVPC",
            max_azs=2,
            cidr="10.7.0.0/16",
            nat_gateways=None,
            subnet_configuration=[ec2.SubnetConfiguration(
                               subnet_type=ec2.SubnetType.PUBLIC,
                               name="Public",
                               cidr_mask=24,
                           ), ec2.SubnetConfiguration(
                               subnet_type=ec2.SubnetType.ISOLATED,
                               name="Private",
                               cidr_mask=24,
                           )
                           ],     
        ) """
        
        linux_ami = ec2.AmazonLinuxImage(generation=ec2.AmazonLinuxGeneration.AMAZON_LINUX,
                                 edition=ec2.AmazonLinuxEdition.STANDARD,
                                 virtualization=ec2.AmazonLinuxVirt.HVM,
                                 storage=ec2.AmazonLinuxStorage.GENERAL_PURPOSE
                                 )

        nat_ec2 = ec2.Instance(self, "NAT", 
            instance_name="NAT",
            vpc=vpc,
            vpc_subnets=ec2.SubnetSelection(subnet_type=ec2.SubnetType.PUBLIC),
            instance_type=ec2.InstanceType(instance_type_identifier="t3.nano"),
            machine_image=linux_ami,
            user_data=ec2.UserData.for_linux(),
            source_dest_check=False,
        )

        # Configure Linux Instance to act as NAT Instance
        nat_ec2.user_data.add_commands("sysctl -w net.ipv4.ip_forward=1")
        nat_ec2.user_data.add_commands("/sbin/iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE")

        # Add a static route to the ISOLATED subnet, pointing 0.0.0.0/0 to a NAT EC2
        selection = vpc.select_subnets(
            subnet_type=ec2.SubnetType.PRIVATE
        )
    
        for subnet in selection.subnets:
            subnet.add_route("DefaultNAT", router_id=nat_ec2.instance_id, router_type=ec2.RouterType.INSTANCE, destination_cidr_block="0.0.0.0/0") 

        # Create ECS Cluster
        cluster = ecs.Cluster(self, "UnicornCluster", vpc=vpc)
        ecr.Repository(self, "unicorn", repository_name="unicorn")

        fargate_service = ecs_patterns.ApplicationLoadBalancedFargateService(self, "UnicornFargateService",
            cluster=cluster,
            cpu=512,
            desired_count=1,
            task_image_options=ecs_patterns.ApplicationLoadBalancedTaskImageOptions(
                image=ecs.ContainerImage.from_registry("057097267726.dkr.ecr.eu-west-1.amazonaws.com/unicorn"),
                # image=ecs.ContainerImage.from_registry(repo.repository_uri_for_tag()),
                container_port=8080,
                container_name="unicorn",
                execution_role=fargate_role,
                ),
                
            memory_limit_mib=1024,
            public_load_balancer=True   
        )

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

        # Update NAT EC2 Security Group, to allow only HTTPS from Fargate Service Security Group.
        nat_ec2.connections.security_groups[0].add_ingress_rule(
            peer = fargate_service.service.connections.security_groups[0],
            connection = ec2.Port.tcp(443),
            description="Allow https from Fargate Service"
        )

        # Grant ECR Access to Fargate by attaching an existing ReadOnly policy. so that Unicorn Docker Image can be pulled.
        #fargate_role.add_managed_policy(iam.ManagedPolicy("AmazonEC2ContainerRegistryReadOnly"))
        fargate_role.add_to_policy(statement=iam.PolicyStatement(
            resources=["*"],
            actions=["ecr:*"]
        ))
Esempio n. 9
0
    def __init__(self, scope: core.Construct, id: str, **kwargs):
        super().__init__(scope, id, **kwargs)

        prj_name = self.node.try_get_context('project_name')
        env_name = self.node.try_get_context('env')

        user_pool2 = cognito.UserPool(
            self,
            id=f'{env_name}-precog',
            auto_verify=cognito.AutoVerifiedAttrs(email=True),
            sign_in_aliases=cognito.SignInAliases(email=True, phone=True),
            self_sign_up_enabled=True,
            user_pool_name=f'{env_name}-cdk-2-user-pool',
            custom_attributes={
                "param1": cognito.StringAttribute(mutable=True)
            },
            password_policy=cognito.PasswordPolicy(min_length=10,
                                                   require_lowercase=True,
                                                   require_digits=True,
                                                   require_symbols=False,
                                                   require_uppercase=True))

        user_pool = cognito.CfnUserPool(
            self,
            id=f'{env_name}-cognito-user-pool',
            auto_verified_attributes=['email'],
            username_attributes=['email', 'phone_number'],
            user_pool_name=f'{env_name}-cdk-user-pool',
            schema=[{
                "attributeDataType": "String",
                "name": "param1",
                "mutable": True
            }],
            policies=cognito.CfnUserPool.PoliciesProperty(
                password_policy=cognito.CfnUserPool.PasswordPolicyProperty(
                    minimum_length=10,
                    require_lowercase=True,
                    require_numbers=True,
                    require_symbols=False,
                    require_uppercase=True)))

        user_pool_client2 = cognito.UserPoolClient(
            self,
            id=f'{env_name}-pool-client2',
            user_pool=user_pool2,
            user_pool_client_name=f'{env_name}-cdk-app-client2')
        identity_pool2 = cognito.CfnIdentityPool(
            self,
            id=f'{env_name}-identify-pool-2',
            allow_unauthenticated_identities=False,
            cognito_identity_providers=[
                cognito.CfnIdentityPool.CognitoIdentityProviderProperty(
                    client_id=user_pool_client2.user_pool_client_id,
                    provider_name=user_pool.attr_provider_name)
            ],
            identity_pool_name=f'{env_name}-cdk-identity-pool2')

        user_pool_client = cognito.CfnUserPoolClient(
            self,
            id=f'{env_name}-pool-client',
            user_pool_id=user_pool.ref,
            client_name=f'{env_name}-cdk-app-client')

        identity_pool = cognito.CfnIdentityPool(
            self,
            id=f'{env_name}-identify-pool',
            allow_unauthenticated_identities=False,
            cognito_identity_providers=[
                cognito.CfnIdentityPool.CognitoIdentityProviderProperty(
                    client_id=user_pool_client.ref,
                    provider_name=user_pool.attr_provider_name)
            ],
            identity_pool_name=f'{env_name}-cdk-identity-pool')

        ssm.StringParameter(
            self,
            id='app-id',
            parameter_name=f"/{env_name}/cognito-app-client-id",
            string_value=user_pool_client.ref)

        ssm.StringParameter(self,
                            id='user-pool-id',
                            parameter_name=f"/{env_name}/cognito-user-pool-id",
                            string_value=user_pool_client.user_pool_id)

        ssm.StringParameter(
            self,
            id='identity-pool-id',
            parameter_name=f"/{env_name}/cognito-identity-pool-id",
            string_value=identity_pool.ref  # ref returns the id
        )
    def add_cognito(self):
        """
        Sets up the cognito infrastructure with the user pool, custom domain
        and app client for use by the ALB.
        """
        # Create the user pool that holds our users
        self.user_pool = cognito.UserPool(
            self,
            "user-pool",
            account_recovery=cognito.AccountRecovery.
            EMAIL_AND_PHONE_WITHOUT_MFA,
            auto_verify=cognito.AutoVerifiedAttrs(email=True, phone=True),
            self_sign_up_enabled=True,
            standard_attributes=cognito.StandardAttributes(
                email=cognito.StandardAttribute(mutable=True, required=True),
                given_name=cognito.StandardAttribute(mutable=True,
                                                     required=True),
                family_name=cognito.StandardAttribute(mutable=True,
                                                      required=True)))

        # Add a lambda function that automatically confirms new users without
        # email/phone verification, just for this demo
        auto_confirm_function = _lambda.Function(
            self,
            "auto-confirm-function",
            code=_lambda.Code.from_asset(path=os.path.join(
                os.path.dirname(__file__), "..", "auto_confirm_function")),
            handler="lambda_handler.lambda_handler",
            runtime=_lambda.Runtime.PYTHON_3_8,
        )

        self.user_pool.add_trigger(
            operation=cognito.UserPoolOperation.PRE_SIGN_UP,
            fn=auto_confirm_function)

        # Add a custom domain for the hosted UI
        self.user_pool_custom_domain = self.user_pool.add_domain(
            "user-pool-domain",
            cognito_domain=cognito.CognitoDomainOptions(
                domain_prefix=self.config.cognito_custom_domain))

        # Create an app client that the ALB can use for authentication
        self.user_pool_client = self.user_pool.add_client(
            "alb-app-client",
            user_pool_client_name="AlbAuthentication",
            generate_secret=True,
            o_auth=cognito.OAuthSettings(
                callback_urls=[
                    # This is the endpoint where the ALB accepts the
                    # response from Cognito
                    f"https://{self.config.application_dns_name}/oauth2/idpresponse",

                    # This is here to allow a redirect to the login page
                    # after the logout has been completed
                    f"https://{self.config.application_dns_name}"
                ],
                flows=cognito.OAuthFlows(authorization_code_grant=True),
                scopes=[cognito.OAuthScope.OPENID]),
            supported_identity_providers=[
                cognito.UserPoolClientIdentityProvider.COGNITO
            ])

        # Logout URLs and redirect URIs can't be set in CDK constructs natively ...yet
        user_pool_client_cf: cognito.CfnUserPoolClient = self.user_pool_client.node.default_child
        user_pool_client_cf.logout_ur_ls = [
            # This is here to allow a redirect to the login page
            # after the logout has been completed
            f"https://{self.config.application_dns_name}"
        ]

        self.user_pool_full_domain = self.user_pool_custom_domain.base_url()
        redirect_uri = urllib.parse.quote('https://' +
                                          self.config.application_dns_name)
        self.user_pool_logout_url = f"{self.user_pool_full_domain}/logout?" \
                                    + f"client_id={self.user_pool_client.user_pool_client_id}&" \
                                    + f"logout_uri={redirect_uri}"

        self.user_pool_user_info_url = f"{self.user_pool_full_domain}/oauth2/userInfo"
    def __init__(self, scope: core.Construct, id: str,
                 cognito_domain_name: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        # The code that defines your stack goes here):

        svc_name = self.node.try_get_context("service_name")

        url_name = core.CfnParameter(
            self,
            id="cognitoDomainName",
            description="Enter the name the domain you want to use",
            type="String",
            default="mystique")

        # Create an Identity Pool
        self.unicorn_user_pool = _cognito.UserPool(
            self,
            "unicornUserPool",
            user_pool_name="Miztiik-Unicorn-App-User-Pool",
            # sign_in_aliases={"username": True,"email": True}
            sign_in_aliases=_cognito.SignInAliases(username=True, email=True),
            standard_attributes={
                "email": {
                    "required": True,
                    "mutable": False
                },
                "fullname": {
                    "required": False,
                    "mutable": True
                }
            },
            auto_verify=_cognito.AutoVerifiedAttrs(email=True))

        # OAuth Scopes
        self.unicorn_user_pool_res_srv_identifier = f"premium_api"
        self.unicorn_read_scope = f"read"
        self.unicorn_write_scope = f"write"

        unicorn_users_auth_domain = _cognito.UserPoolDomain(
            self,
            "userPoolDomain",
            user_pool=self.unicorn_user_pool,
            cognito_domain=_cognito.CognitoDomainOptions(
                domain_prefix=f"{cognito_domain_name}"))

        self.unicorn_user_pool.user_pool_arn

        user_pool_res_srv = _cognito.CfnUserPoolResourceServer(
            self,
            "ResourceServer",
            # Having URL format is recommended
            # identifier=f"{premium_content.url}",
            identifier=f"{self.unicorn_user_pool_res_srv_identifier}",
            name=f"premium-api-authorizer",
            user_pool_id=self.unicorn_user_pool.user_pool_id,
            scopes=[{
                "scopeName": f"{self.unicorn_read_scope}",
                "scopeDescription": "Get Premium Api Content"
            }, {
                "scopeName": f"{self.unicorn_write_scope}",
                "scopeDescription": "Put Premium Api Content"
            }])

        user_pool_client = _cognito.UserPoolClient(
            self,
            "AppClient",
            user_pool=self.unicorn_user_pool,
            user_pool_client_name="premium_app_users",
            generate_secret=True,
            # We'll allow both Flows, Implicit and Authorization Code, and decide in the app which to use.
            auth_flows=_cognito.AuthFlow(admin_user_password=False,
                                         custom=True,
                                         refresh_token=True,
                                         user_password=False,
                                         user_srp=True),
            prevent_user_existence_errors=True,
            o_auth=_cognito.OAuthSettings(
                flows=_cognito.OAuthFlows(authorization_code_grant=False,
                                          implicit_code_grant=False,
                                          client_credentials=True),
                scopes=[
                    # _cognito.OAuthScope.EMAIL,
                    # _cognito.OAuthScope.OPENID,
                    # _cognito.OAuthScope.COGNITO_ADMIN,
                    # _cognito.OAuthScope.PROFILE,
                    # https://docs.aws.amazon.com/cdk/api/latest/python/aws_cdk.aws_cognito/OAuthScope.html#aws_cdk.aws_cognito.OAuthScope
                    _cognito.OAuthScope.custom(
                        f"{self.unicorn_user_pool_res_srv_identifier}/{self.unicorn_read_scope}"
                    ),
                    _cognito.OAuthScope.custom(
                        f"{self.unicorn_user_pool_res_srv_identifier}/{self.unicorn_write_scope}"
                    )
                ]))

        # Add dependency so that ResourceServer is deployed before App Client
        user_pool_client.node.add_dependency(user_pool_res_srv)

        # Retrieve Cognito App Client Secret and Add to Secrets Manager
        app_secrets = CognitoAppClientSecretRetrieverStack(
            self,
            "appClientSecrets",
            user_pool_id=self.unicorn_user_pool.user_pool_id,
            user_pool_client_id=user_pool_client.user_pool_client_id,
            user_pool_oauth2_endpoint=
            f"https://{unicorn_users_auth_domain.domain_name}.auth.{core.Aws.REGION}.amazoncognito.com/oauth2/token",
            unicorn_user_pool_res_srv_identifier=
            f"{self.unicorn_user_pool_res_srv_identifier}",
            unicorn_read_scope=f"{self.unicorn_read_scope}",
            unicorn_write_scope=f"{self.unicorn_write_scope}")

        # Export Value
        self.unicorn_user_pool_secrets_arn = app_secrets.response

        ###########################################
        ################# OUTPUTS #################
        ###########################################

        output_0 = core.CfnOutput(
            self,
            "AutomationFrom",
            value=f"{global_args.SOURCE_INFO}",
            description=
            "To know more about this automation stack, check out our github page."
        )
        # https://AUTH_DOMAIN/oauth2/token
        output_1 = core.CfnOutput(
            self,
            "UnicornIdentityAuthDomain",
            value=
            f"https://{unicorn_users_auth_domain.domain_name}.auth.{core.Aws.REGION}.amazoncognito.com/oauth2/token",
            description="Authenticate Against this endpoint")

        output_2 = core.CfnOutput(self,
                                  "AppPoolSecretsArn",
                                  value=f"{app_secrets.response}",
                                  description="AppPoolSecretsArn")
Esempio n. 12
0
    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        # Create a VPC for Aurora (required for serverless Aurora)
        aurora_vpc = ec2.Vpc(self, 'AuroraVpc')

        # Create serverless MySQL Aurora cluster
        aurora_cluster = rds.ServerlessCluster(
            self,
            'AuroraCluster',
            vpc=aurora_vpc,
            engine=rds.DatabaseClusterEngine.AURORA_MYSQL,
            default_database_name='core')

        # Handler for the hello endpoint
        hello_lambda = _lambda.Function(
            self,
            'HelloHandler',
            runtime=_lambda.Runtime.PYTHON_3_8,
            code=_lambda.Code.asset('lambda'),
            handler='hello.handler',
        )

        # Handler for the GET /user_accounts endpoint
        get_user_account_lambda = _lambda.Function(
            self,
            'GetUserAccount',
            runtime=_lambda.Runtime.PYTHON_3_8,
            code=_lambda.Code.asset('lambda'),
            handler='get_user_account.handler',
            environment={
                'CLUSTER_ARN': aurora_cluster.cluster_arn,
                'SECRET_ARN': aurora_cluster.secret.secret_arn,
                'DB_NAME': 'core',
            })

        # Grant the lambda function the required permissions to access the database
        aurora_cluster.grant_data_api_access(
            get_user_account_lambda)  # This also enables the data api

        # Cognito User Pool for sign up and authorization
        user_pool = cognito.UserPool(
            self,
            'user-pool',
            self_sign_up_enabled=True,
            auto_verify=cognito.AutoVerifiedAttrs(email=True, phone=False),
            sign_in_aliases=cognito.SignInAliases(email=True, username=True))

        # User pool client - used for sign up and sign in and password recovery
        user_pool.add_client('app-client',
                             auth_flows=cognito.AuthFlow(user_password=True,
                                                         user_srp=True))

        # Create the API Gateway REST API
        api = apigw.RestApi(self, 'test-api')

        # Create custom authorizer, modeled after work around detailed here:
        # https://github.com/aws/aws-cdk/issues/723#issuecomment-504753280
        cognito_authorizer = apigw.CfnAuthorizer(
            self,
            'CognitoAuthorizer',
            type='COGNITO_USER_POOLS',
            provider_arns=[user_pool.user_pool_arn],
            identity_source='method.request.header.Authorization',
            rest_api_id=api.rest_api_id,
            name='CognitoAuthorizer')

        # Hello resource and endpoint, handled by the hello lambda
        hello_resource = api.root.add_resource('hello')
        hello_method = hello_resource.add_method(
            'GET', integration=apigw.LambdaIntegration(hello_lambda))
        hello_method_resource = hello_method.node.find_child('Resource')
        hello_method_resource.add_property_override(
            'AuthorizationType', apigw.AuthorizationType.COGNITO)
        hello_method_resource.add_property_override(
            'AuthorizerId', {"Ref": cognito_authorizer.logical_id})

        # Add user_account resource
        user_account_resource = api.root.add_resource('user_account')

        # Add GET handler for user_account
        get_user_account_method = user_account_resource.add_method(
            'GET',
            integration=apigw.LambdaIntegration(get_user_account_lambda))
        get_user_account_method_resource = get_user_account_method.node.find_child(
            'Resource')
        get_user_account_method_resource.add_property_override(
            'AuthorizationType', apigw.AuthorizationType.COGNITO)
        get_user_account_method_resource.add_property_override(
            'AuthorizerId', {"Ref": cognito_authorizer.logical_id})
    def __init__(self, scope: core.Construct, construct_id: str,
                 cognito_prefix: str, stack_log_level: str, **kwargs) -> None:

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

        # The code that defines your stack goes here
        # Cognito User Pool for Kibana Access
        self.es_user_pool = _cognito.UserPool(
            self,
            "esCognitoUserPool",
            user_pool_name=f"{cognito_prefix}-log-parser-es-users",
            self_sign_up_enabled=False,
            sign_in_aliases=_cognito.SignInAliases(username=True, email=True),
            sign_in_case_sensitive=False,
            standard_attributes={
                "email": {
                    "required": True,
                    "mutable": False
                },
                "fullname": {
                    "required": False,
                    "mutable": True
                }
            },
            password_policy=_cognito.PasswordPolicy(min_length=8),
            auto_verify=_cognito.AutoVerifiedAttrs(email=True))

        # Create User Pool Domain to enable SignUp, SignIn, Auth & Callback Urls
        es_auth_domain = _cognito.UserPoolDomain(
            self,
            "esCognitoUserPoolDomain",
            user_pool=self.es_user_pool,
            cognito_domain=_cognito.CognitoDomainOptions(
                domain_prefix=f"{cognito_prefix}-{core.Aws.ACCOUNT_ID}"))

        # Role Authenticated Cognito Users will assume in ES Service
        self.es_role = _iam.Role(
            self,
            "esKibanaIamRole",
            assumed_by=_iam.ServicePrincipal("es.amazonaws.com"),
            managed_policies=[
                _iam.ManagedPolicy.from_aws_managed_policy_name(
                    managed_policy_name="AmazonESCognitoAccess")
            ],
        )

        # Create Cognito Federated Identity Pool to exchange Cognito auth token for AWS Token
        self.es_id_pool = _cognito.CfnIdentityPool(
            self,
            "esIdentityPool",
            allow_unauthenticated_identities=False,
            cognito_identity_providers=[],
        )

        # Role For Cognito Federated Idenity Pool Authenticated Users
        self.es_auth_role = _iam.Role(
            self,
            "esAuthIamRole",
            assumed_by=_iam.FederatedPrincipal(
                federated="cognito-identity.amazonaws.com",
                conditions={
                    "StringEquals": {
                        "cognito-identity.amazonaws.com:aud":
                        self.es_id_pool.ref
                    },
                    "ForAnyValue:StringLike": {
                        "cognito-identity.amazonaws.com:amr": "authenticated"
                    },
                },
                assume_role_action="sts:AssumeRoleWithWebIdentity"),
        )

        # Attach a Role to Cognito Federated Idenity Pool Authenticated Users
        _cognito.CfnIdentityPoolRoleAttachment(
            self,
            "cognitoFederatedIdentityPoolRoleAttachment",
            identity_pool_id=self.es_id_pool.ref,
            roles={"authenticated": self.es_auth_role.role_arn})

        ###########################################
        ################# 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,
            "CreateCognitoUserConsole",
            value=
            f"https://{core.Aws.REGION}.console.aws.amazon.com/cognito/users?region={core.Aws.REGION}#/pool/{self.es_user_pool.user_pool_id}/users",
            description="Create a new user in the user pool here.")