Skip to content

lgallard/terraform-aws-ecr

Repository files navigation

Terraform

terraform-aws-ecr

Terraform module to create AWS ECR (Elastic Container Registry) which is a fully-managed Docker container registry.

Test

Architecture

The terraform-aws-ecr module enables several common architectures for container image management.

Basic ECR Architecture

┌──────────────┐     ┌───────────────────────┐     ┌─────────────────┐
│              │     │                       │     │                 │
│  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.

Versioning

This module follows Semantic Versioning principles. For full details on the versioning scheme, release process, and compatibility guarantees, see the following documentation:

Usage

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 (manual and automatic approaches)
  • Replication - ECR repository with built-in cross-region replication support
  • Advanced Tagging - Comprehensive tagging strategies with templates, validation, and normalization
  • Enhanced Security - Advanced security features with scanning and compliance
  • Lifecycle Policies - Image lifecycle management with predefined templates
  • Pull Request Rules - Governance and approval workflows for container images
  • Enhanced KMS - Advanced KMS key configuration with custom policies and access control

Simple example

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
  }
}

Complete example with logging

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

CloudWatch Logging

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 Group
  • logging_role_arn - The ARN of the IAM role used for logging

CloudWatch Monitoring and Alerting

The module provides comprehensive CloudWatch monitoring with metric alarms and SNS notifications for proactive repository management. When enabled:

  • Creates CloudWatch metric alarms for key ECR metrics
  • Monitors storage usage, API calls, and security findings
  • Sends notifications via SNS for alarm state changes
  • Provides visibility into repository usage and costs

Basic Monitoring Setup

module "ecr" {
  source = "lgallard/ecr/aws"

  name              = "monitored-app"
  enable_monitoring = true

  # Configure monitoring thresholds
  monitoring_threshold_storage         = 10    # GB
  monitoring_threshold_api_calls       = 1000  # calls per minute
  monitoring_threshold_security_findings = 5   # findings count

  # Create SNS topic for notifications
  create_sns_topic      = true
  sns_topic_name        = "ecr-alerts"
  sns_topic_subscribers = ["admin@company.com", "devops@company.com"]
}

Monitoring Features

CloudWatch Alarms Created:

  • Storage Usage: Monitors repository size in GB
  • API Call Volume: Monitors API operations per minute
  • Image Push Count: Monitors push frequency (10 pushes per 5 minutes)
  • Image Pull Count: Monitors pull frequency (100 pulls per 5 minutes)
  • Security Findings: Monitors vulnerability count (requires enhanced scanning)

SNS Integration:

  • Automatic SNS topic creation with configurable name
  • Email subscriptions for immediate notifications
  • Alarm and OK state notifications
  • Support for existing SNS topics

Advanced Monitoring Configuration

module "ecr" {
  source = "lgallard/ecr/aws"

  name = "production-app"

  # Enable monitoring with custom thresholds
  enable_monitoring                    = true
  monitoring_threshold_storage         = 50    # 50 GB threshold
  monitoring_threshold_api_calls       = 2000  # 2000 calls/minute
  monitoring_threshold_security_findings = 0   # Zero tolerance for vulnerabilities

  # Use existing SNS topic
  create_sns_topic = false
  sns_topic_name   = "existing-alerts-topic"

  # Enable enhanced scanning for security monitoring
  enable_registry_scanning = true
  registry_scan_type      = "ENHANCED"
  enable_secret_scanning  = true
}

Monitoring Outputs:

  • monitoring_status - Complete monitoring configuration status
  • sns_topic_arn - ARN of the SNS topic (if created)
  • cloudwatch_alarms - Details of all created CloudWatch alarms

Cost Considerations:

  • CloudWatch alarms: $0.10 per alarm per month
  • SNS notifications: First 1,000 emails free, then $0.75 per 1,000
  • No additional charges for metrics collection

Cross-Region Replication

The module now supports automatic cross-region replication for disaster recovery and multi-region deployments. When enabled, images are automatically replicated to specified regions whenever they are pushed to the primary repository.

module "ecr" {
  source = "lgallard/ecr/aws"

  name = "my-application"

  # Enable cross-region replication
  enable_replication  = true
  replication_regions = ["us-west-2", "eu-west-1", "ap-southeast-1"]

  tags = {
    Environment = "production"
    Application = "my-app"
  }
}

Key Benefits:

  • Disaster Recovery - Images remain available if a region becomes unavailable
  • Reduced Latency - Pull images from the nearest region
  • High Availability - Improved resilience for multi-region workloads
  • Automatic Sync - No manual intervention required for replication

Important Notes:

  • Replication is configured at the registry level (affects all repositories in the account)
  • Use immutable tags (image_tag_mutability = "IMMUTABLE") for consistency across regions
  • Additional costs apply for cross-region data transfer and storage
  • Replication is one-way from the source region to destination regions

The module provides replication-related outputs:

  • replication_status - Overall replication configuration status
  • replication_regions - List of destination regions
  • replication_configuration_arn - ARN of the replication configuration

For more detailed examples, see the replication example and multi-region example.

Complete example

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
}
  1. Apply the configuration change:
terraform apply
  1. 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.

Advanced Tagging Configuration

The module provides comprehensive tagging strategies to support better resource management, cost allocation, and organizational compliance. These features enable consistent, validated, and normalized tagging across all ECR resources while maintaining full backward compatibility.

Key Features

  • Default Tag Templates: Predefined organizational tag standards for common scenarios
  • Tag Validation: Ensure required tags are present and follow naming conventions
  • Tag Normalization: Consistent casing and format across all resources
  • Cost Allocation: Specialized tags for financial tracking and reporting
  • Compliance: Tags required for security and regulatory frameworks
  • Backward Compatible: All advanced features are opt-in

Default Tag Templates

The module provides four predefined templates for common organizational needs:

Basic Template

module "ecr" {
  source = "lgallard/ecr/aws"

  name = "my-app"

  # Enable basic organizational tagging
  enable_default_tags = true
  default_tags_template = "basic"
  default_tags_environment = "production"
  default_tags_owner = "platform-team"
  default_tags_project = "user-service"
}

Applied tags: CreatedBy, ManagedBy, Environment, Owner, Project

Cost Allocation Template

module "ecr" {
  source = "lgallard/ecr/aws"

  name = "my-app"

  # Enable cost allocation tagging
  enable_default_tags = true
  default_tags_template = "cost_allocation"
  default_tags_environment = "production"
  default_tags_owner = "platform-team"
  default_tags_project = "user-service"
  default_tags_cost_center = "engineering-cc-001"
}

Applied tags: All basic tags plus CostCenter, BillingProject, ResourceType, Service, Billable

Compliance Template

module "ecr" {
  source = "lgallard/ecr/aws"

  name = "my-app"

  # Enable compliance tagging
  enable_default_tags = true
  default_tags_template = "compliance"
  default_tags_environment = "production"
  default_tags_owner = "security-team"
  default_tags_project = "payment-service"
  default_tags_cost_center = "security-cc-002"
}

