def create_user_pool_domain(self, user_pool: aws_cognito.CfnUserPool, tag: str): domain_prefix = self._secrets["hostedui.domain_prefix"] domain = aws_cognito.CfnUserPoolDomain( self, f"user_pool_domain_{tag}", domain=domain_prefix, user_pool_id=user_pool.ref, ) return domain
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, **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: core.Construct, id: str, domain_prefix: str, other_account: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) user_pool = cognito.CfnUserPool( scope=self, id="user-pool", admin_create_user_config=cognito.CfnUserPool.AdminCreateUserConfigProperty( allow_admin_create_user_only=True, ), policies=cognito.CfnUserPool.PoliciesProperty( password_policy=cognito.CfnUserPool.PasswordPolicyProperty( minimum_length=20, )), username_attributes=["email"], auto_verified_attributes=["email"], ) cognito.CfnUserPoolDomain( scope=self, id="cognito-user-pool-domain", domain=f"{domain_prefix}-{core.Aws.ACCOUNT_ID}", user_pool_id=user_pool.ref, ) id_pool = cognito.CfnIdentityPool( scope=self, id="identity-pool", allow_unauthenticated_identities=False, cognito_identity_providers=[], ) auth_role = iam.Role( scope=self, id="auth-role", assumed_by=iam.FederatedPrincipal( federated="cognito-identity.amazonaws.com", conditions={ "StringEquals": {"cognito-identity.amazonaws.com:aud": id_pool.ref}, "ForAnyValue:StringLike": {"cognito-identity.amazonaws.com:amr": "authenticated"}, }, assume_role_action="sts:AssumeRoleWithWebIdentity"), ) es_role = iam.Role( scope=self, id="es-role", assumed_by=iam.ServicePrincipal('es.amazonaws.com'), managed_policies=[ iam.ManagedPolicy.from_aws_managed_policy_name( managed_policy_name="AmazonESCognitoAccess" ) ], ) es_domain = elasticsearch.CfnDomain( scope=self, id="search-domain", elasticsearch_cluster_config=elasticsearch.CfnDomain.ElasticsearchClusterConfigProperty( instance_count=2, instance_type="m5.large.elasticsearch", ), node_to_node_encryption_options=elasticsearch.CfnDomain.NodeToNodeEncryptionOptionsProperty( enabled=True), encryption_at_rest_options=elasticsearch.CfnDomain.EncryptionAtRestOptionsProperty( enabled=True), ebs_options=elasticsearch.CfnDomain.EBSOptionsProperty( ebs_enabled=True, volume_size=20), elasticsearch_version="7.4", domain_name=domain_prefix, access_policies={ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": auth_role.role_arn }, "Action": [ "es:ESHttpGet", "es:ESHttpPut", "es:ESHttpPost", "es:ESHttpDelete" ], "Resource": "arn:aws:es:" + core.Aws.REGION + ":" + core.Aws.ACCOUNT_ID + ":domain/" + domain_prefix + "/*" }, ] }, ) es_domain.add_property_override( 'CognitoOptions.Enabled', True) es_domain.add_property_override( 'CognitoOptions.IdentityPoolId', id_pool.ref) es_domain.add_property_override( 'CognitoOptions.RoleArn', es_role.role_arn) es_domain.add_property_override( 'CognitoOptions.UserPoolId', user_pool.ref) cognito.CfnIdentityPoolRoleAttachment( scope=self, id='user-pool-role-attachment', identity_pool_id=id_pool.ref, roles={ 'authenticated': auth_role.role_arn } ) es_external_role = iam.Role( scope=self, id="logger-role", assumed_by=iam.CompositePrincipal( iam.ServicePrincipal("lambda.amazonaws.com"), iam.AccountPrincipal(other_account), ), description="role to use elastic search assumed by lambda", inline_policies={ "es_policy": iam.PolicyDocument(statements=[ iam.PolicyStatement( actions=[ "es:ESHttpPost", ], resources=[ es_domain.attr_arn + "/*", ], )]), }, ) core.CfnOutput( scope=self, id="es-host", value=es_domain.attr_domain_endpoint, ) core.CfnOutput( scope=self, id="es-region", value=core.Aws.REGION, ) core.CfnOutput( scope=self, id="es-external-role", value=es_external_role.role_arn, )