Let’s get started with the Amazon Elastic Container Registry or ECR – AWS CLI commands. The AWS CLI is an easy and quick method to verify something or learn about the various API’s available for an AWS Service. This post covers Amazon’s Elastic Container Registry (ECR) way of tagging an container image, creating a private Amazon ECR repository, authenticating with ECR repository, pushing a container image to ECR, getting the images scan findings, deleting images, and sharing unexpected error messages I received along with their possible solution.
Push Images to Elastic Container Registry ECR
I want to transfer the container images I downloaded from Docker to Amazon Elastic Container Registry (ECR).
You’ll need AWS IAM policy to create and manage the repository. Below is the minimum to get started. For ECR IAM policy best practices see the AWS documentation. Or use IAM Access Analyzer policy generation.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ecr",
"Effect": "Allow",
"Action": [
"ecr:PutLifecyclePolicy",
"ecr:DescribeImageScanFindings",
"ecr:StartImageScan",
"ecr:GetLifecyclePolicyPreview",
"ecr:GetDownloadUrlForLayer",
"ecr:PutImageScanningConfiguration",
"ecr:DescribeImageReplicationStatus",
"ecr:ListTagsForResource",
"ecr:ListImages",
"ecr:BatchGetRepositoryScanningConfiguration",
"ecr:DeleteRepository",
"ecr:PutImage",
"ecr:UntagResource",
"ecr:BatchGetImage",
"ecr:DescribeImages",
"ecr:TagResource",
"ecr:DescribeRepositories",
"ecr:BatchCheckLayerAvailability",
"ecr:GetRepositoryPolicy",
"ecr:GetLifecyclePolicy",
"ecr:SetRepositoryPolicy",
"ecr:DeleteRepositoryPolicy",
"ecr:DeleteLifecyclePolicy"
],
"Resource": "arn:aws:ecr:<aws-region>:<account-id>:repository/<repo-name-here>"
},
{
"Effect": "Allow",
"Action": [
"ecr:GetRegistryPolicy",
"ecr:CreateRepository",
"ecr:DescribePullThroughCacheRules",
"ecr:DescribeRegistry",
"ecr:GetAuthorizationToken",
"ecr:PutRegistryScanningConfiguration",
"ecr:GetRegistryScanningConfiguration"
],
"Resource": "*"
}
]
}
Create ECR repository – AWS CLI
aws ecr create-repository \
--repository-name <repo-name> \
--image-scanning-configuration scanOnPush=true \
--encryption-configuration encryptionType="AES256" # Dev: Use KMS in Production for fine grain control of the key
aws ecr put-lifecycle-policy \
--repository-name <repo-name> \
--lifecycle-policy-text "file://policy.json"
# Contents of policy.json:
{
"rules": [
{
"rulePriority": 1,
"description": "Keep the last 5 images.",
"selection": {
"tagStatus": "tagged",
"tagPrefixList": [
"v"
],
"countType": "imageCountMoreThan",
"countNumber": 5
},
"action": {
"type": "expire"
}
}
]
}
Create ECR repository – Terraform
For this tutorial we’ll create a private repository with basic image scanning, enable scans on push, and for now we’ll use an Amazon managed encryption key. For production environments please take the time to create a KMS key and use that for your repo.
#tfsec:ignore:aws-ecr-repository-customer-key
module "repo" {
source = "terraform-aws-modules/ecr/aws"
repository_name = format("%s-%s-image-gallery", var.environment, var.aws_region)
repository_type = "private"
registry_scan_type = "BASIC" # No additional cost for basic scanning!
repository_image_scan_on_push = true
repository_encryption_type = "AES256" # Production: Use KMS for fine grain control of the key
repository_lifecycle_policy = jsonencode({
rules = [
{
rulePriority = 1,
description = "Keep the last 5 images.",
selection = {
tagStatus = "tagged",
tagPrefixList = ["v"],
countType = "imageCountMoreThan",
countNumber = 5
},
action = {
type = "expire"
}
}
]
})
tags = {
Name = format("%s-%s-img-repo", var.environment, var.aws_region)
}
}
Authenticate with Amazon ECR
Get the commands from the AWS ECR console. Navigate to the repo and click on View Push Commands button at the top right.