Applied tags: All cost allocation tags plus DataClass, Compliance, BackupRequired, MonitoringLevel, SecurityReview

SDLC Template

module "ecr" {
  source = "lgallard/ecr/aws"

  name = "my-app"

  # Enable SDLC tagging
  enable_default_tags = true
  default_tags_template = "sdlc"
  default_tags_environment = "development"
  default_tags_owner = "dev-team"
  default_tags_project = "mobile-app"
}

Applied tags: Basic organizational tags plus Application, Version, DeploymentStage, LifecycleStage, MaintenanceWindow

Tag Validation and Compliance

Ensure organizational compliance by validating that required tags are present:

module "ecr" {
  source = "lgallard/ecr/aws"

  name = "my-app"

  # Enable tag validation
  enable_tag_validation = true
  required_tags = [
    "Environment",
    "Owner",
    "Project",
    "CostCenter"
  ]

  # This will fail if any required tags are missing
  default_tags_environment = "production"
  default_tags_owner = "platform-team"
  default_tags_project = "user-service"
  default_tags_cost_center = "eng-001"
}

Tag Normalization

Ensure consistent tag formatting across all resources:

module "ecr" {
  source = "lgallard/ecr/aws"

  name = "my-app"

  # Enable tag normalization
  enable_tag_normalization = true
  tag_key_case = "PascalCase"  # Options: PascalCase, camelCase, snake_case, kebab-case
  normalize_tag_values = true

  tags = {
    "cost-center" = "  engineering-001  "  # Will be normalized to "CostCenter" = "engineering-001"
    "data_class"  = "internal"             # Will be normalized to "DataClass" = "internal"
  }
}

Custom Default Tags

Configure custom default tags without using templates:

module "ecr" {
  source = "lgallard/ecr/aws"

  name = "my-app"

  # Enable custom default tags
  enable_default_tags = true
  default_tags_template = null  # Use custom configuration
  default_tags_environment = "staging"
  default_tags_owner = "full-stack-team"
  default_tags_project = "analytics-service"
  default_tags_cost_center = "data-cc-003"

  # Additional custom tags
  tags = {
    team_slack = "analytics-team"
    oncall_rotation = "analytics-oncall"
  }
}

Legacy Compatibility

Disable advanced tagging features for backward compatibility:

module "ecr" {
  source = "lgallard/ecr/aws"

  name = "my-app"

  # Disable advanced tagging features
  enable_default_tags = false
  enable_tag_validation = false
  enable_tag_normalization = false

  # Traditional manual tagging
  tags = {
    Environment = "production"
    Owner = "legacy-team"
    ManagedBy = "Terraform"
  }
}

Tagging Best Practices

  1. Start with Templates: Use predefined templates that match your organizational needs
  2. Enable Validation: Enforce required tags for compliance and consistency
  3. Normalize Consistently: Choose a casing strategy and apply it across all resources
  4. Plan for Cost Allocation: Include cost center and billing project tags early
  5. Consider Compliance: Include data classification and security review tags for regulated environments
  6. Monitor Tag Drift: Use the validation and normalization features to maintain consistency

For comprehensive examples demonstrating different tagging strategies, see examples/advanced-tagging.

Enhanced Lifecycle Policy Configuration

The module provides enhanced lifecycle policy configuration through helper variables and predefined templates, making it easier to implement common lifecycle patterns without writing complex JSON. This feature significantly simplifies ECR image lifecycle management while maintaining full backwards compatibility.

Configuration Methods

There are three ways to configure lifecycle policies, listed in order of precedence:

  1. Manual JSON Policy (lifecycle_policy) - Highest precedence, full control
  2. Predefined Templates (lifecycle_policy_template) - Medium precedence, common patterns
  3. Helper Variables - Lowest precedence, individual settings

🚨 Important: Configuration Precedence Rules

  • When lifecycle_policy is specified, ALL template and helper variable settings are ignored
  • When lifecycle_policy_template is specified, ALL helper variable settings are ignored
  • Only helper variables are used when neither lifecycle_policy nor lifecycle_policy_template are specified
  • AWS ECR Limitations: Maximum 25 rules per policy, rule priorities must be unique (1-999), up to 100 tag prefixes per rule

For complete AWS ECR lifecycle policy documentation and examples, see AWS ECR Lifecycle Policy Documentation.

Helper Variables

Configure lifecycle policies using individual helper variables for maximum flexibility:

module "ecr" {
  source = "lgallard/ecr/aws"

  name = "my-app"

  # Keep only the latest 30 images (range: 1-10000)
  lifecycle_keep_latest_n_images = 30

  # Delete untagged images after 7 days (range: 1-3650)
  lifecycle_expire_untagged_after_days = 7

  # Delete tagged images after 90 days (range: 1-3650)
  lifecycle_expire_tagged_after_days = 90

  # Apply rules only to specific tag prefixes (optional)
  lifecycle_tag_prefixes_to_keep = ["v", "release", "prod"]
}

Helper Variable Details

  • lifecycle_keep_latest_n_images: Controls how many of the most recent images to retain. When combined with lifecycle_tag_prefixes_to_keep, only applies to images with those tag prefixes.
  • lifecycle_expire_untagged_after_days: Automatically deletes untagged images after the specified number of days.
  • lifecycle_expire_tagged_after_days: Automatically deletes tagged images after the specified number of days.
  • lifecycle_tag_prefixes_to_keep: When specified, limits the lifecycle_keep_latest_n_images rule to only images with these tag prefixes. Other lifecycle rules still apply to all images.

Predefined Templates

Use predefined templates for common scenarios. Each template encapsulates best practices for specific environments:

# Development environment - optimized for frequent builds and testing
module "ecr_dev" {
  source = "lgallard/ecr/aws"
  name   = "dev-app"
  lifecycle_policy_template = "development"
}

# Production environment - balanced retention and stability
module "ecr_prod" {
  source = "lgallard/ecr/aws"
  name   = "prod-app"
  lifecycle_policy_template = "production"
}

# Cost optimization - minimal storage costs
module "ecr_cost" {
  source = "lgallard/ecr/aws"
  name   = "test-app"
  lifecycle_policy_template = "cost_optimization"
}

# Compliance - long retention for audit requirements
module "ecr_compliance" {
  source = "lgallard/ecr/aws"
  name   = "audit-app"
  lifecycle_policy_template = "compliance"
}

Available Templates

Template Keep Images Untagged Expiry Tagged Expiry Tag Prefixes Use Case
development 50 7 days - ["dev", "feature"] Development workflows with frequent builds
production 100 14 days 90 days ["v", "release", "prod"] Production environments requiring stability
cost_optimization 10 3 days 30 days [] (all images) Test environments with aggressive cost optimization
compliance 200 30 days 365 days ["v", "release", "audit"] Compliance environments requiring long retention

