Deployment in ECS
This section guides deploying ESET PRIVATE Static Scanning Engine on an AWS Fargate ECS cluster exposed via Network Load Balancer (NLB), with validation using a one-off scanner agent task for end-to-end testing.
The variable values (e.g., AWS account ID via $(aws sts get-caller-identity ...), ECR image tags, cluster names, regions) shown here are for demonstration purposes only. Update the values based on your specific customer requirements, AWS account details, and ESET image availability before running these commands. |
1.Variables
Define environment variables for ECS cluster, AWS account (auto-detected), IAM policy, and ESET container images to ensure consistent referencing across deployment steps.
export ECS_CLUSTER_NAME=ecs-demo export AWS_REGION=us-east-1 export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) export IAM_POLICY_NAME=ECSListReceivedLicenses export SCANNER_IMAGE=709825985650.dkr.ecr.us-east-1.amazonaws.com/eset/eset-cloud-scanner-hourly:1.34.0-amd64 export SCANNER_AGENT_IMAGE=709825985650.dkr.ecr.us-east-1.amazonaws.com/eset/eset-private-scanner-agent:1.13.0-amd64 |
Customize all variables to match your environment, including the ECS cluster name, AWS region, IAM policy name, and container image URIs, before running the subsequent deployment steps. |
2.Create Cluster
Provision a basic ECS cluster to host Fargate tasks; no node provisioning needed as Fargate is serverless.
aws ecs create-cluster --cluster-name "$ECS_CLUSTER_NAME" --region "$AWS_REGION" |
3.Create License Manager IAM Policy
Create custom IAM policy for license-manager:ListReceivedLicenses to enable subscription eligibility verification, following least privilege.
aws iam create-policy \ --policy-name "$IAM_POLICY_NAME" \ --policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["license-manager:ListReceivedLicenses"],"Resource":"*"}]}' |
4.Create IAM Roles
Provision task role (ecs-scanner-task-role) for AWS API access (Metering + License Manager) and execution role (ecs-scanner-execution-role) for ECR pulls and CloudWatch logging.
TRUST='{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"ecs-tasks.amazonaws.com"},"Action":"sts:AssumeRole"}]}'
aws iam create-role --role-name ecs-scanner-task-role --assume-role-policy-document "$TRUST" aws iam attach-role-policy --role-name ecs-scanner-task-role --policy-arn arn:aws:iam::aws:policy/AWSMarketplaceMeteringRegisterUsage aws iam attach-role-policy --role-name ecs-scanner-task-role --policy-arn arn:aws:iam::${AWS_ACCOUNT_ID}:policy/${IAM_POLICY_NAME}
aws iam create-role --role-name ecs-scanner-execution-role --assume-role-policy-document "$TRUST" aws iam attach-role-policy --role-name ecs-scanner-execution-role --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy |
5.Register Task Definition
Define Fargate-compatible task for scanner with gRPC ports, logging to CloudWatch, resource allocation (in this example deployment 1 vCPU, 2GB), and IAM roles for secure operation.
aws logs create-log-group --log-group-name /ecs/scanner --region "$AWS_REGION"
aws ecs register-task-definition \ --family scanner \ --network-mode awsvpc \ --requires-compatibilities FARGATE \ --cpu 1024 --memory 2048 \ --task-role-arn arn:aws:iam::${AWS_ACCOUNT_ID}:role/ecs-scanner-task-role \ --execution-role-arn arn:aws:iam::${AWS_ACCOUNT_ID}:role/ecs-scanner-execution-role \ --container-definitions "[{ \"name\": \"app\", \"image\": \"${SCANNER_IMAGE}\", \"command\": [\"-o\",\"grpc_scan_enable_ssl=false\",\"-o\",\"main_log_file=/dev/stdout\",\"-o\",\"default_log_type=fifo\"], \"portMappings\": [ {\"containerPort\": 50051, \"protocol\": \"tcp\"}, {\"containerPort\": 50053, \"protocol\": \"tcp\"} ], \"logConfiguration\": { \"logDriver\": \"awslogs\", \"options\": { \"awslogs-group\": \"/ecs/scanner\", \"awslogs-region\": \"${AWS_REGION}\", \"awslogs-stream-prefix\": \"ecs\" } } }]" |
6.Create NLB and Target Group
Set up Network Load Balancer with TCP listener on port 50051 and IP target group for external access to scanner service using default VPC subnets.
export VPC_ID=$(aws ec2 describe-vpcs --filters "Name=isDefault,Values=true" --query "Vpcs[0].VpcId" --output text) export SUBNET_IDS=$(aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPC_ID" --query "Subnets[*].SubnetId" --output text | tr '\t' ' ')
export NLB_ARN=$(aws elbv2 create-load-balancer \ --name scanner-nlb --type network --subnets $SUBNET_IDS \ --query "LoadBalancers[0].LoadBalancerArn" --output text)
export TG_ARN=$(aws elbv2 create-target-group \ --name scanner-tg --protocol TCP --port 50051 \ --vpc-id "$VPC_ID" --target-type ip \ --query "TargetGroups[0].TargetGroupArn" --output text)
aws elbv2 create-listener \ --load-balancer-arn "$NLB_ARN" --protocol TCP --port 50051 \ --default-actions Type=forward,TargetGroupArn="$TG_ARN" |
7.Create Service
Launch Fargate service with 1 task, integrated with NLB target group, public IP assignment, and security group allowing inbound TCP/50051 from anywhere.
export SG_ID=$(aws ec2 create-security-group --group-name scanner-sg --description "Scanner SG" --vpc-id "$VPC_ID" --query GroupId --output text) aws ec2 authorize-security-group-ingress --group-id "$SG_ID" --protocol tcp --port 50051 --cidr 0.0.0.0/0
aws ecs create-service \ --cluster "$ECS_CLUSTER_NAME" --service-name scanner \ --task-definition scanner --desired-count 1 \ --launch-type FARGATE \ --load-balancers "targetGroupArn=$TG_ARN,containerName=app,containerPort=50051" \ --network-configuration "awsvpcConfiguration={subnets=[$(echo $SUBNET_IDS | tr ' ' ',')],securityGroups=[$SG_ID],assignPublicIp=ENABLED}" \ --region "$AWS_REGION" |
8.Confirm Scanner is Running
Retrieve running task ID and tail CloudWatch logs to verify scanner startup, license checks, and operational readiness.
# Get the running task ID SCANNER_TASK_ID=$(aws ecs list-tasks --cluster "$ECS_CLUSTER_NAME" --service-name scanner \ --region "$AWS_REGION" --query "taskArns[0]" --output text | grep -o '[^/]*$') echo "Scanner task ID: $SCANNER_TASK_ID" [[ -z "$SCANNER_TASK_ID" || "$SCANNER_TASK_ID" == "None" ]] && echo "ERROR: no running task yet, wait and retry" && return 1
# Tail scanner logs (filtered to this task's stream only) aws logs tail /ecs/scanner --region "$AWS_REGION" \ --log-stream-names "ecs/app/$SCANNER_TASK_ID" --follow |
9.Get NLB Endpoint
Extract NLB DNS name for external access and agent validation targeting.
export NLB_DNS=$(aws elbv2 describe-load-balancers \ --load-balancer-arns "$NLB_ARN" \ --query "LoadBalancers[0].DNSName" --output text) echo "$NLB_DNS" |
10.Validate — Run Scanner Agent
Register and run one-off Fargate task with agent image targeting NLB endpoint, then check logs to confirm successful connection and scanning handshake.
# Register agent task definition aws ecs register-task-definition \ --family scanner-agent \ --network-mode awsvpc \ --requires-compatibilities FARGATE \ --cpu 512 --memory 1024 \ --task-role-arn arn:aws:iam::${AWS_ACCOUNT_ID}:role/ecs-scanner-task-role \ --execution-role-arn arn:aws:iam::${AWS_ACCOUNT_ID}:role/ecs-scanner-execution-role \ --region "$AWS_REGION" \ --container-definitions "[{ \"name\": \"agent\", \"image\": \"${SCANNER_AGENT_IMAGE}\", \"entryPoint\": [\"scanner_agent\"], \"command\": [\"-t\",\"${NLB_DNS}:50051\",\"--skip_cloud_reputation\",\"/bin/bash\"], \"logConfiguration\": { \"logDriver\": \"awslogs\", \"options\": { \"awslogs-group\": \"/ecs/scanner\", \"awslogs-region\": \"${AWS_REGION}\", \"awslogs-stream-prefix\": \"ecs\" } } }]"
# Run the agent task and tail its logs AGENT_TASK_ID=$(aws ecs run-task \ --cluster "$ECS_CLUSTER_NAME" \ --launch-type FARGATE \ --network-configuration "awsvpcConfiguration={subnets=[$(echo $SUBNET_IDS | tr ' ' ',')],securityGroups=[$SG_ID],assignPublicIp=ENABLED}" \ --task-definition scanner-agent \ --region "$AWS_REGION" \ --query "tasks[0].taskArn" --output text | grep -o '[^/]*$')
echo "Agent task ID: $AGENT_TASK_ID" [[ -z "$AGENT_TASK_ID" || "$AGENT_TASK_ID" == "None" ]] && echo "ERROR: task failed to launch, check run-task output" && return 1
# Wait for task to finish, then read logs aws ecs wait tasks-stopped --cluster "$ECS_CLUSTER_NAME" --tasks "$AGENT_TASK_ID" --region "$AWS_REGION" aws logs tail /ecs/scanner --region "$AWS_REGION" --log-stream-names "ecs/agent/$AGENT_TASK_ID" |
11.Cleanup
Scale down service, deregister tasks, delete cluster/NLB/TG/SG/roles/policy/log group to eliminate cost-relevant resources.
# Scale down and delete ECS service aws ecs update-service --cluster "$ECS_CLUSTER_NAME" --service scanner --desired-count 0 --region "$AWS_REGION" aws ecs delete-service --cluster "$ECS_CLUSTER_NAME" --service scanner --region "$AWS_REGION"
# Deregister all task definition revisions (scanner and scanner-agent) for family in scanner scanner-agent; do for rev in $(aws ecs list-task-definitions --family-prefix "$family" --region "$AWS_REGION" --query "taskDefinitionArns[*]" --output text | tr '\t' '\n' | grep -o "${family}:[0-9]*"); do aws ecs deregister-task-definition --task-definition "$rev" --region "$AWS_REGION" --query "taskDefinition.taskDefinitionArn" --output text done done
# Delete ECS cluster aws ecs delete-cluster --cluster "$ECS_CLUSTER_NAME" --region "$AWS_REGION"
# Delete NLB listener and load balancer (target group deleted after NLB is gone) LISTENER_ARN=$(aws elbv2 describe-listeners --load-balancer-arn "$NLB_ARN" --query "Listeners[0].ListenerArn" --output text --region "$AWS_REGION") aws elbv2 delete-listener --listener-arn "$LISTENER_ARN" --region "$AWS_REGION" aws elbv2 delete-load-balancer --load-balancer-arn "$NLB_ARN" --region "$AWS_REGION"
# Wait for NLB and task ENIs to be fully released before deleting TG and SG sleep 60 aws elbv2 delete-target-group --target-group-arn "$TG_ARN" --region "$AWS_REGION"
# Delete security group aws ec2 delete-security-group --group-id "$SG_ID" --region "$AWS_REGION"
# Delete IAM roles aws iam detach-role-policy --role-name ecs-scanner-task-role --policy-arn arn:aws:iam::aws:policy/AWSMarketplaceMeteringRegisterUsage aws iam detach-role-policy --role-name ecs-scanner-task-role --policy-arn arn:aws:iam::${AWS_ACCOUNT_ID}:policy/${IAM_POLICY_NAME} aws iam delete-role --role-name ecs-scanner-task-role
aws iam detach-role-policy --role-name ecs-scanner-execution-role --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy aws iam delete-role --role-name ecs-scanner-execution-role
# Delete IAM policy aws iam delete-policy --policy-arn arn:aws:iam::${AWS_ACCOUNT_ID}:policy/${IAM_POLICY_NAME}
# Delete CloudWatch log group aws logs delete-log-group --log-group-name /ecs/scanner --region "$AWS_REGION" |