In this article, we will outline how to create an Amazon Elastic Container Registry (ECR) in the Amazon Web Service (AWS) Cloud using Terraform.
We will explain what AWS ECR is, what it is used for, and its benefits, before presenting a step-by-step guide to constructing your Terraform file through to successfully deploying ECR.
Lifecycle policies are important to include when setting up your ECR, so we will cover them next. We will also discuss how to import an existing ECR registry into your Terraform state files. In the final example, we will discuss the AWS ECR Terraform module and demonstrate how it is used.
AWS ECR (Elastic Container Registry) is a service offered by AWS that allows developers to store, manage, and deploy Docker container images and Open Container Initiative (OCI) images. It integrates seamlessly with Amazon ECS, EKS, and other AWS services, enabling streamlined containerized application development and deployment. ECR also provides features like image versioning, access control, and automated scanning for vulnerabilities.
Each ECR account has its own private registry, where you can create one or more repositories to store Docker images, OCI images, and compatible artifacts.
Clients must authenticate to an Amazon ECR private registry as an AWS user before pushing or pulling images. Access must also be granted using the repository policy, which allows you to control permissions for repositories and their contents. Once authenticated, you can push and pull container images to and from your repositories for local development or use in Amazon ECS (Elastic Container Service) and Amazon EKS (Elastic Kubernetes Service).
Benefits of using Amazon ECR
If you’re developing any kind of containerized application on AWS, ECR is a valuable service to consider for storing and managing your container images.
- Security: ECR is a secure way to store your container images with private repositories that use IAM permissions to control access. ECR provides security features such as encryption of images at rest using Amazon S3-managed encryption keys (SSE-S3) or customer-managed keys (SSE-CMKs).
AWS ECR offers image scanning capabilities that help identify vulnerabilities in your container images, ensuring that you deploy secure and compliant containers.
- Scalability: ECR can handle a large number of container images and scales automatically to meet your needs, allowing you to handle growing storage and traffic demands without manual intervention.
- Integration: ECR integrates well with other AWS services like Amazon Elastic Container Service (ECS), Amazon Elastic Kubernetes Service (EKS), and AWS Lambda, making it easy to deploy containerized applications.
- Fully managed service: AWS ECR eliminates the need to operate and scale the infrastructure required for container image management.
- High availability and durability: ECR ensures high availability and durability of your container images with Amazon S3 storage, providing 99.999999999% (11 9’s) of durability. It also supports Cross-Region and Cross-Account Replication to replicate images where needed easily.
- Cost: ECR offers a pay-as-you-go pricing model, meaning you only pay for the amount of storage used and the data transferred without upfront costs.
Below is an example of how you can write a Terraform configuration to create an ECR repository.
The steps to creating an ECR repository with Terraform are as follows:
- Install Terraform
- Configure Terraform AWS provider
- Create an ECR repository
- Run terraform init
- Run terraform plan
- Apply your configuration
- Verify the deployment in the AWS console
- Run terraform destroy
Step 1 — Install Terraform
Make sure you have Terraform installed on your machine.
You can download it from the official website or follow our step-by-step installation guide.
Step 2 — Configure Terraform AWS provider
Once you have Terraform installed, it’s time to add some configuration files.
Create a new Terraform configuration file (e.g., main.tf). Inside the file, define the AWS provider block with your credentials.
For authentication, you can use environment variables, a shared credentials file, or IAM roles.
provider "aws" {
region = "us-east-1" # Replace with your desired region
}
Step 3 — Create an ECR repository
Define the aws_ecr_repository
resource block in your main.tf file and specify the name of your ECR repository.
resource "aws_ecr_repository" "my_ecr_repo" {
name = "my-ecr-repo"
image_tag_mutability = "MUTABLE" # or "IMMUTABLE" based on your requirement
image_scanning_configuration {
scan_on_push = true
}
}
You can find a full list of available arguments for the resource on the official Terraform documentation pages.
Step 4 — Run terraform init
Open a terminal in your project directory and run terraform init
command to initialize the Terraform configuration and download the necessary plugins for the AWS provider.
Step 5 — Run terraform plan
Now, let’s run terraform plan
to preview the changes Terraform will make to your AWS infrastructure based on your configuration. This allows you to review what will be created before applying it.
Step 6 — Apply your configuration
If the terraform plan
output looks good, run terraform apply
to create the ECR repository in your AWS account.
Step 7 — Verify the deployment in the AWS console
Log in to the AWS Management Console and navigate to the ECR service. Your newly created repository should be listed.
Step 8 — Run terraform destroy
When you’re finished with the ECR repository and want to remove it, run terraform destroy
. This will destroy the resources in your AWS account.
Note: Be cautious, as this cannot be undone by default.
With AWS ECR, you can define lifecycle policies to manage the lifecycle of your container images, such as automatically deleting old or unused images, which helps manage costs and storage.
To do this using Terraform, you’ll use the aws_ecr_lifecycle_policy
resource block. This resource manages the lifecycle policy for your ECR repository.
For the policy definition, Terraform requires JSON documents to be encoded as a string. You can use the jsonencode
function to convert your policy document into a Terraform-usable format.
Add this to your main.tf file:
resource "aws_ecr_lifecycle_policy" "my_policy" {
repository = aws_ecr_repository.my_ecr_repo.name
policy = jsonencode({
rules = [
{
rule_priority = 1
description = "Keep only 10 images"
selection = {
count_type = "imageCountMoreThan"
count_number = 10
tag_status = "tagged"
tag_prefix_list = ["prod"]
}
action = {
type = "expire"
}
}
]
})
}
You can customize your policy as needed by configuring multiple rules within the rules
list.
rulePriority
defines the order of execution (lower number executes first).selection
defines the criteria for image selection:countType
: Choose between “sinceImagePushed” (keeps the most recently pushed images) or “imageTagPrefix” (keeps images with specific tag prefixes).countNumber
: Specifies the number of images to keep based on the chosen countType.tagStatus
: Choose between “any” (considers all images) or “tagged” (only considers images with tags).action
: The only supported action for ECR lifecycle policies is"type" :
"expire"
.
If you have an existing ECR repository you want to bring under Terraform management, you’ll need to import it into the state. Terraform versions 1.5.0 and later allow importing existing resources using the import block within your resource definition.
Define the aws_ecr_repository
resource block in your Terraform file, and inside this block, add the import block with two arguments:
to
: This specifies the name you want to assign to the imported ECR repository within your Terraform state.id
: This is the actual identifier (name) of the existing ECR repository in your AWS account.
resource "aws_ecr_repository" "my_existing_repo" {
import {
to = "my_ecr_repo"
id = "existing-repo-name" # Replace with your existing repo name
}
}
Run the command replacing existing-repo-name
with the actual name of your ECR repository:
terraform import aws_ecr_repository.my_existing_repo existing-repo-name
Now, run terraform plan
to verify the import and review any changes.
If you plan to create multiple ECR repositories with similar configurations, you should consider using Terraform modules for reusability best practice.
Modules allow you to define the ECR repository configuration once and reuse it for multiple repositories with minor adjustments. Centralizing configuration in a module makes it easier to manage and update the code for all ECR repositories. Modules also promote code organization and separation between core infrastructure and specific resource configurations.
You can use a public or private module for this. Public modules offer a quick starting point, while private modules provide greater control and customization. Here is a good example of a public one.
If you prefer to develop a private module:
- Create a separate directory for your module code (e.g.,
modules/ecr
). - Inside the directory, create a file named
main.tf
containing theaws_ecr_repository
resource definition with desired configuration options. - Optionally, create a
variables.tf
file to define variables that can be customized when using the module. - Create an
outputs.tf
file (also optional) to expose module outputs, such as the ECR repository URI.
Example: Using the Public ECR module in Terraform
Here’s an example using the cloudposse/terraform-aws-ecr
module from their Github pages (note it includes the lifecycle policy).
You can follow the steps listed previously. Simply replace the code in Step 3 with the following to use the module rather than the resource directly.
module "ecr" {
source = "terraform-aws-modules/ecr/aws"
repository_name = "private-example"
repository_read_write_access_arns = ["arn:aws:iam::012345678901:role/terraform"]
repository_lifecycle_policy = jsonencode({
rules = [
{
rulePriority = 1,
description = "Keep last 30 images",
selection = {
tagStatus = "tagged",
tagPrefixList = ["v"],
countType = "imageCountMoreThan",
countNumber = 30
},
action = {
type = "expire"
}
}
]
})
tags = {
Terraform = "true"
Environment = "dev"
}
}
The Spacelift module registry provides a private Terraform module registry that is fully compatible with Terraform. The module registry allows you to define reusable Terraform modules and share them with various different audiences. This includes sharing modules with multiple Stacks in your Spacelift account but also includes sharing modules with other accounts. The module registry is available to all Spacelift accounts regardless of tier.
1. Declaring the module source
Creating a module is simple, and if you already have an existing Terraform module, you’re almost ready to go. The only additional steps are defining a config.yml file that describes the module and pointing Spacelift at your module source.
The basic folder structure of a Spacelift module looks like this:
- /
+ - .spacelift/
|
+ - config.yml
+ - examples/
|
+ - example-1/
+ - example-2/
+ - main.tf
+ - ...
+ - something-else.tf
As you can see, as well as any Terraform definitions your module requires, there’s also a .spacelift folder. This folder contains a single file: config.yml, which defines certain pieces of information about your module, including its version, along with any test cases.
A minimum example of a config.yml file looks like this:
# version defines the version of the config.yml configuration format. Currently the only option is 1.
version: 1
# module_version defines the version of the module to publish.
module_version: 0.0.1
The folder structure above also includes an examples folder. This folder is not required, and neither does itneed to be called examples. It would typically contain one or more test cases that can double as usage examples for your module.
2. Adding the module to your Spacelift account
There are two main ways to add a module to your Spacelift account: using the UI or using the Terraform provider. The following steps will show how to add the module via the UI, but if you prefer to manage your account via Terraform, look at the spacelift_module resource for more information.
The first step is to navigate to the modules section of the Spacelift UI and click the Add module button:
Next, choose the repository containing your module along with your main branch.
For now, we’ll assume that your repo only contains a single module, so the project root can be left empty:
For simple use cases, you should be able to use the default behavior settings:
Finally, fill out the description settings:
For simple situations where your repository contains a single module and is named using HashiCorp’s module repository naming scheme of terraform-<PROVIDER>-<NAME>, Spacelift will automatically infer the Name and Provider for you, meaning that you don’t need to enter anything on this screen.
For example, the repository used in the screenshots above was named terraform-blog-8ball, giving it a name of 8ball and a provider of blog.
3. Publishing a version
Before you can actually use your module, you need to publish a version. To publish the initial version of your module, click on the Trigger button. Once the version has been published, it should look something like this:
Further versions can be published simply by pushing changes to your Git repository. The only thing to be aware of is that new versions will only be published if you update the module_version in your config.yml file. Another approach to module versioning using git tags is described later in this post.
4. Consuming that version
Now that you have a version published, you need to consume that version from a stack. Consuming a Spacelift module is exactly the same as consuming a module from the Terraform registry. The only difference is you need to specify a different source:
module "8ball" {
source = "spacelift.io/adamconnelly/8ball/blog"
version = "0.0.1"
}
If you click the Show Instructions button in the Spacelift UI, it will give you a usage example:
Note: Terraform needs to be authenticated with the Spacelift module registry to be able to access your module. This happens automatically for you when running Terraform inside a Spacelift stack.
AWS ECR simplifies the process of managing and deploying Docker container images, providing a secure, scalable, and highly available container registry service integrated within the AWS ecosystem.
Creating and managing your ECR with Terraform (using resource blocks or modules) allows you to reap the benefits of infrastructure as code, including improving the consistency, efficiency, and reliability of your AWS deployments.
If you want to learn more about Spacelift, create a free account today, or book a demo with one of our engineers.
Note: New versions of Terraform are placed under the BUSL license, but everything created before version 1.5.x stays open-source. OpenTofu is an open-source version of Terraform that expands on Terraform’s existing concepts and offerings. It is a viable alternative to HashiCorp’s Terraform, being forked from Terraform version 1.5.6.
Automate Terraform Deployments with Spacelift
Automate your infrastructure provisioning, build more complex workflows based on Terraform using policy as code, programmatic configuration, context sharing, drift detection, resource visualization, and many more.