Terraform module to create AWS ECR (Elastic Container Registry) which is a fully-managed Docker container registry.
The terraform-aws-ecr module enables several common architectures for container image management.
┌──────────────┐ ┌───────────────────────┐ ┌─────────────────┐
│ │ │ │ │ │
│ Developer │────▶│ AWS ECR Registry │◀────│ CI/CD Pipeline │
│ Workstation │ │ │ │ │
│ │ └───────────────────────┘ └─────────────────┘
└──────────────┘ │ ▲
│ │
▼ │
┌─────────────────┐
│ │
│ ECS / EKS │
│ Services │
│ │
└─────────────────┘
For more detailed architecture diagrams including CI/CD integration, multi-region deployments, and security controls, see docs/diagrams.md.
This module follows Semantic Versioning principles. For full details on the versioning scheme, release process, and compatibility guarantees, see the following documentation:
- VERSIONING.md - Details on the semantic versioning scheme and release process
- VERSION_COMPATIBILITY.md - Terraform and AWS provider compatibility matrix
You can use this module to create an ECR registry using few parameters (simple example) or define in detail every aspect of the registry (complete example).
Check the examples directory for examples including:
- Simple - Basic ECR repository with minimal configuration
- Complete - Full-featured ECR repository with all options
- Protected - Repository with deletion protection
- With ECS Integration - ECR configured for use with ECS
- Multi-Region - Repository configured for cross-region replication
This example creates an ECR registry using few parameters
module "ecr" {
source = "lgallard/ecr/aws"
name = "ecr-repo-dev"
# Tags
tags = {
Owner = "DevOps team"
Environment = "dev"
Terraform = true
}
}
In this example, the registry is defined in detail including CloudWatch logging:
module "ecr" {
source = "lgallard/ecr/aws"
name = "ecr-repo-dev"
scan_on_push = true
timeouts_delete = "60m"
image_tag_mutability = "IMMUTABLE"
encryption_type = "KMS"
# Enable CloudWatch logging
enable_logging = true
log_retention_days = 14
// ...rest of configuration...
}
The module supports sending ECR API actions and image push/pull events to CloudWatch Logs. When enabled:
- Creates a CloudWatch Log Group
/aws/ecr/{repository-name}
- Sets up necessary IAM roles and policies for ECR to write logs
- Configurable log retention period (default: 30 days)
To enable logging:
module "ecr" {
source = "lgallard/ecr/aws"
name = "ecr-repo-dev"
enable_logging = true
# Optional: customize retention period (in days)
log_retention_days = 14 # Valid values: 0,1,3,5,7,14,30,60,90,120,150,180,365,400,545,731,1827,3653
}
The module outputs logging-related ARNs:
cloudwatch_log_group_arn
- The ARN of the CloudWatch Log Grouplogging_role_arn
- The ARN of the IAM role used for logging
In this example the register is defined in detailed.
module "ecr" {
source = "lgallard/ecr/aws"
name = "ecr-repo-dev"
scan_on_push = true
timeouts_delete = "60m"
image_tag_mutability = "MUTABLE"
prevent_destroy = true # Protect repository from accidental deletion
# Note that currently only one policy may be applied to a repository.
policy = <<EOF
{
"Version": "2008-10-17",
"Statement": [
{
"Sid": "repo policy",
"Effect": "Allow",
"Principal": "*",
"Action": [
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:BatchCheckLayerAvailability",
"ecr:PutImage",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload",
"ecr:DescribeRepositories",
"ecr:GetRepositoryPolicy",
"ecr:ListImages",
"ecr:DeleteRepository",
"ecr:BatchDeleteImage",
"ecr:SetRepositoryPolicy",
"ecr:DeleteRepositoryPolicy"
]
}
]
}
EOF
# Only one lifecycle policy can be used per repository.
# To apply multiple rules, combined them in one policy JSON.
lifecycle_policy = <<EOF
{
"rules": [
{
"rulePriority": 1,
"description": "Expire untagged images older than 14 days",
"selection": {
"tagStatus": "untagged",
"countType": "sinceImagePushed",
"countUnit": "days",
"countNumber": 14
},
"action": {
"type": "expire"
}
},
{
"rulePriority": 2,
"description": "Keep last 30 dev images",
"selection": {
"tagStatus": "tagged",
"tagPrefixList": ["dev"],
"countType": "imageCountMoreThan",
"countNumber": 30
},
"action": {
"type": "expire"
}
}
]
}
EOF
# Tags
tags = {
Owner = "DevOps team"
Environment = "dev"
Terraform = true
}
}
### Deleting ECR Repositories Protected with prevent_destroy
By default, ECR repositories created by this module have `prevent_destroy = true` set in their lifecycle configuration to prevent accidental deletion. When you need to remove a repository:
1. Set the `prevent_destroy` parameter to `false` for the module:
```hcl
module "ecr" {
source = "lgallard/ecr/aws"
name = "ecr-repo-dev"
prevent_destroy = false # Allow repository to be destroyed
}
- Apply the configuration change:
terraform apply
- After successful apply, run destroy as normal:
terraform destroy
This approach allows protecting repositories by default while providing a controlled way to remove them when needed.
Here are key security best practices for your ECR repositories:
-
Enable Immutable Tags: Prevent tags from being overwritten to ensure image integrity.
image_tag_mutability = "IMMUTABLE"
-
Enable Vulnerability Scanning: Automatically scan images for security vulnerabilities.
scan_on_push = true
-
Implement Least Privilege Access: Use repository policies that grant only necessary permissions.
-
Enable KMS Encryption: Use AWS KMS for enhanced encryption of container images.
encryption_type = "KMS"
-
Configure Lifecycle Policies: Automatically clean up old or unused images.
For a comprehensive guide with detailed examples, see docs/security-best-practices.md.
Common issues and solutions when working with ECR repositories:
Issue | Solution |
---|---|
Authentication failures | Re-authenticate with aws ecr get-login-password |
Permission denied errors | Check IAM policies and repository policies |
Cannot delete repository | Check for prevent_destroy setting and set to false |
Image scan failures | Verify supported image format and AWS region |
Lifecycle policy not working | Check rule syntax and priorities |
For detailed troubleshooting steps, see docs/troubleshooting.md.
This module offers many configuration options through variables. Here are some examples of common variable configurations:
module "ecr" {
source = "lgallard/ecr/aws"
name = "my-app-repo"
tags = {
Environment = "Production"
}
}
module "ecr" {
source = "lgallard/ecr/aws"
name = "secure-repo"
image_tag_mutability = "IMMUTABLE" # Prevent tag overwriting
scan_on_push = true # Enable vulnerability scanning
encryption_type = "KMS" # Use KMS encryption
prevent_destroy = true # Protect from accidental deletion
}
module "ecr" {
source = "lgallard/ecr/aws"
name = "advanced-repo"
force_delete = false
enable_logging = true
# Set custom timeouts
timeouts = {
delete = "45m"
}
}
For detailed examples of all variables with explanations, see docs/variable-examples.md.
Name | Version |
---|---|
terraform | >= 1.3.0 |
aws | >= 5.0.0 |
Name | Version |
---|---|
aws | >= 5.0.0 |
No modules.
Name | Type |
---|---|
aws_cloudwatch_log_group.ecr_logs | resource |
aws_ecr_lifecycle_policy.lifecycle_policy | resource |
aws_ecr_repository.repo | resource |
aws_ecr_repository.repo_protected | resource |
aws_ecr_repository_policy.policy | resource |
aws_iam_role.ecr_logging | resource |
aws_iam_role_policy.ecr_logging | resource |
aws_kms_alias.kms_key_alias | resource |
aws_kms_key.kms_key | resource |
aws_caller_identity.current | data source |
Name | Description | Type | Default | Required |
---|---|---|---|---|
enable_logging | Whether to enable CloudWatch logging for the repository. When enabled, ECR API actions and image push/pull events will be logged to CloudWatch. Defaults to false. |
bool |
false |
no |
encryption_type | The encryption type for the repository. Valid values are "KMS" or "AES256". | string |
"AES256" |
no |
force_delete | Whether to delete the repository even if it contains images. Setting this to true will delete all images in the repository when the repository is deleted. Use with caution as this operation cannot be undone. Defaults to false for safety. |
bool |
false |
no |
image_scanning_configuration | Configuration block that defines image scanning configuration for the repository. Set to null to use the scan_on_push variable setting. Example: { scan_on_push = true } |
object({ |
null |
no |
image_tag_mutability | The tag mutability setting for the repository. - MUTABLE: Image tags can be overwritten - IMMUTABLE: Image tags cannot be overwritten (recommended for production) Defaults to MUTABLE to maintain backwards compatibility. |
string |
"MUTABLE" |
no |
kms_key | The ARN of an existing KMS key to use for repository encryption. Only applicable when encryption_type is set to 'KMS'. If not specified when using KMS encryption, a new KMS key will be created. |
string |
null |
no |
lifecycle_policy | JSON string representing the lifecycle policy. If null (default), no lifecycle policy will be created. See: https://docs.aws.amazon.com/AmazonECR/latest/userguide/lifecycle_policy_examples.html |
string |
null |
no |
log_retention_days | Number of days to retain ECR logs in CloudWatch. Only applicable when enable_logging is true. Defaults to 30 days. |
number |
30 |
no |
name | Name of the ECR repository. This name must be unique within the AWS account and region. | string |
n/a | yes |
policy | JSON string representing the repository policy. If null (default), no repository policy will be created. See: https://docs.aws.amazon.com/AmazonECR/latest/userguide/repository-policies.html |
string |
null |
no |
prevent_destroy | Whether to protect the repository from being destroyed. When set to true, the repository will have the lifecycle block with prevent_destroy = true. When set to false, the repository can be destroyed. This provides a way to dynamically control protection against accidental deletion. Defaults to false to allow repository deletion. |
bool |
false |
no |
scan_on_push | Indicates whether images should be scanned for vulnerabilities after being pushed to the repository. - true: Images will be automatically scanned after each push - false: Images must be scanned manually Only used if image_scanning_configuration is null. |
bool |
true |
no |
tags | A map of tags to assign to all resources created by this module. Tags are key-value pairs that help you manage, identify, organize, search for and filter resources. Example: { Environment = "Production", Owner = "Team" } |
map(string) |
{} |
no |
timeouts | Timeout configuration for repository operations. Specify as an object with a 'delete' key containing a duration string (e.g. "20m"). Example: { delete = "20m" } |
object({ |
{} |
no |
timeouts_delete | Deprecated: Use timeouts = { delete = "duration" } instead. How long to wait for a repository to be deleted. Specify as a duration string, e.g. "20m" for 20 minutes. |
string |
null |
no |
Name | Description |
---|---|
cloudwatch_log_group_arn | The ARN of the CloudWatch Log Group used for ECR logs (if logging is enabled) |
kms_key_arn | The ARN of the KMS key used for repository encryption. |
logging_role_arn | The ARN of the IAM role used for ECR logging (if logging is enabled) |
registry_id | ID of the ECR registry |
repository_arn | ARN of the ECR repository |
repository_name | Name of the ECR repository |
repository_url | URL of the ECR repository |