Configuration Precedence and Examples

The module follows a clear precedence order when multiple configuration methods are specified:

1. Manual Policy (Highest Precedence)

module "ecr" {
  source = "lgallard/ecr/aws"
  name   = "custom-app"

  # This manual policy takes precedence over everything else
  lifecycle_policy = jsonencode({
    rules = [
      {
        rulePriority = 1
        description  = "Custom rule"
        selection = {
          tagStatus   = "untagged"
          countType   = "sinceImagePushed"
          countUnit   = "days"
          countNumber = 5
        }
        action = { type = "expire" }
      }
    ]
  })

  # These are ignored when lifecycle_policy is specified
  lifecycle_policy_template = "production"
  lifecycle_keep_latest_n_images = 30
}

2. Template Configuration

module "ecr" {
  source = "lgallard/ecr/aws"
  name   = "template-app"

  # Template takes precedence over helper variables
  lifecycle_policy_template = "production"

  # These helper variables are ignored when template is specified
  lifecycle_keep_latest_n_images = 30
  lifecycle_expire_untagged_after_days = 5
}

3. Helper Variables (Lowest Precedence)

module "ecr" {
  source = "lgallard/ecr/aws"
  name   = "helper-app"

  # Only helper variables specified - these will be used
  lifecycle_keep_latest_n_images = 30
  lifecycle_expire_untagged_after_days = 5
  lifecycle_expire_tagged_after_days = 60
  lifecycle_tag_prefixes_to_keep = ["v", "stable"]
}

Advanced Usage Patterns

Environment-Specific Configurations

# Development with custom retention
module "ecr_dev_custom" {
  source = "lgallard/ecr/aws"
  name   = "dev-custom-app"

  lifecycle_keep_latest_n_images      = 20    # Fewer than default dev template
  lifecycle_expire_untagged_after_days = 3    # Faster cleanup than default
  lifecycle_tag_prefixes_to_keep      = ["dev", "feat", "fix"]
}

# Production with extended retention for releases
module "ecr_prod_extended" {
  source = "lgallard/ecr/aws"
  name   = "prod-extended-app"

  lifecycle_keep_latest_n_images      = 150   # More than default prod template
  lifecycle_expire_untagged_after_days = 21   # Longer than default
  lifecycle_expire_tagged_after_days   = 180  # Extended retention
  lifecycle_tag_prefixes_to_keep      = ["v", "release", "hotfix"]
}

Cost-Conscious Multi-Environment Setup

# Aggressive cleanup for test environments
module "ecr_test" {
  source = "lgallard/ecr/aws"
  name   = "test-app"

  lifecycle_keep_latest_n_images      = 5     # Minimal retention
  lifecycle_expire_untagged_after_days = 1    # Daily cleanup
  lifecycle_expire_tagged_after_days   = 7    # Weekly cleanup
}

# Balanced approach for staging
module "ecr_staging" {
  source = "lgallard/ecr/aws"
  name   = "staging-app"

  lifecycle_policy_template = "development"  # Use template for consistency
}

Best Practices

Template Selection Guidelines

  • Use development for: CI/CD environments, feature branch testing, development workflows
  • Use production for: Live applications, staging environments, release candidates
  • Use cost_optimization for: Temporary test environments, proof-of-concepts, experimental workloads
  • Use compliance for: Regulated environments, audit trails, long-term archival needs

Custom Configuration Guidelines

  1. Start with a template that's closest to your needs, then use helper variables if needed
  2. Use tag prefixes to apply different retention rules to different image types
  3. Monitor storage costs and adjust retention periods based on usage patterns
  4. Consider compliance requirements when setting retention periods

Tag Prefix Strategy Examples

# Strategy 1: Environment-based prefixes
lifecycle_tag_prefixes_to_keep = ["prod", "staging", "release"]

# Strategy 2: Version-based prefixes
lifecycle_tag_prefixes_to_keep = ["v", "release-"]

# Strategy 3: Branch-based prefixes
lifecycle_tag_prefixes_to_keep = ["main", "develop", "hotfix"]

# Strategy 4: Mixed strategy
lifecycle_tag_prefixes_to_keep = ["v", "release", "prod", "stable"]

Validation and Constraints

The module includes built-in validation to prevent common configuration errors:

  • Image count: Must be between 1 and 10,000
  • Days: Must be between 1 and 3,650 (10 years)
  • Tag prefixes: Maximum 100 prefixes, each up to 255 characters
  • Template names: Must be one of the four predefined templates

Generated Policy Structure

When using helper variables or templates, the module generates policies with this structure:

  1. Rule 1: Expire untagged images (if expire_untagged_after_days specified)
  2. Rule 2: Keep latest N images (if keep_latest_n_images specified)
  3. Rule 3: Expire tagged images (if expire_tagged_after_days specified)

Migration from Manual Policies

To migrate from existing manual lifecycle_policy to the enhanced configuration:

Migration Steps

  1. Analyze Your Current Policy: Review your existing JSON lifecycle policy to understand the rules.

    # Get current policy from Terraform state
    terraform show | grep -A 20 lifecycle_policy
  2. Choose Migration Path:

    • Use a template if your policy matches common patterns
    • Use helper variables for custom configurations
    • Keep manual policy for complex, non-standard rules
  3. Template Migration Example:

    # Before (manual policy)
    module "ecr" {
      source = "lgallard/ecr/aws"
      name   = "my-app"
    
      lifecycle_policy = jsonencode({
        rules = [
          {
            rulePriority = 1
            description  = "Keep last 100 images"
            selection = {
              tagStatus   = "any"
              countType   = "imageCountMoreThan"
              countNumber = 100
            }
            action = { type = "expire" }
          },
          {
            rulePriority = 2
            description  = "Expire untagged after 14 days"
            selection = {
              tagStatus   = "untagged"
              countType   = "sinceImagePushed"
              countUnit   = "days"
              countNumber = 14
            }
            action = { type = "expire" }
          }
        ]
      })
    }
    
    # After (using production template)
    module "ecr" {
      source = "lgallard/ecr/aws"
      name   = "my-app"
    
      lifecycle_policy_template = "production"  # Matches the pattern above
    }
  4. Helper Variables Migration Example:

    # Before (manual policy)
    lifecycle_policy = jsonencode({
      rules = [
        {
          rulePriority = 1
          description  = "Keep 30 images with specific prefixes"
          selection = {
            tagStatus     = "tagged"
            tagPrefixList = ["v", "release"]
            countType     = "imageCountMoreThan"
            countNumber   = 30
          }
          action = { type = "expire" }
        }
      ]
    })
    
    # After (using helper variables)
    lifecycle_keep_latest_n_images = 30
    lifecycle_tag_prefixes_to_keep = ["v", "release"]
  5. Test Migration: Apply changes in a non-production environment first:

    terraform plan  # Review the changes carefully
    terraform apply # Apply when ready
  6. Verify Results: Check that lifecycle policies are correctly applied:

    aws ecr describe-lifecycle-policy --repository-name my-app

