from typing import Optional, Dict, Any, List, TYPE_CHECKING from pydantic import Field, field_validator from pydantic_core.core_schema import FieldValidationInfo from phi.app.base import AppBase # noqa: F401 from phi.app.context import ContainerContext from phi.aws.app.context import AwsBuildContext from phi.utils.log import logger if TYPE_CHECKING: from phi.aws.resource.base import AwsResource from phi.aws.resource.ec2.security_group import SecurityGroup from phi.aws.resource.ecs.cluster import EcsCluster from phi.aws.resource.ecs.container import EcsContainer from phi.aws.resource.ecs.service import EcsService from phi.aws.resource.ecs.task_definition import EcsTaskDefinition from phi.aws.resource.elb.listener import Listener from phi.aws.resource.elb.load_balancer import LoadBalancer from phi.aws.resource.elb.target_group import TargetGroup class AwsApp(AppBase): # -*- Workspace Configuration # Path to the workspace directory inside the container workspace_dir_container_path: str = "/usr/local/app" # -*- Networking Configuration # List of subnets for the app: Type: Union[str, Subnet] # Added to the load balancer, target group, and ECS service subnets: Optional[List[Any]] = None # -*- ECS Configuration ecs_cluster: Optional[Any] = None # Create a cluster if ecs_cluster is None create_ecs_cluster: bool = True # Name of the ECS cluster ecs_cluster_name: Optional[str] = None ecs_launch_type: str = "FARGATE" ecs_task_cpu: str = "1024" ecs_task_memory: str = "2048" ecs_service_count: int = 1 ecs_enable_service_connect: bool = False ecs_service_connect_protocol: Optional[str] = None ecs_service_connect_namespace: str = "default" assign_public_ip: Optional[bool] = None ecs_bedrock_access: bool = True ecs_exec_access: bool = True ecs_secret_access: bool = True ecs_s3_access: bool = True # -*- Security Group Configuration # List of security groups for the ECS Service. Type: SecurityGroup security_groups: Optional[List[Any]] = None # If create_security_groups=True, # Create security groups for the app and load balancer create_security_groups: bool = True # inbound_security_groups to add to the app security group inbound_security_groups: Optional[List[Any]] = None # inbound_security_group_ids to add to the app security group inbound_security_group_ids: Optional[List[str]] = None # -*- LoadBalancer Configuration load_balancer: Optional[Any] = None # Create a load balancer if load_balancer is None create_load_balancer: bool = False # Enable HTTPS on the load balancer load_balancer_enable_https: bool = False # ACM certificate for HTTPS # load_balancer_certificate or load_balancer_certificate_arn # is required if enable_https is True load_balancer_certificate: Optional[Any] = None # ARN of the certificate for HTTPS, required if enable_https is True load_balancer_certificate_arn: Optional[str] = None # Security groups for the load balancer: List[SecurityGroup] # The App creates a security group for the load balancer if: # load_balancer_security_groups is None # and create_load_balancer is True # and create_security_groups is True load_balancer_security_groups: Optional[List[Any]] = None # -*- Listener Configuration listeners: Optional[List[Any]] = None # Create a listener if listener is None create_listeners: Optional[bool] = Field(None, validate_default=True) # -*- TargetGroup Configuration target_group: Optional[Any] = None # Create a target group if target_group is None create_target_group: Optional[bool] = Field(None, validate_default=True) # HTTP or HTTPS. Recommended to use HTTP because HTTPS is handled by the load balancer target_group_protocol: str = "HTTP" # Port number for the target group # If target_group_port is None, then use container_port target_group_port: Optional[int] = None target_group_type: str = "ip" health_check_protocol: Optional[str] = None health_check_port: Optional[str] = None health_check_enabled: Optional[bool] = None health_check_path: Optional[str] = None health_check_interval_seconds: Optional[int] = None health_check_timeout_seconds: Optional[int] = None healthy_threshold_count: Optional[int] = None unhealthy_threshold_count: Optional[int] = None # -*- Add NGINX reverse proxy enable_nginx: bool = False nginx_image: Optional[Any] = None nginx_image_name: str = "nginx" nginx_image_tag: str = "1.25.2-alpine" nginx_container_port: int = 80 @field_validator("create_listeners", mode="before") def update_create_listeners(cls, create_listeners, info: FieldValidationInfo): if create_listeners: return create_listeners # If create_listener is False, then create a listener if create_load_balancer is True return info.data.get("create_load_balancer", None) @field_validator("create_target_group", mode="before") def update_create_target_group(cls, create_target_group, info: FieldValidationInfo): if create_target_group: return create_target_group # If create_target_group is False, then create a target group if create_load_balancer is True return info.data.get("create_load_balancer", None) def get_container_context(self) -> Optional[ContainerContext]: logger.debug("Building ContainerContext") if self.container_context is not None: return self.container_context workspace_name = self.workspace_name if workspace_name is None: raise Exception("Could not determine workspace_name") workspace_root_in_container = self.workspace_dir_container_path if workspace_root_in_container is None: raise Exception("Could not determine workspace_root in container") workspace_parent_paths = workspace_root_in_container.split("/")[0:-1] workspace_parent_in_container = "/".join(workspace_parent_paths) self.container_context = ContainerContext( workspace_name=workspace_name, workspace_root=workspace_root_in_container, workspace_parent=workspace_parent_in_container, ) if self.workspace_settings is not None and self.workspace_settings.scripts_dir is not None: self.container_context.scripts_dir = f"{workspace_root_in_container}/{self.workspace_settings.scripts_dir}" if self.workspace_settings is not None and self.workspace_settings.storage_dir is not None: self.container_context.storage_dir = f"{workspace_root_in_container}/{self.workspace_settings.storage_dir}" if self.workspace_settings is not None and self.workspace_settings.workflows_dir is not None: self.container_context.workflows_dir = ( f"{workspace_root_in_container}/{self.workspace_settings.workflows_dir}" ) if self.workspace_settings is not None and self.workspace_settings.workspace_dir is not None: self.container_context.workspace_dir = ( f"{workspace_root_in_container}/{self.workspace_settings.workspace_dir}" ) if self.workspace_settings is not None and self.workspace_settings.ws_schema is not None: self.container_context.workspace_schema = self.workspace_settings.ws_schema if self.requirements_file is not None: self.container_context.requirements_file = f"{workspace_root_in_container}/{self.requirements_file}" return self.container_context def get_container_env(self, container_context: ContainerContext, build_context: AwsBuildContext) -> Dict[str, str]: from phi.constants import ( PHI_RUNTIME_ENV_VAR, PYTHONPATH_ENV_VAR, REQUIREMENTS_FILE_PATH_ENV_VAR, SCRIPTS_DIR_ENV_VAR, STORAGE_DIR_ENV_VAR, WORKFLOWS_DIR_ENV_VAR, WORKSPACE_DIR_ENV_VAR, WORKSPACE_HASH_ENV_VAR, WORKSPACE_ID_ENV_VAR, WORKSPACE_ROOT_ENV_VAR, ) # Container Environment container_env: Dict[str, str] = self.container_env or {} container_env.update( { "INSTALL_REQUIREMENTS": str(self.install_requirements), "PRINT_ENV_ON_LOAD": str(self.print_env_on_load), PHI_RUNTIME_ENV_VAR: "ecs", REQUIREMENTS_FILE_PATH_ENV_VAR: container_context.requirements_file or "", SCRIPTS_DIR_ENV_VAR: container_context.scripts_dir or "", STORAGE_DIR_ENV_VAR: container_context.storage_dir or "", WORKFLOWS_DIR_ENV_VAR: container_context.workflows_dir or "", WORKSPACE_DIR_ENV_VAR: container_context.workspace_dir or "", WORKSPACE_ROOT_ENV_VAR: container_context.workspace_root or "", } ) try: if container_context.workspace_schema is not None: if container_context.workspace_schema.id_workspace is not None: container_env[WORKSPACE_ID_ENV_VAR] = str(container_context.workspace_schema.id_workspace) or "" if container_context.workspace_schema.ws_hash is not None: container_env[WORKSPACE_HASH_ENV_VAR] = container_context.workspace_schema.ws_hash except Exception: pass if self.set_python_path: python_path = self.python_path if python_path is None: python_path = container_context.workspace_root if self.add_python_paths is not None: python_path = "{}:{}".format(python_path, ":".join(self.add_python_paths)) if python_path is not None: container_env[PYTHONPATH_ENV_VAR] = python_path # Set aws region and profile self.set_aws_env_vars(env_dict=container_env, aws_region=build_context.aws_region) # Update the container env using env_file env_data_from_file = self.get_env_file_data() if env_data_from_file is not None: container_env.update({k: str(v) for k, v in env_data_from_file.items() if v is not None}) # Update the container env using secrets_file secret_data_from_file = self.get_secret_file_data() if secret_data_from_file is not None: container_env.update({k: str(v) for k, v in secret_data_from_file.items() if v is not None}) # Update the container env with user provided env_vars # this overwrites any existing variables with the same key if self.env_vars is not None and isinstance(self.env_vars, dict): container_env.update({k: v for k, v in self.env_vars.items() if v is not None}) # logger.debug("Container Environment: {}".format(container_env)) return container_env def get_load_balancer_security_groups(self) -> Optional[List["SecurityGroup"]]: from phi.aws.resource.ec2.security_group import SecurityGroup, InboundRule load_balancer_security_groups: Optional[List[SecurityGroup]] = self.load_balancer_security_groups if load_balancer_security_groups is None: # Create security group for the load balancer if self.create_load_balancer and self.create_security_groups: load_balancer_security_groups = [] lb_sg = SecurityGroup( name=f"{self.get_app_name()}-lb-security-group", description=f"Security group for {self.get_app_name()} load balancer", inbound_rules=[ InboundRule( description="Allow HTTP traffic from the internet", port=80, cidr_ip="0.0.0.0/0", ), ], ) if self.load_balancer_enable_https: if lb_sg.inbound_rules is None: lb_sg.inbound_rules = [] lb_sg.inbound_rules.append( InboundRule( description="Allow HTTPS traffic from the internet", port=443, cidr_ip="0.0.0.0/0", ) ) load_balancer_security_groups.append(lb_sg) return load_balancer_security_groups def security_group_definition(self) -> "SecurityGroup": from phi.aws.resource.ec2.security_group import SecurityGroup, InboundRule from phi.aws.resource.reference import AwsReference # Create security group for the app app_sg = SecurityGroup( name=f"{self.get_app_name()}-security-group", description=f"Security group for {self.get_app_name()}", ) # Add inbound rules for the app security group # Allow traffic from the load balancer security groups load_balancer_security_groups = self.get_load_balancer_security_groups() if load_balancer_security_groups is not None: if app_sg.inbound_rules is None: app_sg.inbound_rules = [] if app_sg.depends_on is None: app_sg.depends_on = [] for lb_sg in load_balancer_security_groups: app_sg.inbound_rules.append( InboundRule( description=f"Allow traffic from {lb_sg.name} to the {self.get_app_name()}", port=self.container_port, source_security_group_id=AwsReference(lb_sg.get_security_group_id), ) ) app_sg.depends_on.append(lb_sg) # Allow traffic from inbound_security_groups if self.inbound_security_groups is not None: if app_sg.inbound_rules is None: app_sg.inbound_rules = [] if app_sg.depends_on is None: app_sg.depends_on = [] for inbound_sg in self.inbound_security_groups: app_sg.inbound_rules.append( InboundRule( description=f"Allow traffic from {inbound_sg.name} to the {self.get_app_name()}", port=self.container_port, source_security_group_id=AwsReference(inbound_sg.get_security_group_id), ) ) # Allow traffic from inbound_security_group_ids if self.inbound_security_group_ids is not None: if app_sg.inbound_rules is None: app_sg.inbound_rules = [] if app_sg.depends_on is None: app_sg.depends_on = [] for inbound_sg_id in self.inbound_security_group_ids: app_sg.inbound_rules.append( InboundRule( description=f"Allow traffic from {inbound_sg_id} to the {self.get_app_name()}", port=self.container_port, source_security_group_id=inbound_sg_id, ) ) return app_sg def get_security_groups(self) -> Optional[List["SecurityGroup"]]: from phi.aws.resource.ec2.security_group import SecurityGroup security_groups: Optional[List[SecurityGroup]] = self.security_groups if security_groups is None: # Create security group for the service if self.create_security_groups: security_groups = [] app_security_group = self.security_group_definition() if app_security_group is not None: security_groups.append(app_security_group) return security_groups def get_all_security_groups(self) -> Optional[List["SecurityGroup"]]: from phi.aws.resource.ec2.security_group import SecurityGroup security_groups: List[SecurityGroup] = [] load_balancer_security_groups = self.get_load_balancer_security_groups() if load_balancer_security_groups is not None: for lb_sg in load_balancer_security_groups: if isinstance(lb_sg, SecurityGroup): security_groups.append(lb_sg) service_security_groups = self.get_security_groups() if service_security_groups is not None: for sg in service_security_groups: if isinstance(sg, SecurityGroup): security_groups.append(sg) return security_groups if len(security_groups) > 0 else None def ecs_cluster_definition(self) -> "EcsCluster": from phi.aws.resource.ecs.cluster import EcsCluster ecs_cluster = EcsCluster( name=f"{self.get_app_name()}-cluster", ecs_cluster_name=self.ecs_cluster_name or self.get_app_name(), capacity_providers=[self.ecs_launch_type], ) if self.ecs_enable_service_connect: ecs_cluster.service_connect_namespace = self.ecs_service_connect_namespace return ecs_cluster def get_ecs_cluster(self) -> "EcsCluster": from phi.aws.resource.ecs.cluster import EcsCluster if self.ecs_cluster is None: if self.create_ecs_cluster: return self.ecs_cluster_definition() raise Exception("Please provide ECSCluster or set create_ecs_cluster to True") elif isinstance(self.ecs_cluster, EcsCluster): return self.ecs_cluster else: raise Exception(f"Invalid ECSCluster: {self.ecs_cluster} - Must be of type EcsCluster") def load_balancer_definition(self) -> "LoadBalancer": from phi.aws.resource.elb.load_balancer import LoadBalancer return LoadBalancer( name=f"{self.get_app_name()}-lb", subnets=self.subnets, security_groups=self.get_load_balancer_security_groups(), protocol="HTTPS" if self.load_balancer_enable_https else "HTTP", ) def get_load_balancer(self) -> Optional["LoadBalancer"]: from phi.aws.resource.elb.load_balancer import LoadBalancer if self.load_balancer is None: if self.create_load_balancer: return self.load_balancer_definition() return None elif isinstance(self.load_balancer, LoadBalancer): return self.load_balancer else: raise Exception(f"Invalid LoadBalancer: {self.load_balancer} - Must be of type LoadBalancer") def target_group_definition(self) -> "TargetGroup": from phi.aws.resource.elb.target_group import TargetGroup return TargetGroup( name=f"{self.get_app_name()}-tg", port=self.target_group_port or self.container_port, protocol=self.target_group_protocol, subnets=self.subnets, target_type=self.target_group_type, health_check_protocol=self.health_check_protocol, health_check_port=self.health_check_port, health_check_enabled=self.health_check_enabled, health_check_path=self.health_check_path, health_check_interval_seconds=self.health_check_interval_seconds, health_check_timeout_seconds=self.health_check_timeout_seconds, healthy_threshold_count=self.healthy_threshold_count, unhealthy_threshold_count=self.unhealthy_threshold_count, ) def get_target_group(self) -> Optional["TargetGroup"]: from phi.aws.resource.elb.target_group import TargetGroup if self.target_group is None: if self.create_target_group: return self.target_group_definition() return None elif isinstance(self.target_group, TargetGroup): return self.target_group else: raise Exception(f"Invalid TargetGroup: {self.target_group} - Must be of type TargetGroup") def listeners_definition( self, load_balancer: Optional["LoadBalancer"], target_group: Optional["TargetGroup"] ) -> List["Listener"]: from phi.aws.resource.elb.listener import Listener listener = Listener( name=f"{self.get_app_name()}-listener", load_balancer=load_balancer, target_group=target_group, ) if self.load_balancer_certificate_arn is not None: listener.certificates = [{"CertificateArn": self.load_balancer_certificate_arn}] if self.load_balancer_certificate is not None: listener.acm_certificates = [self.load_balancer_certificate] listeners: List[Listener] = [listener] if self.load_balancer_enable_https: # Add a listener to redirect HTTP to HTTPS listeners.append( Listener( name=f"{self.get_app_name()}-redirect-listener", port=80, protocol="HTTP", load_balancer=load_balancer, default_actions=[ { "Type": "redirect", "RedirectConfig": { "Protocol": "HTTPS", "Port": "443", "StatusCode": "HTTP_301", "Host": "#{host}", "Path": "/#{path}", "Query": "#{query}", }, } ], ) ) return listeners def get_listeners( self, load_balancer: Optional["LoadBalancer"], target_group: Optional["TargetGroup"] ) -> Optional[List["Listener"]]: from phi.aws.resource.elb.listener import Listener if self.listeners is None: if self.create_listeners: return self.listeners_definition(load_balancer, target_group) return None elif isinstance(self.listeners, list): for listener in self.listeners: if not isinstance(listener, Listener): raise Exception(f"Invalid Listener: {listener} - Must be of type Listener") return self.listeners else: raise Exception(f"Invalid Listener: {self.listeners} - Must be of type List[Listener]") def get_container_command(self) -> Optional[List[str]]: if isinstance(self.command, str): return self.command.strip().split(" ") return self.command def get_ecs_container_port_mappings(self) -> List[Dict[str, Any]]: port_mapping: Dict[str, Any] = {"containerPort": self.container_port} # To enable service connect, we need to set the port name to the app name if self.ecs_enable_service_connect: port_mapping["name"] = self.get_app_name() if self.ecs_service_connect_protocol is not None: port_mapping["appProtocol"] = self.ecs_service_connect_protocol return [port_mapping] def get_ecs_container(self, container_context: ContainerContext, build_context: AwsBuildContext) -> "EcsContainer": from phi.aws.resource.ecs.container import EcsContainer # -*- Get Container Environment container_env: Dict[str, str] = self.get_container_env( container_context=container_context, build_context=build_context ) # -*- Get Container Command container_cmd: Optional[List[str]] = self.get_container_command() if container_cmd: logger.debug("Command: {}".format(" ".join(container_cmd))) aws_region = build_context.aws_region or ( self.workspace_settings.aws_region if self.workspace_settings else None ) return EcsContainer( name=self.get_app_name(), image=self.get_image_str(), port_mappings=self.get_ecs_container_port_mappings(), command=container_cmd, essential=True, environment=[{"name": k, "value": v} for k, v in container_env.items()], log_configuration={ "logDriver": "awslogs", "options": { "awslogs-group": self.get_app_name(), "awslogs-region": aws_region, "awslogs-create-group": "true", "awslogs-stream-prefix": self.get_app_name(), }, }, linux_parameters={"initProcessEnabled": True}, env_from_secrets=self.aws_secrets, ) def get_ecs_task_definition(self, ecs_container: "EcsContainer") -> "EcsTaskDefinition": from phi.aws.resource.ecs.task_definition import EcsTaskDefinition return EcsTaskDefinition( name=f"{self.get_app_name()}-td", family=self.get_app_name(), network_mode="awsvpc", cpu=self.ecs_task_cpu, memory=self.ecs_task_memory, containers=[ecs_container], requires_compatibilities=[self.ecs_launch_type], add_bedrock_access_to_task=self.ecs_bedrock_access, add_exec_access_to_task=self.ecs_exec_access, add_secret_access_to_ecs=self.ecs_secret_access, add_secret_access_to_task=self.ecs_secret_access, add_s3_access_to_task=self.ecs_s3_access, ) def get_ecs_service( self, ecs_container: "EcsContainer", ecs_task_definition: "EcsTaskDefinition", ecs_cluster: "EcsCluster", target_group: Optional["TargetGroup"], ) -> Optional["EcsService"]: from phi.aws.resource.ecs.service import EcsService service_security_groups = self.get_security_groups() ecs_service = EcsService( name=f"{self.get_app_name()}-service", desired_count=self.ecs_service_count, launch_type=self.ecs_launch_type, cluster=ecs_cluster, task_definition=ecs_task_definition, target_group=target_group, target_container_name=ecs_container.name, target_container_port=self.container_port, subnets=self.subnets, security_groups=service_security_groups, assign_public_ip=self.assign_public_ip, # Force delete the service. force_delete=True, # Force a new deployment of the service on update. force_new_deployment=True, enable_execute_command=self.ecs_exec_access, ) if self.ecs_enable_service_connect: # namespace is used from the cluster ecs_service.service_connect_configuration = { "enabled": True, "services": [ { "portName": self.get_app_name(), "clientAliases": [ { "port": self.container_port, "dnsName": self.get_app_name(), } ], }, ], } return ecs_service def build_resources(self, build_context: AwsBuildContext) -> List["AwsResource"]: from phi.aws.resource.base import AwsResource from phi.aws.resource.ec2.security_group import SecurityGroup from phi.aws.resource.ecs.cluster import EcsCluster from phi.aws.resource.elb.load_balancer import LoadBalancer from phi.aws.resource.elb.target_group import TargetGroup from phi.aws.resource.elb.listener import Listener from phi.aws.resource.ecs.container import EcsContainer from phi.aws.resource.ecs.task_definition import EcsTaskDefinition from phi.aws.resource.ecs.service import EcsService from phi.aws.resource.ecs.volume import EcsVolume from phi.docker.resource.image import DockerImage from phi.utils.defaults import get_default_volume_name logger.debug(f"------------ Building {self.get_app_name()} ------------") # -*- Get Container Context container_context: Optional[ContainerContext] = self.get_container_context() if container_context is None: raise Exception("Could not build ContainerContext") logger.debug(f"ContainerContext: {container_context.model_dump_json(indent=2)}") # -*- Get Security Groups security_groups: Optional[List[SecurityGroup]] = self.get_all_security_groups() # -*- Get ECS cluster ecs_cluster: EcsCluster = self.get_ecs_cluster() # -*- Get Load Balancer load_balancer: Optional[LoadBalancer] = self.get_load_balancer() # -*- Get Target Group target_group: Optional[TargetGroup] = self.get_target_group() # Point the target group to the nginx container port if: # - nginx is enabled # - user provided target_group is None # - user provided target_group_port is None if self.enable_nginx and self.target_group is None and self.target_group_port is None: if target_group is not None: target_group.port = self.nginx_container_port # -*- Get Listener listeners: Optional[List[Listener]] = self.get_listeners(load_balancer=load_balancer, target_group=target_group) # -*- Get ECSContainer ecs_container: EcsContainer = self.get_ecs_container( container_context=container_context, build_context=build_context ) # -*- Add nginx container if nginx is enabled nginx_container: Optional[EcsContainer] = None nginx_shared_volume: Optional[EcsVolume] = None if self.enable_nginx and ecs_container is not None: nginx_container_name = f"{self.get_app_name()}-nginx" nginx_shared_volume = EcsVolume(name=get_default_volume_name(self.get_app_name())) nginx_image_str = f"{self.nginx_image_name}:{self.nginx_image_tag}" if self.nginx_image and isinstance(self.nginx_image, DockerImage): nginx_image_str = self.nginx_image.get_image_str() nginx_container = EcsContainer( name=nginx_container_name, image=nginx_image_str, essential=True, port_mappings=[{"containerPort": self.nginx_container_port}], environment=ecs_container.environment, log_configuration={ "logDriver": "awslogs", "options": { "awslogs-group": self.get_app_name(), "awslogs-region": build_context.aws_region or (self.workspace_settings.aws_region if self.workspace_settings else None), "awslogs-create-group": "true", "awslogs-stream-prefix": nginx_container_name, }, }, mount_points=[ { "sourceVolume": nginx_shared_volume.name, "containerPath": container_context.workspace_root, } ], linux_parameters=ecs_container.linux_parameters, env_from_secrets=ecs_container.env_from_secrets, save_output=ecs_container.save_output, output_dir=ecs_container.output_dir, skip_create=ecs_container.skip_create, skip_delete=ecs_container.skip_delete, wait_for_create=ecs_container.wait_for_create, wait_for_delete=ecs_container.wait_for_delete, ) # Add shared volume to ecs_container ecs_container.mount_points = nginx_container.mount_points # -*- Get ECS Task Definition ecs_task_definition: EcsTaskDefinition = self.get_ecs_task_definition(ecs_container=ecs_container) # -*- Add nginx container to ecs_task_definition if nginx is enabled if self.enable_nginx: if ecs_task_definition is not None: if nginx_container is not None: if ecs_task_definition.containers: ecs_task_definition.containers.append(nginx_container) else: logger.error("While adding Nginx container, found TaskDefinition.containers to be None") else: logger.error("While adding Nginx container, found nginx_container to be None") if nginx_shared_volume: ecs_task_definition.volumes = [nginx_shared_volume] # -*- Get ECS Service ecs_service: Optional[EcsService] = self.get_ecs_service( ecs_cluster=ecs_cluster, ecs_task_definition=ecs_task_definition, target_group=target_group, ecs_container=ecs_container, ) # -*- Add nginx container as target_container if nginx is enabled if self.enable_nginx: if ecs_service is not None: if nginx_container is not None: ecs_service.target_container_name = nginx_container.name ecs_service.target_container_port = self.nginx_container_port else: logger.error("While adding Nginx container as target_container, found nginx_container to be None") # -*- List of AwsResources created by this App app_resources: List[AwsResource] = [] if security_groups: app_resources.extend(security_groups) if load_balancer: app_resources.append(load_balancer) if target_group: app_resources.append(target_group) if listeners: app_resources.extend(listeners) if ecs_cluster: app_resources.append(ecs_cluster) if ecs_task_definition: app_resources.append(ecs_task_definition) if ecs_service: app_resources.append(ecs_service) logger.debug(f"------------ {self.get_app_name()} Built ------------") return app_resources