Amazon ECS

Anchore uses a go binary called anchore-ecs-inventory that leverages the AWS Go SDK to gather an inventory of containers and their images running on Amazon ECS and report back to Anchore.

The Amazon ECS Inventory Agent can be installed via Helm Chart or as an ECS task definition.

Deploying via Helm on Kubernetes

You can install the chart via the Anchore repository:

helm repo add anchore https://charts.anchore.io
helm install <release-name> -f <values.yaml> anchore/ecs-inventory

A basic values file can be found here.

IAM Role Configuration

The following IAM role permissions should be used in order to allow the Anchore ECS Inventory Agent to poll the ECS service API for running inventory:

cat <<EOF > ecs-read-only-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "ecs:Describe*",
                "ecs:List*"
            ],
            "Effect": "Allow",
            "Resource": "*"
        }
    ]
}
EOF
aws iam create-policy \
  --policy-name ECSReadOnly \
  --policy-document file://ecs-read-only-policy.json

Follow the AWS instructions found here to assign your IAM role to a Kubernetes service account in your cluster where the Anchore ECS Inventory Agent will be running. Then configure the following in your values.yaml to ensure the agent has access to the ECS service API:

serviceAccountName: "service_account_name"

Using existing secrets

For those users unable to use IAM roles (e.g. the ECS Inventory Agent is not running on Kubernetes or ECS), the (ecsInventory.useExistingSecret and ecsInventory.existingSecretName) or ecsInventory.injectSecretsViaEnv keys allows you to create your own secret and provide it in the values file or place the required secret into the pod via different means such as injecting the secrets into the pod using hashicorp vault. For example:

  • Create a secret in kubernetes:

    apiVersion: v1
    kind: Secret
    metadata:
      name: ecs-inventory-secrets
    type: Opaque
    stringData:
      ANCHORE_ECS_INVENTORY_ANCHORE_PASSWORD: foobar
      AWS_ACCESS_KEY_ID: someKeyId
      AWS_SECRET_ACCESS_KEY: someSecretAccessKey
    
  • Provide it to the helm chart via the values file:

    ecsInventory:
        useExistingSecret: true
        existingSecretName: "ecs-inventory-secrets"
    

Deploying on Amazon ECS

It is also possible to deploy the ecs-inventory container on Amazon ECS, using the same IAM role for access permissions to the ECS service API, as seen above. Here is an sample task definition that could be used to deploy ecs-inventory with a default configuration:

export aws_account_id=$(aws sts get-caller-identity --query Account --output text);
export AWS_DEFAULT_REGION=us-east-1
# VPC ID here
export vpc_id="vpc-0f14583bb6ffca152";
# Subnet ID(s) here (comma separated)
export subnet_ids="subnet-08636facfc2fbbaff,subnet-00a124b8117b3aead";
# Security group IDS here (allowing outbound access to your Anchore environment)
export security_group_id="sg-0f775aa6bc7b5f947";


export ANCHORE_URL="https://my.anchore.com";
# NOTE: We don't suggest embedding the admin account & user but it is being used as an example
# Anchore Account name (tenant name) goes here
export ANCHORE_ACCOUNT="admin";
# Anchore Username goes here
export ANCHORE_USERNAME="admin";
# Populate your password here (stored in an SSM parameter)
export ANCHORE_PASSWORD="";


aws iam create-policy \
  --policy-name ECSReadOnly \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Action": [
          "ecs:Describe*",
          "ecs:List*"
        ],
        "Effect": "Allow",
        "Resource": "*"
      }
    ]
  }'

aws iam create-role \
  --role-name AnchoreECSInventoryTaskRole \
  --assume-role-policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "Service": "ecs-tasks.amazonaws.com"
        },
        "Action": "sts:AssumeRole"
      }
    ]
  }'

aws iam create-role \
  --role-name AnchoreECSInventoryExecutionRole \
  --assume-role-policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "Service": "ecs-tasks.amazonaws.com"
        },
        "Action": "sts:AssumeRole"
      }
    ]
  }'


aws iam attach-role-policy \
    --role-name AnchoreECSInventoryExecutionRole \
    --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy

aws iam attach-role-policy \
  --role-name AnchoreECSInventoryTaskRole \
  --policy-arn arn:aws:iam::${aws_account_id}:policy/ECSReadOnly

aws iam attach-role-policy \
  --role-name AnchoreECSInventoryExecutionRole \
  --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy

aws logs create-log-group --log-group-name /anchore/ecs-inventory --region ${AWS_DEFAULT_REGION}

aws ssm put-parameter \
  --name "/ANCHORE_ECS_INVENTORY_ANCHORE_PASSWORD" \
  --type "SecureString" \
  --value "${ANCHORE_PASSWORD}" \
  --overwrite