Migration Validation

  • Before migration: Document current retention behavior
  • After migration: Verify identical behavior with new configuration
  • Monitor: Watch for unexpected image deletions in the first week

For more complex migration scenarios, see the migration examples.

See the lifecycle policies example for comprehensive usage examples and the troubleshooting guide for common issues.

Pull Request Rules Configuration

The module provides pull request rules functionality to implement governance and approval workflows for container images, similar to pull request approval processes for code repositories. This feature enables organizations to enforce quality control, security validation, and compliance requirements before images are deployed to production.

Overview

Pull request rules provide:

  • Approval workflows: Require manual approval for production images
  • Security validation: Automatic checks for vulnerabilities and compliance
  • CI/CD integration: Webhook notifications for external systems
  • Governance controls: Policy-based restrictions on image usage
  • Audit trails: Complete tracking of image approval workflows

Basic Configuration

module "ecr" {
  source = "lgallard/ecr/aws"
  name   = "production-app"

  # Enable pull request rules
  enable_pull_request_rules = true

  # Configure approval requirements
  pull_request_rules = [
    {
      name    = "production-approval"
      type    = "approval"
      enabled = true
      conditions = {
        tag_patterns       = ["prod-*", "release-*"]
        severity_threshold = "HIGH"
      }
      actions = {
        require_approval_count = 2
        notification_topic_arn = "arn:aws:sns:region:account:topic"
      }
    }
  ]
}

Rule Types

1. Approval Rules (type = "approval")

Require manual approval before images can be used in production:

{
  name    = "security-approval"
  type    = "approval"
  enabled = true
  conditions = {
    tag_patterns            = ["prod-*", "release-*"]
    severity_threshold      = "HIGH"
    require_scan_completion = true
    allowed_principals      = ["arn:aws:iam::account:role/SecurityTeam"]
  }
  actions = {
    require_approval_count  = 2
    notification_topic_arn  = "arn:aws:sns:region:account:topic"
    block_on_failure       = true
    approval_timeout_hours = 24
  }
}

2. Security Scan Rules (type = "security_scan")

Automatically validate images against security criteria:

{
  name    = "vulnerability-check"
  type    = "security_scan"
  enabled = true
  conditions = {
    severity_threshold      = "MEDIUM"
    require_scan_completion = true
  }
  actions = {
    notification_topic_arn = "arn:aws:sns:region:account:topic"
    block_on_failure       = true
  }
}

3. CI Integration Rules (type = "ci_integration")

Integrate with CI/CD systems through webhooks:

{
  name    = "ci-validation"
  type    = "ci_integration"
  enabled = true
  conditions = {
    tag_patterns = ["feature-*", "dev-*"]
  }
  actions = {
    webhook_url      = "https://ci.company.com/webhook/ecr"
    block_on_failure = false
  }
}

Configuration Options

Conditions

  • tag_patterns: List of tag patterns that trigger the rule
  • severity_threshold: Minimum vulnerability severity (LOW, MEDIUM, HIGH, CRITICAL)
  • require_scan_completion: Whether to require completed security scans
  • allowed_principals: List of IAM principals allowed to interact with approved images

Actions

  • require_approval_count: Number of approvals required (1-10)
  • notification_topic_arn: SNS topic for notifications
  • webhook_url: Webhook URL for external integrations
  • block_on_failure: Whether to block operations on rule failure
  • approval_timeout_hours: Hours to wait for approval (1-168)

Approval Workflow

  1. Image Push: Developer pushes image to ECR repository
  2. Rule Evaluation: Pull request rules evaluate the image against configured criteria
  3. Notification: If approval is required, notifications are sent to configured channels
  4. Security Scan: Automatic vulnerability scanning is performed
  5. Manual Review: Security team reviews scan results and compliance
  6. Approval: If acceptable, image is tagged with approval status
  7. Deployment: Approved images can be deployed to production

Example: Complete Governance Setup

module "ecr_governance" {
  source = "lgallard/ecr/aws"
  name   = "critical-application"

  enable_pull_request_rules = true
  pull_request_rules = [
    # Production approval workflow
    {
      name    = "production-security-approval"
      type    = "approval"
      enabled = true
      conditions = {
        tag_patterns            = ["prod-*", "release-*"]
        severity_threshold      = "HIGH"
        require_scan_completion = true
        allowed_principals = [
          "arn:aws:iam::123456789012:role/SecurityTeam",
          "arn:aws:iam::123456789012:role/ReleaseManagers"
        ]
      }
      actions = {
        require_approval_count  = 3
        notification_topic_arn  = aws_sns_topic.security_alerts.arn
        block_on_failure       = true
        approval_timeout_hours = 48
      }
    },
    # Automatic security validation
    {
      name    = "security-scan-gate"
      type    = "security_scan"
      enabled = true
      conditions = {
        tag_patterns            = ["*"]
        severity_threshold      = "MEDIUM"
        require_scan_completion = true
      }
      actions = {
        notification_topic_arn = aws_sns_topic.security_alerts.arn
        block_on_failure       = true
      }
    },
    # CI/CD integration
    {
      name    = "ci-pipeline-integration"
      type    = "ci_integration"
      enabled = true
      conditions = {
        tag_patterns = ["feature-*", "dev-*", "staging-*"]
      }
      actions = {
        webhook_url      = "https://ci.company.com/webhook/ecr-validation"
        block_on_failure = false
      }
    }
  ]

  # Enhanced security scanning
  enable_registry_scanning = true
  registry_scan_type      = "ENHANCED"
  enable_secret_scanning  = true
}

Approval Commands

After implementing pull request rules, use these commands to manage approvals:

# Check image scan results
aws ecr describe-image-scan-findings \
  --repository-name production-app \
  --image-id imageTag=prod-v1.0.0

# Approve image for production use
aws ecr put-image \
  --repository-name production-app \
  --image-tag prod-v1.0.0 \
  --tag-list Key=ApprovalStatus,Value=approved

# List images with approval status
aws ecr describe-images \
  --repository-name production-app \
  --query 'imageDetails[*].[imageTags[0],imageTagMutability,imageScanFindingsSummary.findings]'

Best Practices

  1. Layered Approach: Use multiple rule types for comprehensive governance
  2. Graduated Enforcement: Strict rules for production, flexible for development
  3. Clear Workflows: Document approval processes and responsibilities
  4. Monitoring: Set up CloudWatch alarms for rule violations
  5. Regular Reviews: Periodically review and update rule configurations
  6. Testing: Test rule configurations in non-production environments first

Integration with CI/CD

Pull request rules integrate seamlessly with CI/CD pipelines:

# GitHub Actions example
- name: Check ECR approval status
  run: |
    STATUS=$(aws ecr describe-images \
      --repository-name $REPO_NAME \
      --image-ids imageTag=$IMAGE_TAG \
      --query 'imageDetails[0].imageTags' \
      --output text | grep -o 'ApprovalStatus.*approved' || echo "not-approved")

    if [[ "$STATUS" != *"approved"* ]]; then
      echo "Image not approved for deployment"
      exit 1
    fi

