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)
def __init__(self, scope: core.Construct, id: str) -> None: super().__init__(scope, id) self.user_pool = aws_cognito.UserPool( self, "UsersPool", sign_in_aliases=aws_cognito.SignInAliases(username=True)) cfn_user_pool: aws_cognito.CfnUserPool = self.user_pool.node.default_child cfn_user_pool.policies = aws_cognito.CfnUserPool.PoliciesProperty( password_policy=aws_cognito.CfnUserPool.PasswordPolicyProperty( minimum_length=8, require_lowercase=False, require_numbers=False, require_symbols=False, require_uppercase=False)) user_pool_output = core.CfnOutput(self, id="KesherUserPoolID", value=self.user_pool.user_pool_id) user_pool_output.override_logical_id("KesherUserPoolID") user_pool_arn_output = core.CfnOutput( self, id="KesherUserPoolArn", value=self.user_pool.user_pool_arn) user_pool_arn_output.override_logical_id("KesherUserPoolArn") self.user_pool_client = aws_cognito.UserPoolClient( self, "PoolClient", user_pool=self.user_pool, auth_flows=AuthFlow(admin_user_password=False, user_password=True), ) auth_client_output = core.CfnOutput( self, id="AuthClientID", value=self.user_pool_client.user_pool_client_id) auth_client_output.override_logical_id("AuthClientID")
def __init__(self, scope: cdk.Construct, construct_id: str, user_pool=None, web_bucket=None, api_gateway=None, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) dirname = os.path.dirname(__file__) client = cognito.UserPoolClient(self, "UserPoolClient", user_pool=user_pool, auth_flows={ "user_password": True }, o_auth={ "flows": { "implicit_code_grant": True }, "callback_urls": ["{}/web/token.html".format(api_gateway.url)] } ) directory = Path("{}/target".format(dirname)) directory.mkdir(parents=True, exist_ok=True) for child in directory.iterdir(): os.remove(str(child)) with open("{}/target/client_id.json".format(dirname), "w") as text_file: text_file.write(json.dumps({'clientId': client.user_pool_client_id})) src_dir = Path("{}/src".format(dirname)) for child in src_dir.iterdir(): shutil.copy(str(child), str(directory)) s3_deployment.BucketDeployment(self, "WebDeployment", destination_bucket=web_bucket, sources=[s3_deployment.Source.asset(str(directory))] )
def _create_user_pool_client(self) -> cognito.UserPoolClient: return cognito.UserPoolClient( scope=self, id="user-pool-client", user_pool=self.user_pool, auth_flows=cognito.AuthFlow(user_srp=True, admin_user_password=False, custom=False), generate_secret=False, prevent_user_existence_errors=True, user_pool_client_name="orbit", )
def __init__(self, scope: core.Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) # Retrieves the ACM certificate arn from an SSM parameter certificate_arn = aws_ssm.StringParameter.value_from_lookup(self, 'cognito_certificate_arn') # Retrieves the domain name from an SSM parameter domain_name = aws_ssm.StringParameter.value_from_lookup(self, 'kubeflow-cognito-domain-name') # Creates the cognito user pool user_pool = aws_cognito.UserPool(self, 'UserPool', user_pool_name = 'mlplatform-user-pool', mfa = aws_cognito.Mfa.OFF, sign_in_aliases = aws_cognito.SignInAliases( username=True, email=True)) # Creates the cognito user pool client user_pool_client = aws_cognito.UserPoolClient(self, 'UserPoolClient', user_pool = user_pool, generate_secret = True, o_auth = aws_cognito.OAuthSettings( flows = aws_cognito.OAuthFlows( authorization_code_grant = True ), scopes = [ aws_cognito.OAuthScope.EMAIL, aws_cognito.OAuthScope.OPENID, aws_cognito.OAuthScope.PROFILE, aws_cognito.OAuthScope.COGNITO_ADMIN ], callback_urls = ['https://kubeflow.' + domain_name + '/oauth2/idpresponse'] ), user_pool_client_name = 'mlplatform-user-pool-client') # Initialises the ACM certificate cognito_custom_domain_certificate = aws_certificatemanager.Certificate.from_certificate_arn(self, 'DomainCertificate', certificate_arn) # Creates the cognito user pool domain user_pool_domain = aws_cognito.UserPoolDomain(self, 'UserPoolDomain', user_pool = user_pool, custom_domain = aws_cognito.CustomDomainOptions( certificate = cognito_custom_domain_certificate, domain_name = 'auth.' + domain_name ) )
def _create_user_pool(self) -> aws_cognito.UserPool: user_pool = aws_cognito.UserPool( scope=self, id="UserPoolX", # auto_verify=aws_cognito.AutoVerifiedAttrs(email=True), self_sign_up_enabled=True, required_attributes=RequiredAttributes(email=True), sign_in_aliases=SignInAliases(email=True), user_verification=UserVerificationConfig( email_style=VerificationEmailStyle.LINK)) aws_cognito.UserPoolClient(scope=self, user_pool=user_pool, id="AuthClientWeb", generate_secret=False) return user_pool
def __init__(self, scope: core.Construct, id: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) servu_userpool = aws_cognito.UserPool( self, 'servu-userpool', user_pool_name='ServU Users', sign_in_aliases=aws_cognito.SignInAliases(email=True), standard_attributes=aws_cognito.StandardAttributes( email=aws_cognito.StandardAttribute(required=True, mutable=True), fullname=aws_cognito.StandardAttribute(required=True, mutable=True), address=aws_cognito.StandardAttribute(required=True, mutable=True), phone_number=aws_cognito.StandardAttribute(required=True, mutable=True)), password_policy=aws_cognito.PasswordPolicy( min_length=8, require_digits=True, require_lowercase=True, require_symbols=True, require_uppercase=True, temp_password_validity=core.Duration.days(1)), sign_in_case_sensitive=False) servu_userpool_web_client = aws_cognito.UserPoolClient( self, 'servu-userpool-web-client', user_pool=servu_userpool, auth_flows=aws_cognito.AuthFlow(custom=True, refresh_token=True, user_srp=True), user_pool_client_name='ServU Web Client') # TODO: Manually configure the domain and callback URLs. Look at CFN for the Pool ID and Client ID core.CfnOutput(self, 'servu-userpool-id', value=servu_userpool.user_pool_id, export_name='servu-userpool-id') core.CfnOutput(self, 'servu-userpool-client-id', value=servu_userpool_web_client.user_pool_client_id, export_name='servu-userpool-client-id')
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/"))
def __init__(self, scope: core.Construct, id: str, props, **kwargs) -> None: super().__init__(scope, id, **kwargs) power_transformers = aws_dynamodb.Table( self, "PowerTransformers", table_name="PowerTransformers", partition_key=aws_dynamodb.Attribute( name="name", type=aws_dynamodb.AttributeType.STRING), removal_policy=core.RemovalPolicy.DESTROY) function = _lambda.Function( self, "power_transformers_data_enrichment", function_name="power_transformers_data_enrichment", runtime=_lambda.Runtime.PYTHON_3_7, handler="lambda_function.handler", code=_lambda.Code.asset("./lambda/data-enrichment")) function.add_environment('TABLE_NAME', power_transformers.table_name) function.add_to_role_policy( iam.PolicyStatement(actions=['dynamodb:GetItem'], resources=[f"{power_transformers.table_arn}"], effect=iam.Effect.ALLOW)) function.add_permission( principal=iam.ServicePrincipal('iotanalytics.amazonaws.com'), action='lambda:InvokeFunction', id='pt-iot-analytics') bucket = s3.Bucket( self, 'PowerTransformersTelemetryBucket', bucket_name=f"{props['projectName'].lower()}-{core.Aws.ACCOUNT_ID}", removal_policy=core.RemovalPolicy.DESTROY) output_bucket = s3.Bucket( self, 'PowerTransformersProcessedDataBucket', bucket_name= f"{props['projectName'].lower()}-output-{core.Aws.ACCOUNT_ID}", removal_policy=core.RemovalPolicy.DESTROY) # Apply least privilege s3_role = iam.Role( self, "IotAnalyticsS3Role", assumed_by=iam.ServicePrincipal("iotanalytics.amazonaws.com"), managed_policies=[ iam.ManagedPolicy.from_aws_managed_policy_name( 'AmazonS3FullAccess') ]) # s3_role.add_to_policy(iam.PolicyStatement(actions=["s3:PutObject", "s3:DeleteObject", "s3:GetBucketLocation"], # resources=[f"{bucket.bucket_arn}", f"{bucket.bucket_arn}/*"], effect=iam.Effect.ALLOW)) # Apply least privilege s3_output_role = iam.Role( self, "IotAnalyticsS3OutputRole", assumed_by=iam.ServicePrincipal("iotanalytics.amazonaws.com"), managed_policies=[ iam.ManagedPolicy.from_aws_managed_policy_name( 'AmazonS3FullAccess') ], ) # s3_output_role.add_to_policy(iam.PolicyStatement(actions=["s3:PutObject", "s3:DeleteObject", "s3:GetBucketLocation"], # resources=[f"{output_bucket.bucket_arn}", f"{output_bucket.bucket_arn}/*"], effect=iam.Effect.ALLOW)) project_name = props['projectName'].lower().replace('-', '_') channel_name = f"{project_name}_channel" datastore_name = f"{project_name}_datastore" channel_s3 = CHANNEL.CustomerManagedS3Property( bucket=bucket.bucket_name, key_prefix='raw/', role_arn=s3_role.role_arn) channel_storage = CHANNEL.ChannelStorageProperty( customer_managed_s3=channel_s3) CHANNEL(self, 'iot_channel', channel_name=channel_name, channel_storage=channel_storage) datastore_s3 = DATASTORE.CustomerManagedS3Property( bucket=bucket.bucket_name, key_prefix='processed/', role_arn=s3_role.role_arn) datastore_storage = DATASTORE.DatastoreStorageProperty( customer_managed_s3=datastore_s3) datastore = DATASTORE(self, 'iot_datastore', datastore_name=datastore_name, datastore_storage=datastore_storage) channel_activity = PIPELINE.ChannelProperty(name='ChannelActivity', channel_name=channel_name, next='LambdaActivity') lambda_activity = PIPELINE.LambdaProperty( name='LambdaActivity', lambda_name='power_transformers_data_enrichment', next='DatastoreActivity', batch_size=10) datastore_activity = PIPELINE.DatastoreProperty( name='DatastoreActivity', datastore_name=datastore_name) pipeline_activities = PIPELINE.ActivityProperty( channel=channel_activity, lambda_=lambda_activity, datastore=datastore_activity) pipeline = PIPELINE(self, 'iot_pipeline', pipeline_name=f"{project_name}_pipeline", pipeline_activities=[pipeline_activities]) pipeline.add_depends_on(datastore) query_action = DATASET.QueryActionProperty( sql_query=f"SELECT * FROM {datastore_name}") action = DATASET.ActionProperty(query_action=query_action, action_name='sqlAction') schedule_expression = DATASET.ScheduleProperty( schedule_expression='cron(1/5 * * * ? *)') trigger_schedule = DATASET.TriggerProperty( schedule=schedule_expression) dataset_s3_destination = DATASET.S3DestinationConfigurationProperty( bucket=output_bucket.bucket_name, key= 'dataset/Version/!{iotanalytics:scheduleTime}_!{iotanalytics:versionId}.csv', role_arn=s3_output_role.role_arn) dataset_destination = DATASET.DatasetContentDeliveryRuleDestinationProperty( s3_destination_configuration=dataset_s3_destination) content_delivery_rules = DATASET.DatasetContentDeliveryRuleProperty( destination=dataset_destination) dataset = DATASET(self, 'iot_dataset', dataset_name=f"{project_name}_dataset", actions=[action], triggers=[trigger_schedule], content_delivery_rules=[content_delivery_rules]) dataset.add_depends_on(datastore) user_pool = aws_cognito.UserPool( self, 'kibanaUserPool', self_sign_up_enabled=False, sign_in_aliases=aws_cognito.SignInAliases(username=True, email=True)) aws_cognito.CfnUserPoolDomain( self, 'userPoolDomain', user_pool_id=user_pool.user_pool_id, domain= f"{props['projectName'].lower()}-{''.join(random.choices(string.ascii_lowercase + string.digits, k=6))}" ) user_pool_client = aws_cognito.UserPoolClient(self, 'kibanaClientId', user_pool=user_pool, generate_secret=True) identity_provider = aws_cognito.CfnIdentityPool.CognitoIdentityProviderProperty( client_id=user_pool_client.user_pool_client_id, provider_name=user_pool.user_pool_provider_name) identity_pool = aws_cognito.CfnIdentityPool( self, 'identityPool', allow_unauthenticated_identities=False, cognito_identity_providers=[identity_provider]) # Apply least privilege cognito_authenticated_role = iam.Role( self, "CognitoAuthRole", assumed_by=iam.FederatedPrincipal( "cognito-identity.amazonaws.com", assume_role_action='sts:AssumeRoleWithWebIdentity', conditions={ 'StringEquals': { 'cognito-identity.amazonaws.com:aud': identity_pool.ref }, 'ForAnyValue:StringLike': { 'cognito-identity.amazonaws.com:amr': 'authenticated' } }), managed_policies=[ iam.ManagedPolicy.from_aws_managed_policy_name( 'AmazonESFullAccess') ]) aws_cognito.CfnIdentityPoolRoleAttachment( self, 'identityPoolRoleAttachment', identity_pool_id=identity_pool.ref, roles={'authenticated': cognito_authenticated_role.role_arn}) cognito_options = DOMAIN.CognitoOptionsProperty( enabled=True, user_pool_id=user_pool.user_pool_id, identity_pool_id=identity_pool.ref, role_arn= f"arn:aws:iam::{core.Aws.ACCOUNT_ID}:role/service-role/CognitoAccessForAmazonES" ) ebs_options = DOMAIN.EBSOptionsProperty(ebs_enabled=True, volume_size=10, volume_type='gp2') elasticsearch_cluster_config = DOMAIN.ElasticsearchClusterConfigProperty( instance_count=1, instance_type='r5.large.elasticsearch') encryption_at_rest_options = DOMAIN.EncryptionAtRestOptionsProperty( enabled=True) node_to_node_encryption_options = DOMAIN.NodeToNodeEncryptionOptionsProperty( enabled=True) snapshot_options = DOMAIN.SnapshotOptionsProperty( automated_snapshot_start_hour=0) es_domain_arn = f"arn:aws:es:{core.Aws.REGION}:{core.Aws.ACCOUNT_ID}:domain/{props['projectName'].lower()}/*" es_policy_statement = iam.PolicyStatement(actions=['es:*'], resources=[es_domain_arn]) es_policy_statement.add_arn_principal( cognito_authenticated_role.role_arn) policy_document = iam.PolicyDocument() policy_document.add_statements(es_policy_statement) domain = DOMAIN( self, 'elasticsearch', domain_name=f"{props['projectName'].lower()}", cognito_options=cognito_options, ebs_options=ebs_options, elasticsearch_cluster_config=elasticsearch_cluster_config, encryption_at_rest_options=encryption_at_rest_options, node_to_node_encryption_options=node_to_node_encryption_options, snapshot_options=snapshot_options, elasticsearch_version='6.8', access_policies=policy_document) function = _lambda.Function( self, "load_data_from_s3_to_es", function_name="load_data_from_s3_to_es", runtime=_lambda.Runtime.PYTHON_3_7, handler="lambda_function.handler", code=_lambda.Code.asset("./lambda/load-data-from-s3-to-es.zip")) function.add_environment('ES_HOST', domain.attr_domain_endpoint) function.add_environment('ES_REGION', f"{core.Aws.REGION}") function.add_to_role_policy( iam.PolicyStatement(actions=['es:ESHttpPost'], resources=[es_domain_arn], effect=iam.Effect.ALLOW)) function.add_to_role_policy( iam.PolicyStatement(actions=['s3:GetObject'], resources=[f"{output_bucket.bucket_arn}/*"], effect=iam.Effect.ALLOW)) notification = aws_s3_notifications.LambdaDestination(function) output_bucket.add_event_notification(s3.EventType.OBJECT_CREATED, notification) load_ddb_custom_resource = LoadDDBDataCustomResource( self, "LoadDDBData", table_name=power_transformers.table_name, table_arn=power_transformers.table_arn) load_ddb_custom_resource.node.add_dependency(power_transformers) load_es_index_custom_resource = LoadESIndexCustomResource( self, "LoadESIndex", es_host=domain.attr_domain_endpoint, es_region=f"{core.Aws.REGION}", es_domain_arn=es_domain_arn) load_es_index_custom_resource.node.add_dependency(domain) load_kibana_dashboards_custom_resource = LoadKibanaDashboardsCustomResource( self, "LoadKibanaDashboards", es_host=domain.attr_domain_endpoint, es_region=f"{core.Aws.REGION}", es_domain_arn=es_domain_arn) load_kibana_dashboards_custom_resource.node.add_dependency( load_es_index_custom_resource)
def __init__(self, scope: core.Construct, id: str, instance_id: str, contact_flow_id: str, source_phone_number: str, timeout: int, **kwargs): super().__init__(scope, id, **kwargs) web_bucket = _s3.Bucket(self, "StaticWebBucket", website_index_document="index.html", website_error_document="index.html", removal_policy=core.RemovalPolicy.DESTROY, public_read_access=True) core.CfnOutput(self, 'WebBucketUrl', value=web_bucket.bucket_domain_name) web_distribution = _clf.CloudFrontWebDistribution( self, 'StaticWebDistribution', origin_configs=[ _clf.SourceConfiguration( s3_origin_source=_clf.S3OriginConfig( s3_bucket_source=web_bucket), behaviors=[_clf.Behavior(is_default_behavior=True)]) ], viewer_protocol_policy=_clf.ViewerProtocolPolicy.REDIRECT_TO_HTTPS) _s3d.BucketDeployment( self, "S3StaticWebContentDeploymentWithInvalidation", sources=[ _s3d.Source.asset( f"{pathlib.Path(__file__).parent.absolute()}/site-content/build" ) ], destination_bucket=web_bucket, distribution=web_distribution, distribution_paths=["/*"]) file_bucket = _s3.Bucket(self, "FileBucket", removal_policy=core.RemovalPolicy.DESTROY) call_dead_letter_queue = _sqs.Queue(self, "CallDeadLetterQueue", fifo=True, content_based_deduplication=True) call_sqs_queue = _sqs.Queue( self, "CallSqsQueue", fifo=True, content_based_deduplication=True, visibility_timeout=core.Duration.seconds(120), dead_letter_queue=_sqs.DeadLetterQueue( max_receive_count=1, queue=call_dead_letter_queue)) async_call_dead_letter_queue = _sqs.Queue( self, "AsyncCallDeadLetterQueue", fifo=True, content_based_deduplication=True) async_callout_queue = _sqs.Queue( self, "AsyncCalloutQueue", fifo=True, content_based_deduplication=True, visibility_timeout=core.Duration.seconds(120), dead_letter_queue=_sqs.DeadLetterQueue( max_receive_count=1, queue=async_call_dead_letter_queue)) call_job_complete_sns_topic = _sns.Topic( self, "CallJobCompleteSnsTopic", display_name="CallJobCompletion") call_result_table = _dynamodb.Table( self, "CallResultDynamodbTable", billing_mode=_dynamodb.BillingMode.PAY_PER_REQUEST, partition_key=_dynamodb.Attribute( name="task_id", type=_dynamodb.AttributeType.STRING), sort_key=_dynamodb.Attribute(name="receiver_id", type=_dynamodb.AttributeType.STRING), removal_policy=core.RemovalPolicy.DESTROY) callout_record_table = _dynamodb.Table( self, "CallTaskDynamodbTable", billing_mode=_dynamodb.BillingMode.PAY_PER_REQUEST, partition_key=_dynamodb.Attribute( name="task_id", type=_dynamodb.AttributeType.STRING), sort_key=_dynamodb.Attribute(name="created_at", type=_dynamodb.AttributeType.NUMBER), removal_policy=core.RemovalPolicy.DESTROY) callout_record_table.add_global_secondary_index( partition_key=_dynamodb.Attribute( name='call_type', type=_dynamodb.AttributeType.STRING), sort_key=_dynamodb.Attribute(name='created_at', type=_dynamodb.AttributeType.NUMBER), index_name='CallTypeCreatedAtGlobalIndex', projection_type=_dynamodb.ProjectionType.ALL) python_function_layer = _lambda.LayerVersion( self, "LambdaPythonFunctionLayer", code=_lambda.Code.asset("aws_callouts_cdk/layer/_python"), compatible_runtimes=[ _lambda.Runtime.PYTHON_3_7, _lambda.Runtime.PYTHON_3_8 ], license="Available under the MIT-0 license") nodejs_function_layer = _lambda.LayerVersion( self, "LambdaNodeJsFunctionLayer", code=_lambda.Code.asset("aws_callouts_cdk/layer/_nodejs"), compatible_runtimes=[ _lambda.Runtime.NODEJS_10_X, _lambda.Runtime.NODEJS_12_X ], license="Available under the MIT-0 license") global_python_function_arguments = { "code": _lambda.Code.asset("aws_callouts_cdk/src/python"), "layers": [python_function_layer], "runtime": _lambda.Runtime.PYTHON_3_7 } global_nodeje_function_arguments = { "code": _lambda.Code.asset("aws_callouts_cdk/src/nodejs"), "layers": [nodejs_function_layer], "runtime": _lambda.Runtime.NODEJS_12_X } get_callout_job_function = _lambda.Function( self, "GetCalloutJobFunction", handler="get_call_job.lambda_handler", **global_python_function_arguments) get_callout_job_function.add_environment(key="S3Bucket", value=file_bucket.bucket_name) file_bucket.grant_read(get_callout_job_function) callout_function = _lambda.Function(self, "CalloutFunction", handler="send_call.lambda_handler", **global_python_function_arguments) callout_function.add_environment( key="ContactFlowArn", value= f"arn:aws:connect:{self.region}:{self.account}:instance/{instance_id}/contact-flow/{contact_flow_id}" ) callout_function.add_environment(key="SourcePhoneNumber", value=source_phone_number) callout_function.add_environment(key="ExcelFileBucket", value=file_bucket.bucket_name) callout_function.add_environment(key="AsynCalloutQueueUrl", value=async_callout_queue.queue_url) callout_function.add_to_role_policy(statement=_iam.PolicyStatement( resources=[ f"arn:aws:connect:{self.region}:{self.account}:instance/{instance_id}/*" ], actions=["connect:StartOutboundVoiceContact"])) callout_function.add_event_source(source=_les.SqsEventSource( queue=async_callout_queue, batch_size=1)) 'arn:aws:connect:751225572132:ap-southeast-2:instance/9d0c7cc5-7d2a-42e4-a3dd-70f402e0d040' file_bucket.grant_read_write(callout_function) response_handler_function = _lambda.Function( self, "ResponseHandlerFunction", handler="response_handler.lambda_handler", **global_python_function_arguments) response_handler_function.add_permission( id="ResponseHandlerFunctionLambdaInvokePermission", principal=_iam.ServicePrincipal(service="connect.amazonaws.com"), action="lambda:InvokeFunction", source_account=self.account, source_arn= f"arn:aws:connect:{self.region}:{self.account}:instance/{instance_id}" ) send_task_success_function = _lambda.Function( self, "SendTaskSuccessFunction", handler="send_task_success.lambda_handler", **global_python_function_arguments) send_task_success_function.add_permission( id="SendTaskSuccessFunctionLambdaInvokePermission", principal=_iam.ServicePrincipal(service="connect.amazonaws.com"), action="lambda:InvokeFunction", source_account=self.account, source_arn= f"arn:aws:connect:{self.region}:{self.account}:instance/{instance_id}" ) get_call_result_function = _lambda.Function( self, "GetCallResultFunction", handler="get_call_result.lambda_handler", memory_size=512, **global_python_function_arguments) get_call_result_function.add_environment( key="CallResultDynamoDBTable", value=call_result_table.table_name) get_call_result_function.add_environment(key="S3Bucket", value=file_bucket.bucket_name) call_result_table.grant_read_data(grantee=get_call_result_function) file_bucket.grant_read_write(get_call_result_function) iterator_function = _lambda.Function( self, "IteratorFunction", handler="iterator.lambda_handler", **global_python_function_arguments) iterator_function.add_permission( id="IteratorFunctionLambdaInvokePermission", principal=_iam.ServicePrincipal(service="connect.amazonaws.com"), action="lambda:InvokeFunction", source_account=self.account, source_arn= f"arn:aws:connect:{self.region}:{self.account}:instance/{instance_id}" ) create_appsync_call_task_function = _lambda.Function( self, "CreateAppSyncCallTaskFunction", handler="create_appsync_call_task.lambda_handler", **global_nodeje_function_arguments) create_appsync_call_task_function.add_environment( key="CallSqsQueueUrl", value=call_sqs_queue.queue_url) create_appsync_call_task_function.add_environment( key="CallRecordTableName", value=callout_record_table.table_name) call_sqs_queue.grant_send_messages(create_appsync_call_task_function) callout_record_table.grant_write_data( create_appsync_call_task_function) create_call_report_record_function = _lambda.Function( self, "CreateCallReportRecordFunction", handler="create_call_report_record.lambda_handler", **global_nodeje_function_arguments) create_excel_call_task_function = _lambda.Function( self, "CreateExcelCallTaskFunction", handler="create_excel_call_task.lambda_handler", **global_python_function_arguments) create_excel_call_task_function.add_environment( key="CallSqsQueueUrl", value=call_sqs_queue.queue_url) call_sqs_queue.grant_send_messages(create_excel_call_task_function) create_excel_call_task_function.add_event_source( source=_les.S3EventSource(bucket=file_bucket, events=[_s3.EventType.OBJECT_CREATED], filters=[ _s3.NotificationKeyFilter( prefix="call_task", suffix=".xlsx") ])) start_callout_flow_function = _lambda.Function( self, "StartCalloutFlowFunction", handler="start_call_out_flow.lambda_handler", reserved_concurrent_executions=1, **global_python_function_arguments) start_callout_flow_function.add_environment( key="CallSqsQueueUrl", value=call_sqs_queue.queue_url) start_callout_flow_function.add_environment( key="ResponseHandlerFunctionArn", value=response_handler_function.function_arn) start_callout_flow_function.add_environment( key="IteratorFunctionArn", value=iterator_function.function_arn) start_callout_flow_function.add_environment( key="SendTaskSuccessFunctionArn", value=send_task_success_function.function_arn) start_callout_flow_function.add_environment( key="S3Bucket", value=file_bucket.bucket_name) start_callout_flow_function.add_event_source( source=_les.SqsEventSource(queue=call_sqs_queue, batch_size=1)) file_bucket.grant_read_write(start_callout_flow_function) call_state_machine_definition = { "Comment": "Reading messages from an SQS queue and iteratively processing each message.", "StartAt": "Start", "States": { "Start": { "Type": "Pass", "Next": "Process Call Messages" }, "Process Call Messages": { "Type": "Map", "Next": "Get Call Result", "InputPath": "$", "ItemsPath": "$", "OutputPath": "$.[0]", "Iterator": { "StartAt": "Get Call out job", "States": { "Get Call out job": { "Type": "Task", "Resource": get_callout_job_function.function_arn, "Next": "Callout with AWS Connect" }, "Callout with AWS Connect": { "Type": "Task", "Resource": "arn:aws:states:::sqs:sendMessage.waitForTaskToken", "TimeoutSeconds": timeout, "Parameters": { "QueueUrl": async_callout_queue.queue_url, "MessageGroupId": "1", "MessageBody": { "Message.$": "$", "TaskToken.$": "$$.Task.Token" } }, "Catch": [{ "ErrorEquals": ["States.Timeout"], "ResultPath": None, "Next": "Call Timeout" }], "Next": "Save call result" }, "Call Timeout": { "Type": "Pass", "ResultPath": None, "Next": "Save call result" }, "Save call result": { "Type": "Task", "Resource": "arn:aws:states:::dynamodb:putItem", "Parameters": { "TableName": call_result_table.table_name, "Item": { "receiver_id": { "S.$": "$.receiver_id" }, "task_id": { "S.$": "$.task_id" }, "username": { "S.$": "$.username" }, "phone_number": { "S.$": "$.phone_number" }, "status": { "S.$": "$.status" }, "answers": { "S.$": "$.answers" }, "error": { "S.$": "$.error" }, "call_at": { "S.$": "$.call_at" } } }, "ResultPath": "$.Result", "OutputPath": "$.task_id", "End": True } } } }, "Get Call Result": { "Type": "Task", "Resource": get_call_result_function.function_arn, "Next": "Create Call Report Record" }, "Create Call Report Record": { "Type": "Task", "Resource": create_call_report_record_function.function_arn, "Next": "Send Completion message to SNS" }, "Send Completion message to SNS": { "Type": "Task", "Resource": "arn:aws:states:::sns:publish", "Parameters": { "TopicArn": call_job_complete_sns_topic.topic_arn, "Message.$": "$" }, "Next": "Finish" }, "Finish": { "Type": "Succeed" } } } callout_state_machine_role = _iam.Role( self, "CalloutStatesExecutionRole", assumed_by=_iam.ServicePrincipal( f"states.{self.region}.amazonaws.com")) callout_state_machine_role.add_to_policy( _iam.PolicyStatement( actions=[ "sqs:SendMessage", "dynamodb:PutItem", "lambda:InvokeFunction", "SNS:Publish" ], resources=[ async_callout_queue.queue_arn, call_result_table.table_arn, get_callout_job_function.function_arn, get_call_result_function.function_arn, call_job_complete_sns_topic.topic_arn, create_appsync_call_task_function.function_arn, create_call_report_record_function.function_arn ])) callout_state_machine = _sfn.CfnStateMachine( self, "CalloutStateMachine", role_arn=callout_state_machine_role.role_arn, definition_string=json.dumps(call_state_machine_definition)) send_task_success_function.add_to_role_policy( _iam.PolicyStatement(actions=["states:SendTaskSuccess"], resources=[callout_state_machine.ref])) start_callout_flow_function.add_environment( key="CalloutStateMachineArn", value=callout_state_machine.ref) start_callout_flow_function.add_to_role_policy( _iam.PolicyStatement(effect=_iam.Effect.ALLOW, resources=[callout_state_machine.ref], actions=['states:StartExecution'])) user_pool = _cognito.UserPool( self, "UserPool", sign_in_type=_cognito.SignInType.USERNAME) user_pool_client = _cognito.UserPoolClient(self, "UserPoolClient", user_pool=user_pool) appsync_api = _appsync.GraphQLApi( self, "AppSyncApi", name="AWSCalloutApi", user_pool_config=_appsync.UserPoolConfig( user_pool=user_pool, default_action=_appsync.UserPoolDefaultAction.ALLOW), log_config=_appsync.LogConfig( field_log_level=_appsync.FieldLogLevel.ALL), schema_definition_file= f"{pathlib.Path(__file__).parent.absolute()}/schema.graphql") callout_record_ddb_ds = appsync_api.add_dynamo_db_data_source( name="CalloutRecordDdb", description="Callout Record DynamoDB Data Source", table=callout_record_table) callout_record_ddb_ds.create_resolver( type_name="Query", field_name="getLatestCallTaskRecords", request_mapping_template=_appsync.MappingTemplate.from_string( '{"version":"2017-02-28","operation":"Query","index":"CallTypeCreatedAtGlobalIndex","query":{"expression":"call_type = :call_type","expressionValues":{":call_type":{"S":"TASK"}}},"scanIndexForward":false,"limit":${ctx.args.limit}}' ), response_mapping_template=_appsync.MappingTemplate. dynamo_db_result_list()) callout_record_ddb_ds.create_resolver( type_name="Query", field_name="getLatestCallReportRecords", request_mapping_template=_appsync.MappingTemplate.from_string( '{"version":"2017-02-28","operation":"Query","index":"CallTypeCreatedAtGlobalIndex","query":{"expression":"call_type = :call_type","expressionValues":{":call_type":{"S":"REPORT"}}},"scanIndexForward":false,"limit":${ctx.args.limit}}' ), response_mapping_template=_appsync.MappingTemplate. dynamo_db_result_list()) callout_record_ddb_ds.create_resolver( type_name="Mutation", field_name="createCallReport", request_mapping_template=_appsync.MappingTemplate.from_string( '{"version":"2017-02-28","operation":"PutItem","key":{"task_id":{"S":"${ctx.args.report.task_id}"},"created_at":{"N":"${ctx.args.report.created_at}"}},"attributeValues":$util.dynamodb.toMapValuesJson($ctx.args.report)}' ), response_mapping_template=_appsync.MappingTemplate. dynamo_db_result_item()) call_task_lambda_ds = appsync_api.add_lambda_data_source( name="CallTaskLambda", description="Call Task Lambda Data Source", lambda_function=create_appsync_call_task_function) call_task_lambda_ds.create_resolver( type_name="Mutation", field_name="createCallTask", request_mapping_template=_appsync.MappingTemplate.lambda_request( "$utils.toJson($ctx.args)"), response_mapping_template=_appsync.MappingTemplate.lambda_result()) create_call_report_record_function.add_environment( value=appsync_api.graph_ql_url, key="AppSyncGraphQlApiUrl") create_call_report_record_function.add_to_role_policy( statement=_iam.PolicyStatement( effect=_iam.Effect.ALLOW, actions=['appsync:GraphQL'], resources=[ f"{appsync_api.arn}/types/Mutation/fields/createCallReport" ])) core.CfnOutput(self, id="OutputCallSqsQueue", value=call_sqs_queue.queue_arn) core.CfnOutput(self, id="OutputCallJobCompletionSNSTopic", value=call_job_complete_sns_topic.topic_arn) core.CfnOutput(self, id="OutputExcelFileS3Bucket", value=file_bucket.bucket_name) core.CfnOutput(self, id="OutputStaticWebS3Bucket", value=web_bucket.bucket_name) core.CfnOutput(self, id="OutputStaticWebUrl", value=web_bucket.bucket_website_url) identity_pool = _cognito.CfnIdentityPool( self, "IdentityPool", allow_unauthenticated_identities=True, cognito_identity_providers=[ _cognito.CfnIdentityPool.CognitoIdentityProviderProperty( provider_name=user_pool.user_pool_provider_name, client_id=user_pool_client.user_pool_client_id) ]) identity_pool_unauthorized_role = _iam.Role( self, 'IdentityPoolUnAuthorizedRole', assumed_by=_iam.FederatedPrincipal( federated="cognito-identity.amazonaws.com", assume_role_action="sts:AssumeRoleWithWebIdentity", conditions={ "StringEquals": { "cognito-identity.amazonaws.com:aud": identity_pool.ref }, "ForAnyValue:StringLike": { "cognito-identity.amazonaws.com:amr": "unauthenticated" } })) identity_pool_unauthorized_role.add_to_policy( _iam.PolicyStatement( effect=_iam.Effect.ALLOW, actions=["appsync:GraphQL"], resources=[ f"{appsync_api.arn}/types/*", # f"{appsync_api.arn}/types/Query/fields/getLatestCallTaskRecords", # f"{appsync_api.arn}/types/Query/fields/getLatestCallReportRecords", # f"{appsync_api.arn}/types/Mutation/fields/createCallRecord", # f"{appsync_api.arn}/types/Subscription/fields/createCallTask", # f"{appsync_api.arn}/types/Subscription/fields/createCallReport" ])) _cognito.CfnIdentityPoolRoleAttachment( self, "CognitoIdentityPoolRoleAttachment", identity_pool_id=identity_pool.ref, roles={ "unauthenticated": identity_pool_unauthorized_role.role_arn }) core.CfnOutput(self, id="UserPoolId", value=user_pool.user_pool_id) core.CfnOutput(self, id="UserPoolClientId", value=user_pool_client.user_pool_client_id) core.CfnOutput(self, id="IdentityPoolId", value=identity_pool.ref)
def __init__(self, scope: core.Construct, id: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) # Get the hosted Zone and create a certificate for our domain hosted_zone = route53.HostedZone.from_hosted_zone_attributes( self, "HostedZone", hosted_zone_id=HOSTED_ZONE_ID, zone_name=HOSTED_ZONE_NAME) cert = certificatemanager.DnsValidatedCertificate( self, "Certificate", hosted_zone=hosted_zone, domain_name=APP_DNS_NAME) # Set up a new VPC vpc = ec2.Vpc(self, "FargateDemoVpc", max_azs=2) # Set up an ECS Cluster for fargate cluster = ecs.Cluster(self, "FargateCluster", vpc=vpc) # Configure the user pool and related entities for authentication user_pool = cognito.UserPool( self, "UserPool", self_sign_up_enabled=True, user_pool_name="FargateDemoUserPool", ) user_pool_custom_domain = cognito.CfnUserPoolDomain( self, "CustomDomain", domain=COGNITO_CUSTOM_DOMAIN, user_pool_id=user_pool.user_pool_id) user_pool_client = cognito.UserPoolClient( self, "AppClient", user_pool=user_pool, user_pool_client_name="AlbAuthentication", generate_secret=True) # Set the attributes on the user pool client that can't be updated via the construct user_pool_client_cf: cognito.CfnUserPoolClient = user_pool_client.node.default_child user_pool_client_cf.allowed_o_auth_flows = ["code"] user_pool_client_cf.allowed_o_auth_scopes = ["openid"] user_pool_client_cf.callback_ur_ls = [ f"https://{APP_DNS_NAME}/oauth2/idpresponse", f"https://{APP_DNS_NAME}" ] user_pool_client_cf.default_redirect_uri = f"https://{APP_DNS_NAME}/oauth2/idpresponse" user_pool_client_cf.logout_ur_ls = [ f"https://{APP_DNS_NAME}/logout", f"https://{APP_DNS_NAME}/" ] user_pool_client_cf.supported_identity_providers = [ # This is where you'd add external identity providers as well. "COGNITO" ] user_pool_client_cf.allowed_o_auth_flows_user_pool_client = True # Define the Docker Image for our container (the CDK will do the build and push for us!) docker_image = ecr_assets.DockerImageAsset( self, "JwtApp", directory=os.path.join(os.path.dirname(__file__), "..", "src")) user_pool_domain = f"{user_pool_custom_domain.domain}.auth.{self.region}.amazoncognito.com" # Define the fargate service + ALB fargate_service = ecs_patterns.ApplicationLoadBalancedFargateService( self, "FargateService", cluster=cluster, certificate=cert, domain_name=f"{APP_DNS_NAME}", domain_zone=hosted_zone, task_image_options={ "image": ecs.ContainerImage.from_docker_image_asset(docker_image), "environment": { "PORT": "80", "LOGOUT_URL": f"https://{user_pool_domain}/logout?" + f"client_id={user_pool_client.user_pool_client_id}&" + f"redirect_uri={ urllib.parse.quote(f'https://{APP_DNS_NAME}')}&" + f"response_type=code&state=STATE&scope=openid" } }) # Add an additional HTTPS egress rule to the Load Balancers security group to talk to Cognito lb_security_group = fargate_service.load_balancer.connections.security_groups[ 0] lb_security_group.add_egress_rule( peer=ec2.Peer.any_ipv4(), connection=ec2.Port(protocol=ec2.Protocol.TCP, string_representation="443", from_port=443, to_port=443), description="Outbound HTTPS traffic to get to Cognito") # Allow 10 seconds for in flight requests before termination, the default of 5 minutes is much too high. fargate_service.target_group.set_attribute( key="deregistration_delay.timeout_seconds", value="10") # Enable authentication on the Load Balancer alb_listener: elb.CfnListener = fargate_service.listener.node.default_child elb.CfnListenerRule( self, "AuthenticateRule", actions=[{ "type": "authenticate-cognito", "authenticateCognitoConfig": elb.CfnListenerRule.AuthenticateCognitoConfigProperty( user_pool_arn=user_pool.user_pool_arn, user_pool_client_id=user_pool_client.user_pool_client_id, user_pool_domain=user_pool_custom_domain.domain), "order": 1 }, { "type": "forward", "order": 10, "targetGroupArn": fargate_service.target_group.target_group_arn }], conditions=[{ "field": "host-header", "hostHeaderConfig": { "values": [f"{APP_DNS_NAME}"] } }], # Reference the Listener ARN listener_arn=alb_listener.ref, priority=1000)
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) user_pool = aws_cognito.UserPool( self, 'UserPool', user_pool_name='UserPoolForApiGateway', removal_policy=cdk.RemovalPolicy.DESTROY, self_sign_up_enabled=True, sign_in_aliases={'email': True}, auto_verify={'email': True}, password_policy={ 'min_length': 8, 'require_lowercase': False, 'require_digits': False, 'require_uppercase': False, 'require_symbols': False, }, account_recovery=aws_cognito.AccountRecovery.EMAIL_ONLY) user_pool_client = aws_cognito.UserPoolClient( self, 'UserPoolClient', user_pool=user_pool, auth_flows={ 'admin_user_password': True, 'user_password': True, 'custom': True, 'user_srp': True }, supported_identity_providers=[ aws_cognito.UserPoolClientIdentityProvider.COGNITO ]) auth = aws_apigateway.CognitoUserPoolsAuthorizer( self, 'AuthorizerForHelloWorldApi', cognito_user_pools=[user_pool]) helloworld_lambda_fn = aws_lambda.Function( self, 'HelloWorldLambdaFn', runtime=aws_lambda.Runtime.PYTHON_3_9, function_name="HelloWorldApi", handler="helloworld.lambda_handler", description='Function that returns 200 with "Hello world!"', code=aws_lambda.Code.from_asset( os.path.join(os.path.dirname(__file__), 'src/main/python')), timeout=cdk.Duration.minutes(5)) helloworld_lambda_rest_api = aws_apigateway.LambdaRestApi( self, 'HelloWorldLambdaRestApi', rest_api_name="helloworld-api", handler=helloworld_lambda_fn, proxy=False, deploy=True, deploy_options=aws_apigateway.StageOptions(stage_name="v1"), endpoint_export_name='ApiGatewayRestApiEndpoint') hello = helloworld_lambda_rest_api.root.add_resource("hello") hello.add_method( 'GET', aws_apigateway.LambdaIntegration(handler=helloworld_lambda_fn), authorization_type=aws_apigateway.AuthorizationType.COGNITO, authorizer=auth) cdk.CfnOutput(self, 'UserPoolId', value=user_pool.user_pool_id) cdk.CfnOutput(self, 'UserPoolClientId', value=user_pool_client.user_pool_client_id)
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 base_cognito_user_pool(construct, **kwargs): """ Function that generates a Cognito User Pool. :param construct: Custom construct that will use this function. From the external construct is usually 'self'. :param kwargs: Consist of required 'queue_name' and optionals 'queue_delivery_delay' and 'queue_visibility_timeout'. :return: DynamoDB Table Construct. """ user_pool_name = construct.prefix + "_" + kwargs[ "pool_name"] + "_pool_" + construct.environment_ if kwargs.get("email") is not None: email_settings = cognito.EmailSettings( from_=kwargs["email"]["from"], reply_to=kwargs["email"].get("reply_to")) else: email_settings = None password_policy_settings = kwargs.get("password_policy") temporary_password_validation_time = ( core.Duration.days( password_policy_settings.get("temporary_password_duration")) if password_policy_settings.get("temporary_password_duration") is not None else None) password_policy = cognito.PasswordPolicy( min_length=password_policy_settings.get("minimum_length"), temp_password_validity=temporary_password_validation_time, require_lowercase=password_policy_settings.get("require", {}).get("lower_case"), require_uppercase=password_policy_settings.get("require", {}).get("upper_case"), require_digits=password_policy_settings.get("require", {}).get("digits"), require_symbols=password_policy_settings.get("require", {}).get("symbols"), ) sign_up_info = kwargs["sign_up"] self_sing_up = sign_up_info["enabled"] user_verification_info = base_user_verification( sign_up_info=sign_up_info["user_verification"]) user_invitation = kwargs.get("invitation") user_invitation_configuration = cognito.UserInvitationConfig( email_subject=user_invitation.get("email", {}).get("subject"), email_body=user_invitation.get("email", {}).get("body"), sms_message=user_invitation.get("sms", {}).get("body"), ) trigger_functions = kwargs.get("triggers", {}) lambda_triggers = base_lambda_triggers(construct, trigger_functions=trigger_functions) sign_in_list = kwargs.get("sign_in").get("order", list()) sing_in_kwargs = dict() for element in sign_in_list: sing_in_kwargs[element] = True sign_in_aliases = cognito.SignInAliases(**sing_in_kwargs) attributes = kwargs.get("attributes") standard_attributes_list = attributes.get("standard", list()) standard_attributes_dict = dict() for element in standard_attributes_list: standard_attributes_dict[element["name"]] = cognito.StandardAttribute( mutable=element.get("mutable"), required=element.get("required")) standard_attributes = cognito.StandardAttributes( **standard_attributes_dict) custom_attributes_list = attributes.get("custom", list()) if len(custom_attributes_list) > 0: custom_attributes = base_custom_attributes( custom_attributes_list=custom_attributes_list) else: custom_attributes = None user_pool = cognito.UserPool( construct, id=user_pool_name, user_pool_name=user_pool_name, email_settings=email_settings, password_policy=password_policy, self_sign_up_enabled=self_sing_up, user_verification=user_verification_info, user_invitation=user_invitation_configuration, sign_in_aliases=sign_in_aliases, standard_attributes=standard_attributes, custom_attributes=custom_attributes, lambda_triggers=lambda_triggers, ) user_pool_client = None if kwargs.get("app_client", {}).get("enabled") is True: client_name = kwargs["app_client"]["client_name"] generate_secret = kwargs["app_client"]["generate_secret"] user_pool_client_name = construct.prefix + "_" + client_name + "_client_" + construct.environment_ auth_flows = None auth_flows_configuration = kwargs["app_client"].get("auth_flows") if auth_flows_configuration is not None: auth_flows = cognito.AuthFlow(**auth_flows_configuration) user_pool_client = cognito.UserPoolClient( construct, id=user_pool_client_name, user_pool_client_name=user_pool_client_name, generate_secret=generate_secret, auth_flows=auth_flows, user_pool=user_pool, ) return user_pool, user_pool_client
def __init__( self, scope: core.Construct, id: str, create_dependency_layer: Callable[[], None], **kwargs ) -> None: super().__init__(scope, id, **kwargs) # create dependency layer zip for lambda function create_dependency_layer() api_secret = secretsmanager.Secret( self, "ActualApiSecret", description="Secrets required to communicate with Aarogya Setu OpenAPI", ) # create cognito user pool for authentication user_pool = cognito.UserPool( self, "AppUserPool", self_sign_up_enabled=True, account_recovery=cognito.AccountRecovery.PHONE_AND_EMAIL, user_verification=cognito.VerificationEmailStyle.CODE, auto_verify={"email": True}, standard_attributes={"email": {"required": True, "mutable": True}}, ) user_pool_client = cognito.UserPoolClient( self, "UserPoolClient", user_pool=user_pool ) # Create storage and queue bulk_request_queue = sqs.Queue( self, "BulkRequestQueue", ) user_status_table = ddb.Table( self, "UserStatusTable", partition_key={"name": "mobile_number", "type": ddb.AttributeType.STRING}, time_to_live_attribute="expdate", ) self._user_status_table = user_status_table requests_table = ddb.Table( self, "RequestsTable", partition_key={"name": "mobile_number", "type": ddb.AttributeType.STRING}, time_to_live_attribute="expdate", ) # Create layer for lambda run time dependencies dependency_layer = _lambda.LayerVersion( self, "PythonDependencies", code=_lambda.Code.from_asset(path.join("lambda", "dependency-layer.zip")), compatible_runtimes=[_lambda.Runtime.PYTHON_3_7], description="The layer contains requests and pyjwt dependencies", ) # Create Lambda functions single_request = _lambda.Function( self, "SingleRequesetHandler", runtime=_lambda.Runtime.PYTHON_3_7, code=_lambda.Code.asset("lambda"), handler="single_request.handler", timeout=core.Duration.seconds(10), layers=[dependency_layer], environment={ "USER_STATUS_TABLE": user_status_table.table_name, "REQUESTS_TABLE": requests_table.table_name, "API_SECRET_ARN": api_secret.secret_full_arn, }, ) # give lambda access permissions to ddb tables and secrets user_status_table.grant_read_write_data(single_request) requests_table.grant_read_write_data(single_request) api_secret.grant_read(single_request) bulk_request = _lambda.Function( self, "BulkRequestHandler", runtime=_lambda.Runtime.PYTHON_3_7, code=_lambda.Code.asset("lambda"), handler="bulk_request.handler", timeout=core.Duration.seconds(30), environment={ "QUEUE_URL": bulk_request_queue.queue_url, }, ) # give lambda access to write to queue bulk_request_queue.grant_send_messages(bulk_request) queue_receiver = _lambda.Function( self, "QueueReceiverHandler", runtime=_lambda.Runtime.PYTHON_3_7, code=_lambda.Code.asset("lambda"), handler="queue_receiver.handler", timeout=core.Duration.seconds(10), layers=[dependency_layer], environment={ "USER_STATUS_TABLE": user_status_table.table_name, "REQUESTS_TABLE": requests_table.table_name, "QUEUE_URL": bulk_request_queue.queue_url, "API_SECRET_ARN": api_secret.secret_full_arn, }, ) # lambda gets triggered by sqs queue and writes to both tables queue_receiver.add_event_source( events.SqsEventSource(bulk_request_queue, batch_size=1) ) # give queue receiver access to tables, queue and secrets bulk_request_queue.grant_consume_messages(queue_receiver) user_status_table.grant_read_write_data(queue_receiver) requests_table.grant_read_write_data(queue_receiver) api_secret.grant_read(queue_receiver) scan_table = _lambda.Function( self, "ScanTableHandler", runtime=_lambda.Runtime.PYTHON_3_7, code=_lambda.Code.asset("lambda"), handler="scan_table.handler", timeout=core.Duration.seconds(30), environment={ "USER_STATUS_TABLE": user_status_table.table_name, }, ) user_status_table.grant_read_data(scan_table) # create api endpoints with authorization api = apigw.RestApi( self, "ASetuApiGateway", default_cors_preflight_options=apigw.CorsOptions( allow_origins=apigw.Cors.ALL_ORIGINS ), ) auth = apigw.CfnAuthorizer( self, "ApiCognitoAuthorizer", name="CognitoAuthorizer", type="COGNITO_USER_POOLS", authorizer_result_ttl_in_seconds=300, identity_source="method.request.header.Authorization", rest_api_id=api.rest_api_id, provider_arns=[user_pool.user_pool_arn], ) single_request_integration = apigw.LambdaIntegration(single_request, proxy=True) single_request_resource = api.root.add_resource("status") single_method = single_request_resource.add_method( "POST", single_request_integration, api_key_required=False, authorizer=auth, authorization_type=apigw.AuthorizationType.COGNITO, ) bulk_request_integration = apigw.LambdaIntegration(bulk_request, proxy=True) bulk_request_resource = api.root.add_resource("bulk_status") bulk_method = bulk_request_resource.add_method( "POST", bulk_request_integration, api_key_required=False, authorizer=auth, authorization_type=apigw.AuthorizationType.COGNITO, ) scan_table_integration = apigw.LambdaIntegration(scan_table, proxy=True) scan_table_resource = api.root.add_resource("scan") scan_method = scan_table_resource.add_method( "GET", scan_table_integration, api_key_required=False, authorizer=auth, authorization_type=apigw.AuthorizationType.COGNITO, ) # Override authorizer to use COGNITO to authorize apis # Solution from: https://github.com/aws/aws-cdk/issues/9023#issuecomment-658309644 methods = [single_method, bulk_method, scan_method] for method in methods: method.node.find_child("Resource").add_property_override( "AuthorizationType", "COGNITO_USER_POOLS" ) method.node.find_child("Resource").add_property_override( "AuthorizerId", {"Ref": auth.logical_id} ) # Export output values for frontend application core.CfnOutput( self, "user-pool-id", value=user_pool.user_pool_id, export_name="USER-POOL-ID", ) core.CfnOutput( self, "user-pool-web-client", value=user_pool_client.user_pool_client_id, export_name="WEB-CLIENT-ID", ) core.CfnOutput( self, "api-endpoint-url", value=api.url, export_name="API-ENDPOINT-URL" ) core.CfnOutput( self, "deployment-region", value=self.region, export_name="REGION", ) core.CfnOutput( self, "stack-name", value=self.stack_name, export_name="STACK-NAME" ) core.CfnOutput( self, "api-secret-arn", value=api_secret.secret_full_arn, export_name="API-SECRET-ARN", )
def __init__(self, scope: cdk.Construct, id: str, cognito_user_pool: cognito.UserPool, s3_bucket_name: str, create_configuration_lambda_role_arn: str, redis: ec.CfnCacheCluster, domain_name: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) config_yaml = yaml.load(open("config.yaml"), Loader=yaml.FullLoader) spoke_accounts = config_yaml.get("spoke_accounts", []) cognito_user_pool_client = cognito.UserPoolClient( self, "UserPoolClient", user_pool=cognito_user_pool, generate_secret=True, supported_identity_providers=[ cognito.UserPoolClientIdentityProvider.COGNITO ], prevent_user_existence_errors=True, o_auth=cognito.OAuthSettings( callback_urls=[ "https://" + domain_name + "/auth", "https://" + domain_name + "/oauth2/idpresponse", ], logout_urls=["https://" + domain_name + "/logout"], flows=cognito.OAuthFlows(authorization_code_grant=True, implicit_code_grant=True), scopes=[cognito.OAuthScope.OPENID, cognito.OAuthScope.EMAIL], ), auth_flows=cognito.AuthFlow(user_password=True, user_srp=True), ) describe_cognito_user_pool_client = cr.AwsCustomResource( self, "UserPoolClientIDResource", policy=cr.AwsCustomResourcePolicy.from_sdk_calls( resources=cr.AwsCustomResourcePolicy.ANY_RESOURCE), on_create=cr.AwsSdkCall( service="CognitoIdentityServiceProvider", action="describeUserPoolClient", parameters={ "UserPoolId": cognito_user_pool.user_pool_id, "ClientId": cognito_user_pool_client.user_pool_client_id, }, physical_resource_id=cr.PhysicalResourceId.of( cognito_user_pool_client.user_pool_client_id), ), install_latest_aws_sdk=True, log_retention=logs.RetentionDays.ONE_WEEK, ) cognito_user_pool_client_secret = ( describe_cognito_user_pool_client.get_response_field( "UserPoolClient.ClientSecret")) imported_create_configuration_lambda_role = iam.Role.from_role_arn( self, "ImportedCreateConfigurationFileLambdaRole", role_arn=create_configuration_lambda_role_arn, ) jwt_secret = config_yaml["jwt_secret"] config_secret_dict = { "oidc_secrets": { "client_id": cognito_user_pool_client.user_pool_client_id, "secret": cognito_user_pool_client_secret, "client_scope": ["email", "openid"], }, "jwt_secret": jwt_secret, } config_secret_yaml = yaml.dump( config_secret_dict, explicit_start=True, default_flow_style=False, ) config_secret = cr.AwsCustomResource( self, "ConfigSecretResource", policy=cr.AwsCustomResourcePolicy.from_sdk_calls( resources=cr.AwsCustomResourcePolicy.ANY_RESOURCE), on_update=cr.AwsSdkCall( service="SecretsManager", action="updateSecret", parameters={ "SecretId": CONFIG_SECRET_NAME, "SecretString": config_secret_yaml, }, physical_resource_id=cr.PhysicalResourceId.from_response( "Name"), ), on_create=cr.AwsSdkCall( service="SecretsManager", action="createSecret", parameters={ "Name": CONFIG_SECRET_NAME, "Description": "Sensitive configuration parameters for ConsoleMe", "SecretString": config_secret_yaml, }, physical_resource_id=cr.PhysicalResourceId.from_response( "Name"), ), on_delete=cr.AwsSdkCall( service="SecretsManager", action="deleteSecret", parameters={ "SecretId": CONFIG_SECRET_NAME, "ForceDeleteWithoutRecovery": True, }, ), install_latest_aws_sdk=True, log_retention=logs.RetentionDays.ONE_WEEK, ) create_configuration_lambda = lambda_.Function( self, "CreateConfigurationFileLambda", code=lambda_.Code.from_asset("resources/create_config_lambda"), handler="index.handler", timeout=cdk.Duration.seconds(30), layers=[create_dependencies_layer(self, "create_config_lambda")], runtime=lambda_.Runtime.PYTHON_3_8, role=imported_create_configuration_lambda_role, environment={ "DEPLOYMENT_BUCKET": s3_bucket_name, "OIDC_METADATA_URL": "https://cognito-idp." + self.region + ".amazonaws.com/" + cognito_user_pool.user_pool_id + "/.well-known/openid-configuration", "REDIS_HOST": redis.attr_redis_endpoint_address, "SES_IDENTITY_ARN": "arn:aws:ses:" + self.region + ":" + self.account + ":identity/" + domain_name, "SUPPORT_CHAT_URL": "https://discord.gg/nQVpNGGkYu", "APPLICATION_ADMIN": "consoleme_admin", "ACCOUNT_NUMBER": self.account, "ISSUER": domain_name, "SPOKE_ACCOUNTS": ",".join(spoke_accounts), "CONFIG_SECRET_NAME": CONFIG_SECRET_NAME, }, ) create_configuration_resource_provider = cr.Provider( self, "CreateConfigurationFileProvider", on_event_handler=create_configuration_lambda, log_retention=logs.RetentionDays.ONE_WEEK, ) create_configuration_lambda_resource = cdk.CustomResource( self, "CreateConfigurationFile", service_token=create_configuration_resource_provider.service_token, removal_policy=cdk.RemovalPolicy.DESTROY, properties={"UUID": str(uuid4())}, ) create_configuration_lambda_resource.node.add_dependency(config_secret)
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) #XXX: For createing Amazon MWAA in the existing VPC, # remove comments from the below codes and # comments out vpc = aws_ec2.Vpc(..) codes, # then pass -c vpc_name=your-existing-vpc to cdk command # for example, # cdk -c vpc_name=your-existing-vpc syth # # vpc_name = self.node.try_get_context('vpc_name') # vpc = aws_ec2.Vpc.from_lookup(self, 'ExistingVPC', # is_default=True, # vpc_name=vpc_name # ) vpc = aws_ec2.Vpc(self, "ApiGatewayDynamoDBVPC", max_azs=2, gateway_endpoints={ "S3": aws_ec2.GatewayVpcEndpointOptions( service=aws_ec2.GatewayVpcEndpointAwsService.S3 ), "DynamoDB": aws_ec2.GatewayVpcEndpointOptions( service=aws_ec2.GatewayVpcEndpointAwsService.DYNAMODB ) } ) DDB_TABLE_SUFFIX = ''.join(random.sample((string.ascii_lowercase + string.digits), k=7)) DDB_TABLE_NAME = "Comments-{}".format(DDB_TABLE_SUFFIX) ddb_table = aws_dynamodb.Table(self, "DynamoDbTable", table_name=DDB_TABLE_NAME, removal_policy=cdk.RemovalPolicy.DESTROY, partition_key=aws_dynamodb.Attribute(name="commentId", type=aws_dynamodb.AttributeType.STRING), time_to_live_attribute="ttl", billing_mode=aws_dynamodb.BillingMode.PROVISIONED, read_capacity=15, write_capacity=5, ) ddb_table.add_global_secondary_index( read_capacity=15, write_capacity=5, index_name="pageId-index", partition_key=aws_dynamodb.Attribute(name='pageId', type=aws_dynamodb.AttributeType.STRING), projection_type=aws_dynamodb.ProjectionType.ALL ) user_pool = aws_cognito.UserPool(self, 'UserPool', user_pool_name='UserPoolForApiGateway', removal_policy=cdk.RemovalPolicy.DESTROY, self_sign_up_enabled=True, sign_in_aliases={'email': True}, auto_verify={'email': True}, password_policy={ 'min_length': 8, 'require_lowercase': False, 'require_digits': False, 'require_uppercase': False, 'require_symbols': False, }, account_recovery=aws_cognito.AccountRecovery.EMAIL_ONLY ) user_pool_client = aws_cognito.UserPoolClient(self, 'UserPoolClient', user_pool=user_pool, auth_flows={ 'admin_user_password': True, 'user_password': True, 'custom': True, 'user_srp': True }, supported_identity_providers=[aws_cognito.UserPoolClientIdentityProvider.COGNITO] ) auth = aws_apigateway.CognitoUserPoolsAuthorizer(self, 'AuthorizerForDynamoDBApi', cognito_user_pools=[user_pool] ) ddb_access_policy_doc = aws_iam.PolicyDocument() ddb_access_policy_doc.add_statements(aws_iam.PolicyStatement(**{ "effect": aws_iam.Effect.ALLOW, "resources": [ddb_table.table_arn], "actions": [ "dynamodb:DeleteItem", "dynamodb:PartiQLInsert", "dynamodb:UpdateTimeToLive", "dynamodb:BatchWriteItem", "dynamodb:PutItem", "dynamodb:PartiQLUpdate", "dynamodb:UpdateItem", "dynamodb:PartiQLDelete" ] })) apigw_dynamodb_role = aws_iam.Role(self, "ApiGatewayRoleForDynamoDB", role_name='APIGatewayRoleForDynamoDB', assumed_by=aws_iam.ServicePrincipal('apigateway.amazonaws.com'), inline_policies={ 'DynamoDBAccessPolicy': ddb_access_policy_doc }, managed_policies=[ aws_iam.ManagedPolicy.from_aws_managed_policy_name('AmazonDynamoDBReadOnlyAccess'), ] ) dynamodb_api = aws_apigateway.RestApi(self, "DynamoDBProxyAPI", rest_api_name="comments-api", description="An Amazon API Gateway REST API that integrated with an Amazon DynamoDB.", endpoint_types=[aws_apigateway.EndpointType.REGIONAL], default_cors_preflight_options={ "allow_origins": aws_apigateway.Cors.ALL_ORIGINS }, deploy=True, deploy_options=aws_apigateway.StageOptions(stage_name="v1"), endpoint_export_name="DynamoDBProxyAPIEndpoint" ) all_resources = dynamodb_api.root.add_resource("comments") one_resource = all_resources.add_resource("{pageId}") apigw_error_responses = [ aws_apigateway.IntegrationResponse(status_code="400", selection_pattern="4\d{2}"), aws_apigateway.IntegrationResponse(status_code="500", selection_pattern="5\d{2}") ] apigw_ok_responses = [ aws_apigateway.IntegrationResponse( status_code="200" ) ] ddb_put_item_options = aws_apigateway.IntegrationOptions( credentials_role=apigw_dynamodb_role, integration_responses=[*apigw_ok_responses, *apigw_error_responses], request_templates={ 'application/json': json.dumps({ "TableName": DDB_TABLE_NAME, "Item": { "commentId": { "S": "$context.requestId" }, "pageId": { "S": "$input.path('$.pageId')" }, "userName": { "S": "$input.path('$.userName')" }, "message": { "S": "$input.path('$.message')" } } }, indent=2) }, passthrough_behavior=aws_apigateway.PassthroughBehavior.WHEN_NO_TEMPLATES ) create_integration = aws_apigateway.AwsIntegration( service='dynamodb', action='PutItem', integration_http_method='POST', options=ddb_put_item_options ) method_responses = [ aws_apigateway.MethodResponse(status_code='200'), aws_apigateway.MethodResponse(status_code='400'), aws_apigateway.MethodResponse(status_code='500') ] all_resources.add_method('POST', create_integration, method_responses=method_responses, authorization_type=aws_apigateway.AuthorizationType.COGNITO, authorizer=auth ) get_response_templates = ''' #set($inputRoot = $input.path('$')) { "comments": [ #foreach($elem in $inputRoot.Items) { "commentId": "$elem.commentId.S", "userName": "******", "message": "$elem.message.S" }#if($foreach.hasNext),#end #end ] }''' ddb_query_item_options = aws_apigateway.IntegrationOptions( credentials_role=apigw_dynamodb_role, integration_responses=[ aws_apigateway.IntegrationResponse( status_code="200", response_templates={ 'application/json': get_response_templates } ), *apigw_error_responses ], request_templates={ 'application/json': json.dumps({ "TableName": DDB_TABLE_NAME, "IndexName": "pageId-index", "KeyConditionExpression": "pageId = :v1", "ExpressionAttributeValues": { ":v1": { "S": "$input.params('pageId')" } } }, indent=2) }, passthrough_behavior=aws_apigateway.PassthroughBehavior.WHEN_NO_TEMPLATES ) get_integration = aws_apigateway.AwsIntegration( service='dynamodb', action='Query', integration_http_method='POST', options=ddb_query_item_options ) one_resource.add_method('GET', get_integration, method_responses=method_responses, authorization_type=aws_apigateway.AuthorizationType.COGNITO, authorizer=auth ) cdk.CfnOutput(self, 'DynamoDBTableName', value=ddb_table.table_name) cdk.CfnOutput(self, 'UserPoolId', value=user_pool.user_pool_id) cdk.CfnOutput(self, 'UserPoolClientId', value=user_pool_client.user_pool_client_id)
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")