aws iam put-role-policy \
  --role-name AnchoreECSInventoryExecutionRole \
  --policy-name ECSInventorySSMAccess \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Action": [
          "ssm:GetParameters",
          "ssm:GetParameter"
        ],
        "Resource": "arn:aws:ssm:'${AWS_DEFAULT_REGION}':'${aws_account_id}':parameter/ANCHORE_ECS_INVENTORY_ANCHORE_PASSWORD"
      }
    ]
  }'


cat << EOF > task-definition.json
{
  "family": "anchore-ecs-inventory-example-task-definition",
  "cpu": "512",
  "memory": "1024",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "executionRoleArn": "arn:aws:iam::${aws_account_id}:role/AnchoreECSInventoryExecutionRole",
  "taskRoleArn": "arn:aws:iam::${aws_account_id}:role/AnchoreECSInventoryTaskRole",
  "containerDefinitions": [
    {
      "name": "ecs-inventory",
      "image": "docker.io/anchore/ecs-inventory:latest",
      "cpu": 0,
      "essential": true,
      "environment": [
        {
          "name": "ANCHORE_ECS_INVENTORY_ANCHORE_URL",
          "value": "${ANCHORE_URL}"
        },
        {
          "name": "ANCHORE_ECS_INVENTORY_ANCHORE_USER",
          "value": "${ANCHORE_USERNAME}"
        },
        {
          "name": "ANCHORE_ECS_INVENTORY_ANCHORE_ACCOUNT",
          "value": "${ANCHORE_ACCOUNT}"
        },
        {
          "name": "ANCHORE_ECS_INVENTORY_REGION",
          "value": "${AWS_DEFAULT_REGION}"
        }
      ],
      "secrets": [
        {
          "name": "ANCHORE_ECS_INVENTORY_ANCHORE_PASSWORD",
          "valueFrom": "arn:aws:ssm:${AWS_DEFAULT_REGION}:${aws_account_id}:parameter/ANCHORE_ECS_INVENTORY_ANCHORE_PASSWORD"
        }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-create-group": "true",
          "awslogs-group": "/anchore/ecs-inventory",
          "awslogs-region": "${AWS_DEFAULT_REGION}",
          "awslogs-stream-prefix": "ecs"
        }
      }
    }
  ]
}
EOF

aws ecs register-task-definition \
  --cli-input-json file://task-definition.json


aws ecs create-cluster \
  --cluster-name anchore-ecs-inventory-cluster


#vpc_id=$(aws ec2 describe-vpcs \
#  --filters "Name=isDefault,Values=true" \
#  --query "Vpcs[0].VpcId" --output text)

#subnet_ids=$(aws ec2 describe-subnets \
#  --filters "Name=vpc-id,Values=$vpc_id" \
#  --query "Subnets[*].SubnetId" --output text)

#security_group_id=$(aws ec2 describe-security-groups \
#  --filters "Name=vpc-id,Values=$vpc_id" \
#  --query "SecurityGroups[?GroupName=='default'].GroupId" --output text)

#echo $vpc_id
#echo $subnet_id
#echo $security_group_id

export vpc_id="vpc-0f14583bb6ffca152";
export subnet_ids="subnet-08636facfc2fbbaff,subnet-00a124b8117b3aead";
export security_group_id="sg-0f775aa6bc7b5f947";

aws ecs run-task \
  --cluster anchore-ecs-inventory-cluster \
  --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={
    subnets=[${subnet_ids}],
    securityGroups=[${security_group_id}],
    assignPublicIp=ENABLED
  }" \
  --task-definition anchore-ecs-inventory-example-task-definition

Usage

To verify that you are tracking Amazon ECS inventory in your Anchore Enterprise deployment you can access inventory results with the command anchorectl inventory list and look for results where the TYPE is ecs.

Auto analyze new inventory

It is possible to create a subscription to watch for new Amazon ECS inventory that is reported to Anchore and automatically schedule those images for analysis.

1. Create the subscription

A subscription can be created by sending a POST to /v2/subscriptions with the following payload:

{
  "subscription_key": "<SUBSCRIPTION_KEY>",
  "subscription_type": "runtime_inventory"
}

Curl example:

curl -X POST -u USERNAME:PASSWORD --url ANCHORE_URL/v2/subscriptions --header 'Content-Type: application/json' --data '{
  "subscription_key": "arn:aws:ecs:eu-west-2:123456789012:cluster/myclustername",
  "subscription_type": "runtime_inventory"
}'

The subscription_key can be set to any part of an Amazon ECS ClusterARN. For example setting the subscription_key to the:

  • full ClusterARN arn:aws:ecs:us-east-1:012345678910:cluster/telemetry will create a subscription that only watches this cluster
  • partial ClusterARN arn:aws:ecs:eu-west-2:988505687240 will result in a subscription that watches every cluster within the account 988505687240

2. Activate the subscription

After a subscription has been created it needs to be activated. This can be achieved with anchorectl.

anchorectl subscription activate <SUBSCRIPTION_KEY> runtime_inventory

General Runtime Management

See Data Management

Last modified July 29, 2025