See the pull request rules example for a complete implementation guide.

Security Best Practices

Here are key security best practices for your ECR repositories:

  1. Enable Immutable Tags: Prevent tags from being overwritten to ensure image integrity.

    image_tag_mutability = "IMMUTABLE"
  2. Enable Enhanced Scanning: Use AWS Inspector for comprehensive vulnerability assessment.

    enable_registry_scanning = true
    registry_scan_type      = "ENHANCED"
    enable_secret_scanning  = true
  3. Configure Pull-Through Cache: Reduce external dependencies and improve performance.

    enable_pull_through_cache = true
    pull_through_cache_rules = [
      {
        ecr_repository_prefix = "docker-hub"
        upstream_registry_url = "registry-1.docker.io"
      }
    ]
  4. Enable Basic Scanning: Automatically scan images for security vulnerabilities (if not using enhanced).

    scan_on_push = true
  5. Implement Least Privilege Access: Use repository policies that grant only necessary permissions.

  6. Enable KMS Encryption: Use AWS KMS for enhanced encryption of container images.

    encryption_type = "KMS"

    For advanced KMS configuration options, see the Enhanced KMS Configuration section below.

  7. Configure Lifecycle Policies: Automatically clean up old or unused images.

For a comprehensive guide with detailed examples, see docs/security-best-practices.md.

Troubleshooting

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.

Enhanced KMS Configuration

This module includes a dedicated KMS submodule that provides enhanced encryption configuration options for ECR repositories. The KMS submodule offers fine-grained control over key policies, rotation settings, and access management.

Basic KMS Configuration

module "ecr" {
  source = "lgallard/ecr/aws"

  name            = "my-encrypted-repo"
  encryption_type = "KMS"

  # Enhanced KMS options
  kms_deletion_window_in_days = 14
  kms_enable_key_rotation     = true
  kms_key_rotation_period     = 90

  tags = {
    Environment = "production"
  }
}

Advanced KMS Configuration

module "ecr" {
  source = "lgallard/ecr/aws"

  name            = "production-app"
  encryption_type = "KMS"

  # Advanced KMS configuration
  kms_deletion_window_in_days = 30
  kms_enable_key_rotation     = true
  kms_key_rotation_period     = 180
  kms_multi_region           = true

  # Access control
  kms_key_administrators = [
    "arn:aws:iam::123456789012:role/KMSAdminRole"
  ]

  kms_key_users = [
    "arn:aws:iam::123456789012:role/ECRAccessRole",
    "arn:aws:iam::123456789012:role/CI-CD-Role"
  ]

  # Custom alias
  kms_alias_name = "production/ecr/my-app"

  # KMS-specific tags
  kms_tags = {
    KeyType      = "ECR-Encryption"
    Rotation     = "180-days"
    MultiRegion  = "true"
  }
}

Custom KMS Policy

module "ecr" {
  source = "lgallard/ecr/aws"

  name            = "custom-policy-repo"
  encryption_type = "KMS"

  # Custom policy statements
  kms_custom_policy_statements = [
    {
      sid    = "AllowCrossAccountAccess"
      effect = "Allow"
      principals = {
        type        = "AWS"
        identifiers = ["arn:aws:iam::TRUSTED-ACCOUNT:root"]
      }
      actions = [
        "kms:Encrypt",
        "kms:Decrypt",
        "kms:ReEncrypt*",
        "kms:GenerateDataKey*",
        "kms:DescribeKey"
      ]
      conditions = [
        {
          test     = "StringEquals"
          variable = "kms:ViaService"
          values   = ["ecr.us-east-1.amazonaws.com"]
        }
      ]
    }
  ]
}

KMS Configuration Options

Feature Variable Description
Key Management kms_deletion_window_in_days Days before key deletion (7-30)
kms_enable_key_rotation Enable automatic rotation
kms_key_rotation_period Rotation period in days (90-2555)
kms_multi_region Create multi-region key
Access Control kms_key_administrators Principals with full key access
kms_key_users Principals with encrypt/decrypt access
kms_additional_principals Additional principals with basic access
Policy Customization kms_custom_policy_statements Additional policy statements
kms_custom_policy Complete custom policy JSON
Naming & Tagging kms_alias_name Custom alias name
kms_tags KMS-specific tags

Benefits of Enhanced KMS Configuration

  1. Granular Access Control: Define specific roles for key administration and usage
  2. Flexible Rotation: Configure custom rotation periods for compliance requirements
  3. Multi-Region Support: Create keys that work across multiple AWS regions
  4. Custom Policies: Add specific policy statements or use completely custom policies
  5. Enhanced Monitoring: KMS-specific tags for better cost tracking and compliance
  6. Cross-Account Access: Secure sharing of encrypted repositories across AWS accounts

Example: Production Setup

module "production_ecr" {
  source = "lgallard/ecr/aws"

  name = "production-microservice"

  # Production-grade KMS encryption
  encryption_type = "KMS"
  kms_deletion_window_in_days = 30  # Longer window for recovery
  kms_enable_key_rotation     = true
  kms_key_rotation_period     = 90   # Quarterly rotation
  kms_multi_region           = true  # Multi-region deployment

  # Role-based access control
  kms_key_administrators = [
    "arn:aws:iam::123456789012:role/ProductionKMSAdmins"
  ]

  kms_key_users = [
    "arn:aws:iam::123456789012:role/ProductionECRAccess",
    "arn:aws:iam::123456789012:role/GitHubActions-Production"
  ]

  # Production tagging strategy
  tags = {
    Environment = "production"
    Application = "microservice"
    Owner       = "platform-team"
    CostCenter  = "engineering"
  }

  kms_tags = {
    EncryptionType = "ECR-Production"
    ComplianceLevel = "SOC2"
    BackupRequired = "true"
  }
}

For complete examples and advanced use cases, see the enhanced-kms example.

Variable Usage Examples

This module offers many configuration options through variables. Here are some examples of common variable configurations:

Basic Configuration

module "ecr" {
  source = "lgallard/ecr/aws"

  name   = "my-app-repo"
  tags   = {
    Environment = "Production"
  }
}

Security Settings

module "ecr" {
  source = "lgallard/ecr/aws"

  name                 = "secure-repo"
  image_tag_mutability = "IMMUTABLE"    # Prevent tag overwriting
  scan_on_push         = true           # Enable basic vulnerability scanning
  encryption_type      = "KMS"          # Use KMS encryption
  prevent_destroy      = true           # Protect from accidental deletion
}

Enhanced Security Configuration

module "ecr" {
  source = "lgallard/ecr/aws"

  name                 = "enhanced-secure-repo"
  image_tag_mutability = "IMMUTABLE"
  encryption_type      = "KMS"

