def __create_api_authorizer(self, user_pool_arn: str, api: apigw.RestApi) -> apigw.CfnAuthorizer: authorizer = apigw.CfnAuthorizer( scope=self, name="KesherApiAuth", id="KesherApiAuth", type="COGNITO_USER_POOLS", provider_arns=[user_pool_arn], rest_api_id=api.rest_api_id, identity_source="method.request.header.Authorization") return authorizer
def create_root_api_authorizer( self, user_pool: aws_cognito.UserPool, api: aws_apigateway.RestApi) -> aws_apigateway.CfnAuthorizer: authorizer = aws_apigateway.CfnAuthorizer( scope=self, name="MasterStackApiAuth", id="MasterStackApiAuth", type="COGNITO_USER_POOLS", provider_arns=[user_pool.user_pool_arn], rest_api_id=api.rest_api_id, identity_source="method.request.header.Authorization") return authorizer
def __init__(self, scope: core.Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) # Ensure the Cognito ARN is set cognito_arn = os.environ.get('COGNITO_ARN') if cognito_arn is None: raise ValueError( 'An environment variable for COGNITO_ARN is required') # Deploy Lambda my_lambda = _lambda.Function( self, 'HelloHandler', runtime=_lambda.Runtime.PYTHON_3_7, code=_lambda.Code.asset('lambda'), handler='hello.handler', ) # create the API gateway api = apigw.RestApi( self, 'HelloHandler2', rest_api_name="LCAOA_Demo", description="Demo of lambda, cognito, Azure AD and OpenId auth" ) # configure the Authorizer auth = apigw.CfnAuthorizer( scope=self, id='cognito_authorizer', rest_api_id=api.rest_api_id, name='MyAuth', type='COGNITO_USER_POOLS', identity_source='method.request.header.Authorization', provider_arns=[ cognito_arn ] ) resource = api.root.add_resource("endpoint") lambda_integration = apigw.LambdaIntegration( my_lambda, proxy=True) method = resource.add_method("GET", lambda_integration) method_resource = method.node.find_child('Resource') method_resource.add_property_override( 'AuthorizationType', 'COGNITO_USER_POOLS') method_resource.add_property_override( 'AuthorizerId', {"Ref": auth.logical_id})
def __init__(self, scope: core.Construct, id: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) # Create simple, publically available API gateway resource, with CORS on preflight requests api = aws_apigateway.RestApi(self, 'testAPI', rest_api_name='testAPI', default_cors_preflight_options={ "allow_origins": ["*"], "allow_methods": ["GET", "POST", "OPTIONS", "PUT", "DELETE"] }) # Create a COGNITO_USER_POOLS Authorizer to use with the API auth = aws_apigateway.CfnAuthorizer( self, "testAuth", rest_api_id=api.rest_api_id, type='COGNITO_USER_POOLS', identity_source='method.request.header.Authorization', provider_arns=[ 'arn:aws:cognito-idp:...' ], name="testAuth" ) # Add a resource (path) to the API resource = api.root.add_resource("endpoint") # Create lambda lambda_function = aws_lambda.Function( self, "lambdaFunction", handler='app.lambda_handler', runtime=aws_lambda.Runtime.PYTHON_3_8, code=aws_lambda.Code.from_asset( "path/to/code" ) ) # Lambda integration lambda_integration = aws_apigateway.LambdaIntegration(lambda_function, proxy=True) method = resource.add_method("GET", lambda_integration) # Get the Resource subkey of the method method_resource = method.node.find_child('Resource') # Override the properties method_resource.add_property_override('AuthorizationType', 'COGNITO_USER_POOLS') method_resource.add_property_override('AuthorizerId', {"Ref": auth.logical_id})
def __init__( self, scope: core.Construct, id: str, host_name, cert_arn, zone_id, admin_user: str, admin_password: str, cloud9_instance_size: str, participant_limit: str, **kwargs, ) -> None: super().__init__(scope, id, **kwargs) stack = core.Stack.of(self) stack.template_options.description = "Connected Drink Dispenser Workshop" # Static Website props: StaticSiteProps = StaticSiteProps( fqdn=host_name, hosted_zone_id=zone_id, certificate_arn=cert_arn, error_configuration=[ { "error_code": 403, "error_caching_min_ttl": 300, "response_code": 200, "response_page_path": "/index.html", }, { "error_code": 404, "error_caching_min_ttl": 300, "response_code": 200, "response_page_path": "/index.html", }, ], output_name="CDDWebSite", ) cdd_site = StaticSiteConstruct(self, "StaticSite", props) # Custom resource to clean out static website bucket prior to delete # TODO: Move this to the StaticSiteConstruct as option props: CustomResourceProps = CustomResourceProps( name=id + "-CR-S3DeleteObjects", lambda_directory="./lambda_functions/cr_s3_delete", handler="index.main", timeout=30, runtime=lambda_.Runtime.PYTHON_3_7, environment={"BUCKET_NAME": cdd_site.bucket_name}, ) s3_delete_cr = CustomResourceConstruct(self, "EmptyCddS3Bucket", props) # DependsOn the bucket (we need to delete objects before the bucket is deleted) s3_delete_cr.resource.node.add_dependency(cdd_site.bucket_resource) policy_statement = iam.PolicyStatement() policy_statement.add_actions("s3:GetBucket*") policy_statement.add_actions("s3:GetObject*") policy_statement.add_actions("s3:DeleteObject*") policy_statement.add_actions("s3:List*") policy_statement.add_resources(cdd_site.bucket_resource.bucket_arn) policy_statement.add_resources( f"{cdd_site.bucket_resource.bucket_arn}/*") s3_delete_cr.add_policy_to_role(policy_statement) # IAM Constructs user_group = iam.Group( self, "UserGroup", group_name=id + "-CDDUserGroup", managed_policies=[ iam.ManagedPolicy.from_aws_managed_policy_name( "ReadOnlyAccess") ], ) # DynamoDB tables user_db = dynamodb.Table( # UserId as key, user "admin" tracks next available dispenser id # No access to users, RW to Cognito Lambda self, "UserTable", table_name=id + "-UserTable", partition_key={ "name": "userName", "type": dynamodb.AttributeType.STRING }, billing_mode=dynamodb.BillingMode.PAY_PER_REQUEST, removal_policy=core.RemovalPolicy.DESTROY, ) dispenser_db = dynamodb.Table( # Dispenser ID and credit amount - RO to users, RW to APIs self, "DispenserTable", table_name=id + "-DispenserTable", partition_key={ "name": "dispenserId", "type": dynamodb.AttributeType.STRING, }, billing_mode=dynamodb.BillingMode.PAY_PER_REQUEST, removal_policy=core.RemovalPolicy.DESTROY, ) dispenser_events = dynamodb.Table( # Recorded events from dispenser actions self, "DispenserEvents", table_name=id + "-DispenserEvents", partition_key={ "name": "dispenserId", "type": dynamodb.AttributeType.STRING, }, sort_key={ "name": "timestamp", "type": dynamodb.AttributeType.STRING }, billing_mode=dynamodb.BillingMode.PAY_PER_REQUEST, removal_policy=core.RemovalPolicy.DESTROY, ) # Cognito Resources # User pool with phone_number as username props: CognitoUserPoolProps = CognitoUserPoolProps( user_pool_name=id + "-users", client_name=id + "-webclient", auto_verified_attributes=["phone_number"], schema=[ { "name": "group", "attributeDataType": "String", "mutable": True, "required": False, }, { "name": "dispenserId", "attributeDataType": "String", "mutable": True, "required": False, }, ], policies={ "passwordPolicy": { "minimumLength": 6, "requireLowercase": True, "requireNumbers": True, "requireSymbols": False, "requireUppercase": False, } }, ) user_pool = CognitoUserPoolConstruct(self, "UserPool", props) # Role and lambda triggers lambda_cognito_access_role = iam.Role( # Access to IDP calls (for triggers) self, "LambdaCognitoAccessRole", assumed_by=iam.ServicePrincipal("lambda.amazonaws.com"), inline_policies=[ iam.PolicyDocument(statements=[ iam.PolicyStatement( actions=[ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents", ], resources=["arn:aws:logs:*:*:*"], ), iam.PolicyStatement(actions=["cognito-idp:*"], resources=["*"]), iam.PolicyStatement(actions=["dynamodb:*"], resources=["*"]), ]) ], ) # Triggers for UserPool # Pre-sign-up: triggered when username, password, and phone number submitted lambda_cognito_trigger_pre_signup = lambda_.Function( self, "CogntioTriggerPreSignUp", function_name=id + "-CogntioTriggerPreSignUp", code=lambda_.AssetCode("./lambda_functions/cog_pre_signup"), handler="lambda.handler", runtime=lambda_.Runtime.PYTHON_3_7, role=lambda_cognito_access_role, timeout=core.Duration.seconds(6), environment={ "USER_TABLE": user_db.table_name, "PARTICIPANT_LIMIT": participant_limit, }, ) lambda_cognito_trigger_pre_signup.add_permission( "AllowCognitoPreSign", principal=iam.ServicePrincipal("cognito-idp.amazonaws.com"), source_arn=user_pool.user_pool_arn, ) # Post confirmation: triggered after validation code provided lambda_cognito_trigger_post_confirm = lambda_.Function( self, "CogntioTriggerPostConfirm", function_name=id + "-CogntioTriggerPostConfirm", code=lambda_.AssetCode("./lambda_functions/cog_post_confirm"), handler="lambda.handler", runtime=lambda_.Runtime.PYTHON_3_7, role=lambda_cognito_access_role, timeout=core.Duration.seconds(6), environment={ "USER_TABLE": user_db.table_name, "PARTICIPANT_LIMIT": participant_limit, }, ) lambda_cognito_trigger_post_confirm.add_permission( "AllowCognitoPostConfirm", principal=iam.ServicePrincipal("cognito-idp.amazonaws.com"), source_arn=user_pool.user_pool_arn, ) # Attach triggers to pool user_pool.user_pool.lambda_config = cognito.CfnUserPool.LambdaConfigProperty( pre_sign_up=lambda_cognito_trigger_pre_signup.function_arn, post_confirmation=lambda_cognito_trigger_post_confirm.function_arn, ) cognito.CfnUserPoolGroup( self, "UserPoolCDDUser", group_name="cdd_user", description="General users of CDD (participants)", user_pool_id=user_pool.user_pool_id, ) cognito.CfnUserPoolGroup( self, "UserPoolCDDAdmin", group_name="cdd_admin", description="CDD administrators", user_pool_id=user_pool.user_pool_id, ) identity_pool = cognito.CfnIdentityPool( self, "IdentityPool", identity_pool_name=id.replace("-", "") + "_idpool", allow_unauthenticated_identities=False, cognito_identity_providers=[{ "clientId": user_pool.client_id, "providerName": user_pool.provider_name, }], ) core.CfnOutput( self, "CognitoIdentityPoolId", export_name="CognitoIdentityPoolId", value=identity_pool.ref, ) # Custom resource to create admin user - cannot do via CFn to set password props: CustomResourceProps = CustomResourceProps( name=id + "-CR-CreateCognitoAdminUser", lambda_directory="./lambda_functions/cr_create_admin_user", handler="index.main", timeout=30, runtime=lambda_.Runtime.PYTHON_3_7, environment={ "COGNITO_USER_POOL_ID": user_pool.user_pool_id, "COGNITO_CLIENT_ID": user_pool.client_id, "ADMIN_USERNAME": admin_user, "ADMIN_PASSWORD": admin_password, }, ) create_admin_user_cr = CustomResourceConstruct(self, "CreateAdminUser", props) # DependsOn the user pool create_admin_user_cr.resource.node.add_dependency(user_pool) policy_statement = iam.PolicyStatement() policy_statement.add_actions("cognito-idp:SignUp") policy_statement.add_actions("cognito-idp:AdminConfirmSignUp") policy_statement.add_resources("*") create_admin_user_cr.add_policy_to_role(policy_statement) # IAM roles for identity pool auth/unauth cog_unauth_role = iam.Role( self, "cognitoUnauthRole", role_name=f"Cognito_{identity_pool.identity_pool_name}_Unauth_Role", assumed_by=iam.FederatedPrincipal( "cognito-identity.amazonaws.com", conditions={ "StringEquals": { "cognito-identity.amazonaws.com:aud": identity_pool.ref }, "ForAnyValue:StringLike": { "cognito-identity.amazonaws.com:amr": "unauthenticated" }, }, assume_role_action="sts:AssumeRoleWithWebIdentity", ), ) cog_unauth_role.attach_inline_policy( iam.Policy( self, "cognitoUnauth", policy_name="cognitoUnauth", statements=[ iam.PolicyStatement( actions=[ "mobileanalytics:PutEvents", "cognito-sync:*" ], resources=["*"], ) ], )) cog_auth_role = iam.Role( self, "cognitoAuthRole", role_name=f"Cognito_{identity_pool.identity_pool_name}_Auth_Role", managed_policies=[ iam.ManagedPolicy.from_aws_managed_policy_name( "AmazonESCognitoAccess") ], assumed_by=iam.FederatedPrincipal( "cognito-identity.amazonaws.com", conditions={ "StringEquals": { "cognito-identity.amazonaws.com:aud": identity_pool.ref }, "ForAnyValue:StringLike": { "cognito-identity.amazonaws.com:amr": "authenticated" }, }, assume_role_action="sts:AssumeRoleWithWebIdentity", ), ) cog_auth_role.attach_inline_policy( iam.Policy( self, "cognitoAuth", policy_name="cognitoAuth", statements=[ iam.PolicyStatement( actions=[ "mobileanalytics:PutEvents", "cognito-sync:*", "execute-api:*", ], resources=["*"], ), # Provide full access to IoT for the authenticated user # The AWS IoT policy scopes down the access iam.PolicyStatement(actions=["iot:*"], resources=["*"]), ], )) # Finally, attach auth and unauth roles to Identity pool cognito.CfnIdentityPoolRoleAttachment( self, "CDDIdentityPoolRoleAttach", identity_pool_id=identity_pool.ref, roles={ "authenticated": cog_auth_role.role_arn, "unauthenticated": cog_unauth_role.role_arn, }, ) ### Supporting IAM Roles and Policies lambda_full_access_role = iam.Role( # Wide open role for Lambda's to access other services self, "LambdaFullAccessRole", assumed_by=iam.ServicePrincipal("lambda.amazonaws.com"), inline_policies=[ iam.PolicyDocument(statements=[ iam.PolicyStatement( actions=[ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents", ], resources=["arn:aws:logs:*:*:*"], ), iam.PolicyStatement(actions=["*"], resources=["*"]), ]) ], ) lambda_iot_full_access_role = iam.Role( # Wide open role for Lambda's to access other services self, "LambdaIoTFullAccessRole", assumed_by=iam.ServicePrincipal("lambda.amazonaws.com"), inline_policies=[ iam.PolicyDocument(statements=[ iam.PolicyStatement( actions=[ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents", ], resources=["arn:aws:logs:*:*:*"], ), iam.PolicyStatement(actions=["dynamodb:*", "iot:*"], resources=["*"]), ]) ], ) lambda_api_app_role = iam.Role( # Role for APIG Lambda functions - make specific per Lambda/method if needed self, "ApiAppRole", assumed_by=iam.ServicePrincipal("lambda.amazonaws.com"), inline_policies=[ iam.PolicyDocument(statements=[ iam.PolicyStatement( actions=[ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents", ], resources=["arn:aws:logs:*:*:*"], ), iam.PolicyStatement( actions=["dynamodb:*"], resources=[ f"arn:aws:dynamodb:{stack.region}:{stack.account}:table/{dispenser_db.table_name}", f"arn:aws:dynamodb:{stack.region}:{stack.account}:table/{dispenser_events.table_name}", f"arn:aws:dynamodb:{stack.region}:{stack.account}:table/{user_db.table_name}", ], ), iam.PolicyStatement(actions=["iot:*"], resources=["*"]), ]) ], ) lambda_api_delete_user_role = iam.Role( # Role for APIG Lambda delete user - specific as this has to delete multiple services self, "ApiDeleteUserRole", assumed_by=iam.ServicePrincipal("lambda.amazonaws.com"), inline_policies=[ iam.PolicyDocument(statements=[ iam.PolicyStatement( actions=[ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents", ], resources=["arn:aws:logs:*:*:*"], ), iam.PolicyStatement( actions=["dynamodb:*"], resources=[ f"arn:aws:dynamodb:{stack.region}:{stack.account}:table/{dispenser_db.table_name}", f"arn:aws:dynamodb:{stack.region}:{stack.account}:table/{dispenser_events.table_name}", f"arn:aws:dynamodb:{stack.region}:{stack.account}:table/{user_db.table_name}", ], ), iam.PolicyStatement(actions=["cloud9:DeleteEnvironment"], resources=["*"]), iam.PolicyStatement( actions=[ "iam:DeleteLoginProfile", "iam:ListGroupsForUser", "iam:RemoveUserFromGroup", "iam:DeleteUser", ], resources=["*"], ), iam.PolicyStatement( actions=["cognito-idp:AdminDeleteUser"], resources=["*"]), iam.PolicyStatement(actions=["iot:*"], resources=["*"]), ]) ], ) lambda_api_dispense_role = iam.Role( # Role for lambda self, "CommandRole", assumed_by=iam.ServicePrincipal("lambda.amazonaws.com"), inline_policies=[ iam.PolicyDocument(statements=[ iam.PolicyStatement( actions=[ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents", ], resources=["arn:aws:logs:*:*:*"], ), iam.PolicyStatement( actions=["dynamodb:*"], resources=[ f"arn:aws:dynamodb:{stack.region}:{stack.account}:table/{dispenser_db.table_name}", f"arn:aws:dynamodb:{stack.region}:{stack.account}:table/{dispenser_events.table_name}", ], ), iam.PolicyStatement(actions=["iot:*"], resources=["*"]), ]) ], ) # IoT Policies iot_policy_dispenser_limited = iot.CfnPolicy( self, "IoTDispenserLimitedPolicy", policy_name=id + "-DispenserLimitedAccess", policy_document={ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["iot:Connect"], "Resource": [ f"arn:aws:iot:{stack.region}:{stack.account}:client/${{iot:Connection.Thing.ThingName}}" ], "Condition": { "Bool": { "iot:Connection.Thing.IsAttached": [True] } }, }, { "Effect": "Allow", "Action": ["iot:Receive"], "Resource": ["*"] }, { "Effect": "Allow", "Action": ["iot:Subscribe"], "Resource": [ f"arn:aws:iot:{stack.region}:{stack.account}:topicfilter/$aws/things/${{iot:Certificate.Subject.CommonName}}/shadow/*", f"arn:aws:iot:{stack.region}:{stack.account}:topicfilter/$aws/things/${{iot:Certificate.Subject.CommonName}}/cmd/${{iot:Certificate.Subject.CommonName}}", ], }, { "Effect": "Allow", "Action": ["iot:Publish"], "Resource": [ f"arn:aws:iot:{stack.region}:{stack.account}:topic/$aws/things/${{iot:Certificate.Subject.CommonName}}/shadow/update", f"arn:aws:iot:{stack.region}:{stack.account}:topic/$aws/things/${{iot:Certificate.Subject.CommonName}}/shadow/get", f"arn:aws:iot:{stack.region}:{stack.account}:topic/test/${{iot:Certificate.Subject.CommonName}}", f"arn:aws:iot:{stack.region}:{stack.account}:topic/cmd/${{iot:Certificate.Subject.CommonName}}/response", ], }, ], }, ) iot_policy_client = iot.CfnPolicy( self, "IoTClientPolicy", policy_name=id + "-IoTClientAccess", policy_document={ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["iot:Connect", "iot:Receive"], "Resource": ["*"], }, { "Effect": "Allow", "Action": ["iot:Subscribe"], "Resource": [ f"arn:aws:iot:{stack.region}:{stack.account}:topicfilter/events/*", f"arn:aws:iot:{stack.region}:{stack.account}:topicfilter/$aws/things/*/shadow/update/accepted", ], }, ], }, ) ### Lambda Functions # General Lambda Functions NOT associated with APIG lambda_process_events = lambda_.Function( self, "ProcessEvents", function_name=id + "-ProcessEvents", code=lambda_.AssetCode("./lambda_functions/process_events"), handler="process_events.handler", runtime=lambda_.Runtime.PYTHON_3_7, role=lambda_iot_full_access_role, timeout=core.Duration.seconds(20), environment={ "EVENT_TABLE": dispenser_events.table_name, "STATUS_TABLE": dispenser_db.table_name, }, ) ## API Lambda functions # Return credit for dispenser api_credit_dispenser_function = lambda_.Function( self, "ApiCreditDispenserFunction", function_name=id + "-ApiCreditDispenserFunction", code=lambda_.AssetCode("./lambda_functions/api_credit_dispenser"), handler="credit_dispenser.handler", runtime=lambda_.Runtime.PYTHON_3_7, role=lambda_api_app_role, timeout=core.Duration.seconds(15), memory_size=128, environment={ "DISPENSER_TABLE": dispenser_db.table_name, "EVENT_TABLE": dispenser_events.table_name, }, ) # Command api_command_function = lambda_.Function( self, "ApiCommandFunction", function_name=id + "-ApiCommandFunction", code=lambda_.AssetCode("./lambda_functions/api_command"), handler="command.handler", runtime=lambda_.Runtime.PYTHON_3_7, role=lambda_api_app_role, timeout=core.Duration.seconds(15), memory_size=128, environment={ "DispenserTable": dispenser_db.table_name, "EventTable": dispenser_events.table_name, }, ) # Request dispense operation (set shadow or command to dispense) api_dispense_function = lambda_.Function( self, "ApiDispenseFunction", function_name=id + "-ApiDispenseFunction", code=lambda_.AssetCode("./lambda_functions/api_dispense"), handler="dispense.handler", runtime=lambda_.Runtime.PYTHON_3_7, role=lambda_api_dispense_role, timeout=core.Duration.seconds(15), memory_size=128, environment={ "DISPENSER_TABLE": dispenser_db.table_name, "EVENT_TABLE": dispenser_events.table_name, }, ) # Request dispense operation (set shadow or command to dispense) api_dispenser_status_function = lambda_.Function( self, "ApiDispenserStatusFunction", function_name=id + "-ApiDispenserStatusFunction", code=lambda_.AssetCode("./lambda_functions/api_dispenser_status"), handler="dispenser_status.handler", runtime=lambda_.Runtime.PYTHON_3_7, role=lambda_api_app_role, timeout=core.Duration.seconds(15), memory_size=128, environment={"DISPENSER_TABLE": dispenser_db.table_name}, ) # Request user details from user table, create resources if needed # NOTE: This uses an overley permissive policy to create the resources needed api_get_resources_function = lambda_.Function( self, "ApiGetResourcesFunction", function_name=id + "-ApiGetResourcesFunction", code=lambda_.AssetCode("./lambda_functions/api_get_resources"), handler="get_resources.handler", runtime=lambda_.Runtime.PYTHON_3_7, role=lambda_full_access_role, # Timeout is for user creation: certain tasks such as Cloud9 may take longer timeout=core.Duration.seconds(300), memory_size=128, environment={ "DISPENSER_TABLE": dispenser_db.table_name, "EVENT_TABLE": dispenser_events.table_name, "USER_TABLE": user_db.table_name, "USER_PERMISSIONS_GROUP": user_group.group_name, "IOT_POLICY_DISPENSER_LIMITED": iot_policy_dispenser_limited.policy_name, "IOT_POLICY_CLIENT": iot_policy_client.policy_name, "CLOUD9_INSTANCE_SIZE": cloud9_instance_size, }, ) # Request user details from user table api_delete_user_function = lambda_.Function( self, "ApiDeleteUserFunction", function_name=id + "-ApiDeleteUserFunction", code=lambda_.AssetCode("./lambda_functions/api_delete_user"), handler="delete_user.handler", runtime=lambda_.Runtime.PYTHON_3_7, role=lambda_api_delete_user_role, timeout=core.Duration.seconds(28), memory_size=256, environment={ "DISPENSER_TABLE": dispenser_db.table_name, "EVENT_TABLE": dispenser_events.table_name, "USER_TABLE": user_db.table_name, "USER_POOL_ID": user_pool.user_pool_id, }, ) ### API Gateway api = apigateway.RestApi( self, id + "-API", api_key_source_type=apigateway.ApiKeySourceType.HEADER, deploy_options=apigateway.StageOptions(stage_name="prod"), ) core.CfnOutput( self, "APIEndpoint", export_name="APIEndpoint", value= f"https://{api.rest_api_id}.execute-api.{stack.region}.amazonaws.com/prod/", ) # Although / is not used as method, provide OPTIONS for hinting CORS add_cors_options(api.root) # Define Cognito authorizer and attach to gateway cog_authorizer = apigateway.CfnAuthorizer( self, "CognitoAuth", name="CognitoAuthName", rest_api_id=api.rest_api_id, type="COGNITO_USER_POOLS", identity_source="method.request.header.Authorization", provider_arns=[user_pool.user_pool_arn], ) # # Resources (paths) and methods (GET, POST, etc.), for the API api_credit_resource = api.root.add_resource("credit") add_resource_method( api_credit_resource, http_method="GET", integration=apigateway.LambdaIntegration( api_credit_dispenser_function), authorization_type=apigateway.AuthorizationType.COGNITO, authorizer=cog_authorizer, ) add_cors_options(api_credit_resource) # command api_command_resource = api.root.add_resource("command") add_resource_method( api_command_resource, http_method="GET", integration=apigateway.LambdaIntegration(api_command_function), authorization_type=apigateway.AuthorizationType.COGNITO, authorizer=cog_authorizer, ) add_cors_options(api_command_resource) # Actuate dispenser api_dispense_resource = api.root.add_resource("dispense") add_resource_method( api_dispense_resource, http_method="GET", integration=apigateway.LambdaIntegration(api_dispense_function), authorization_type=apigateway.AuthorizationType.COGNITO, authorizer=cog_authorizer, ) add_cors_options(api_dispense_resource) # Return dispenser status (from DynamoDB) api_dispenser_status_resource = api.root.add_resource("status") add_resource_method( api_dispenser_status_resource, http_method="GET", integration=apigateway.LambdaIntegration( api_dispenser_status_function), authorization_type=apigateway.AuthorizationType.COGNITO, authorizer=cog_authorizer, ) add_cors_options(api_dispenser_status_resource) # Return user details from User Table api_get_resources_resource = api.root.add_resource("getResources") add_resource_method( api_get_resources_resource, http_method="POST", integration=apigateway.LambdaIntegration( api_get_resources_function), authorization_type=apigateway.AuthorizationType.COGNITO, authorizer=cog_authorizer, ) add_cors_options(api_get_resources_resource) # Create a user based on valid token api_delete_user_resource = api.root.add_resource("deleteUser") add_resource_method( api_delete_user_resource, http_method="POST", integration=apigateway.LambdaIntegration(api_delete_user_function), authorization_type=apigateway.AuthorizationType.COGNITO, authorizer=cog_authorizer, ) add_cors_options(api_delete_user_resource) # Create policy and reference group iam.Policy( self, "UserPermissionsPolicy", groups=[user_group], policy_name=id + "-UserPermissions", statements=[ # Elevated permissions beyond the ReadOnlyUser # Allow seeing all MQTT messages iam.PolicyStatement( actions=["iot:Subscribe", "iot:Connect", "iot:Receive"], resources=["*"], ), # Allow search indexing iam.PolicyStatement( actions=["iot:SearchIndex"], resources=[ f"arn:aws:iot:{stack.region}:{stack.account}:index/AWS_Things" ], ), # Allow changing of security group ingress on EC2 (Cloud9) to support mapping 443 to iam.PolicyStatement( actions=[ "ec2:AuthorizeSecurityGroupIngress", "ec2:RevokeSecurityGroupIngress", ], resources=[ f"arn:aws:ec2:{stack.region}:{stack.account}:security-group/*" ], ), # DENY access to credentials table iam.PolicyStatement( effect=iam.Effect.DENY, actions=["dynamodb:*"], resources=[ f"arn:aws:dynamodb:{stack.region}:{stack.account}:table/{user_db.table_name}" ], ), # DENY access to S3 overall iam.PolicyStatement(effect=iam.Effect.DENY, actions=["s3:*"], resources=["*"]), ], ) # IoT Constructs # Rule to process shadow events and send to logging iot_rule_log_shadow_events = iot.CfnTopicRule( self, "LogShadowEventsRule", rule_name=id.replace("-", "") + "_LogShadowEvents", topic_rule_payload=iot.CfnTopicRule.TopicRulePayloadProperty( description= "Based on shadow topic and content, process messages via Lambda", rule_disabled=False, aws_iot_sql_version="2016-03-23", sql= "select *, topic() AS topic FROM '$aws/things/+/shadow/update/documents'", actions=[ iot.CfnTopicRule.ActionProperty( lambda_=iot.CfnTopicRule.LambdaActionProperty( function_arn=lambda_process_events.function_arn)) ], ), ) # Allow rule to invoke the logging function lambda_process_events.add_permission( "AllowIoTRule1", principal=iam.ServicePrincipal("iot.amazonaws.com"), source_arn=iot_rule_log_shadow_events.attr_arn, ) # Rule to process generic events and send to logging iot_rule_log_generic_events = iot.CfnTopicRule( self, "LogGenericEventsRule", rule_name=id.replace("-", "") + "_LogGenericEvents", topic_rule_payload=iot.CfnTopicRule.TopicRulePayloadProperty( description="Log generic events, enrich, then send to Lambda", rule_disabled=False, aws_iot_sql_version="2016-03-23", sql= "select *, timestamp() AS ts, topic() AS topic FROM 'events'", actions=[ iot.CfnTopicRule.ActionProperty( lambda_=iot.CfnTopicRule.LambdaActionProperty( function_arn=lambda_process_events.function_arn)) ], ), ) # Allow generic_events rule to Invoke the process_events function lambda_process_events.add_permission( "AllowIoTRule2", principal=iam.ServicePrincipal("iot.amazonaws.com"), source_arn=iot_rule_log_generic_events.attr_arn, ) # Rule to process dispenser specific events and send to logging iot_rule_log_dispenser_events = iot.CfnTopicRule( self, "LogDispenserEventsRule", rule_name=id.replace("-", "") + "_LogDispenserEvents", topic_rule_payload=iot.CfnTopicRule.TopicRulePayloadProperty( description= "Log specific dispenser events, enrich, then send to Lambda", rule_disabled=False, aws_iot_sql_version="2016-03-23", sql= "select *, timestamp() AS ts, topic() AS topic FROM 'events/+'", actions=[ iot.CfnTopicRule.ActionProperty( lambda_=iot.CfnTopicRule.LambdaActionProperty( function_arn=lambda_process_events.function_arn)) ], ), ) # Allow log_dispenser_events rule to Invoke the process_events function lambda_process_events.add_permission( "AllowIoTRule3", principal=iam.ServicePrincipal("iot.amazonaws.com"), source_arn=iot_rule_log_dispenser_events.attr_arn, ) # Rule to process cmd/NNN/response WHERE "command=dispense" iot_rule_command_response_dispense = iot.CfnTopicRule( self, "DispenseCommandResponseRule", rule_name=id.replace("-", "") + "_DispenseCommandResponse", topic_rule_payload=iot.CfnTopicRule.TopicRulePayloadProperty( description= "Invoke Lambda to process dispense commands from dispenser", rule_disabled=False, aws_iot_sql_version="2016-03-23", sql= "select *, topic() AS topic FROM '$aws/things/+/shadow/update/accepted' WHERE isUndefined(state.reported.response) = False", actions=[ iot.CfnTopicRule.ActionProperty( lambda_=iot.CfnTopicRule.LambdaActionProperty( function_arn=api_dispense_function.function_arn)) ], ), ) # Allow command_response rule to Invoke the dispense function to reconcile outstanding requests api_dispense_function.add_permission( "AllowIoTCommandResponseRule", principal=iam.ServicePrincipal("iot.amazonaws.com"), source_arn=iot_rule_command_response_dispense.attr_arn, ) # Custom resource to delete workshop users - run to clean up any lingering ones # if the admin user didn't clean up. A lot of dependsOn as users are created with bindings # to other resources props: CustomResourceProps = CustomResourceProps( name=id + "-CR-DeleteParticipantUsers", lambda_directory="./lambda_functions/cr_delete_participant_users", handler="index.main", timeout=30, runtime=lambda_.Runtime.PYTHON_3_7, environment={ # Read user records from UserTable "USER_TABLE": user_db.table_name, # Invoke the api_delete_user function "DELETE_USER_LAMBDA_FUNCTION": api_delete_user_function.function_arn, }, ) delete_participant_users_cr = CustomResourceConstruct( self, "DeleteParticpantUsers", props) # DependsOn the API Delete User Function delete_participant_users_cr.resource.node.add_dependency( api_delete_user_function) # DependsOn the user pool to delete Cognito users delete_participant_users_cr.resource.node.add_dependency(user_pool) # DependsOn the DynamoDB UserTable delete_participant_users_cr.resource.node.add_dependency(user_db) # DependsOn the IoT dispenser and client policies delete_participant_users_cr.resource.node.add_dependency( iot_policy_dispenser_limited) delete_participant_users_cr.resource.node.add_dependency( iot_policy_client) # DependsOn the IoT IAM user group delete_participant_users_cr.resource.node.add_dependency(user_group) # Permissions for function to delete users policy_statement = iam.PolicyStatement() policy_statement.add_actions("dynamodb:*") policy_statement.add_resources( f"arn:aws:dynamodb:{stack.region}:{stack.account}:table/{user_db.table_name}" ) delete_participant_users_cr.add_policy_to_role(policy_statement) policy_statement = iam.PolicyStatement() policy_statement.add_actions("lambda:InvokeFunction") policy_statement.add_resources(api_delete_user_function.function_arn) delete_participant_users_cr.add_policy_to_role(policy_statement)
def __init__(self, scope: core.Construct, id: str, unicorn_user_pool_arn, unicorn_user_pool_res_srv_identifier, unicorn_read_scope, unicorn_write_scope, **kwargs) -> None: super().__init__(scope, id, **kwargs) # Create Serverless Event Processor using Lambda): # Read Lambda Code try: with open("premium_api/lambda_src/premium_content.py", mode="r") as f: premium_content_fn_code = f.read() except OSError: print("Unable to read Lambda Function Code") raise premium_content_fn = _lambda.Function( self, "premiumContentFunction", function_name="premium_function", runtime=_lambda.Runtime.PYTHON_3_7, handler="index.lambda_handler", code=_lambda.InlineCode(premium_content_fn_code), timeout=core.Duration.seconds(3), reserved_concurrent_executions=1, environment={ "LOG_LEVEL": "INFO", "Environment": "Production" }) # Create Custom Loggroup # /aws/lambda/function-name premium_content_fn_lg = _logs.LogGroup( self, "premiumContentFnLoggroup", log_group_name=f"/aws/lambda/{premium_content_fn.function_name}", retention=_logs.RetentionDays.ONE_WEEK, removal_policy=core.RemovalPolicy.DESTROY) # Add API GW front end for the Lambda walled_garden_api_stage_options = _apigw.StageOptions( stage_name="prod", throttling_rate_limit=10, throttling_burst_limit=100, logging_level=_apigw.MethodLoggingLevel.INFO) # Create API Gateway api_01 = _apigw.LambdaRestApi( self, "walledGardenApi", rest_api_name="walled-garden-api", deploy_options=walled_garden_api_stage_options, endpoint_types=[_apigw.EndpointType.REGIONAL], handler=premium_content_fn, proxy=False) # Add the wall to the garden - API Authorizer api_01_authorizer = _apigw.CfnAuthorizer( self, "walledGardenApiAuthorizer", name="walledGardenSentry", rest_api_id=api_01.rest_api_id, type="COGNITO_USER_POOLS", provider_arns=[unicorn_user_pool_arn], authorizer_result_ttl_in_seconds=15, identity_source="method.request.header.Authorization") get_content = api_01.root.add_resource("home") # premium_content = get_content.add_resource("{premium}") premium_content = get_content.add_resource("premium") premium_content_method_get = premium_content.add_method( "GET", authorization_type=_apigw.AuthorizationType.COGNITO, authorization_scopes=[ f"{unicorn_user_pool_res_srv_identifier}/{unicorn_read_scope}" ]) premium_content_method_get.node.find_child( "Resource").add_property_override("AuthorizerId", api_01_authorizer.ref) # Add POST method premium_content_method_post = premium_content.add_method( "POST", authorization_type=_apigw.AuthorizationType.COGNITO, authorization_scopes=[ f"{unicorn_user_pool_res_srv_identifier}/{unicorn_write_scope}" ]) premium_content_method_post.node.find_child( "Resource").add_property_override("AuthorizerId", api_01_authorizer.ref) # Export API Endpoint URL self.premium_content_api_url = premium_content.url # Outputs output_1 = core.CfnOutput( self, "PremiumApiUrl", value=f"{premium_content.url}", description="Use a browser to access this url")
def __init__(self, scope: core.Construct, id_: str, profile: str) -> None: super().__init__(scope, id_) self._profile = profile self._username = getpass.getuser().capitalize() # define DynamoDB table with streams self.table = aws_dynamodb.Table( self, TenantCrudAPI._TABLE_NAME, partition_key=aws_dynamodb.Attribute( name="id", type=aws_dynamodb.AttributeType.STRING), billing_mode=aws_dynamodb.BillingMode.PAY_PER_REQUEST, removal_policy=core.RemovalPolicy.DESTROY, stream=aws_dynamodb.StreamViewType.NEW_AND_OLD_IMAGES, ) # add table name to stack outputs table_output = core.CfnOutput(self, id="TenantsTableName", value=self.table.table_name) table_output.override_logical_id("TenantsTableName") # define API GW self.rest_api: aws_apigateway.LambdaRestApi = aws_apigateway.RestApi( self, "tenant-mgmt-rest-api", rest_api_name="Tenant Management Rest API", description="This service handles tenant managemnet") # create congnito authorizer self.user_pool_arn = get_stack_resource(get_user_pool_stack_name(), 'TenantManagementUserPoolArn', True) self.api_authorizer: aws_apigateway.CfnAuthorizer = aws_apigateway.CfnAuthorizer( scope=self, name="TenantMgmtApiAuth", id="TenantMgmtApiAuth", type="COGNITO_USER_POOLS", provider_arns=[self.user_pool_arn], rest_api_id=self.rest_api.rest_api_id, identity_source="method.request.header.Authorization", ) default_cors_preflight_options = { "allow_headers": aws_apigateway.Cors.DEFAULT_HEADERS, "allow_origins": aws_apigateway.Cors.ALL_ORIGINS } # add API GW - create "{domain}/tenants" self.tenants_resource: aws_apigateway.Resource = self.rest_api.root.add_resource( "tenants", default_cors_preflight_options=default_cors_preflight_options) # add API GW /tenants/{tenant_id} path self.tenant_id_resource = self.tenants_resource.add_resource( "{tenant_id}", default_cors_preflight_options=default_cors_preflight_options) # add API GW endpoint to outputs endpoint_output = core.CfnOutput(self, id=self._API_GW_NAME, value=self.rest_api.url) endpoint_output.override_logical_id(self._API_GW_NAME) # create REST API Lambdas self._create_func = self._add_create_lambda_integration( id_, self.table) self._list_func = self._add_list_lambda_integration(id_, self.table) self._get_func = self._add_get_lambda_integration(id_, self.table)
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, **kwargs) -> None: super().__init__(scope, id, **kwargs) # ######## Dynamo DB ############### events_table = Dynamo.Table( self, "events_table", table_name='events', partition_key=Dynamo.Attribute( name="name", type=Dynamo.AttributeType.STRING ), billing_mode=Dynamo.BillingMode.PAY_PER_REQUEST, removal_policy=core.RemovalPolicy.DESTROY # Don't use in production ) # ######## Lambda Function ############### lambda_function = Lambda.Function( self, 'lambda_events', code=Lambda.Code.asset('./resources/events'), handler='events_info.handler', runtime=Lambda.Runtime.PYTHON_3_8, description='Lambda function to get information about Python events.', environment={ "EVENTS_TABLE_NAME": events_table.table_name } ) # grant permission for reading on Dynamo table events_table.grant_read_data(lambda_function) events_authorizer = Lambda.Function( self, 'authorizer', code=Lambda.Code.asset('./resources/events'), handler='authorizer.handler', runtime=Lambda.Runtime.PYTHON_3_8, description='Authorizer for events endpoint' ) auth_role = Iam.Role( self, 'auth_role_handler', assumed_by=Iam.ServicePrincipal("apigateway.amazonaws.com") ) events_authorizer.grant_invoke(auth_role) # ######## API Gateway ############### api = Apigateway.RestApi( self, 'api_events', description='REST API for Python events', deploy_options={ "method_options": { "/*/*": { "throttling_rate_limit": 10, "throttling_burst_limit": 5 } } } ) api_authorizer = Apigateway.CfnAuthorizer( self, 'api_authorizer', authorizer_credentials=auth_role.role_arn, authorizer_uri=f"arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/{events_authorizer.function_arn}/invocations", identity_source="method.request.header.Authorization", name="rest-api-authorizer", rest_api_id=api.rest_api_id, type="REQUEST" ) events_integration = Apigateway.LambdaIntegration( lambda_function) root_v1 = api.root.add_resource("v1") get_events = root_v1.add_resource("events") # {URL}/v1/events # {URL}/v1/event/{1} get_events_method = get_events.add_method( "GET", events_integration, authorization_type=Apigateway.AuthorizationType.CUSTOM ) get_events_method.node.find_child('Resource').add_property_override( 'AuthorizerId', api_authorizer.ref )
def __init__(self, scope: core.Construct, id: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) # Add SNS Topic for emails list sns_topic = aws_sns.Topic(self, "RescueCentreAPISNS") for email in emails_list["Parameter"]["Value"].split(","): sns_topic.add_subscription( aws_sns_subscriptions.EmailSubscription(email)) # ******* API gateway # Create simple, publically available API gateway resource, with CORS on preflight requests rescue_centre_api = aws_apigateway.RestApi( self, 'rescueCentreAPI', rest_api_name='rescueCentreAPI', default_cors_preflight_options={ "allow_origins": ["*"], "allow_methods": ["GET", "POST", "OPTIONS", "PUT", "DELETE"] }) # ******* COGNITO_USER_POOLS Auth auth = aws_apigateway.CfnAuthorizer( self, "adminSectionAuth", rest_api_id=rescue_centre_api.rest_api_id, type='COGNITO_USER_POOLS', identity_source='method.request.header.Authorization', provider_arns=[ 'arn:aws:cognito-idp:eu-west-2:040684591284:userpool/eu-west-2_3T4vtfKJE' ], name="adminSectionAuth") # ******* Create the databases and all the API resources We use the same Lambda code multiple times # and create very simlar resources so this is the simplest way of doing it. Minimizes effect of typos also resources = { "rehomers": { "methodsNotRequiringAuth": ["POST"], "methodsToExclude": [] }, "horses": { "methodsNotRequiringAuth": ["GET"], "methodsToExclude": ["PUT"] }, "queries": { "methodsNotRequiringAuth": ["POST"], "methodsToExclude": ["PUT"] }, "volunteers": { "methodsNotRequiringAuth": ["POST"], "methodsToExclude": [] } } # The method each lambda is supposed to handle methods = { "POST": "lambdas/createItemLambda", "PUT": "lambdas/createItemLambda", "DELETE": "lambdas/deleteItemLambda", "GET": "lambdas/getItemLambda" } for i in resources: # Add the resource (API endpoint) to the API GW API resource = rescue_centre_api.root.add_resource(i) # Create a dynamodb table for the resource Table = aws_dynamodb.Table( self, i + "Table", partition_key=aws_dynamodb.Attribute( name="id", type=aws_dynamodb.AttributeType.STRING), read_capacity=2, write_capacity=2, billing_mode=aws_dynamodb.BillingMode.PROVISIONED) # Add the methods to the resource for method in methods: if method not in resources[i]["methodsToExclude"]: details = { "table": Table, "method": method, "resource": resource, "lambdaName": i + method, "lambdaCode": methods[method], "requiresAuth": True, "topic": sns_topic } if method in resources[i]["methodsNotRequiringAuth"]: details["requiresAuth"] = False self.makeLambda(details, auth) else: self.makeLambda(details, auth) # ******* S3 bucket for website content websiteBucket = aws_s3.Bucket( self, "websiteBucket", bucket_name="www.leighrescuecentre.co.uk", public_read_access=True, website_index_document="index.html", access_control=aws_s3.BucketAccessControl.PUBLIC_READ, cors=[{ "allowedMethods": [ aws_s3.HttpMethods.GET, aws_s3.HttpMethods.PUT, aws_s3.HttpMethods.HEAD, aws_s3.HttpMethods.POST, aws_s3.HttpMethods.DELETE ], "allowedOrigins": ["*"], "allowedHeader": ["*"], "exposeHeader": ["ETag"] }]) # ******* S3 bucket for image and video content imagesBucket = aws_s3.Bucket( self, "imagesBucket", bucket_name="media.leighrescuecentre.co.uk", public_read_access=True, access_control=aws_s3.BucketAccessControl.PUBLIC_READ, cors=[{ "allowedMethods": [ aws_s3.HttpMethods.GET, aws_s3.HttpMethods.PUT, aws_s3.HttpMethods.HEAD, aws_s3.HttpMethods.POST, aws_s3.HttpMethods.DELETE ], "allowedOrigins": ["*"], "allowedHeader": ["*"], "exposeHeader": ["ETag"] }]) # ******* CloudFront distribution distribution = aws_cloudfront.CloudFrontWebDistribution( self, "websiteBucketDistribution", origin_configs=[ aws_cloudfront.SourceConfiguration( s3_origin_source=aws_cloudfront.S3OriginConfig( s3_bucket_source=websiteBucket), behaviors=[ aws_cloudfront.Behavior(is_default_behavior=True) ]), aws_cloudfront.SourceConfiguration( s3_origin_source=aws_cloudfront.S3OriginConfig( s3_bucket_source=imagesBucket), behaviors=[ aws_cloudfront.Behavior(path_pattern="/media/*") ]) ]) # ******* Code to automatically deploy the frontend code to the website bucket deployment = aws_s3_deployment.BucketDeployment( self, "deployStaticWebsite", sources=[aws_s3_deployment.Source.asset("../frontend")], destination_bucket=websiteBucket, distribution=distribution)
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: core.Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) # Create repo for Amplify static site amplify_repo = codecommit.Repository( self, 'amplify-wild-rydes-repo', repository_name='amplify-wild-rydes', description='Repo for the Wild Rydes static site for Amplify') # Create repo for holding the code for this project app_repo = codecommit.Repository( self, 'app-serverless-workshop-repo', repository_name='app-wild-rydes-serverless-workshop', description= 'Repo for project from webapp.serverlessworkshops.io/staticwebhosting/overview/' ) # IAM Role & Policy for Amplify amplify_role = iam.Role( self, 'amplify-wild-rydes-role', role_name='amplify-wild-rydes-role', assumed_by=iam.ServicePrincipal('amplify.amazonaws.com')) # Amplify amplify_static_site = amplify.App( self, 'amplify-wild-rydes-site', source_code_provider=amplify.CodeCommitSourceCodeProvider( repository=amplify_repo), description='Wild Rydes Amplify Static Site', role=amplify_role, app_name='wild-rydes-site') master = amplify_static_site.add_branch("master") # Policy is fairly open # Ran into issues when I deployed the cognito user pools through the amplify cli # It creates a new CloudFormation stack and deploys several resources amplify_policy = iam.Policy( self, 'amplify-wild-rydes-policy', roles=[amplify_role], statements=[ iam.PolicyStatement(effect=iam.Effect.ALLOW, actions=['codecommit:GitPull'], resources=[amplify_repo.repository_arn]), iam.PolicyStatement(effect=iam.Effect.ALLOW, actions=[ 'amplify:GetApp', 'amplify:CreateBackendEnvironment', 'cloudformation:*', 'cognito:*', 'lambda:*', 's3:*', 'iam:*' ], resources=['*']) ]) # DynamoDB # removal_policy=core.RemovalPolicy.DESTROY is to ensure it is deleted since this is only a lab # table_name is required to be Rides, its configured in the nodejs code that the lambda function runs rides_table = ddb.Table(self, 'Table', table_name='Rides', partition_key=ddb.Attribute( name='RideId', type=ddb.AttributeType.STRING), removal_policy=core.RemovalPolicy.DESTROY) # Lambda Functions request_unicorn_role = iam.Role( self, 'RequestUnicornRole', role_name='wild-rydes-lambda-role', assumed_by=iam.ServicePrincipal('lambda.amazonaws.com'), managed_policies=[ iam.ManagedPolicy.from_aws_managed_policy_name( 'service-role/AWSLambdaBasicExecutionRole') ]) # Grant write access to the lambda role rides_table.grant_write_data(request_unicorn_role) request_unicorn = _lambda.Function( self, 'request-unicorn', handler='requestUnicorn.handler', runtime=_lambda.Runtime.NODEJS_12_X, code=_lambda.AssetCode('request_unicorn'), role=request_unicorn_role, function_name='request-unicorn-wild-rydes') # Rest API ride_api_gw = apigw.RestApi( self, 'wild-rydes-apigw', rest_api_name='WildRydes', endpoint_types=[apigw.EndpointType.REGIONAL]) # APIGW Lambda Integration # proxy enabled for the workshop ride_api_gw_lambda_integration = apigw.LambdaIntegration( request_unicorn, proxy=True, integration_responses=[{ 'statusCode': '200', 'responseParameters': { 'method.response.header.Access-Control-Allow-Origin': "'*'", } }]) post_ride_resource = ride_api_gw.root.add_resource('ride') post_ride_resource_method = post_ride_resource.add_method( 'POST', ride_api_gw_lambda_integration, method_responses=[{ 'statusCode': '200', 'responseParameters': { 'method.response.header.Access-Control-Allow-Origin': True, } }]) # This needs to be created after the Amplify site unless you create the cognito userpool in the cdk # I went through the Amplify CLI to create the backend ride_api_gw_authorizer = apigw.CfnAuthorizer( self, 'wild-rydes-apigw-authorizer', rest_api_id=ride_api_gw.rest_api_id, name='wild-rydes-apigw-authorizer', type='COGNITO_USER_POOLS', identity_source='method.request.header.name.Authorization', identity_validation_expression="Bearer (.*)", provider_arns=[ 'arn:aws:cognito-idp:us-east-1:<ACCOUNT_ID>:userpool/<USER_POOL_ID>' ]) # https://github.com/aws/aws-cdk/issues/5618 post_ride_resource_fix = post_ride_resource_method.node.find_child( 'Resource') post_ride_resource_fix.add_property_override('AuthorizationType', 'COGNITO_USER_POOLS') post_ride_resource_fix.add_property_override( 'AuthorizerId', {"Ref": ride_api_gw_authorizer.logical_id}) # Enable CORS for the workshop post_ride_resource.add_method( 'OPTIONS', apigw.MockIntegration(integration_responses=[{ 'statusCode': '200', 'responseParameters': { 'method.response.header.Access-Control-Allow-Headers': "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'", 'method.response.header.Access-Control-Allow-Origin': "'*'", 'method.response.header.Access-Control-Allow-Methods': "'POST,OPTIONS'" } }], passthrough_behavior=apigw. PassthroughBehavior.WHEN_NO_MATCH, request_templates={ "application/json": "{\"statusCode\":200}" }), method_responses=[{ 'statusCode': '200', 'responseParameters': { 'method.response.header.Access-Control-Allow-Headers': True, 'method.response.header.Access-Control-Allow-Methods': True, 'method.response.header.Access-Control-Allow-Origin': True, } }]) # Outputs amplify_repo_url = core.CfnOutput( self, 'amplify-repo-url', value=amplify_repo.repository_clone_url_http) app_repo_url = core.CfnOutput(self, 'app-repo-url', value=app_repo.repository_clone_url_http) amplify_default_domain = core.CfnOutput( self, 'amplify-default-domain', value=amplify_static_site.default_domain) request_unicorn_apigw = core.CfnOutput(self, 'request-unicorn-apigw', value=request_unicorn_apigw.url)
def __init__(self, scope: core.App, id: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) # create Cognito user pool user_pool = cognito.UserPool( self, "testUserPool", auto_verified_attributes=[cognito.UserPoolAttribute.EMAIL], sign_in_type=cognito.SignInType.EMAIL) cfn_user_pool = user_pool.node.default_child cfn_user_pool.policies = { "passwordPolicy": { "minimumLength": 8, "requireLowercase": True, "requireNumbers": True, "requireUppercase": True, "requireSymbols": False } } # attach a domain # random.seed(20191113) # random_domain = ''.join(random.choice(string.ascii_lowercase) for i in range(20)) # cfn_user_pool_domain = cognito.CfnUserPoolDomain( # self, "testUserPoolDomain", # domain=random_domain, # user_pool_id=user_pool.user_pool_id # ) # create pool client pool_client = cognito.CfnUserPoolClient( self, 'testUserPoolClient', user_pool_id=user_pool.user_pool_id, supported_identity_providers=["COGNITO"], generate_secret=False, refresh_token_validity=1, explicit_auth_flows=["USER_PASSWORD_AUTH"], allowed_o_auth_flows_user_pool_client=True, allowed_o_auth_flows=["implicit"], allowed_o_auth_scopes=[ "email", "openid", "aws.cognito.signin.user.admin" ], callback_ur_ls=["http://localhost"], logout_ur_ls=["http://localhost"]) # output some stuff core.CfnOutput(self, "User Pool ID", value=user_pool.user_pool_id) core.CfnOutput(self, "Pool Client ID", value=pool_client.ref) # core.CfnOutput(self, "User pool domain", value=cfn_user_pool_domain.domain) # create REST API resource api = apigw.RestApi(self, 'my_API') # new resource - /test test_resource = api.root.add_resource('test') # Cognito authorizer cfn_authorizer = apigw.CfnAuthorizer( self, "my_cognito", name='API_authorizer', type='COGNITO_USER_POOLS', identity_source='method.request.header.Authorization', rest_api_id=api.rest_api_id, provider_arns=[user_pool.user_pool_arn]) # lambda handler hello_world_handler = _lambda.Function( self, 'my_handler', code=_lambda.AssetCode('lambda'), handler='index.handler', runtime=_lambda.Runtime.PYTHON_3_7) # attach GET method hello_world_integration = apigw.LambdaIntegration(hello_world_handler) meth = test_resource.add_method( "GET", hello_world_integration, authorization_type=apigw.AuthorizationType.COGNITO) meth.node.find_child('Resource').add_property_override( 'AuthorizerId', cfn_authorizer.ref)
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})