Delete ECR images using CloudFormation Custom Resources
If you use CloudFormation to create an ECR and push an image to it, you may encounter an error during the CloudFormation stack.
This is due to an attempt to delete the ECR with the image still in place.
In this case, we will use CloudFormation custom resources to automatically remove the images from the ECR repository when deleting the CloudFormation stack.
By deleting the image in advance, we aim to ensure that the stack is deleted without error.
Environment
Create a CloudFormation stack and define two resources inside it.
The first is the ECR.
Push images to this repository.
The second is a Lambda function.
This function is set up as a custom resource.
It should be configured to automatically remove images from the ECR repository when the stack is deleting.
The runtime for the function is Python 3.8.
CloudFormation template files
Build the above configuration with CloudFormation.
The CloudFormation templates are located at the following URL
https://github.com/awstut-an-r/awstut-fa/tree/main/078
Explanation of key points of the template files
This page focuses on how to remove an ECR image using a custom resource.
For more information on CloudFormation custom resources, please refer to the following page
Lambda functions to invoke with custom resources
Resources:
Function:
Type: AWS::Lambda::Function
Properties:
Code:
ZipFile: |
import boto3
import cfnresponse
import os
account_id = os.environ['ACCOUNT_ID']
ecr_repository_name = os.environ['ECR_REPOSITORY_NAME']
ecr_client = boto3.client('ecr')
DELETE = 'Delete'
response_data = {}
def lambda_handler(event, context):
try:
if event['RequestType'] == DELETE:
list_images_response = ecr_client.list_images(
registryId=account_id,
repositoryName=ecr_repository_name
)
image_ids = list_images_response['imageIds']
if len(image_ids) == 0:
cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data)
return
batch_delete_image_response = ecr_client.batch_delete_image(
registryId=account_id,
repositoryName=ecr_repository_name,
imageIds=image_ids
)
print(batch_delete_image_response)
cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data)
except Exception as e:
print(e)
cfnresponse.send(event, context, cfnresponse.FAILED, response_data)
Environment:
Variables:
ACCOUNT_ID: !Ref AWS::AccountId
ECR_REPOSITORY_NAME: !Ref ECRRepositoryName
FunctionName: !Sub "${Prefix}-function"
Handler: !Ref Handler
Runtime: !Ref Runtime
Role: !GetAtt FunctionRole.Arn
Code language: YAML (yaml)
There are no special settings for the function itself.
The environment variable is the key point.
The parameters for deleting an image (repository name or account ID) are passed to the function as environment variables.
Refer to the value of event[‘RequestType’] to implement the process according to the stack operation.
“Delete” is used when deleting the stack.
So use an if statement to ensure that the following processing is performed when the stack is deleting.
- Obtain a list of images stored in the repository using the list_images method.
- If the number of images is 0, the process is aborted.
- In the batch_delete_image method, delete the images.
A function invocation completion message must be returned to the CloudFormation stack.
In this case, we use cfnresponse.send to implement this.
There is one issue with the object deletion we have implemented this time.
It is not intended to delete more than 1000 images.
When boto3’s list_images tries to retrieve more than 1000 images, it returns a token to retrieve the remaining data.
In this code, that part of the process is not yet implemented.
The following is the IAM role for the above function.
Resources:
FunctionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service:
- lambda.amazonaws.com
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: !Sub "${Prefix}-ECRDeleteImagesPolicy"
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- ecr:BatchDeleteImage
- ecr:ListImages
Resource:
- !Ref ECRRepositoryArn
Code language: YAML (yaml)
As we saw earlier, the function will list and delete images stored in the ECR.
Therefore, we grant permission to allow these.
Custom Resources
Resources:
CustomResource:
Type: Custom::CustomResource
Properties:
ServiceToken: !Ref FunctionArn
Code language: YAML (yaml)
Specify the ARN of the aforementioned Lambda function in the ServiceToken property.
This setting will cause the function to be invoked each time the CloudFormation stack is operated.
ECR
Resources:
ECRRepository:
Type: AWS::ECR::Repository
Properties:
RepositoryName: !Ref Prefix
Code language: YAML (yaml)
ECR Repository.
No special configuration is required.
Architecting
Use CloudFormation to build this environment and check the actual behavior.
Create CloudFormation stacks and check resources in stacks
Create a CloudFormation stacks.
For information on how to create stacks and check each stack, please refer to the following page
After checking the resources in each stack, information on the main resources created this time is as follows
- ECR: fa-078
Check Action
Image push
Now that everything is ready, push the image to ECR.
$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin [account-id].dkr.ecr.ap-northeast-1.amazonaws.com
...
Login Succeeded
$ docker build -t fa-078 .
...
Successfully tagged fa-078:latest
$ docker tag fa-078:latest [account-id].dkr.ecr.ap-northeast-1.amazonaws.com/fa-078:latest
$ docker push [account-id].dkr.ecr.ap-northeast-1.amazonaws.com/fa-078:latest
The push refers to repository [[account-id].dkr.ecr.ap-northeast-1.amazonaws.com/fa-078]
209cb42bdfb7: Pushed
latest: digest: sha256:4adf0089f316607778fd6a5e073205b767bd849ac8a2234921fddc4351139b96 size: 529
Code language: JavaScript (javascript)
Image build/push successfully executed.
Check the ECR.
The image is indeed stored.
Delete CloudFormation stack
Attempt to delete the stack with the image still in the ECR.
Wait until the deletion process is complete.
Stack deletion is completed.
This means that when the stack is deleted, the CloudFormation custom resource has preemptively deleted the remaining images in the ECR repository.
Checking the Operation of the Lambda function associated with the custom resource.
The logs delivered to CloudWatch Logs show two things.
The first is that we are getting a list of images stored in the ECR.
In this case, we can read that one image is stored.
The second is that this function has acted as a CloudFormation custom resource.
It can be read that a message is sent to the CloudFormation stack using the cfnresponse module.
This means that when the stack is deleted, the image is automatically removed from the ECR repository before the stack is deleted.
Summary
We have seen how to use CloudFormation custom resources to automatically delete images from the ECR repository when deleting the CloudFormation stack.