  # Enhanced scanning with AWS Inspector
  enable_registry_scanning = true
  registry_scan_type      = "ENHANCED"
  enable_secret_scanning  = true

  # Registry scan filters for high/critical vulnerabilities
  registry_scan_filters = [
    {
      name   = "PACKAGE_VULNERABILITY_SEVERITY"
      values = ["HIGH", "CRITICAL"]
    }
  ]

  # Pull-through cache for Docker Hub
  enable_pull_through_cache = true
  pull_through_cache_rules = [
    {
      ecr_repository_prefix = "docker-hub"
      upstream_registry_url = "registry-1.docker.io"
    }
  ]
}

Advanced Options

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.

Testing

This module uses Terratest for automated testing of the module functionality. The tests validate that the module can correctly:

  • Create an ECR repository with basic settings
  • Apply repository and lifecycle policies
  • Configure KMS encryption
  • Set up image tag mutability
  • Configure scan on push features

Running Tests Locally

To run the tests locally, you'll need:

  1. Go 1.16+
  2. Terraform 1.3.0+
  3. AWS credentials configured locally
# Clone the repository
git clone https://github.com/lgallard/terraform-aws-ecr.git
cd terraform-aws-ecr

# Run the tests
cd test
go mod tidy
go test -v

For more details on tests, see the test directory README.

Requirements

Name Version
terraform >= 1.3.0
archive >= 2.0.0
aws >= 5.0.0

Providers

Name Version
archive >= 2.0.0
aws >= 5.0.0

Modules

Name Source Version
kms ./modules/kms n/a

Resources

Name Type
aws_cloudwatch_event_rule.pull_request_rules resource
aws_cloudwatch_event_target.pull_request_rules_sns resource
aws_cloudwatch_event_target.pull_request_rules_webhook resource
aws_cloudwatch_log_group.ecr_logs resource
aws_cloudwatch_metric_alarm.api_call_volume resource
aws_cloudwatch_metric_alarm.image_pull_count resource
aws_cloudwatch_metric_alarm.image_push_count resource
aws_cloudwatch_metric_alarm.repository_storage_usage resource
aws_cloudwatch_metric_alarm.security_findings resource
aws_ecr_lifecycle_policy.lifecycle_policy resource
aws_ecr_lifecycle_policy.test_policy resource
aws_ecr_pull_through_cache_rule.cache_rules resource
aws_ecr_registry_scanning_configuration.scanning resource
aws_ecr_replication_configuration.replication resource
aws_ecr_repository.repo resource
aws_ecr_repository.repo_protected resource
aws_ecr_repository.test_repo resource
aws_ecr_repository_policy.policy resource
aws_ecr_repository_policy.test_repo_policy resource
aws_iam_role.ecr_logging resource
aws_iam_role.pull_request_rules_webhook resource
aws_iam_role.pull_through_cache resource
aws_iam_role_policy.ecr_logging resource
aws_iam_role_policy.pull_through_cache resource
aws_iam_role_policy_attachment.pull_request_rules_webhook resource
aws_lambda_function.pull_request_rules_webhook resource
aws_lambda_permission.pull_request_rules_webhook resource
aws_sns_topic.ecr_monitoring resource
aws_sns_topic.pull_request_rules resource
aws_sns_topic_subscription.ecr_monitoring_email resource
archive_file.pull_request_rules_webhook data source
aws_caller_identity.current data source
aws_region.current data source

Inputs

Name Description Type Default Required
create_sns_topic Whether to create an SNS topic for CloudWatch alarm notifications.
When enabled, creates a new SNS topic for sending alerts.
Only applicable when enable_monitoring is true.
Defaults to false.
bool false no
default_tags_cost_center Cost center tag value for financial tracking and allocation.
Should specify the cost center, budget code, or billing department.
Example: "engineering", "marketing", "cc-1234"
Set to null to disable automatic cost center tagging.
string null no
default_tags_environment Environment tag value to be automatically applied to all resources.
Common values: production, staging, development, test
Set to null to disable automatic environment tagging.
string null no
default_tags_owner Owner tag value to be automatically applied to all resources.
Should specify the team, department, or individual responsible for the resource.
Example: "platform-team", "data-engineering", "john.doe@company.com"
Set to null to disable automatic owner tagging.
string null no
default_tags_project Project tag value to be automatically applied to all resources.
Should specify the project or application name this resource belongs to.
Example: "web-app", "data-pipeline", "user-service"
Set to null to disable automatic project tagging.
string null no
default_tags_template Predefined default tag template to use for organizational compliance.

Available templates:
- "basic": Minimal set of organizational tags (CreatedBy, ManagedBy, Environment)
- "cost_allocation": Tags optimized for cost tracking and allocation
- "compliance": Tags required for security and compliance frameworks
- "sdlc": Tags for software development lifecycle management
- null: Use custom default_tags configuration

When using a template, it will override individual default_tags_* variables.
string null no
enable_default_tags Whether to enable automatic default tags for all resources.
When enabled, standard organizational tags will be automatically applied.
Defaults to true for better resource management and compliance.
bool true no
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
enable_monitoring Whether to enable CloudWatch monitoring and alerting for the ECR repository.
When enabled, creates metric alarms for storage usage, API calls, and security findings.
Defaults to false to maintain backward compatibility.
bool false no
enable_pull_request_rules Whether to enable pull request rules for enhanced governance and quality control.
Pull request rules provide approval workflows and validation requirements for container images,
similar to pull request approval processes for code repositories.
When enabled, additional governance controls will be applied to the ECR repository.
Defaults to false.
bool false no
enable_pull_through_cache Whether to create pull-through cache rules.
Pull-through cache rules allow you to cache images from upstream registries.
Defaults to false.
bool false no
enable_registry_scanning Whether to enable enhanced scanning for the ECR registry.
Enhanced scanning uses Amazon Inspector to provide detailed vulnerability assessments.
This is a registry-level configuration that affects all repositories in the account.
Defaults to false.
bool false no
enable_replication Whether to enable cross-region replication for the ECR registry.
When enabled, images will be automatically replicated to the specified regions.
Note: This is a registry-level configuration that affects all repositories in the account.
Defaults to false.
bool false no
enable_secret_scanning Whether to enable secret scanning as part of enhanced scanning.
This feature detects secrets like API keys, passwords, and tokens in container images.
When enabled, automatically sets the registry scan type to ENHANCED, overriding registry_scan_type.
Requires enable_registry_scanning to be true.
Defaults to false.
bool false no
enable_tag_normalization Whether to enable automatic tag normalization.
When enabled, normalizes tag keys to consistent casing and handles special characters.
Defaults to true for better tag consistency across resources.
bool true no
enable_tag_validation Whether to enable tag validation to ensure compliance with organizational standards.
When enabled, validates that required tags are present and follow naming conventions.
Defaults to false to maintain backward compatibility.
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({
scan_on_push = bool
})
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_additional_principals List of additional IAM principals (ARNs) to grant access to the KMS key.
These principals will be granted encrypt/decrypt permissions.
Only applicable when a new KMS key is created by this module.
Example: ["arn:aws:iam::123456789012:role/CrossAccountRole"]
list(string) [] no
kms_alias_name Custom alias name for the KMS key (without 'alias/' prefix).
If not provided, uses 'ecr/{repository_name}'.
Only applicable when a new KMS key is created by this module.
Example: "production/ecr/my-app"
string null no
kms_custom_policy Complete custom policy JSON for the KMS key.
If specified, this will override all other policy settings.
Only applicable when a new KMS key is created by this module.
Use with caution as this bypasses all built-in security policies.
string null no
kms_custom_policy_statements List of custom policy statements to add to the KMS key policy.
These statements will be added to the generated policy.
Only applicable when a new KMS key is created by this module.

