Going to AWS re:Invent 2024?

➡️ Book a meeting with Spacelift

Terraform

How to Create ECR Repository in AWS Using Terraform

terraform ecr

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.

  1. What is AWS ECR?
  2. Example: Creating an ECR repository with Terraform
  3. Setting up AWS ECR lifecycle policies in Terraform
  4. How to import existing ECR repository into Terraform
  5. AWS ECR Terraform module

What is AWS ECR (Elastic Container Registry)?

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.

Example: Creating an ECR repository with Terraform

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:

  1. Install Terraform
  2. Configure Terraform AWS provider
  3. Create an ECR repository
  4. Run terraform init
  5. Run terraform plan
  6. Apply your configuration
  7. Verify the deployment in the AWS console
  8. 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.

Setting up AWS ECR lifecycle policies in Terraform

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".

How to import existing ECR repository into Terraform

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.

AWS ECR Terraform module

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:

  1. Create a separate directory for your module code (e.g., modules/ecr).
  2. Inside the directory, create a file named main.tf containing the aws_ecr_repository resource definition with desired configuration options.
  3. Optionally, create a variables.tf file to define variables that can be customized when using the module.
  4. 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"
  }
}

Using Spacelift module registry

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:

terraform ecr module

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:

terraform ecr repo

For simple use cases, you should be able to use the default behavior settings:

terraform ecr module settings

Finally, fill out the description settings:

terraform ecr add reposiory

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.

terraform ecr example

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:

terraform ecr trigger

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.

Key points

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.

Learn more

The Practitioner’s Guide to Scaling Infrastructure as Code

Transform your IaC management to scale

securely, efficiently, and productively

into the future.

ebook global banner
Share your data and download the guide