How to notify by email when the "exitCode" of an ECS Fargate task ends with a value other than 0

4 minute read
Content level: Intermediate
0

By detecting an event when "exitCode" is other than 0, the container side will be notified that an abnormal termination has occurred.

I created an EventBridge to notify when an ECS Fargate task terminates abnormally.

AWS configuration to achieve notifications

a

Let's create the structure of the image above.
Although it is not shown in the Components diagram, an ECR will also be created to store the container image.

AWS resource creation

Create an ECR with the CloudFormation template below.

AWSTemplateFormatVersion: "2010-09-09"

Description: ECR Stack

Metadata:
# ------------------------------------------------------------#
# Metadata
# ------------------------------------------------------------#
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: Parameters for System Name Prefix
        Parameters:
          - Environment

Parameters:
# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
  Environment:
    AllowedValues:
      - test
    Default: test
    Type: String

Resources:
# ------------------------------------------------------------#
# ECR
# ------------------------------------------------------------#
  ECR:
    Type: AWS::ECR::Repository
    Properties:
      EmptyOnDelete: true
      EncryptionConfiguration:
        EncryptionType: AES256
      RepositoryName: !Sub ecr-${Environment}-${AWS::Region}

After creating the ECR, create a container image.
Use the following Dockerfile to create the container image.

FROM python:3.12.4

WORKDIR /app
ADD ./function/ /app

CMD ["python3", "index.py"]

index.py uses the code below.

import sys

print("test")
sys.exit(1)

Arrange the files so that they have the following structure.

├── Dockerfile
└── function/
    └── index.py

After placing the file, run the following command in the directory where Dockerfile is located.
Please change "<your-region>" and "<your-account-id>" according to your environment.

aws ecr get-login-password --region <your-region> | docker login --username AWS --password-stdin <your-account-id>.dkr.ecr.<your-region>.amazonaws.com
docker build --platform linux/amd64 -t ecr-test-<your-region> .
docker tag ecr-test-<your-region>:latest <your-account-id>.dkr.ecr.<your-region>.amazonaws.com/ecr-test-<your-region>:latest
docker push <your-account-id>.dkr.ecr.<your-region>.amazonaws.com/ecr-test-<your-region>:latest

After saving the container image in ECR, create AWS resources such as ECS using CloudFormation below.

AWSTemplateFormatVersion: "2010-09-09"

Parameters:
  EmailAddress:
    Type: String

Resources:
# ------------------------------------------------------------#
# VPC
# ------------------------------------------------------------# 
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 172.30.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags: 
        - Key: Name
          Value: test-vpc

# ------------------------------------------------------------#
# InternetGateway
# ------------------------------------------------------------# 
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags: 
        - Key: Name
          Value: !Sub test-igw

  InternetGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC

# ------------------------------------------------------------#
# Subnet
# ------------------------------------------------------------# 
  PublicSubnet01:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 172.30.3.0/24
      MapPublicIpOnLaunch: true
      Tags: 
        - Key: Name
          Value: !Sub test-public-subnet-01
      VpcId: !Ref VPC

# ------------------------------------------------------------#
# RouteTable
# ------------------------------------------------------------# 
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: test-public-rtb

  PublicRouteTableRoute:
    Type: AWS::EC2::Route
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
      RouteTableId: !Ref PublicRouteTable

  PublicRtAssociation1:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet01

# ------------------------------------------------------------#
# Security Group
# ------------------------------------------------------------# 
  ECSSG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: for ecs
      GroupName: test-sg-ecs
      Tags: 
        - Key: Name
          Value: test-sg-ecs
      VpcId: !Ref VPC

# ------------------------------------------------------------#
# CloudWatch Logs
# ------------------------------------------------------------# 
  ECSLogGroup:
    Type: "AWS::Logs::LogGroup"
    Properties:
      LogGroupName: !Sub "/ecs/logs/ecs-test-log"

# ------------------------------------------------------------#
# IAM
# ------------------------------------------------------------# 
  TaskExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ecs-tasks.amazonaws.com
            Action:
              - sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
      RoleName: ecs-test-python-execution-role

# ------------------------------------------------------------#
# ECS
# ------------------------------------------------------------# 
  ECSCluster:
    Type: AWS::ECS::Cluster
    Properties:
      CapacityProviders:
        - FARGATE
      ClusterName: ecs-test-cluster
      DefaultCapacityProviderStrategy:
        - CapacityProvider: FARGATE
          Weight: 1

  ECSTaskDef:
    Type: AWS::ECS::TaskDefinition
    Properties:
      ContainerDefinitions:
        - Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/ecr-test-${AWS::Region}:latest
          LogConfiguration:
            LogDriver: awslogs
            Options: 
              awslogs-group: "/ecs/logs/ecs-test-log"
              awslogs-region: !Ref "AWS::Region"
              awslogs-stream-prefix: "ecs-test-log"
          Name: ecs-test-task
      Cpu: 256
      ExecutionRoleArn: !Ref TaskExecutionRole
      Family: ecs-test-task-def
      Memory: 512
      NetworkMode: awsvpc
      RequiresCompatibilities:
        - FARGATE

# ------------------------------------------------------------#
# SNS
# ------------------------------------------------------------# 
  SNSTopic:
    Type: AWS::SNS::Topic
    Properties:
      FifoTopic: false
      TopicName: !Sub sns-test-${AWS::Region}

  SNSTopicPolicy:
    Type: AWS::SNS::TopicPolicy
    Properties:
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Sid: "Account used"
            Effect: "Allow"
            Principal:
              AWS: "*"
            Action:
              - "SNS:GetTopicAttributes"
              - "SNS:SetTopicAttributes"
              - "SNS:AddPermission"
              - "SNS:RemovePermission"
              - "SNS:DeleteTopic"
              - "SNS:Subscribe"
              - "SNS:ListSubscriptionsByTopic"
              - "SNS:Publish"
            Resource: !Ref SNSTopic
            Condition:
              StringEquals:
                "AWS:SourceOwner": !Ref "AWS::AccountId"
          - Sid: "EventBridge used"
            Effect: "Allow"
            Principal:
              Service: "events.amazonaws.com"
            Action: "SNS:Publish"
            Resource: !Ref SNSTopic
      Topics:
        - !Ref SNSTopic

  Subscription1:
    Type: AWS::SNS::Subscription
    Properties:
      Endpoint: !Ref EmailAddress
      Protocol: email
      TopicArn: !Ref SNSTopic

# ------------------------------------------------------------#
# EventBridge
# ------------------------------------------------------------# 
  EventsRule:
    Type: AWS::Events::Rule
    Properties:
      EventBusName: default
      EventPattern:
        detail-type:
          - ECS Task State Change
        source:
          - aws.ecs
        detail:
          clusterArn:
            - !GetAtt ECSCluster.Arn
          containers:
            exitCode:
              - anything-but: 0
          lastStatus:
            - STOPPED
      Targets:
          - Arn: !Ref SNSTopic
            Id: !Sub eventbridge-test-${AWS::Region}-001
      Id: !Sub eventbridge-test-${AWS::Region}-001
      State: ENABLED
      Name: !Sub eventbridge-test-${AWS::Region}-001

Once the ECS creation is complete, launch the task from the cluster screen.
a

a

a

a

You will receive an email notification shortly after starting the task in this state.

Points to note

This EventBridge cannot be used unless "essential" in the task definition is "true".
https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#container_definitions:~:text=hosted%20on%20Fargate.-,essential,-Type%3A%20Boolean