Example:
[
{
sid = "AllowCloudTrailEncryption"
effect = "Allow"
principals = {
type = "Service"
identifiers = ["cloudtrail.amazonaws.com"]
}
actions = [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
]
resources = ["*"]
}
]
list(object({
sid = optional(string)
effect = string
principals = optional(object({
type = string
identifiers = list(string)
}))
actions = list(string)
resources = optional(list(string), ["*"])
conditions = optional(list(object({
test = string
variable = string
values = list(string)
})), [])
}))
[] no
kms_deletion_window_in_days Number of days to wait before actually deleting the KMS key (7-30 days).
Only applicable when a new KMS key is created by this module.
Defaults to 7 days for faster cleanup in development environments.
number 7 no
kms_enable_key_rotation Whether to enable automatic key rotation for the KMS key.
Only applicable when a new KMS key is created by this module.
Defaults to true for enhanced security.
bool true 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
kms_key_administrators List of IAM principals (ARNs) who can administer the KMS key.
These principals will have full administrative access to the key.
Only applicable when a new KMS key is created by this module.
Example: ["arn:aws:iam::123456789012:role/KMSAdminRole"]
list(string) [] no
kms_key_rotation_period Number of days between automatic key rotations (90-2555 days).
Only applicable when a new KMS key is created and key rotation is enabled.
If not specified, AWS uses the default rotation period.
number null no
kms_key_users List of IAM principals (ARNs) who can use the KMS key for cryptographic operations.
These principals will be granted encrypt/decrypt permissions.
Only applicable when a new KMS key is created by this module.
Example: ["arn:aws:iam::123456789012:role/ECRAccessRole"]
list(string) [] no
kms_multi_region Whether to create a multi-region KMS key.
Multi-region keys can be used in multiple AWS regions without cross-region calls.
Only applicable when a new KMS key is created by this module.
Defaults to false.
bool false no
kms_tags Additional tags specific to KMS resources.
These tags will be applied to the KMS key and alias in addition to the general tags.
Only applicable when a new KMS key is created by this module.
Example: { KeyType = "ECR-Encryption", Rotation = "Enabled" }
map(string) {} no
lifecycle_expire_tagged_after_days Number of days after which tagged images should be expired.
If specified, creates a lifecycle policy rule to delete tagged images older than N days.
This rule applies to ALL tagged images regardless of lifecycle_tag_prefixes_to_keep.
Use with caution as this may delete images you want to keep long-term.
Range: 1-3650 days (up to 10 years). Set to null to disable this rule.

Examples:
- 90: Delete tagged images after 90 days (production default)
- 30: Delete tagged images after 30 days (cost optimization)
- 365: Delete tagged images after 1 year (compliance)
number null no
lifecycle_expire_untagged_after_days Number of days after which untagged images should be expired.
If specified, creates a lifecycle policy rule to delete untagged images older than N days.
This rule applies to ALL untagged images regardless of lifecycle_tag_prefixes_to_keep.
Range: 1-3650 days (up to 10 years). Set to null to disable this rule.

Examples:
- 7: Delete untagged images after 7 days (development default)
- 14: Delete untagged images after 14 days (production default)
- 1: Delete untagged images daily (aggressive cleanup)
number null no
lifecycle_keep_latest_n_images Number of latest images to keep in the repository.
If specified, creates a lifecycle policy rule to keep only the N most recent images.
When used with lifecycle_tag_prefixes_to_keep, only applies to images with those tag prefixes.
Other images are not affected by this rule and may be managed by other rules.
Range: 1-10000 images. Set to null to disable this rule.

Examples:
- 30: Keep the 30 most recent images
- 100: Keep the 100 most recent images (production default)
- 10: Keep only 10 images (cost optimization)
number null no
lifecycle_policy JSON string representing the lifecycle policy.
If null (default), no lifecycle policy will be created.
Takes precedence over helper variables and templates if specified.
See: https://docs.aws.amazon.com/AmazonECR/latest/userguide/lifecycle_policy_examples.html
string null no
lifecycle_policy_template Predefined lifecycle policy template to use for common scenarios.
Templates provide tested configurations and best practices for different environments.

Available templates:

- "development": Optimized for dev workflows with frequent builds
* Keep 50 images
* Expire untagged after 7 days
* No tagged expiry (developers may need old builds)
* Tag prefixes: ["dev", "feature"]

- "production": Balanced retention for production stability
* Keep 100 images
* Expire untagged after 14 days
* Expire tagged after 90 days
* Tag prefixes: ["v", "release", "prod"]

- "cost_optimization": Aggressive cleanup to minimize storage costs
* Keep 10 images
* Expire untagged after 3 days
* Expire tagged after 30 days
* Tag prefixes: [] (applies to all images)

- "compliance": Long retention for audit and compliance
* Keep 200 images
* Expire untagged after 30 days
* Expire tagged after 365 days (1 year)
* Tag prefixes: ["v", "release", "audit"]

Set to null to use custom helper variables or manual lifecycle_policy.

Configuration precedence:
1. Manual lifecycle_policy (highest - overrides template)
2. Template lifecycle_policy_template (overrides helper variables)
3. Helper variables (lowest precedence)

Note: When using a template, all helper variables (lifecycle_keep_latest_n_images,
lifecycle_expire_untagged_after_days, etc.) will be ignored to prevent conflicts.
string null no
lifecycle_tag_prefixes_to_keep List of tag prefixes for images that should be managed by the keep-latest rule.
When used with lifecycle_keep_latest_n_images, applies the keep rule ONLY to images with these tag prefixes.
Images without these prefixes are not affected by the keep-latest rule.
The expire rules (untagged/tagged) still apply to ALL images regardless of this setting.

Common patterns:
- ["v"]: Apply keep rule to semantic versions (v1.0.0, v2.1.3, etc.)
- ["release-", "prod-"]: Apply to release and production builds
- ["main", "develop"]: Apply to main branch builds
- []: Apply keep rule to ALL images (empty list)

