def __init__(self, scope: core.Stack, id: str, **kwargs): super().__init__(scope, id, **kwargs) # API Gateway Type: Rest API self.api_gateway = apigw.RestApi( self, "APIGateway", rest_api_name=self.stack_name, ) # Adding base method, this is just to get the API Gateway created self.base_method = self.api_gateway.root.add_method("ANY") # VPC Link setup with list of NLB's try: if config['APIGW_NLBS']: for name, arn in config['APIGW_NLBS'].items(): self.service_nlb = elb.NetworkLoadBalancer.from_network_load_balancer_attributes( self, "{}NLB".format(name), load_balancer_arn=arn ) #self.nlb_uri = self.service_nlb.uri self.gateway_vpc_link = apigw.VpcLink( self, "VPCLink{}".format(name), description=name, targets=[ self.service_nlb ], vpc_link_name=name ) except Exception as e: print("No NLBs present, moving on...") pass
def __init__(self, scope: core.Stack, id: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) # Create VPC self.vpc = ec2.Vpc.from_lookup(self, "VPC", vpc_name='api-gateway/VPC') # Create ECS Cluster self.ecs_cluster = ecs.Cluster(self, "ECSCluster", vpc=self.vpc) # This high level construct will build a docker image, ecr repo and connect the ecs service to allow pull access self.container_image = ecr.DockerImageAsset(self, "Image", directory="./") # Task definition details to define the frontend service container self.task_def = ecs_patterns.NetworkLoadBalancedTaskImageOptions( image=ecs.ContainerImage.from_ecr_repository( repository=self.container_image.repository), container_port=80, enable_logging=True, environment={"GIT_HASH": "12345"}, ) # Create the frontend service self.python_service = ecs_patterns.NetworkLoadBalancedFargateService( self, "PythonService", cpu=256, memory_limit_mib=512, cluster=self.ecs_cluster, desired_count=1, task_image_options=self.task_def, public_load_balancer=False, ) self.python_service.service.connections.allow_from_any_ipv4( port_range=ec2.Port( protocol=ec2.Protocol.ALL, string_representation="All port 80", from_port=80, ), description="Allows traffic on port 80 from NLB") # Create VPC Link from API Gateway to NLB # TODO: Make api id dynamic self.rest_api = apigw.RestApi.from_rest_api_id( self, "APIGateway", rest_api_id="6znhu1vqp6") # TODO: Create stage variable for vpc links self.gateway_vpc_link = apigw.VpcLink( self, "VPCLink", description="VPC Link from API Gateway to ECS Python Service", targets=[self.python_service.load_balancer], vpc_link_name="ECS_VPC_LINK")
def __init__(self, scope: core.Construct, id: str, props: APIGatewayStackProps, **kwargs) -> None: super().__init__(scope, id, **kwargs) nlb = elbv2.NetworkLoadBalancer.from_network_load_balancer_attributes( self, "NLB", load_balancer_arn=props.fargate_service.load_balancer. load_balancer_arn, ) vpc_link = aws_apigateway.VpcLink( self, "VPCLink", vpc_link_name="VPCLink for our REST API", targets=[nlb]) schema = APIGatewayStack._generate_swagger_spec( props.fargate_service.load_balancer.load_balancer_dns_name, vpc_link) json_schema = json.loads(schema) api = aws_apigateway.CfnRestApi( self, "Schema", name="MysfitsApi", body=json_schema, endpoint_configuration=aws_apigateway.CfnRestApi. EndpointConfigurationProperty(types=["REGIONAL"]), fail_on_warnings=True, ) aws_apigateway.CfnDeployment(self, "Prod", rest_api_id=api.ref, stage_name="prod") core.CfnOutput(self, "APIID", value=api.ref, description="API Gateway ID") self._api_gateway = api
def __init__(self, scope: core.Construct, construct_id: str, load_balancer_dns_name, load_balancer_arn, user_pool_id, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) nlb = elbv2.NetworkLoadBalancer.from_network_load_balancer_attributes( self, 'NLB', load_balancer_arn=load_balancer_arn) vpc_link = apigw.VpcLink(self, 'VPCLink', description='VPCLink for our REST API', vpc_link_name='MysfitsApiVpcLink', targets=[nlb]) json_schema = generate_swagger_spec_json(self.account, self.region, load_balancer_dns_name, user_pool_id, vpc_link) api = apigw.CfnRestApi( self, 'Schema', name='MysfitsApi', body=json_schema, endpoint_configuration=apigw.CfnRestApi. EndpointConfigurationProperty( types=['REGIONAL']), # types: EDGE, REGIONAL, PRIVATE fail_on_warnings=True) prod = apigw.CfnDeployment(self, 'Prod', rest_api_id=api.ref, stage_name='prod') core.CfnOutput(self, "APIID", description='API Gateway ID', value=api.ref)
def __init__(self, scope: core.Construct, id: str, stack_name: str, task_definition_cpu: int, task_definition_memory_limit_mib: int, docker_image_name: str, container_port: int, desired_container_count: int, private_subnets: Sequence[aws_ec2.Subnet] = None, public_subnets: Sequence[aws_ec2.Subnet] = None, private_security_group: aws_ec2.SecurityGroup = None, public_security_group: aws_ec2.SecurityGroup = None, vpc: aws_ec2.Vpc = None, fargate_cluster: aws_ecs.Cluster = None, authorizer_lambda_arn: str = None, authorizer_lambda_role_arn: str = None, **kwargs): super().__init__(scope, id, **kwargs) # Role self.role = aws_iam.Role( self, 'Role', assumed_by=aws_iam.ServicePrincipal(service='ecs.amazonaws.com'), managed_policies=[ aws_iam.ManagedPolicy.from_aws_managed_policy_name( managed_policy_name= 'service-role/AmazonECSTaskExecutionRolePolicy') ], inline_policies={ id: aws_iam.PolicyDocument(statements=[ aws_iam.PolicyStatement( effect=aws_iam.Effect.ALLOW, actions=[ 'kms:Encrypt', 'kms:Decrypt', 'kms:ReEncrypt*', 'kms:GenerateDataKey*', 'kms:DescribeKey', 'ec2:CreateNetworkInterface', 'ec2:DescribeNetworkInterfaces', 'ec2:DeleteNetworkInterface', # Remaining actions from https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/quickref-ecs.html 'elasticloadbalancing:DeregisterInstancesFromLoadBalancer', 'elasticloadbalancing:DeregisterTargets', 'elasticloadbalancing:Describe*', 'elasticloadbalancing:RegisterInstancesWithLoadBalancer', 'elasticloadbalancing:RegisterTargets', 'ec2:Describe*', 'ec2:AuthorizeSecurityGroupIngress' ], resources=['*']) ]) }) self.role.assume_role_policy.add_statements( aws_iam.PolicyStatement( actions=['sts:AssumeRole'], principals=[ aws_iam.ServicePrincipal(service='ecs-tasks.amazonaws.com') ])) # Set Defaults if parameters are None if vpc is None: vpc = aws_ec2.Vpc(self, 'Vpc') if private_subnets is None: private_subnets = vpc.private_subnets if public_subnets is None: public_subnets = vpc.public_subnets if public_security_group is None: public_security_group = aws_ec2.SecurityGroup( self, 'PublicSecurityGroup', vpc=vpc, allow_all_outbound=True) # Allow inbound HTTP traffic public_security_group.add_ingress_rule( peer=aws_ec2.Peer.ipv4(cidr_ip='0.0.0.0/0'), connection=aws_ec2.Port.tcp(port=80)) # Allow inbound HTTPS traffic public_security_group.add_ingress_rule( peer=aws_ec2.Peer.ipv4(cidr_ip='0.0.0.0/0'), connection=aws_ec2.Port.tcp(port=443)) if private_security_group is None: private_security_group = aws_ec2.SecurityGroup( self, 'PrivateSecurityGroup', vpc=vpc, allow_all_outbound=True) public_subnet_cidr_blocks = Utils.get_subnet_cidr_blocks( public_subnets) # Create an ingress rule for each of the NLB's subnet's CIDR ranges and add the rules to the ECS service's # security group. This will allow requests from the NLB to go into the ECS service. This allow inbound # traffic from public subnets. for cidr_block in public_subnet_cidr_blocks: private_security_group.add_ingress_rule( peer=aws_ec2.Peer.ipv4(cidr_ip=cidr_block), connection=aws_ec2.Port.tcp(port=container_port)) if fargate_cluster is None: fargate_cluster = aws_ecs.Cluster( self, 'FargateCluster', ) task_def = aws_ecs.FargateTaskDefinition( self, 'TaskDefinition', cpu=task_definition_cpu, memory_limit_mib=task_definition_memory_limit_mib, task_role=self.role, execution_role=self.role) container = aws_ecs.ContainerDefinition( self, 'Container', image=aws_ecs.ContainerImage.from_registry(name=docker_image_name), task_definition=task_def, logging=aws_ecs.AwsLogDriver(stream_prefix='/ecs')) container.add_port_mappings( aws_ecs.PortMapping(container_port=container_port, protocol=aws_ec2.Protocol.TCP)) ecs_service = aws_ecs.FargateService( self, 'FargateService', cluster=fargate_cluster, task_definition=task_def, vpc_subnets=aws_ec2.SubnetSelection(subnets=private_subnets), security_group=private_security_group, desired_count=desired_container_count) target_group = aws_elasticloadbalancingv2.NetworkTargetGroup( self, 'TargetGroup', port=80, # Health check occurs over HTTP health_check=aws_elasticloadbalancingv2.HealthCheck( protocol=aws_elasticloadbalancingv2.Protocol.TCP), targets=[ecs_service], vpc=vpc) nlb = aws_elasticloadbalancingv2.NetworkLoadBalancer( self, 'NetworkLoadBalancer', vpc=vpc, internet_facing=False, vpc_subnets=aws_ec2.SubnetSelection(subnets=public_subnets), ) nlb.add_listener( id='Listener', port=80, # HTTP listener default_target_groups=[target_group]) # nlb.log_access_logs( # todo: add this later when you have time to research the correct bucket policy. # bucket=aws_s3.Bucket( # self, 'LoadBalancerLogBucket', # bucket_name='load-balancer-logs', # public_read_access=False, # block_public_access=aws_s3.BlockPublicAccess( # block_public_policy=True, # restrict_public_buckets=True # ) # ) # ) # Dependencies ecs_service.node.add_dependency(nlb) # API Gateway rest_api = aws_apigateway.RestApi(self, stack_name) resource = rest_api.root.add_resource( path_part='{proxy+}', default_method_options=aws_apigateway.MethodOptions( request_parameters={'method.request.path.proxy': True})) token_authorizer = None if authorizer_lambda_arn and authorizer_lambda_role_arn: token_authorizer = aws_apigateway.TokenAuthorizer( #todo: make this a parameter? self, 'JwtTokenAuthorizer', results_cache_ttl=core.Duration.minutes(5), identity_source='method.request.header.Authorization', assume_role=aws_iam.Role.from_role_arn( self, 'AuthorizerLambdaInvokationRole', role_arn=authorizer_lambda_role_arn), handler=aws_lambda.Function.from_function_arn( self, 'AuthorizerLambda', function_arn=authorizer_lambda_arn)) resource.add_method( http_method='ANY', authorization_type=aws_apigateway.AuthorizationType.CUSTOM, authorizer=token_authorizer, integration=aws_apigateway.HttpIntegration( url=f'http://{nlb.load_balancer_dns_name}/{{proxy}}', http_method='ANY', proxy=True, options=aws_apigateway.IntegrationOptions( request_parameters={ 'integration.request.path.proxy': 'method.request.path.proxy' }, connection_type=aws_apigateway.ConnectionType.VPC_LINK, vpc_link=aws_apigateway.VpcLink( self, 'VpcLink', description= f'API Gateway VPC Link to internal NLB for {stack_name}', vpc_link_name=stack_name, targets=[nlb]))))
def __init__(self, scope: core.Construct, id: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) ## Create S3 Bucket. We'll populate it seperately. bucket_name="{}-s3-ecs-apigw-test".format(core.Aws.ACCOUNT_ID) bucket = s3.Bucket(self, "s3-ecs-apigw-test", bucket_name=bucket_name, versioned=True, public_read_access=False ) ## Create ECS Cluster, Taks and Service ### Create the VPC for the demo vpc = ec2.Vpc(self, "MyVpc", max_azs=3) ### Create the ECS Cluster cluster = ecs.Cluster(self, "ecs-apigw-test-cluster", cluster_name="ecs-apigw-test-cluster", container_insights=True, vpc=vpc) ### Using the Network LoadBalance Fargate patterm, this wills create the container definition, the task definition, the service and the Network load balancer for it. ecs_deploy = ecsp.NetworkLoadBalancedFargateService(self, "ecs-apigw-test", cluster=cluster, cpu=512, desired_count=2, public_load_balancer=False, memory_limit_mib=2048, task_image_options=ecsp.NetworkLoadBalancedTaskImageOptions( image=ecs.ContainerImage.from_registry("strm/helloworld-http") ), health_check_grace_period=core.Duration.minutes(5) ) ### Have to add an Ingress rule to allow traffic through. This applies the CIDR of the VPC. ecs_deploy.service.connections.security_groups[0].add_ingress_rule( ec2.Peer.ipv4(vpc.vpc_cidr_block), ec2.Port( protocol=ec2.Protocol.TCP, string_representation="", from_port=80, to_port=80 ) ) ## Create API Gateway resources ### Create the VPC Link to the Network Load Balancer vpc_link =apigw.VpcLink(self, "ecs-test-vpc-link", targets = [ecs_deploy.load_balancer]) ### Create the API api = apigw.RestApi(self, "ecs-s3-test-api", rest_api_name="ECS S3 Test API", description="Test API for distributing traffic to S3 and ECS", binary_media_types=["image/png"]) ### Create the execution role for the API methods. rest_api_role = iam.Role( self, "RestAPIRole", assumed_by=iam.ServicePrincipal("apigateway.amazonaws.com"), managed_policies=[iam.ManagedPolicy.from_aws_managed_policy_name("AmazonS3ReadOnlyAccess")]) ### Generic Method Response that can be used with each API method method_response = apigw.MethodResponse(status_code="200", response_parameters={"method.response.header.Content-Type": True}) ### Root URI root_integration_response = apigw.IntegrationResponse( status_code="200", response_templates={"text/html": "$input.path('$')"}, response_parameters={"method.response.header.Content-Type": "'text/html'"}) root_integration_options = apigw.IntegrationOptions( credentials_role=rest_api_role, integration_responses=[root_integration_response], request_templates={"application/json": "Action=SendMessage&MessageBody=$input.body"}, passthrough_behavior=apigw.PassthroughBehavior.NEVER, request_parameters={ "integration.request.header.Content-Type": "'application/x-www-form-urlencoded'"}) root_resource_s3_integration = apigw.AwsIntegration( service="s3", integration_http_method="GET", subdomain=bucket_name, path="index.html", options=root_integration_options) root_method = api.root.add_method("GET", root_resource_s3_integration, method_responses=[method_response]) ### API URI api_integration = apigw.Integration( type=apigw.IntegrationType.HTTP_PROXY, uri="http://{}".format(ecs_deploy.load_balancer.load_balancer_dns_name), integration_http_method="GET", options={ "connection_type": apigw.ConnectionType.VPC_LINK, "vpc_link": vpc_link } ) apis = api.root.add_resource("apis") apii = apis.add_resource("{api}") # apis = api.root.add_resource("apis") apii_get = apii.add_method("GET", api_integration, method_responses=[method_response], request_parameters={ "method.request.path.api": True,}) ## Add Images URI image_integration_response = apigw.IntegrationResponse( status_code="200", content_handling=apigw.ContentHandling.CONVERT_TO_BINARY, # response_templates={"text/html": "$input.path('$')"}, response_parameters={"method.response.header.Content-Type": "'image/png'"}) image_integration_options = apigw.IntegrationOptions( credentials_role=rest_api_role, integration_responses=[image_integration_response], request_templates={"application/json": "Action=SendMessage&MessageBody=$input.body"}, passthrough_behavior=apigw.PassthroughBehavior.NEVER, request_parameters={ "integration.request.header.Content-Type": "'application/x-www-form-urlencoded'", "integration.request.path.image": "method.request.path.image"}) images_resource_s3_integration = apigw.AwsIntegration( service="s3", integration_http_method="GET", subdomain=bucket_name, path="images/{image}", options=image_integration_options) images_resource = api.root.add_resource("images") image_resource = images_resource.add_resource("{image}") images_get = image_resource.add_method("GET", images_resource_s3_integration, method_responses=[method_response], request_parameters={ "method.request.path.image": True,}) ## Fall Through folder = api.root.add_resource("{folder}") item = folder.add_resource("{item}") integration_response = apigw.IntegrationResponse( status_code="200", response_templates={"text/html": "$input.path('$')"}, response_parameters={"method.response.header.Content-Type": "'text/html'"}) s3_proxy_integration_options = apigw.IntegrationOptions( credentials_role=rest_api_role, integration_responses=[integration_response], request_templates={"application/json": "Action=SendMessage&MessageBody=$input.body"}, passthrough_behavior=apigw.PassthroughBehavior.NEVER, request_parameters={ "integration.request.header.Content-Type": "'application/x-www-form-urlencoded'", "integration.request.path.item": "method.request.path.item", "integration.request.path.folder": "method.request.path.folder"}) s3_proxy_resource_s3_integration = apigw.AwsIntegration( service="s3", integration_http_method="GET", subdomain=bucket_name, path="{folder}/{item}", options=s3_proxy_integration_options) item_get = item.add_method("GET", s3_proxy_resource_s3_integration, method_responses=[method_response], request_parameters={ "method.request.path.item": True, "method.request.path.folder": True } )