Select your OS tab and follow the instructions as provided.

aws ecr get-login-password --region <aws-region> | docker login --username AWS --password-stdin <account-id>.dkr.ecr.<aws-region>.amazonaws.com
Tag existing container images
I have downloaded images from the Docker Hub so I don’t need to build images. We just need to tag them before we push them.
docker tag bitnami/mariadb:10.6 <account-id>.dkr.ecr.<aws-region>.amazonaws.com/<repo-name>:10.6
docker tag bitnami/wordpress:6 <account-id>.dkr.ecr.<aws-region>.amazonaws.com/<repo-name>:6
Push image to Amazon ECR
docker push <account-id>.dkr.ecr.<aws-region>.amazonaws.com/<repo-name>:10.6
List ECR images – Amazon ECR
aws ecr list-images --repository-name <repo-name> --output json --no-cli-pager
Get image scan findings – Amazon ECR
aws ecr describe-image-scan-findings --repository-name <repo-name> --image-id imageDigest=sha256:<image-id> --no-cli-pager --query "imageScanFindings.findingSeverityCounts"
{
"HIGH": 1,
"MEDIUM": 4,
"LOW": 5,
"UNDEFINED": 5,
"INFORMATIONAL": 29
}
Delete image – Amazon ECR
aws ecr batch-delete-image --image-ids imageDigest=sha256:5879db2e34d5c237677f0af681780543b50be28e8f3b6b4bd44363e0833bdad1 --repository-name <repo-name>
Subscribe: Get the latest cloudly.engineer post directly to your inbox. Just enter your email address below. Thanks!
Issues and solutions
- An error occurred (InvalidParameterException) when calling the PutLifecyclePolicy operation.
An error occurred (InvalidParameterException) when calling the PutLifecyclePolicy operation: Invalid parameter at 'LifecyclePolicyText' failed to satisfy constraint: 'Lifecycle policy validation failure: instance failed to match exactly one schema (matched 0 out of 2)
'
Solution: Remove “countUnit”: “days” from your policy.json file
2. ImageReferencedByManifestList failurecode
You can’t delete the child before the parent image.
{
"imageIds": [],
"failures": [
{
"imageId": {
"imageDigest": "sha256:5879db2e34d5c237677f0af681780543b50be28e8f3b6b4bd44363e0833bdad1"
},
"failureCode": "ImageReferencedByManifestList",
"failureReason": "Requested image referenced by manifest list: [sha256:d326a7001411a2ffae889ed70007258fb70585b4a08caeb3bf21de1ce6552f01]"
}
]
}
{
"imageIds": [
{
"imageDigest": "sha256:d326a7001411a2ffae889ed70007258fb70585b4a08caeb3bf21de1ce6552f01",
"imageTag": "10.6"
},
{
"imageDigest": "sha256:5879db2e34d5c237677f0af681780543b50be28e8f3b6b4bd44363e0833bdad1"
},
{
"imageDigest": "sha256:8f0dfdf7b1012c368633cf88542d38f4c7ea63656fca5dc474cc8661d3e6c617"
}
]
}
Solution: Run list-images and select the root imageDigest first. In the example below. Delete the image digest that starts with “d326” first (line 17), then the ones beneath it.
3. unexpected status: 403 Forbidden. You get the “unexpected status: 403 Forbidden” when attempting to push the image.
Solution: Your AWS credentials didn’t expire or you have access. Also, run ‘aws ecr get-login-password
‘ as specified above.
4. failed to do request error. If you forget to add the repository name in the image tag, then you may get the following error:
docker push <account-id>.dkr.ecr.<aws-region>.amazonaws.com/mariadb:10.6
failed to do request: Post "https://<account-id>.dkr.ecr.<aws-region>.amazonaws.com/v2/mariadb/blobs/uploads/": EOF
Solution: Include the repository name between .com/
and :<version>
docker push <account-id>.dkr.ecr.<aws-region>.amazonaws.com/<repo-name>:10.6