Constraints: Maximum 100 prefixes, each up to 255 characters.
Set to empty list to apply rules to all images.
list(string) [] 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
monitoring_threshold_api_calls API call volume threshold per minute to trigger CloudWatch alarm.
When API calls exceed this threshold, an alarm will be triggered.
Only applicable when enable_monitoring is true.
Defaults to 1000 calls per minute.
number 1000 no
monitoring_threshold_security_findings Security findings threshold to trigger CloudWatch alarm.
When security findings exceed this threshold, an alarm will be triggered.
Only applicable when enable_monitoring is true.
Defaults to 10 findings.
number 10 no
monitoring_threshold_storage Storage usage threshold in GB to trigger CloudWatch alarm.
When repository storage exceeds this threshold, an alarm will be triggered.
Only applicable when enable_monitoring is true.
Defaults to 10 GB.
number 10 no
name Name of the ECR repository. This name must be unique within the AWS account and region. string n/a yes
normalize_tag_values Whether to normalize tag values by trimming whitespace and handling special characters.
Applies common normalizations like removing leading/trailing spaces.
Defaults to true for cleaner tag values.
bool true no
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
pull_request_rules List of pull request rule configurations for enhanced governance.
Each rule defines governance controls for container image changes.

Rule structure:
- name: Unique identifier for the rule
- type: Type of rule (approval, security_scan, ci_integration)
- enabled: Whether the rule is active
- conditions: Conditions that trigger the rule
- actions: Actions to take when rule conditions are met

Example:
[
{
name = "require-security-approval"
type = "approval"
enabled = true
conditions = {
tag_patterns = ["prod-", "release-"]
severity_threshold = "HIGH"
}
actions = {
require_approval_count = 2
notification_topic_arn = "arn:aws:sns:region:account:topic"
}
}
]
list(object({
name = string
type = string
enabled = bool
conditions = optional(object({
tag_patterns = optional(list(string), [])
severity_threshold = optional(string, "MEDIUM")
require_scan_completion = optional(bool, true)
allowed_principals = optional(list(string), [])
}), {})
actions = optional(object({
require_approval_count = optional(number, 1)
notification_topic_arn = optional(string)
webhook_url = optional(string)
block_on_failure = optional(bool, true)
approval_timeout_hours = optional(number, 24)
}), {})
}))
[] no
pull_through_cache_rules List of pull-through cache rules to create.
Each rule should specify ecr_repository_prefix and upstream_registry_url.
Example: [{ ecr_repository_prefix = "docker-hub", upstream_registry_url = "registry-1.docker.io" }]
list(object({
ecr_repository_prefix = string
upstream_registry_url = string
credential_arn = optional(string)
}))
[] no
registry_scan_filters List of scan filters for filtering scan results when querying ECR scan findings.
These filters can be used by external tools or scripts to filter scan results by criteria such as vulnerability severity.
Each filter should specify name and values.
Example: [{ name = "PACKAGE_VULNERABILITY_SEVERITY", values = ["HIGH", "CRITICAL"] }]

Note: These filters are not applied at the registry scanning configuration level, but are made available
as outputs for use in querying and filtering scan results.
list(object({
name = string
values = list(string)
}))
[] no
registry_scan_type The type of scanning to configure for the registry.
- BASIC: Basic scanning for OS vulnerabilities
- ENHANCED: Enhanced scanning with Amazon Inspector integration
Only applicable when enable_registry_scanning is true.
string "ENHANCED" no
replication_regions List of AWS regions to replicate ECR images to.
Only applicable when enable_replication is true.
Example: ["us-west-2", "eu-west-1"]
list(string) [] no
required_tags List of tag keys that are required to be present.
Validation will fail if any of these tags are missing from the final tag set.
Example: ["Environment", "Owner", "Project"]
Empty list disables required tag validation.
list(string) [] 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
scan_repository_filters List of repository filters to apply for registry scanning.
Each filter specifies which repositories should be scanned.
Supports wildcard patterns using '' character.
If empty, defaults to scanning all repositories ("
").
Example: ["my-app-*", "important-service"]
list(string)
[
"*"
]
no
sns_topic_name Name of the SNS topic to create or use for alarm notifications.
If create_sns_topic is true, this will be the name of the created topic.
If create_sns_topic is false, this should be the name of an existing topic.
Only applicable when enable_monitoring is true.
Defaults to null.
string null no
sns_topic_subscribers List of email addresses to subscribe to the SNS topic for alarm notifications.
Each email address will receive notifications when alarms are triggered.
Only applicable when enable_monitoring and create_sns_topic are true.
Example: ["admin@company.com", "devops@company.com"]
list(string) [] no
tag_key_case Enforce consistent casing for tag keys.
- "PascalCase": Capitalize first letter of each word (Environment, CostCenter)
- "camelCase": First word lowercase, subsequent words capitalized (environment, costCenter)
- "snake_case": All lowercase with underscores (environment, cost_center)
- "kebab-case": All lowercase with hyphens (environment, cost-center)
- null: No case enforcement (preserve original casing)
string "PascalCase" 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({
delete = optional(string)
})
{} 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

Outputs

Name Description
applied_tags The final set of tags applied to all resources after normalization and default tag application
cloudwatch_alarms List of CloudWatch alarms created for ECR monitoring
cloudwatch_log_group_arn The ARN of the CloudWatch Log Group used for ECR logs (if logging is enabled)
kms_alias_arn The ARN of the KMS alias (if created by this module).
kms_configuration Complete KMS configuration information.
kms_key_arn The ARN of the KMS key used for repository encryption.
kms_key_id The globally unique identifier for the KMS key (if created by this module).
lifecycle_policy The lifecycle policy JSON applied to the repository (if any)
logging_role_arn The ARN of the IAM role used for ECR logging (if logging is enabled)
monitoring_status Status of CloudWatch monitoring configuration
pull_request_rules Information about pull request rules configuration
pull_through_cache_role_arn The ARN of the IAM role used for pull-through cache operations (if enabled)
pull_through_cache_rules List of pull-through cache rules (if enabled)
registry_id ID of the ECR registry
registry_scan_filters The configured scan filters for filtering scan results (e.g., by vulnerability severity)
registry_scanning_configuration_arn The ARN of the ECR registry scanning configuration (if enhanced scanning is enabled)
registry_scanning_status Status of ECR registry scanning configuration
replication_configuration_arn The ARN of the ECR replication configuration (if replication is enabled)
replication_regions List of regions where ECR images are replicated to (if replication is enabled)
replication_status Status of ECR replication configuration
repository_arn ARN of the ECR repository
repository_name Name of the ECR repository
repository_policy_exists Whether a repository policy exists for this ECR repository
repository_url URL of the ECR repository
security_status Comprehensive security status of the ECR configuration
sns_topic_arn ARN of the SNS topic used for ECR monitoring alerts (if created)
tag_compliance_status Tag compliance and validation status
tagging_strategy Summary of the tagging strategy configuration

About

Terraform module to create AWS ECR (Elastic Container Registry)

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 6