{"id":15681344,"url":"https://github.com/lgallard/terraform-aws-ecr","last_synced_at":"2025-12-27T19:05:17.763Z","repository":{"id":52638273,"uuid":"260306244","full_name":"lgallard/terraform-aws-ecr","owner":"lgallard","description":"Terraform module to create AWS ECR (Elastic Container Registry)  ","archived":false,"fork":false,"pushed_at":"2025-07-20T00:48:36.000Z","size":27394,"stargazers_count":11,"open_issues_count":0,"forks_count":20,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-07-20T05:24:57.988Z","etag":null,"topics":["aws","aws-ecr","aws-ecr-terraform","ecr-registry","terraform","terraform-module","terraform-modules"],"latest_commit_sha":null,"homepage":"","language":"HCL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lgallard.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-04-30T19:59:34.000Z","updated_at":"2025-07-20T00:48:39.000Z","dependencies_parsed_at":"2025-05-07T09:45:15.015Z","dependency_job_id":null,"html_url":"https://github.com/lgallard/terraform-aws-ecr","commit_stats":null,"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"purl":"pkg:github/lgallard/terraform-aws-ecr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lgallard%2Fterraform-aws-ecr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lgallard%2Fterraform-aws-ecr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lgallard%2Fterraform-aws-ecr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lgallard%2Fterraform-aws-ecr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lgallard","download_url":"https://codeload.github.com/lgallard/terraform-aws-ecr/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lgallard%2Fterraform-aws-ecr/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266303012,"owners_count":23908283,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-21T11:47:31.412Z","response_time":64,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws","aws-ecr","aws-ecr-terraform","ecr-registry","terraform","terraform-module","terraform-modules"],"created_at":"2024-10-03T16:52:57.703Z","updated_at":"2025-12-27T19:05:17.755Z","avatar_url":"https://github.com/lgallard.png","language":"HCL","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Terraform](https://lgallardo.com/images/terraform.jpg)\n# terraform-aws-ecr\n\nTerraform module to create [AWS ECR](https://aws.amazon.com/ecr/) (Elastic Container Registry) which is a fully-managed Docker container registry.\n\n[![Test](https://github.com/lgallard/terraform-aws-ecr/actions/workflows/test.yml/badge.svg)](https://github.com/lgallard/terraform-aws-ecr/actions/workflows/test.yml)\n\n## Architecture\n\nThe terraform-aws-ecr module enables several common architectures for container image management.\n\n### Basic ECR Architecture\n\n```\n┌──────────────┐     ┌───────────────────────┐     ┌─────────────────┐\n│              │     │                       │     │                 │\n│  Developer   │────▶│    AWS ECR Registry   │◀────│  CI/CD Pipeline │\n│  Workstation │     │                       │     │                 │\n│              │     └───────────────────────┘     └─────────────────┘\n└──────────────┘               │  ▲\n                               │  │\n                               ▼  │\n                        ┌─────────────────┐\n                        │                 │\n                        │   ECS / EKS     │\n                        │   Services      │\n                        │                 │\n                        └─────────────────┘\n```\n\nFor more detailed architecture diagrams including CI/CD integration, multi-region deployments, and security controls, see [docs/diagrams.md](docs/diagrams.md).\n\n## Submodules\n\nThis module is organized with specialized submodules for better maintainability and reusability:\n\n### KMS Module (`modules/kms/`)\nManages KMS encryption keys for ECR repositories with advanced key policies, rotation, and access control.\n\n### Pull-Through Cache Module (`modules/pull-through-cache/`)\nManages pull-through cache rules and associated IAM resources for upstream registry integration. Supports multiple upstream registries including Docker Hub, Quay.io, GitHub Container Registry, and Amazon ECR Public.\n\n**Key Benefits of Submodule Architecture:**\n- **Separation of Concerns** - Each submodule focuses on a specific functionality\n- **Optional Components** - Use only the features you need\n- **Easier Maintenance** - Isolated testing and development\n- **Reusability** - Submodules can be used independently in other projects\n\n## Versioning\n\nThis module follows [Semantic Versioning](https://semver.org/) principles. For full details on the versioning scheme, release process, and compatibility guarantees, see the following documentation:\n\n- [VERSIONING.md](VERSIONING.md) - Details on the semantic versioning scheme and release process\n- [VERSION_COMPATIBILITY.md](VERSION_COMPATIBILITY.md) - Terraform and AWS provider compatibility matrix\n\n## Usage\nYou 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).\n\nCheck the [examples](examples/) directory for examples including:\n- **Simple** - Basic ECR repository with minimal configuration\n- **Complete** - Full-featured ECR repository with all options\n- **Protected** - Repository with deletion protection\n- **With ECS Integration** - ECR configured for use with ECS\n- **Multi-Region** - Repository configured for cross-region replication (manual and automatic approaches)\n- **Replication** - ECR repository with built-in cross-region replication support\n- **Advanced Tagging** - Comprehensive tagging strategies with templates, validation, and normalization\n- **Enhanced Security** - Advanced security features with scanning and compliance\n- **Lifecycle Policies** - Image lifecycle management with predefined templates\n- **Pull Request Rules** - Governance and approval workflows for container images\n- **Enhanced KMS** - Advanced KMS key configuration with custom policies and access control\n- **Pull-Through Cache** - Cached access to upstream registries (Docker Hub, Quay, GitHub, etc.)\n\n### Simple example\nThis example creates an ECR registry using few parameters\n\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n\n  name         = \"ecr-repo-dev\"\n\n  # Tags\n  tags = {\n    Owner       = \"DevOps team\"\n    Environment = \"dev\"\n    Terraform   = true\n  }\n}\n```\n\n### Complete example with logging\nIn this example, the registry is defined in detail including CloudWatch logging:\n\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n\n  name                 = \"ecr-repo-dev\"\n  scan_on_push        = true\n  image_tag_mutability = \"IMMUTABLE\"\n  encryption_type     = \"KMS\"\n\n  # Enable CloudWatch logging\n  enable_logging     = true\n  log_retention_days = 14\n\n  // ...rest of configuration...\n}\n```\n\n### CloudWatch Logging\n\nThe module supports sending ECR API actions and image push/pull events to CloudWatch Logs. When enabled:\n\n- Creates a CloudWatch Log Group `/aws/ecr/{repository-name}`\n- Sets up necessary IAM roles and policies for ECR to write logs\n- Configurable log retention period (default: 30 days)\n\nTo enable logging:\n\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n\n  name           = \"ecr-repo-dev\"\n  enable_logging = true\n\n  # Optional: customize retention period (in days)\n  log_retention_days = 14  # Valid values: 0,1,3,5,7,14,30,60,90,120,150,180,365,400,545,731,1827,3653\n}\n```\n\nThe module outputs logging-related ARNs:\n- `cloudwatch_log_group_arn` - The ARN of the CloudWatch Log Group\n- `logging_role_arn` - The ARN of the IAM role used for logging\n\n### CloudWatch Monitoring and Alerting\n\nThe module provides comprehensive CloudWatch monitoring with metric alarms and SNS notifications for proactive repository management. When enabled:\n\n- Creates CloudWatch metric alarms for key ECR metrics\n- Monitors storage usage, API calls, and security findings\n- Sends notifications via SNS for alarm state changes\n- Provides visibility into repository usage and costs\n\n#### Basic Monitoring Setup\n\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n\n  name              = \"monitored-app\"\n  enable_monitoring = true\n\n  # Configure monitoring thresholds\n  monitoring_threshold_storage         = 10    # GB\n  monitoring_threshold_api_calls       = 1000  # calls per minute\n  monitoring_threshold_security_findings = 5   # findings count\n\n  # Create SNS topic for notifications\n  create_sns_topic      = true\n  sns_topic_name        = \"ecr-alerts\"\n  sns_topic_subscribers = [\"admin@company.com\", \"devops@company.com\"]\n}\n```\n\n#### Monitoring Features\n\n**CloudWatch Alarms Created:**\n- **Storage Usage**: Monitors repository size in GB\n- **API Call Volume**: Monitors API operations per minute\n- **Image Push Count**: Monitors push frequency (10 pushes per 5 minutes)\n- **Image Pull Count**: Monitors pull frequency (100 pulls per 5 minutes)\n- **Security Findings**: Monitors vulnerability count (requires enhanced scanning)\n\n**SNS Integration:**\n- Automatic SNS topic creation with configurable name\n- Email subscriptions for immediate notifications\n- Alarm and OK state notifications\n- Support for existing SNS topics\n\n#### Advanced Monitoring Configuration\n\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n\n  name = \"production-app\"\n\n  # Enable monitoring with custom thresholds\n  enable_monitoring                    = true\n  monitoring_threshold_storage         = 50    # 50 GB threshold\n  monitoring_threshold_api_calls       = 2000  # 2000 calls/minute\n  monitoring_threshold_security_findings = 0   # Zero tolerance for vulnerabilities\n\n  # Use existing SNS topic\n  create_sns_topic = false\n  sns_topic_name   = \"existing-alerts-topic\"\n\n  # Enable enhanced scanning for security monitoring\n  enable_registry_scanning = true\n  registry_scan_type      = \"ENHANCED\"\n  enable_secret_scanning  = true\n}\n```\n\n**Monitoring Outputs:**\n- `monitoring_status` - Complete monitoring configuration status\n- `sns_topic_arn` - ARN of the SNS topic (if created)\n- `cloudwatch_alarms` - Details of all created CloudWatch alarms\n\n**Cost Considerations:**\n- CloudWatch alarms: $0.10 per alarm per month\n- SNS notifications: First 1,000 emails free, then $0.75 per 1,000\n- No additional charges for metrics collection\n\n### ECR Account Settings\n\nConfigure ECR account-level settings, including scan type version and registry policy scope. AWS is migrating from CLAIR-based scanning to AWS Native scanning technology, with CLAIR being deprecated on February 2, 2026.\n\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n\n  name = \"my-app\"\n\n  # Enable account-level settings management\n  manage_account_setting    = true\n  basic_scan_type_version  = \"AWS_NATIVE\"  # Use new AWS Native scanning (recommended)\n  registry_policy_scope    = \"V2\"         # Enhanced policy scope (recommended)\n\n  tags = {\n    Environment = \"production\"\n    Migration   = \"aws-native-scanning\"\n  }\n}\n```\n\n**Account Settings Configuration:**\n- `manage_account_setting` - Whether to manage account-level ECR settings\n- `basic_scan_type_version` - Scanning technology: `AWS_NATIVE` (recommended) or `CLAIR` (deprecated)\n- `registry_policy_scope` - Registry policy scope: `V2` (recommended) supports all ECR actions, `V1` (legacy) supports limited actions\n\n**Account Settings Output:**\n- `account_setting` - Account setting configuration status and values for both settings\n\n**Important Notes:**\n- Requires AWS Provider \u003e= 5.81.0 for `aws_ecr_account_setting` resource\n- AWS Native scanning provides improved performance and accuracy\n- CLAIR-based scanning will be deprecated on February 2, 2026\n- Registry Policy Scope V2 is the default for new registries and provides granular control over all ECR actions\n- AWS does not recommend reverting from V2 to V1 policy scope\n\n### Cross-Region Replication\n\nThe 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.\n\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n\n  name = \"my-application\"\n\n  # Enable cross-region replication\n  enable_replication  = true\n  replication_regions = [\"us-west-2\", \"eu-west-1\", \"ap-southeast-1\"]\n\n  tags = {\n    Environment = \"production\"\n    Application = \"my-app\"\n  }\n}\n```\n\n**Key Benefits:**\n- **Disaster Recovery** - Images remain available if a region becomes unavailable\n- **Reduced Latency** - Pull images from the nearest region\n- **High Availability** - Improved resilience for multi-region workloads\n- **Automatic Sync** - No manual intervention required for replication\n\n**Important Notes:**\n- Replication is configured at the registry level (affects all repositories in the account)\n- Use immutable tags (`image_tag_mutability = \"IMMUTABLE\"`) for consistency across regions\n- Additional costs apply for cross-region data transfer and storage\n- Replication is one-way from the source region to destination regions\n\nThe module provides replication-related outputs:\n- `replication_status` - Overall replication configuration status\n- `replication_regions` - List of destination regions\n- `replication_configuration_arn` - ARN of the replication configuration\n\nFor more detailed examples, see the [replication example](examples/replication/) and [multi-region example](examples/multi-region/).\n\n### Complete example\nIn this example the register is defined in detailed.\n\n```\nmodule \"ecr\" {\n\n  source = \"lgallard/ecr/aws\"\n\n  name                 = \"ecr-repo-dev\"\n  scan_on_push         = true\n  image_tag_mutability = \"MUTABLE\"\n  prevent_destroy      = true  # Protect repository from accidental deletion\n\n\n  # Note that currently only one policy may be applied to a repository.\n  policy = \u003c\u003cEOF\n{\n    \"Version\": \"2008-10-17\",\n    \"Statement\": [\n        {\n            \"Sid\": \"repo policy\",\n            \"Effect\": \"Allow\",\n            \"Principal\": \"*\",\n            \"Action\": [\n                \"ecr:GetDownloadUrlForLayer\",\n                \"ecr:BatchGetImage\",\n                \"ecr:BatchCheckLayerAvailability\",\n                \"ecr:PutImage\",\n                \"ecr:InitiateLayerUpload\",\n                \"ecr:UploadLayerPart\",\n                \"ecr:CompleteLayerUpload\",\n                \"ecr:DescribeRepositories\",\n                \"ecr:GetRepositoryPolicy\",\n                \"ecr:ListImages\",\n                \"ecr:DeleteRepository\",\n                \"ecr:BatchDeleteImage\",\n                \"ecr:SetRepositoryPolicy\",\n                \"ecr:DeleteRepositoryPolicy\"\n            ]\n        }\n    ]\n}\nEOF\n\n  # Only one lifecycle policy can be used per repository.\n  # To apply multiple rules, combined them in one policy JSON.\n  lifecycle_policy = \u003c\u003cEOF\n{\n    \"rules\": [\n        {\n            \"rulePriority\": 1,\n            \"description\": \"Expire untagged images older than 14 days\",\n            \"selection\": {\n                \"tagStatus\": \"untagged\",\n                \"countType\": \"sinceImagePushed\",\n                \"countUnit\": \"days\",\n                \"countNumber\": 14\n            },\n            \"action\": {\n                \"type\": \"expire\"\n            }\n        },\n        {\n            \"rulePriority\": 2,\n            \"description\": \"Keep last 30 dev images\",\n            \"selection\": {\n                \"tagStatus\": \"tagged\",\n                \"tagPrefixList\": [\"dev\"],\n                \"countType\": \"imageCountMoreThan\",\n                \"countNumber\": 30\n            },\n            \"action\": {\n                \"type\": \"expire\"\n            }\n        }\n    ]\n}\nEOF\n\n  # Tags\n  tags = {\n    Owner       = \"DevOps team\"\n    Environment = \"dev\"\n    Terraform   = true\n  }\n\n}\n\n### Deleting ECR Repositories Protected with prevent_destroy\n\nBy 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:\n\n1. Set the `prevent_destroy` parameter to `false` for the module:\n\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n\n  name            = \"ecr-repo-dev\"\n  prevent_destroy = false  # Allow repository to be destroyed\n}\n```\n\n2. Apply the configuration change:\n\n```bash\nterraform apply\n```\n\n3. After successful apply, run destroy as normal:\n\n```bash\nterraform destroy\n```\n\nThis approach allows protecting repositories by default while providing a controlled way to remove them when needed.\n\n## Advanced Tagging Configuration\n\nThe 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.\n\n### Key Features\n\n- **Default Tag Templates**: Predefined organizational tag standards for common scenarios\n- **Tag Validation**: Ensure required tags are present and follow naming conventions\n- **Tag Normalization**: Consistent casing and format across all resources\n- **Cost Allocation**: Specialized tags for financial tracking and reporting\n- **Compliance**: Tags required for security and regulatory frameworks\n- **Backward Compatible**: All advanced features are opt-in\n\n### Default Tag Templates\n\nThe module provides four predefined templates for common organizational needs:\n\n#### Basic Template\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n\n  name = \"my-app\"\n\n  # Enable basic organizational tagging\n  enable_default_tags = true\n  default_tags_template = \"basic\"\n  default_tags_environment = \"production\"\n  default_tags_owner = \"platform-team\"\n  default_tags_project = \"user-service\"\n}\n```\n\n**Applied tags**: `CreatedBy`, `ManagedBy`, `Environment`, `Owner`, `Project`\n\n#### Cost Allocation Template\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n\n  name = \"my-app\"\n\n  # Enable cost allocation tagging\n  enable_default_tags = true\n  default_tags_template = \"cost_allocation\"\n  default_tags_environment = \"production\"\n  default_tags_owner = \"platform-team\"\n  default_tags_project = \"user-service\"\n  default_tags_cost_center = \"engineering-cc-001\"\n}\n```\n\n**Applied tags**: All basic tags plus `CostCenter`, `BillingProject`, `ResourceType`, `Service`, `Billable`\n\n#### Compliance Template\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n\n  name = \"my-app\"\n\n  # Enable compliance tagging\n  enable_default_tags = true\n  default_tags_template = \"compliance\"\n  default_tags_environment = \"production\"\n  default_tags_owner = \"security-team\"\n  default_tags_project = \"payment-service\"\n  default_tags_cost_center = \"security-cc-002\"\n}\n```\n\n**Applied tags**: All cost allocation tags plus `DataClass`, `Compliance`, `BackupRequired`, `MonitoringLevel`, `SecurityReview`\n\n#### SDLC Template\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n\n  name = \"my-app\"\n\n  # Enable SDLC tagging\n  enable_default_tags = true\n  default_tags_template = \"sdlc\"\n  default_tags_environment = \"development\"\n  default_tags_owner = \"dev-team\"\n  default_tags_project = \"mobile-app\"\n}\n```\n\n**Applied tags**: Basic organizational tags plus `Application`, `Version`, `DeploymentStage`, `LifecycleStage`, `MaintenanceWindow`\n\n### Tag Validation and Compliance\n\nEnsure organizational compliance by validating that required tags are present:\n\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n\n  name = \"my-app\"\n\n  # Enable tag validation\n  enable_tag_validation = true\n  required_tags = [\n    \"Environment\",\n    \"Owner\",\n    \"Project\",\n    \"CostCenter\"\n  ]\n\n  # This will fail if any required tags are missing\n  default_tags_environment = \"production\"\n  default_tags_owner = \"platform-team\"\n  default_tags_project = \"user-service\"\n  default_tags_cost_center = \"eng-001\"\n}\n```\n\n### Tag Normalization\n\nEnsure consistent tag formatting across all resources:\n\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n\n  name = \"my-app\"\n\n  # Enable tag normalization\n  enable_tag_normalization = true\n  tag_key_case = \"PascalCase\"  # Options: PascalCase, camelCase, snake_case, kebab-case\n  normalize_tag_values = true\n\n  tags = {\n    \"cost-center\" = \"  engineering-001  \"  # Will be normalized to \"CostCenter\" = \"engineering-001\"\n    \"data_class\"  = \"internal\"             # Will be normalized to \"DataClass\" = \"internal\"\n  }\n}\n```\n\n### Custom Default Tags\n\nConfigure custom default tags without using templates:\n\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n\n  name = \"my-app\"\n\n  # Enable custom default tags\n  enable_default_tags = true\n  default_tags_template = null  # Use custom configuration\n  default_tags_environment = \"staging\"\n  default_tags_owner = \"full-stack-team\"\n  default_tags_project = \"analytics-service\"\n  default_tags_cost_center = \"data-cc-003\"\n\n  # Additional custom tags\n  tags = {\n    team_slack = \"analytics-team\"\n    oncall_rotation = \"analytics-oncall\"\n  }\n}\n```\n\n### Legacy Compatibility\n\nDisable advanced tagging features for backward compatibility:\n\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n\n  name = \"my-app\"\n\n  # Disable advanced tagging features\n  enable_default_tags = false\n  enable_tag_validation = false\n  enable_tag_normalization = false\n\n  # Traditional manual tagging\n  tags = {\n    Environment = \"production\"\n    Owner = \"legacy-team\"\n    ManagedBy = \"Terraform\"\n  }\n}\n```\n\n### Tagging Best Practices\n\n1. **Start with Templates**: Use predefined templates that match your organizational needs\n2. **Enable Validation**: Enforce required tags for compliance and consistency\n3. **Normalize Consistently**: Choose a casing strategy and apply it across all resources\n4. **Plan for Cost Allocation**: Include cost center and billing project tags early\n5. **Consider Compliance**: Include data classification and security review tags for regulated environments\n6. **Monitor Tag Drift**: Use the validation and normalization features to maintain consistency\n\nFor comprehensive examples demonstrating different tagging strategies, see [examples/advanced-tagging](examples/advanced-tagging/).\n\n## Enhanced Lifecycle Policy Configuration\n\nThe 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.\n\n### Configuration Methods\n\nThere are three ways to configure lifecycle policies, listed in order of precedence:\n\n1. **Manual JSON Policy** (`lifecycle_policy`) - Highest precedence, full control\n2. **Predefined Templates** (`lifecycle_policy_template`) - Medium precedence, common patterns\n3. **Helper Variables** - Lowest precedence, individual settings\n\n**🚨 Important: Configuration Precedence Rules**\n\n- When `lifecycle_policy` is specified, ALL template and helper variable settings are ignored\n- When `lifecycle_policy_template` is specified, ALL helper variable settings are ignored\n- Only helper variables are used when neither `lifecycle_policy` nor `lifecycle_policy_template` are specified\n- **AWS ECR Limitations**: Maximum 25 rules per policy, rule priorities must be unique (1-999), up to 100 tag prefixes per rule\n\nFor complete AWS ECR lifecycle policy documentation and examples, see [AWS ECR Lifecycle Policy Documentation](https://docs.aws.amazon.com/AmazonECR/latest/userguide/lifecycle_policy_examples.html).\n\n### Helper Variables\n\nConfigure lifecycle policies using individual helper variables for maximum flexibility:\n\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n\n  name = \"my-app\"\n\n  # Keep only the latest 30 images (range: 1-10000)\n  lifecycle_keep_latest_n_images = 30\n\n  # Delete untagged images after 7 days (range: 1-3650)\n  lifecycle_expire_untagged_after_days = 7\n\n  # Delete tagged images after 90 days (range: 1-3650)\n  lifecycle_expire_tagged_after_days = 90\n\n  # Apply rules only to specific tag prefixes (optional)\n  lifecycle_tag_prefixes_to_keep = [\"v\", \"release\", \"prod\"]\n}\n```\n\n#### Helper Variable Details\n\n- **`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.\n- **`lifecycle_expire_untagged_after_days`**: Automatically deletes untagged images after the specified number of days.\n- **`lifecycle_expire_tagged_after_days`**: Automatically deletes tagged images after the specified number of days.\n- **`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.\n\n### Predefined Templates\n\nUse predefined templates for common scenarios. Each template encapsulates best practices for specific environments:\n\n```hcl\n# Development environment - optimized for frequent builds and testing\nmodule \"ecr_dev\" {\n  source = \"lgallard/ecr/aws\"\n  name   = \"dev-app\"\n  lifecycle_policy_template = \"development\"\n}\n\n# Production environment - balanced retention and stability\nmodule \"ecr_prod\" {\n  source = \"lgallard/ecr/aws\"\n  name   = \"prod-app\"\n  lifecycle_policy_template = \"production\"\n}\n\n# Cost optimization - minimal storage costs\nmodule \"ecr_cost\" {\n  source = \"lgallard/ecr/aws\"\n  name   = \"test-app\"\n  lifecycle_policy_template = \"cost_optimization\"\n}\n\n# Compliance - long retention for audit requirements\nmodule \"ecr_compliance\" {\n  source = \"lgallard/ecr/aws\"\n  name   = \"audit-app\"\n  lifecycle_policy_template = \"compliance\"\n}\n```\n\n### Available Templates\n\n| Template | Keep Images | Untagged Expiry | Tagged Expiry | Tag Prefixes | Use Case |\n|----------|-------------|-----------------|---------------|--------------|----------|\n| `development` | 50 | 7 days | - | `[\"dev\", \"feature\"]` | Development workflows with frequent builds |\n| `production` | 100 | 14 days | 90 days | `[\"v\", \"release\", \"prod\"]` | Production environments requiring stability |\n| `cost_optimization` | 10 | 3 days | 30 days | `[]` (all images) | Test environments with aggressive cost optimization |\n| `compliance` | 200 | 30 days | 365 days | `[\"v\", \"release\", \"audit\"]` | Compliance environments requiring long retention |\n\n### Configuration Precedence and Examples\n\nThe module follows a clear precedence order when multiple configuration methods are specified:\n\n#### 1. Manual Policy (Highest Precedence)\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n  name   = \"custom-app\"\n\n  # This manual policy takes precedence over everything else\n  lifecycle_policy = jsonencode({\n    rules = [\n      {\n        rulePriority = 1\n        description  = \"Custom rule\"\n        selection = {\n          tagStatus   = \"untagged\"\n          countType   = \"sinceImagePushed\"\n          countUnit   = \"days\"\n          countNumber = 5\n        }\n        action = { type = \"expire\" }\n      }\n    ]\n  })\n\n  # These are ignored when lifecycle_policy is specified\n  lifecycle_policy_template = \"production\"\n  lifecycle_keep_latest_n_images = 30\n}\n```\n\n#### 2. Template Configuration\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n  name   = \"template-app\"\n\n  # Template takes precedence over helper variables\n  lifecycle_policy_template = \"production\"\n\n  # These helper variables are ignored when template is specified\n  lifecycle_keep_latest_n_images = 30\n  lifecycle_expire_untagged_after_days = 5\n}\n```\n\n#### 3. Helper Variables (Lowest Precedence)\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n  name   = \"helper-app\"\n\n  # Only helper variables specified - these will be used\n  lifecycle_keep_latest_n_images = 30\n  lifecycle_expire_untagged_after_days = 5\n  lifecycle_expire_tagged_after_days = 60\n  lifecycle_tag_prefixes_to_keep = [\"v\", \"stable\"]\n}\n```\n\n### Advanced Usage Patterns\n\n#### Environment-Specific Configurations\n```hcl\n# Development with custom retention\nmodule \"ecr_dev_custom\" {\n  source = \"lgallard/ecr/aws\"\n  name   = \"dev-custom-app\"\n\n  lifecycle_keep_latest_n_images      = 20    # Fewer than default dev template\n  lifecycle_expire_untagged_after_days = 3    # Faster cleanup than default\n  lifecycle_tag_prefixes_to_keep      = [\"dev\", \"feat\", \"fix\"]\n}\n\n# Production with extended retention for releases\nmodule \"ecr_prod_extended\" {\n  source = \"lgallard/ecr/aws\"\n  name   = \"prod-extended-app\"\n\n  lifecycle_keep_latest_n_images      = 150   # More than default prod template\n  lifecycle_expire_untagged_after_days = 21   # Longer than default\n  lifecycle_expire_tagged_after_days   = 180  # Extended retention\n  lifecycle_tag_prefixes_to_keep      = [\"v\", \"release\", \"hotfix\"]\n}\n```\n\n#### Cost-Conscious Multi-Environment Setup\n```hcl\n# Aggressive cleanup for test environments\nmodule \"ecr_test\" {\n  source = \"lgallard/ecr/aws\"\n  name   = \"test-app\"\n\n  lifecycle_keep_latest_n_images      = 5     # Minimal retention\n  lifecycle_expire_untagged_after_days = 1    # Daily cleanup\n  lifecycle_expire_tagged_after_days   = 7    # Weekly cleanup\n}\n\n# Balanced approach for staging\nmodule \"ecr_staging\" {\n  source = \"lgallard/ecr/aws\"\n  name   = \"staging-app\"\n\n  lifecycle_policy_template = \"development\"  # Use template for consistency\n}\n```\n\n### Best Practices\n\n#### Template Selection Guidelines\n\n- **Use `development`** for: CI/CD environments, feature branch testing, development workflows\n- **Use `production`** for: Live applications, staging environments, release candidates\n- **Use `cost_optimization`** for: Temporary test environments, proof-of-concepts, experimental workloads\n- **Use `compliance`** for: Regulated environments, audit trails, long-term archival needs\n\n#### Custom Configuration Guidelines\n\n1. **Start with a template** that's closest to your needs, then use helper variables if needed\n2. **Use tag prefixes** to apply different retention rules to different image types\n3. **Monitor storage costs** and adjust retention periods based on usage patterns\n4. **Consider compliance requirements** when setting retention periods\n\n#### Tag Prefix Strategy Examples\n\n```hcl\n# Strategy 1: Environment-based prefixes\nlifecycle_tag_prefixes_to_keep = [\"prod\", \"staging\", \"release\"]\n\n# Strategy 2: Version-based prefixes\nlifecycle_tag_prefixes_to_keep = [\"v\", \"release-\"]\n\n# Strategy 3: Branch-based prefixes\nlifecycle_tag_prefixes_to_keep = [\"main\", \"develop\", \"hotfix\"]\n\n# Strategy 4: Mixed strategy\nlifecycle_tag_prefixes_to_keep = [\"v\", \"release\", \"prod\", \"stable\"]\n```\n\n### Validation and Constraints\n\nThe module includes built-in validation to prevent common configuration errors:\n\n- **Image count**: Must be between 1 and 10,000\n- **Days**: Must be between 1 and 3,650 (10 years)\n- **Tag prefixes**: Maximum 100 prefixes, each up to 255 characters\n- **Template names**: Must be one of the four predefined templates\n\n### Generated Policy Structure\n\nWhen using helper variables or templates, the module generates policies with this structure:\n\n1. **Rule 1**: Expire untagged images (if `expire_untagged_after_days` specified)\n2. **Rule 2**: Keep latest N images (if `keep_latest_n_images` specified)\n3. **Rule 3**: Expire tagged images (if `expire_tagged_after_days` specified)\n\n### Migration from Manual Policies\n\nTo migrate from existing manual `lifecycle_policy` to the enhanced configuration:\n\n#### Migration Steps\n\n1. **Analyze Your Current Policy**: Review your existing JSON lifecycle policy to understand the rules.\n\n   ```bash\n   # Get current policy from Terraform state\n   terraform show | grep -A 20 lifecycle_policy\n   ```\n\n2. **Choose Migration Path**:\n   - Use a **template** if your policy matches common patterns\n   - Use **helper variables** for custom configurations\n   - Keep **manual policy** for complex, non-standard rules\n\n3. **Template Migration Example**:\n   ```hcl\n   # Before (manual policy)\n   module \"ecr\" {\n     source = \"lgallard/ecr/aws\"\n     name   = \"my-app\"\n\n     lifecycle_policy = jsonencode({\n       rules = [\n         {\n           rulePriority = 1\n           description  = \"Keep last 100 images\"\n           selection = {\n             tagStatus   = \"any\"\n             countType   = \"imageCountMoreThan\"\n             countNumber = 100\n           }\n           action = { type = \"expire\" }\n         },\n         {\n           rulePriority = 2\n           description  = \"Expire untagged after 14 days\"\n           selection = {\n             tagStatus   = \"untagged\"\n             countType   = \"sinceImagePushed\"\n             countUnit   = \"days\"\n             countNumber = 14\n           }\n           action = { type = \"expire\" }\n         }\n       ]\n     })\n   }\n\n   # After (using production template)\n   module \"ecr\" {\n     source = \"lgallard/ecr/aws\"\n     name   = \"my-app\"\n\n     lifecycle_policy_template = \"production\"  # Matches the pattern above\n   }\n   ```\n\n4. **Helper Variables Migration Example**:\n   ```hcl\n   # Before (manual policy)\n   lifecycle_policy = jsonencode({\n     rules = [\n       {\n         rulePriority = 1\n         description  = \"Keep 30 images with specific prefixes\"\n         selection = {\n           tagStatus     = \"tagged\"\n           tagPrefixList = [\"v\", \"release\"]\n           countType     = \"imageCountMoreThan\"\n           countNumber   = 30\n         }\n         action = { type = \"expire\" }\n       }\n     ]\n   })\n\n   # After (using helper variables)\n   lifecycle_keep_latest_n_images = 30\n   lifecycle_tag_prefixes_to_keep = [\"v\", \"release\"]\n   ```\n\n5. **Test Migration**: Apply changes in a non-production environment first:\n   ```bash\n   terraform plan  # Review the changes carefully\n   terraform apply # Apply when ready\n   ```\n\n6. **Verify Results**: Check that lifecycle policies are correctly applied:\n   ```bash\n   aws ecr describe-lifecycle-policy --repository-name my-app\n   ```\n\n#### Migration Validation\n\n- **Before migration**: Document current retention behavior\n- **After migration**: Verify identical behavior with new configuration\n- **Monitor**: Watch for unexpected image deletions in the first week\n\nFor more complex migration scenarios, see the [migration examples](examples/lifecycle-policies/migration-examples.md).\n\nSee the [lifecycle policies example](examples/lifecycle-policies/) for comprehensive usage examples and the [troubleshooting guide](docs/troubleshooting.md) for common issues.\n\n## Pull Request Rules Configuration\n\nThe 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.\n\n### Overview\n\nPull request rules provide:\n- **Approval workflows**: Require manual approval for production images\n- **Security validation**: Automatic checks for vulnerabilities and compliance\n- **CI/CD integration**: Webhook notifications for external systems\n- **Governance controls**: Policy-based restrictions on image usage\n- **Audit trails**: Complete tracking of image approval workflows\n\n### Basic Configuration\n\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n  name   = \"production-app\"\n\n  # Enable pull request rules\n  enable_pull_request_rules = true\n\n  # Configure approval requirements\n  pull_request_rules = [\n    {\n      name    = \"production-approval\"\n      type    = \"approval\"\n      enabled = true\n      conditions = {\n        tag_patterns       = [\"prod-*\", \"release-*\"]\n        severity_threshold = \"HIGH\"\n      }\n      actions = {\n        require_approval_count = 2\n        notification_topic_arn = \"arn:aws:sns:region:account:topic\"\n      }\n    }\n  ]\n}\n```\n\n### Rule Types\n\n#### 1. Approval Rules (`type = \"approval\"`)\nRequire manual approval before images can be used in production:\n\n```hcl\n{\n  name    = \"security-approval\"\n  type    = \"approval\"\n  enabled = true\n  conditions = {\n    tag_patterns            = [\"prod-*\", \"release-*\"]\n    severity_threshold      = \"HIGH\"\n    require_scan_completion = true\n    allowed_principals      = [\"arn:aws:iam::account:role/SecurityTeam\"]\n  }\n  actions = {\n    require_approval_count  = 2\n    notification_topic_arn  = \"arn:aws:sns:region:account:topic\"\n    block_on_failure       = true\n    approval_timeout_hours = 24\n  }\n}\n```\n\n#### 2. Security Scan Rules (`type = \"security_scan\"`)\nAutomatically validate images against security criteria:\n\n```hcl\n{\n  name    = \"vulnerability-check\"\n  type    = \"security_scan\"\n  enabled = true\n  conditions = {\n    severity_threshold      = \"MEDIUM\"\n    require_scan_completion = true\n  }\n  actions = {\n    notification_topic_arn = \"arn:aws:sns:region:account:topic\"\n    block_on_failure       = true\n  }\n}\n```\n\n#### 3. CI Integration Rules (`type = \"ci_integration\"`)\nIntegrate with CI/CD systems through webhooks:\n\n```hcl\n{\n  name    = \"ci-validation\"\n  type    = \"ci_integration\"\n  enabled = true\n  conditions = {\n    tag_patterns = [\"feature-*\", \"dev-*\"]\n  }\n  actions = {\n    webhook_url      = \"https://ci.company.com/webhook/ecr\"\n    block_on_failure = false\n  }\n}\n```\n\n### Configuration Options\n\n#### Conditions\n- `tag_patterns`: List of tag patterns that trigger the rule\n- `severity_threshold`: Minimum vulnerability severity (`LOW`, `MEDIUM`, `HIGH`, `CRITICAL`)\n- `require_scan_completion`: Whether to require completed security scans\n- `allowed_principals`: List of IAM principals allowed to interact with approved images\n\n#### Actions\n- `require_approval_count`: Number of approvals required (1-10)\n- `notification_topic_arn`: SNS topic for notifications\n- `webhook_url`: Webhook URL for external integrations\n- `block_on_failure`: Whether to block operations on rule failure\n- `approval_timeout_hours`: Hours to wait for approval (1-168)\n\n### Approval Workflow\n\n1. **Image Push**: Developer pushes image to ECR repository\n2. **Rule Evaluation**: Pull request rules evaluate the image against configured criteria\n3. **Notification**: If approval is required, notifications are sent to configured channels\n4. **Security Scan**: Automatic vulnerability scanning is performed\n5. **Manual Review**: Security team reviews scan results and compliance\n6. **Approval**: If acceptable, image is tagged with approval status\n7. **Deployment**: Approved images can be deployed to production\n\n### Example: Complete Governance Setup\n\n```hcl\nmodule \"ecr_governance\" {\n  source = \"lgallard/ecr/aws\"\n  name   = \"critical-application\"\n\n  enable_pull_request_rules = true\n  pull_request_rules = [\n    # Production approval workflow\n    {\n      name    = \"production-security-approval\"\n      type    = \"approval\"\n      enabled = true\n      conditions = {\n        tag_patterns            = [\"prod-*\", \"release-*\"]\n        severity_threshold      = \"HIGH\"\n        require_scan_completion = true\n        allowed_principals = [\n          \"arn:aws:iam::123456789012:role/SecurityTeam\",\n          \"arn:aws:iam::123456789012:role/ReleaseManagers\"\n        ]\n      }\n      actions = {\n        require_approval_count  = 3\n        notification_topic_arn  = aws_sns_topic.security_alerts.arn\n        block_on_failure       = true\n        approval_timeout_hours = 48\n      }\n    },\n    # Automatic security validation\n    {\n      name    = \"security-scan-gate\"\n      type    = \"security_scan\"\n      enabled = true\n      conditions = {\n        tag_patterns            = [\"*\"]\n        severity_threshold      = \"MEDIUM\"\n        require_scan_completion = true\n      }\n      actions = {\n        notification_topic_arn = aws_sns_topic.security_alerts.arn\n        block_on_failure       = true\n      }\n    },\n    # CI/CD integration\n    {\n      name    = \"ci-pipeline-integration\"\n      type    = \"ci_integration\"\n      enabled = true\n      conditions = {\n        tag_patterns = [\"feature-*\", \"dev-*\", \"staging-*\"]\n      }\n      actions = {\n        webhook_url      = \"https://ci.company.com/webhook/ecr-validation\"\n        block_on_failure = false\n      }\n    }\n  ]\n\n  # Enhanced security scanning\n  enable_registry_scanning = true\n  registry_scan_type      = \"ENHANCED\"\n  enable_secret_scanning  = true\n}\n```\n\n### Approval Commands\n\nAfter implementing pull request rules, use these commands to manage approvals:\n\n```bash\n# Check image scan results\naws ecr describe-image-scan-findings \\\n  --repository-name production-app \\\n  --image-id imageTag=prod-v1.0.0\n\n# Approve image for production use\naws ecr put-image \\\n  --repository-name production-app \\\n  --image-tag prod-v1.0.0 \\\n  --tag-list Key=ApprovalStatus,Value=approved\n\n# List images with approval status\naws ecr describe-images \\\n  --repository-name production-app \\\n  --query 'imageDetails[*].[imageTags[0],imageTagMutability,imageScanFindingsSummary.findings]'\n```\n\n### Best Practices\n\n1. **Layered Approach**: Use multiple rule types for comprehensive governance\n2. **Graduated Enforcement**: Strict rules for production, flexible for development\n3. **Clear Workflows**: Document approval processes and responsibilities\n4. **Monitoring**: Set up CloudWatch alarms for rule violations\n5. **Regular Reviews**: Periodically review and update rule configurations\n6. **Testing**: Test rule configurations in non-production environments first\n\n### Integration with CI/CD\n\nPull request rules integrate seamlessly with CI/CD pipelines:\n\n```yaml\n# GitHub Actions example\n- name: Check ECR approval status\n  run: |\n    STATUS=$(aws ecr describe-images \\\n      --repository-name $REPO_NAME \\\n      --image-ids imageTag=$IMAGE_TAG \\\n      --query 'imageDetails[0].imageTags' \\\n      --output text | grep -o 'ApprovalStatus.*approved' || echo \"not-approved\")\n\n    if [[ \"$STATUS\" != *\"approved\"* ]]; then\n      echo \"Image not approved for deployment\"\n      exit 1\n    fi\n```\n\nSee the [pull request rules example](examples/pull-request-rules/) for a complete implementation guide.\n\n## Security Best Practices\n\nHere are key security best practices for your ECR repositories:\n\n1. **Enable Immutable Tags**: Prevent tags from being overwritten to ensure image integrity.\n   ```hcl\n   image_tag_mutability = \"IMMUTABLE\"\n   ```\n\n2. **Enable Enhanced Scanning**: Use AWS Inspector for comprehensive vulnerability assessment.\n   ```hcl\n   enable_registry_scanning = true\n   registry_scan_type      = \"ENHANCED\"\n   enable_secret_scanning  = true\n   ```\n\n3. **Configure Pull-Through Cache**: Reduce external dependencies and improve performance.\n   ```hcl\n   enable_pull_through_cache = true\n   pull_through_cache_rules = [\n     {\n       ecr_repository_prefix = \"docker-hub\"\n       upstream_registry_url = \"registry-1.docker.io\"\n     }\n   ]\n   ```\n\n4. **Enable Basic Scanning**: Automatically scan images for security vulnerabilities (if not using enhanced).\n   ```hcl\n   scan_on_push = true\n   ```\n\n5. **Implement Least Privilege Access**: Use repository policies that grant only necessary permissions.\n\n6. **Enable KMS Encryption**: Use AWS KMS for enhanced encryption of container images.\n   ```hcl\n   encryption_type = \"KMS\"\n   ```\n\n   For advanced KMS configuration options, see the [Enhanced KMS Configuration](#enhanced-kms-configuration) section below.\n\n7. **Configure Lifecycle Policies**: Automatically clean up old or unused images.\n\nFor a comprehensive guide with detailed examples, see [docs/security-best-practices.md](docs/security-best-practices.md).\n\n## Troubleshooting\n\nCommon issues and solutions when working with ECR repositories:\n\n| Issue | Solution |\n|-------|----------|\n| Authentication failures | Re-authenticate with `aws ecr get-login-password` |\n| Permission denied errors | Check IAM policies and repository policies |\n| Cannot delete repository | Check for `prevent_destroy` setting and set to `false` |\n| Image scan failures | Verify supported image format and AWS region |\n| Lifecycle policy not working | Check rule syntax and priorities |\n\nFor detailed troubleshooting steps, see [docs/troubleshooting.md](docs/troubleshooting.md).\n\n## Enhanced KMS Configuration\n\nThis 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.\n\n### Basic KMS Configuration\n\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n\n  name            = \"my-encrypted-repo\"\n  encryption_type = \"KMS\"\n\n  # Enhanced KMS options\n  kms_deletion_window_in_days = 14\n  kms_enable_key_rotation     = true\n  kms_key_rotation_period     = 90\n\n  tags = {\n    Environment = \"production\"\n  }\n}\n```\n\n### Advanced KMS Configuration\n\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n\n  name            = \"production-app\"\n  encryption_type = \"KMS\"\n\n  # Advanced KMS configuration\n  kms_deletion_window_in_days = 30\n  kms_enable_key_rotation     = true\n  kms_key_rotation_period     = 180\n  kms_multi_region           = true\n\n  # Access control\n  kms_key_administrators = [\n    \"arn:aws:iam::123456789012:role/KMSAdminRole\"\n  ]\n\n  kms_key_users = [\n    \"arn:aws:iam::123456789012:role/ECRAccessRole\",\n    \"arn:aws:iam::123456789012:role/CI-CD-Role\"\n  ]\n\n  # Custom alias\n  kms_alias_name = \"production/ecr/my-app\"\n\n  # KMS-specific tags\n  kms_tags = {\n    KeyType      = \"ECR-Encryption\"\n    Rotation     = \"180-days\"\n    MultiRegion  = \"true\"\n  }\n}\n```\n\n### Custom KMS Policy\n\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n\n  name            = \"custom-policy-repo\"\n  encryption_type = \"KMS\"\n\n  # Custom policy statements\n  kms_custom_policy_statements = [\n    {\n      sid    = \"AllowCrossAccountAccess\"\n      effect = \"Allow\"\n      principals = {\n        type        = \"AWS\"\n        identifiers = [\"arn:aws:iam::TRUSTED-ACCOUNT:root\"]\n      }\n      actions = [\n        \"kms:Encrypt\",\n        \"kms:Decrypt\",\n        \"kms:ReEncrypt*\",\n        \"kms:GenerateDataKey*\",\n        \"kms:DescribeKey\"\n      ]\n      conditions = [\n        {\n          test     = \"StringEquals\"\n          variable = \"kms:ViaService\"\n          values   = [\"ecr.us-east-1.amazonaws.com\"]\n        }\n      ]\n    }\n  ]\n}\n```\n\n### KMS Configuration Options\n\n| Feature | Variable | Description |\n|---------|----------|-------------|\n| **Key Management** | `kms_deletion_window_in_days` | Days before key deletion (7-30) |\n| | `kms_enable_key_rotation` | Enable automatic rotation |\n| | `kms_key_rotation_period` | Rotation period in days (90-2555) |\n| | `kms_multi_region` | Create multi-region key |\n| **Access Control** | `kms_key_administrators` | Principals with full key access |\n| | `kms_key_users` | Principals with encrypt/decrypt access |\n| | `kms_additional_principals` | Additional principals with basic access |\n| **Policy Customization** | `kms_custom_policy_statements` | Additional policy statements |\n| | `kms_custom_policy` | Complete custom policy JSON |\n| **Naming \u0026 Tagging** | `kms_alias_name` | Custom alias name |\n| | `kms_tags` | KMS-specific tags |\n\n### Benefits of Enhanced KMS Configuration\n\n1. **Granular Access Control**: Define specific roles for key administration and usage\n2. **Flexible Rotation**: Configure custom rotation periods for compliance requirements\n3. **Multi-Region Support**: Create keys that work across multiple AWS regions\n4. **Custom Policies**: Add specific policy statements or use completely custom policies\n5. **Enhanced Monitoring**: KMS-specific tags for better cost tracking and compliance\n6. **Cross-Account Access**: Secure sharing of encrypted repositories across AWS accounts\n\n### Example: Production Setup\n\n```hcl\nmodule \"production_ecr\" {\n  source = \"lgallard/ecr/aws\"\n\n  name = \"production-microservice\"\n\n  # Production-grade KMS encryption\n  encryption_type = \"KMS\"\n  kms_deletion_window_in_days = 30  # Longer window for recovery\n  kms_enable_key_rotation     = true\n  kms_key_rotation_period     = 90   # Quarterly rotation\n  kms_multi_region           = true  # Multi-region deployment\n\n  # Role-based access control\n  kms_key_administrators = [\n    \"arn:aws:iam::123456789012:role/ProductionKMSAdmins\"\n  ]\n\n  kms_key_users = [\n    \"arn:aws:iam::123456789012:role/ProductionECRAccess\",\n    \"arn:aws:iam::123456789012:role/GitHubActions-Production\"\n  ]\n\n  # Production tagging strategy\n  tags = {\n    Environment = \"production\"\n    Application = \"microservice\"\n    Owner       = \"platform-team\"\n    CostCenter  = \"engineering\"\n  }\n\n  kms_tags = {\n    EncryptionType = \"ECR-Production\"\n    ComplianceLevel = \"SOC2\"\n    BackupRequired = \"true\"\n  }\n}\n```\n\nFor complete examples and advanced use cases, see the [enhanced-kms example](examples/enhanced-kms/).\n\n## Variable Usage Examples\n\nThis module offers many configuration options through variables. Here are some examples of common variable configurations:\n\n### Basic Configuration\n\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n\n  name   = \"my-app-repo\"\n  tags   = {\n    Environment = \"Production\"\n  }\n}\n```\n\n### Security Settings\n\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n\n  name                 = \"secure-repo\"\n  image_tag_mutability = \"IMMUTABLE\"    # Prevent tag overwriting\n  scan_on_push         = true           # Enable basic vulnerability scanning\n  encryption_type      = \"KMS\"          # Use KMS encryption\n  prevent_destroy      = true           # Protect from accidental deletion\n}\n```\n\n### Enhanced Security Configuration\n\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n\n  name                 = \"enhanced-secure-repo\"\n  image_tag_mutability = \"IMMUTABLE\"\n  encryption_type      = \"KMS\"\n\n  # Enhanced scanning with AWS Inspector\n  enable_registry_scanning = true\n  registry_scan_type      = \"ENHANCED\"\n  enable_secret_scanning  = true\n\n  # Registry scan filters for high/critical vulnerabilities\n  registry_scan_filters = [\n    {\n      name   = \"PACKAGE_VULNERABILITY_SEVERITY\"\n      values = [\"HIGH\", \"CRITICAL\"]\n    }\n  ]\n\n  # Pull-through cache for Docker Hub\n  enable_pull_through_cache = true\n  pull_through_cache_rules = [\n    {\n      ecr_repository_prefix = \"docker-hub\"\n      upstream_registry_url = \"registry-1.docker.io\"\n    }\n  ]\n}\n```\n\n### Advanced Options\n\n```hcl\nmodule \"ecr\" {\n  source = \"lgallard/ecr/aws\"\n\n  name            = \"advanced-repo\"\n  force_delete    = false\n  enable_logging  = true\n\n  # Set custom timeouts\n  timeouts = {\n    delete = \"45m\"\n  }\n}\n```\n\nFor detailed examples of all variables with explanations, see [docs/variable-examples.md](docs/variable-examples.md).\n\n## Testing\n\nThis module uses [Terratest](https://github.com/gruntwork-io/terratest) for automated testing of the module functionality. The tests validate that the module can correctly:\n\n- Create an ECR repository with basic settings\n- Apply repository and lifecycle policies\n- Configure KMS encryption\n- Set up image tag mutability\n- Configure scan on push features\n\n### Running Tests Locally\n\nTo run the tests locally, you'll need:\n\n1. [Go](https://golang.org/) 1.16+\n2. [Terraform](https://www.terraform.io/) 1.3.0+\n3. AWS credentials configured locally\n\n```bash\n# Clone the repository\ngit clone https://github.com/lgallard/terraform-aws-ecr.git\ncd terraform-aws-ecr\n\n# Run the tests\ncd test\ngo mod tidy\ngo test -v\n```\n\nFor more details on tests, see the [test directory README](test/README.md).\n\n\u003c!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK --\u003e\n## Requirements\n\n| Name | Version |\n|------|---------|\n| \u003ca name=\"requirement_terraform\"\u003e\u003c/a\u003e [terraform](#requirement\\_terraform) | \u003e= 1.3.0 |\n| \u003ca name=\"requirement_archive\"\u003e\u003c/a\u003e [archive](#requirement\\_archive) | \u003e= 2.0.0 |\n| \u003ca name=\"requirement_aws\"\u003e\u003c/a\u003e [aws](#requirement\\_aws) | \u003e= 5.0.0 |\n\n## Providers\n\n| Name | Version |\n|------|---------|\n| \u003ca name=\"provider_archive\"\u003e\u003c/a\u003e [archive](#provider\\_archive) | \u003e= 2.0.0 |\n| \u003ca name=\"provider_aws\"\u003e\u003c/a\u003e [aws](#provider\\_aws) | \u003e= 5.0.0 |\n\n## Modules\n\n| Name | Source | Version |\n|------|--------|---------|\n| \u003ca name=\"module_kms\"\u003e\u003c/a\u003e [kms](#module\\_kms) | ./modules/kms | n/a |\n| \u003ca name=\"module_pull_through_cache\"\u003e\u003c/a\u003e [pull\\_through\\_cache](#module\\_pull\\_through\\_cache) | ./modules/pull-through-cache | n/a |\n\n## Resources\n\n| Name | Type |\n|------|------|\n| [aws_cloudwatch_event_rule.pull_request_rules](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_rule) | resource |\n| [aws_cloudwatch_event_target.pull_request_rules_sns](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_target) | resource |\n| [aws_cloudwatch_event_target.pull_request_rules_webhook](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_target) | resource |\n| [aws_cloudwatch_log_group.ecr_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |\n| [aws_cloudwatch_metric_alarm.monitoring](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_metric_alarm) | resource |\n| [aws_ecr_lifecycle_policy.lifecycle_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_lifecycle_policy) | resource |\n| [aws_ecr_registry_scanning_configuration.scanning](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_registry_scanning_configuration) | resource |\n| [aws_ecr_replication_configuration.replication](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_replication_configuration) | resource |\n| [aws_ecr_repository.repo](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_repository) | resource |\n| [aws_ecr_repository.repo_protected](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_repository) | resource |\n| [aws_ecr_repository_policy.policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_repository_policy) | resource |\n| [aws_iam_role.ecr_logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |\n| [aws_iam_role.pull_request_rules_webhook](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |\n| [aws_iam_role_policy.ecr_logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |\n| [aws_iam_role_policy_attachment.pull_request_rules_webhook](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |\n| [aws_lambda_function.pull_request_rules_webhook](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function) | resource |\n| [aws_lambda_permission.pull_request_rules_webhook](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission) | resource |\n| [aws_sns_topic.ecr_monitoring](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource |\n| [aws_sns_topic.pull_request_rules](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource |\n| [aws_sns_topic_subscription.ecr_monitoring_email](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource |\n| [archive_file.pull_request_rules_webhook](https://registry.terraform.io/providers/hashicorp/archive/latest/docs/data-sources/file) | data source |\n| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |\n| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |\n\n## Inputs\n\n| Name | Description | Type | Default | Required |\n|------|-------------|------|---------|:--------:|\n| \u003ca name=\"input_create_sns_topic\"\u003e\u003c/a\u003e [create\\_sns\\_topic](#input\\_create\\_sns\\_topic) | Whether to create an SNS topic for CloudWatch alarm notifications. | `bool` | `false` | no |\n| \u003ca name=\"input_default_tags_cost_center\"\u003e\u003c/a\u003e [default\\_tags\\_cost\\_center](#input\\_default\\_tags\\_cost\\_center) | Cost center tag value for financial tracking. Null to disable. | `string` | `null` | no |\n| \u003ca name=\"input_default_tags_environment\"\u003e\u003c/a\u003e [default\\_tags\\_environment](#input\\_default\\_tags\\_environment) | Environment tag value applied to all resources. Null to disable. | `string` | `null` | no |\n| \u003ca name=\"input_default_tags_owner\"\u003e\u003c/a\u003e [default\\_tags\\_owner](#input\\_default\\_tags\\_owner) | Owner tag value applied to all resources. Null to disable. | `string` | `null` | no |\n| \u003ca name=\"input_default_tags_project\"\u003e\u003c/a\u003e [default\\_tags\\_project](#input\\_default\\_tags\\_project) | Project tag value applied to all resources. Null to disable. | `string` | `null` | no |\n| \u003ca name=\"input_default_tags_template\"\u003e\u003c/a\u003e [default\\_tags\\_template](#input\\_default\\_tags\\_template) | Predefined default tag template. Options: basic, cost\\_allocation, compliance, sdlc. | `string` | `null` | no |\n| \u003ca name=\"input_enable_default_tags\"\u003e\u003c/a\u003e [enable\\_default\\_tags](#input\\_enable\\_default\\_tags) | Whether to enable automatic default tags for all resources. | `bool` | `true` | no |\n| \u003ca name=\"input_enable_logging\"\u003e\u003c/a\u003e [enable\\_logging](#input\\_enable\\_logging) | Whether to enable CloudWatch logging for the repository. | `bool` | `false` | no |\n| \u003ca name=\"input_enable_monitoring\"\u003e\u003c/a\u003e [enable\\_monitoring](#input\\_enable\\_monitoring) | Whether to enable CloudWatch monitoring and alerting for the ECR repository. | `bool` | `false` | no |\n| \u003ca name=\"input_enable_pull_request_rules\"\u003e\u003c/a\u003e [enable\\_pull\\_request\\_rules](#input\\_enable\\_pull\\_request\\_rules) | Whether to enable pull request rules for enhanced governance and quality control. | `bool` | `false` | no |\n| \u003ca name=\"input_enable_pull_through_cache\"\u003e\u003c/a\u003e [enable\\_pull\\_through\\_cache](#input\\_enable\\_pull\\_through\\_cache) | Whether to create pull-through cache rules. | `bool` | `false` | no |\n| \u003ca name=\"input_enable_registry_scanning\"\u003e\u003c/a\u003e [enable\\_registry\\_scanning](#input\\_enable\\_registry\\_scanning) | Whether to enable enhanced scanning for the ECR registry. | `bool` | `false` | no |\n| \u003ca name=\"input_enable_replication\"\u003e\u003c/a\u003e [enable\\_replication](#input\\_enable\\_replication) | Whether to enable cross-region replication for the ECR registry. | `bool` | `false` | no |\n| \u003ca name=\"input_enable_secret_scanning\"\u003e\u003c/a\u003e [enable\\_secret\\_scanning](#input\\_enable\\_secret\\_scanning) | Whether to enable secret scanning. Detects secrets in container images. | `bool` | `false` | no |\n| \u003ca name=\"input_enable_tag_normalization\"\u003e\u003c/a\u003e [enable\\_tag\\_normalization](#input\\_enable\\_tag\\_normalization) | Whether to enable automatic tag normalization. | `bool` | `true` | no |\n| \u003ca name=\"input_enable_tag_validation\"\u003e\u003c/a\u003e [enable\\_tag\\_validation](#input\\_enable\\_tag\\_validation) | Whether to enable tag validation to ensure compliance with organizational standards. | `bool` | `false` | no |\n| \u003ca name=\"input_encryption_type\"\u003e\u003c/a\u003e [encryption\\_type](#input\\_encryption\\_type) | Repository encryption type. Either KMS or AES256. | `string` | `\"AES256\"` | no |\n| \u003ca name=\"input_force_delete\"\u003e\u003c/a\u003e [force\\_delete](#input\\_force\\_delete) | Whether to delete the repository even if it contains images. Use with caution. | `bool` | `false` | no |\n| \u003ca name=\"input_image_scanning_configuration\"\u003e\u003c/a\u003e [image\\_scanning\\_configuration](#input\\_image\\_scanning\\_configuration) | Image scanning configuration block. Set to null to use scan\\_on\\_push variable. | \u003cpre\u003eobject({\u003cbr/\u003e    scan_on_push = bool\u003cbr/\u003e  })\u003c/pre\u003e | `null` | no |\n| \u003ca name=\"input_image_tag_mutability\"\u003e\u003c/a\u003e [image\\_tag\\_mutability](#input\\_image\\_tag\\_mutability) | The tag mutability setting for the repository. Either MUTABLE, IMMUTABLE, IMMUTABLE\\_WITH\\_EXCLUSION, or MUTABLE\\_WITH\\_EXCLUSION. | `string` | `\"MUTABLE\"` | no |\n| \u003ca name=\"input_kms_additional_principals\"\u003e\u003c/a\u003e [kms\\_additional\\_principals](#input\\_kms\\_additional\\_principals) | List of additional IAM principals (ARNs) to grant KMS key access. | `list(string)` | `[]` | no |\n| \u003ca name=\"input_kms_alias_name\"\u003e\u003c/a\u003e [kms\\_alias\\_name](#input\\_kms\\_alias\\_name) | Custom alias name for the KMS key (without 'alias/' prefix). | `string` | `null` | no |\n| \u003ca name=\"input_kms_custom_policy\"\u003e\u003c/a\u003e [kms\\_custom\\_policy](#input\\_kms\\_custom\\_policy) | Complete custom policy JSON for the KMS key. Use with caution. | `string` | `null` | no |\n| \u003ca name=\"input_kms_custom_policy_statements\"\u003e\u003c/a\u003e [kms\\_custom\\_policy\\_statements](#input\\_kms\\_custom\\_policy\\_statements) | List of custom policy statements to add to the KMS key policy. | \u003cpre\u003elist(object({\u003cbr/\u003e    sid    = optional(string)\u003cbr/\u003e    effect = string\u003cbr/\u003e    principals = optional(object({\u003cbr/\u003e      type        = string\u003cbr/\u003e      identifiers = list(string)\u003cbr/\u003e    }))\u003cbr/\u003e    actions   = list(string)\u003cbr/\u003e    resources = optional(list(string), [\"*\"])\u003cbr/\u003e    conditions = optional(list(object({\u003cbr/\u003e      test     = string\u003cbr/\u003e      variable = string\u003cbr/\u003e      values   = list(string)\u003cbr/\u003e    })), [])\u003cbr/\u003e  }))\u003c/pre\u003e | `[]` | no |\n| \u003ca name=\"input_kms_deletion_window_in_days\"\u003e\u003c/a\u003e [kms\\_deletion\\_window\\_in\\_days](#input\\_kms\\_deletion\\_window\\_in\\_days) | Number of days to wait before deleting the KMS key (7-30 days). | `number` | `7` | no |\n| \u003ca name=\"input_kms_enable_key_rotation\"\u003e\u003c/a\u003e [kms\\_enable\\_key\\_rotation](#input\\_kms\\_enable\\_key\\_rotation) | Whether to enable automatic key rotation for the KMS key. | `bool` | `true` | no |\n| \u003ca name=\"input_kms_key\"\u003e\u003c/a\u003e [kms\\_key](#input\\_kms\\_key) | ARN of existing KMS key for repository encryption. If null, a new key is created. | `string` | `null` | no |\n| \u003ca name=\"input_kms_key_administrators\"\u003e\u003c/a\u003e [kms\\_key\\_administrators](#input\\_kms\\_key\\_administrators) | List of IAM principals (ARNs) who can administer the KMS key. | `list(string)` | `[]` | no |\n| \u003ca name=\"input_kms_key_rotation_period\"\u003e\u003c/a\u003e [kms\\_key\\_rotation\\_period](#input\\_kms\\_key\\_rotation\\_period) | Number of days between automatic key rotations (90-2555 days). | `number` | `null` | no |\n| \u003ca name=\"input_kms_key_users\"\u003e\u003c/a\u003e [kms\\_key\\_users](#input\\_kms\\_key\\_users) | List of IAM principals (ARNs) who can use the KMS key for crypto operations. | `list(string)` | `[]` | no |\n| \u003ca name=\"input_kms_multi_region\"\u003e\u003c/a\u003e [kms\\_multi\\_region](#input\\_kms\\_multi\\_region) | Whether to create a multi-region KMS key. | `bool` | `false` | no |\n| \u003ca name=\"input_kms_tags\"\u003e\u003c/a\u003e [kms\\_tags](#input\\_kms\\_tags) | Additional tags specific to KMS resources. | `map(string)` | `{}` | no |\n| \u003ca name=\"input_lifecycle_expire_tagged_after_days\"\u003e\u003c/a\u003e [lifecycle\\_expire\\_tagged\\_after\\_days](#input\\_lifecycle\\_expire\\_tagged\\_after\\_days) | Number of days after which tagged images expire (1-3650). Use with caution. | `number` | `null` | no |\n| \u003ca name=\"input_lifecycle_expire_untagged_after_days\"\u003e\u003c/a\u003e [lifecycle\\_expire\\_untagged\\_after\\_days](#input\\_lifecycle\\_expire\\_untagged\\_after\\_days) | Number of days after which untagged images expire (1-3650). Null to disable. | `number` | `null` | no |\n| \u003ca name=\"input_lifecycle_keep_latest_n_images\"\u003e\u003c/a\u003e [lifecycle\\_keep\\_latest\\_n\\_images](#input\\_lifecycle\\_keep\\_latest\\_n\\_images) | Number of latest images to keep in the repository (1-10000). Null to disable. | `number` | `null` | no |\n| \u003ca name=\"input_lifecycle_policy\"\u003e\u003c/a\u003e [lifecycle\\_policy](#input\\_lifecycle\\_policy) | JSON string representing the lifecycle policy. Takes precedence over helper variables. | `string` | `null` | no |\n| \u003ca name=\"input_lifecycle_policy_template\"\u003e\u003c/a\u003e [lifecycle\\_policy\\_template](#input\\_lifecycle\\_policy\\_template) | Predefined lifecycle policy template. Options: development, production, cost\\_optimization, compliance. | `string` | `null` | no |\n| \u003ca name=\"input_lifecycle_tag_prefixes_to_keep\"\u003e\u003c/a\u003e [lifecycle\\_tag\\_prefixes\\_to\\_keep](#input\\_lifecycle\\_tag\\_prefixes\\_to\\_keep) | List of tag prefixes for keep-latest rule. Empty list applies to all images. Max 100 prefixes. | `list(string)` | `[]` | no |\n| \u003ca name=\"input_log_retention_days\"\u003e\u003c/a\u003e [log\\_retention\\_days](#input\\_log\\_retention\\_days) | Number of days to retain ECR logs in CloudWatch. | `number` | `30` | no |\n| \u003ca name=\"input_monitoring_threshold_api_calls\"\u003e\u003c/a\u003e [monitoring\\_threshold\\_api\\_calls](#input\\_monitoring\\_threshold\\_api\\_calls) | API call volume threshold per minute to trigger CloudWatch alarm. | `number` | `1000` | no |\n| \u003ca name=\"input_monitoring_threshold_image_pull\"\u003e\u003c/a\u003e [monitoring\\_threshold\\_image\\_pull](#input\\_monitoring\\_threshold\\_image\\_pull) | Image pull frequency threshold per 5-minute period to trigger CloudWatch alarm. | `number` | `100` | no |\n| \u003ca name=\"input_monitoring_threshold_image_push\"\u003e\u003c/a\u003e [monitoring\\_threshold\\_image\\_push](#input\\_monitoring\\_threshold\\_image\\_push) | Image push frequency threshold per 5-minute period to trigger CloudWatch alarm. | `number` | `10` | no |\n| \u003ca name=\"input_monitoring_threshold_security_findings\"\u003e\u003c/a\u003e [monitoring\\_threshold\\_security\\_findings](#input\\_monitoring\\_threshold\\_security\\_findings) | Security findings threshold to trigger CloudWatch alarm. | `number` | `10` | no |\n| \u003ca name=\"input_monitoring_threshold_storage\"\u003e\u003c/a\u003e [monitoring\\_threshold\\_storage](#input\\_monitoring\\_threshold\\_storage) | Storage usage threshold in GB to trigger CloudWatch alarm. | `number` | `10` | no |\n| \u003ca name=\"input_name\"\u003e\u003c/a\u003e [name](#input\\_name) | Name of the ECR repository. This name must be unique within the AWS account and region. | `string` | n/a | yes |\n| \u003ca name=\"input_normalize_tag_values\"\u003e\u003c/a\u003e [normalize\\_tag\\_values](#input\\_normalize\\_tag\\_values) | Whether to normalize tag values by trimming whitespace. | `bool` | `true` | no |\n| \u003ca name=\"input_policy\"\u003e\u003c/a\u003e [policy](#input\\_policy) | JSON string representing the repository policy. If null, no policy is created. | `string` | `null` | no |\n| \u003ca name=\"input_prevent_destroy\"\u003e\u003c/a\u003e [prevent\\_destroy](#input\\_prevent\\_destroy) | Whether to protect the repository from being destroyed via lifecycle prevent\\_destroy. | `bool` | `false` | no |\n| \u003ca name=\"input_pull_request_rules\"\u003e\u003c/a\u003e [pull\\_request\\_rules](#input\\_pull\\_request\\_rules) | List of pull request rule configurations for enhanced governance. | \u003cpre\u003elist(object({\u003cbr/\u003e    name    = string\u003cbr/\u003e    type    = string\u003cbr/\u003e    enabled = bool\u003cbr/\u003e    conditions = optional(object({\u003cbr/\u003e      tag_patterns            = optional(list(string), [])\u003cbr/\u003e      severity_threshold      = optional(string, \"MEDIUM\")\u003cbr/\u003e      require_scan_completion = optional(bool, true)\u003cbr/\u003e      allowed_principals      = optional(list(string), [])\u003cbr/\u003e    }), {})\u003cbr/\u003e    actions = optional(object({\u003cbr/\u003e      require_approval_count = optional(number, 1)\u003cbr/\u003e      notification_topic_arn = optional(string)\u003cbr/\u003e      webhook_url            = optional(string)\u003cbr/\u003e      block_on_failure       = optional(bool, true)\u003cbr/\u003e      approval_timeout_hours = optional(number, 24)\u003cbr/\u003e    }), {})\u003cbr/\u003e  }))\u003c/pre\u003e | `[]` | no |\n| \u003ca name=\"input_pull_through_cache_rules\"\u003e\u003c/a\u003e [pull\\_through\\_cache\\_rules](#input\\_pull\\_through\\_cache\\_rules) | List of pull-through cache rules to create. | \u003cpre\u003elist(object({\u003cbr/\u003e    ecr_repository_prefix = string\u003cbr/\u003e    upstream_registry_url = string\u003cbr/\u003e    credential_arn        = optional(string)\u003cbr/\u003e  }))\u003c/pre\u003e | `[]` | no |\n| \u003ca name=\"input_registry_scan_filters\"\u003e\u003c/a\u003e [registry\\_scan\\_filters](#input\\_registry\\_scan\\_filters) | List of scan filters for filtering scan results when querying ECR findings. | \u003cpre\u003elist(object({\u003cbr/\u003e    name   = string\u003cbr/\u003e    values = list(string)\u003cbr/\u003e  }))\u003c/pre\u003e | `[]` | no |\n| \u003ca name=\"input_registry_scan_type\"\u003e\u003c/a\u003e [registry\\_scan\\_type](#input\\_registry\\_scan\\_type) | The type of scanning to configure for the registry. Either BASIC or ENHANCED. | `string` | `\"ENHANCED\"` | no |\n| \u003ca name=\"input_replication_regions\"\u003e\u003c/a\u003e [replication\\_regions](#input\\_replication\\_regions) | List of AWS regions to replicate ECR images to. | `list(string)` | `[]` | no |\n| \u003ca name=\"input_required_tags\"\u003e\u003c/a\u003e [required\\_tags](#input\\_required\\_tags) | List of tag keys that are required to be present. Empty list disables validation. | `list(string)` | `[]` | no |\n| \u003ca name=\"input_scan_on_push\"\u003e\u003c/a\u003e [scan\\_on\\_push](#input\\_scan\\_on\\_push) | Whether images should be scanned after being pushed to the repository. | `bool` | `true` | no |\n| \u003ca name=\"input_scan_repository_filters\"\u003e\u003c/a\u003e [scan\\_repository\\_filters](#input\\_scan\\_repository\\_filters) | List of repository filters to apply for registry scanning. Supports wildcards. | `list(string)` | \u003cpre\u003e[\u003cbr/\u003e  \"*\"\u003cbr/\u003e]\u003c/pre\u003e | no |\n| \u003ca name=\"input_sns_topic_name\"\u003e\u003c/a\u003e [sns\\_topic\\_name](#input\\_sns\\_topic\\_name) | Name of the SNS topic to create or use for alarm notifications. | `string` | `null` | no |\n| \u003ca name=\"input_sns_topic_subscribers\"\u003e\u003c/a\u003e [sns\\_topic\\_subscribers](#input\\_sns\\_topic\\_subscribers) | List of email addresses to subscribe to the SNS topic for alarm notifications. | `list(string)` | `[]` | no |\n| \u003ca name=\"input_tag_key_case\"\u003e\u003c/a\u003e [tag\\_key\\_case](#input\\_tag\\_key\\_case) | Enforce consistent casing for tag keys. Options: PascalCase, camelCase, snake\\_case, kebab-case. | `string` | `\"PascalCase\"` | no |\n| \u003ca name=\"input_tags\"\u003e\u003c/a\u003e [tags](#input\\_tags) | A map of tags to assign to all resources created by this module. | `map(string)` | `{}` | no |\n| \u003ca name=\"input_timeouts\"\u003e\u003c/a\u003e [timeouts](#input\\_timeouts) | Timeout configuration for repository operations. Example: { delete = \"20m\" } | \u003cpre\u003eobject({\u003cbr/\u003e    delete = optional(string)\u003cbr/\u003e  })\u003c/pre\u003e | `{}` | no |\n\n## Outputs\n\n| Name | Description |\n|------|-------------|\n| \u003ca name=\"output_applied_tags\"\u003e\u003c/a\u003e [applied\\_tags](#output\\_applied\\_tags) | The final set of tags applied to all resources after normalization and default tag application |\n| \u003ca name=\"output_cloudwatch_alarms\"\u003e\u003c/a\u003e [cloudwatch\\_alarms](#output\\_cloudwatch\\_alarms) | List of CloudWatch alarms created for ECR monitoring |\n| \u003ca name=\"output_cloudwatch_log_group_arn\"\u003e\u003c/a\u003e [cloudwatch\\_log\\_group\\_arn](#output\\_cloudwatch\\_log\\_group\\_arn) | The ARN of the CloudWatch Log Group used for ECR logs (if logging is enabled) |\n| \u003ca name=\"output_kms_alias_arn\"\u003e\u003c/a\u003e [kms\\_alias\\_arn](#output\\_kms\\_alias\\_arn) | The ARN of the KMS alias (if created by this module). |\n| \u003ca name=\"output_kms_configuration\"\u003e\u003c/a\u003e [kms\\_configuration](#output\\_kms\\_configuration) | Complete KMS configuration information. |\n| \u003ca name=\"output_kms_key_arn\"\u003e\u003c/a\u003e [kms\\_key\\_arn](#output\\_kms\\_key\\_arn) | The ARN of the KMS key used for repository encryption. |\n| \u003ca name=\"output_kms_key_id\"\u003e\u003c/a\u003e [kms\\_key\\_id](#output\\_kms\\_key\\_id) | The globally unique identifier for the KMS key (if created by this module). |\n| \u003ca name=\"output_lifecycle_policy\"\u003e\u003c/a\u003e [lifecycle\\_policy](#output\\_lifecycle\\_policy) | The lifecycle policy JSON applied to the repository (if any) |\n| \u003ca name=\"output_logging_role_arn\"\u003e\u003c/a\u003e [logging\\_role\\_arn](#output\\_logging\\_role\\_arn) | The ARN of the IAM role used for ECR logging (if logging is enabled) |\n| \u003ca name=\"output_monitoring_status\"\u003e\u003c/a\u003e [monitoring\\_status](#output\\_monitoring\\_status) | Status of CloudWatch monitoring configuration |\n| \u003ca name=\"output_pull_request_rules\"\u003e\u003c/a\u003e [pull\\_request\\_rules](#output\\_pull\\_request\\_rules) | Information about pull request rules configuration |\n| \u003ca name=\"output_pull_through_cache_role_arn\"\u003e\u003c/a\u003e [pull\\_through\\_cache\\_role\\_arn](#output\\_pull\\_through\\_cache\\_role\\_arn) | The ARN of the IAM role used for pull-through cache operations (if enabled) |\n| \u003ca name=\"output_pull_through_cache_rules\"\u003e\u003c/a\u003e [pull\\_through\\_cache\\_rules](#output\\_pull\\_through\\_cache\\_rules) | List of pull-through cache rules (if enabled) |\n| \u003ca name=\"output_registry_id\"\u003e\u003c/a\u003e [registry\\_id](#output\\_registry\\_id) | ID of the ECR registry |\n| \u003ca name=\"output_registry_scan_filters\"\u003e\u003c/a\u003e [registry\\_scan\\_filters](#output\\_registry\\_scan\\_filters) | The configured scan filters for filtering scan results (e.g., by vulnerability severity) |\n| \u003ca name=\"output_registry_scanning_configuration_arn\"\u003e\u003c/a\u003e [registry\\_scanning\\_configuration\\_arn](#output\\_registry\\_scanning\\_configuration\\_arn) | The ARN of the ECR registry scanning configuration (if enhanced scanning is enabled) |\n| \u003ca name=\"output_registry_scanning_status\"\u003e\u003c/a\u003e [registry\\_scanning\\_status](#output\\_registry\\_scanning\\_status) | Status of ECR registry scanning configuration |\n| \u003ca name=\"output_replication_configuration_arn\"\u003e\u003c/a\u003e [replication\\_configuration\\_arn](#output\\_replication\\_configuration\\_arn) | The ARN of the ECR replication configuration (if replication is enabled) |\n| \u003ca name=\"output_replication_regions\"\u003e\u003c/a\u003e [replication\\_regions](#output\\_replication\\_regions) | List of regions where ECR images are replicated to (if replication is enabled) |\n| \u003ca name=\"output_replication_status\"\u003e\u003c/a\u003e [replication\\_status](#output\\_replication\\_status) | Status of ECR replication configuration |\n| \u003ca name=\"output_repository_arn\"\u003e\u003c/a\u003e [repository\\_arn](#output\\_repository\\_arn) | ARN of the ECR repository |\n| \u003ca name=\"output_repository_name\"\u003e\u003c/a\u003e [repository\\_name](#output\\_repository\\_name) | Name of the ECR repository |\n| \u003ca name=\"output_repository_policy_exists\"\u003e\u003c/a\u003e [repository\\_policy\\_exists](#output\\_repository\\_policy\\_exists) | Whether a repository policy exists for this ECR repository |\n| \u003ca name=\"output_repository_url\"\u003e\u003c/a\u003e [repository\\_url](#output\\_repository\\_url) | URL of the ECR repository |\n| \u003ca name=\"output_security_status\"\u003e\u003c/a\u003e [security\\_status](#output\\_security\\_status) | Comprehensive security status of the ECR configuration |\n| \u003ca name=\"output_sns_topic_arn\"\u003e\u003c/a\u003e [sns\\_topic\\_arn](#output\\_sns\\_topic\\_arn) | ARN of the SNS topic used for ECR monitoring alerts (if created) |\n| \u003ca name=\"output_tag_compliance_status\"\u003e\u003c/a\u003e [tag\\_compliance\\_status](#output\\_tag\\_compliance\\_status) | Tag compliance and validation status |\n| \u003ca name=\"output_tagging_strategy\"\u003e\u003c/a\u003e [tagging\\_strategy](#output\\_tagging\\_strategy) | Summary of the tagging strategy configuration |\n\u003c!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK --\u003e\n\n## Automation \u0026 Feature Discovery\n\n### Automated Feature Discovery System\n\nThis module includes an automated feature discovery system that runs weekly to identify new AWS ECR features, deprecations, and bug fixes from the AWS provider. The system uses Claude Code with MCP (Model Context Protocol) servers to analyze provider documentation and automatically create GitHub issues for new functionality.\n\n#### How It Works\n\n1. **Weekly Scanning**: Every Sunday at 00:00 UTC, the system scans the latest AWS provider documentation\n2. **MCP Integration**: Uses Terraform and Context7 MCP servers to access up-to-date provider docs\n3. **Intelligent Analysis**: Compares provider capabilities with current module implementation\n4. **Automated Issues**: Creates categorized GitHub issues for discovered items:\n   - 🚀 **New Features** - ECR resources/arguments not yet implemented\n   - ⚠️ **Deprecations** - Features being phased out requiring action\n   - 🐛 **Bug Fixes** - Important provider fixes affecting the module\n\n#### Feature Discovery Workflow\n\nThe discovery process follows this workflow:\n\n```\n┌─────────────────┐    ┌──────────────────────┐    ┌─────────────────────┐\n│                 │    │                      │    │                     │\n│  Weekly Trigger │───▶│   Claude Code CLI    │───▶│   GitHub Issues     │\n│  (GitHub Action)│    │   + MCP Servers      │    │   (Auto-created)    │\n│                 │    │                      │    │                     │\n└─────────────────┘    └──────────────────────┘    └─────────────────────┘\n                                 │\n                                 ▼\n                       ┌──────────────────────┐\n                       │                      │\n                       │  Feature Tracking    │\n                       │    Database          │\n                       │  (.github/tracker/)  │\n                       │                      │\n                       └──────────────────────┘\n```\n\n#### Manual Discovery\n\nYou can manually trigger feature discovery:\n\n```bash\n# Standard discovery\ngh workflow run feature-discovery.yml\n\n# Dry run mode (analyze without creating issues)\ngh workflow run feature-discovery.yml -f dry_run=true\n\n# Specific provider version\ngh workflow run feature-discovery.yml -f provider_version=5.82.0\n\n# Force full scan\ngh workflow run feature-discovery.yml -f force_scan=true\n```\n\n#### Discovery Categories\n\nThe system identifies and categorizes findings as:\n\n**New Features (`enhancement` label):**\n- New ECR resources (`aws_ecr_*`)\n- New arguments on existing resources\n- New data sources (`data.aws_ecr_*`)\n- New lifecycle configurations\n- New security/monitoring features\n\n**Deprecations (`deprecation` label):**\n- Arguments marked for removal\n- Resources being phased out\n- Configuration patterns no longer recommended\n\n**Bug Fixes (`bug` label):**\n- Provider fixes affecting module functionality\n- Performance improvements\n- Security patches\n\n#### Issue Templates\n\nEach discovery type uses a structured template:\n\n- **New Features**: Implementation checklist, examples, testing requirements\n- **Deprecations**: Migration guidance, timeline, impact assessment\n- **Bug Fixes**: Impact analysis, testing strategy, version requirements\n\n#### Feature Tracking\n\nAll discoveries are tracked in `.github/feature-tracker/ecr-features.json`:\n\n```json\n{\n  \"metadata\": {\n    \"last_scan\": \"2025-01-21T00:00:00Z\",\n    \"provider_version\": \"5.82.0\",\n    \"scan_count\": 42\n  },\n  \"current_implementation\": {\n    \"resources\": {\n      \"aws_ecr_repository\": {\n        \"implemented\": [\"name\", \"image_tag_mutability\", \"scan_on_push\"],\n        \"pending\": [\"force_delete\"]\n      }\n    }\n  },\n  \"discovered_features\": {\n    \"new_resources\": {},\n    \"deprecations\": {},\n    \"bug_fixes\": {}\n  }\n}\n```\n\n#### MCP Server Integration\n\nThe system leverages Model Context Protocol servers for real-time documentation access:\n\n- **Terraform MCP**: `@modelcontextprotocol/server-terraform@latest`\n  - AWS provider resource documentation\n  - Argument specifications and examples\n  - Version compatibility information\n\n- **Context7 MCP**: `@upstash/context7-mcp@latest`\n  - Provider changelogs and release notes\n  - Community discussions and best practices\n  - Historical change tracking\n\n#### Benefits\n\n- **Stay Current**: Never miss new AWS ECR features\n- **Proactive Maintenance**: Identify deprecations before they break\n- **Automated Tracking**: Comprehensive feature database\n- **Community Value**: Users benefit from latest AWS capabilities\n- **Reduced Manual Work**: No need for manual provider monitoring\n\n#### Contributing to Discovery\n\nThe system is designed to minimize false positives, but you can help improve accuracy:\n\n1. **Review Auto-Created Issues**: Validate and prioritize discoveries\n2. **Update Tracking**: Mark features as implemented when complete\n3. **Improve Templates**: Suggest enhancements to issue templates\n4. **Report Gaps**: Let us know if the system misses important features\n\nFor more details on the discovery system architecture, see `.github/scripts/discovery-prompt.md`.\n\n\u003c!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK --\u003e\n## Requirements\n\n| Name | Version |\n|------|---------|\n| \u003ca name=\"requirement_terraform\"\u003e\u003c/a\u003e [terraform](#requirement\\_terraform) | \u003e= 1.3.0 |\n| \u003ca name=\"requirement_archive\"\u003e\u003c/a\u003e [archive](#requirement\\_archive) | \u003e= 2.0.0 |\n| \u003ca name=\"requirement_aws\"\u003e\u003c/a\u003e [aws](#requirement\\_aws) | \u003e= 5.0.0 |\n\n## Providers\n\n| Name | Version |\n|------|---------|\n| \u003ca name=\"provider_archive\"\u003e\u003c/a\u003e [archive](#provider\\_archive) | \u003e= 2.0.0 |\n| \u003ca name=\"provider_aws\"\u003e\u003c/a\u003e [aws](#provider\\_aws) | \u003e= 5.0.0 |\n\n## Modules\n\n| Name | Source | Version |\n|------|--------|---------|\n| \u003ca name=\"module_kms\"\u003e\u003c/a\u003e [kms](#module\\_kms) | ./modules/kms | n/a |\n| \u003ca name=\"module_pull_through_cache\"\u003e\u003c/a\u003e [pull\\_through\\_cache](#module\\_pull\\_through\\_cache) | ./modules/pull-through-cache | n/a |\n\n## Resources\n\n| Name | Type |\n|------|------|\n| [aws_cloudwatch_event_rule.pull_request_rules](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_rule) | resource |\n| [aws_cloudwatch_event_target.pull_request_rules_sns](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_target) | resource |\n| [aws_cloudwatch_event_target.pull_request_rules_webhook](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_target) | resource |\n| [aws_cloudwatch_log_group.ecr_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |\n| [aws_cloudwatch_metric_alarm.monitoring](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_metric_alarm) | resource |\n| [aws_ecr_lifecycle_policy.lifecycle_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_lifecycle_policy) | resource |\n| [aws_ecr_registry_scanning_configuration.scanning](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_registry_scanning_configuration) | resource |\n| [aws_ecr_replication_configuration.replication](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_replication_configuration) | resource |\n| [aws_ecr_repository.repo](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_repository) | resource |\n| [aws_ecr_repository.repo_protected](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_repository) | resource |\n| [aws_ecr_repository_policy.policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_repository_policy) | resource |\n| [aws_iam_role.ecr_logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |\n| [aws_iam_role.pull_request_rules_webhook](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |\n| [aws_iam_role_policy.ecr_logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |\n| [aws_iam_role_policy_attachment.pull_request_rules_webhook](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |\n| [aws_lambda_function.pull_request_rules_webhook](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function) | resource |\n| [aws_lambda_permission.pull_request_rules_webhook](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission) | resource |\n| [aws_sns_topic.ecr_monitoring](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource |\n| [aws_sns_topic.pull_request_rules](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource |\n| [aws_sns_topic_subscription.ecr_monitoring_email](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource |\n| [archive_file.pull_request_rules_webhook](https://registry.terraform.io/providers/hashicorp/archive/latest/docs/data-sources/file) | data source |\n| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |\n| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |\n\n## Inputs\n\n| Name | Description | Type | Default | Required |\n|------|-------------|------|---------|:--------:|\n| \u003ca name=\"input_create_sns_topic\"\u003e\u003c/a\u003e [create\\_sns\\_topic](#input\\_create\\_sns\\_topic) | Whether to create an SNS topic for CloudWatch alarm notifications. | `bool` | `false` | no |\n| \u003ca name=\"input_default_tags_cost_center\"\u003e\u003c/a\u003e [default\\_tags\\_cost\\_center](#input\\_default\\_tags\\_cost\\_center) | Cost center tag value for financial tracking. Null to disable. | `string` | `null` | no |\n| \u003ca name=\"input_default_tags_environment\"\u003e\u003c/a\u003e [default\\_tags\\_environment](#input\\_default\\_tags\\_environment) | Environment tag value applied to all resources. Null to disable. | `string` | `null` | no |\n| \u003ca name=\"input_default_tags_owner\"\u003e\u003c/a\u003e [default\\_tags\\_owner](#input\\_default\\_tags\\_owner) | Owner tag value applied to all resources. Null to disable. | `string` | `null` | no |\n| \u003ca name=\"input_default_tags_project\"\u003e\u003c/a\u003e [default\\_tags\\_project](#input\\_default\\_tags\\_project) | Project tag value applied to all resources. Null to disable. | `string` | `null` | no |\n| \u003ca name=\"input_default_tags_template\"\u003e\u003c/a\u003e [default\\_tags\\_template](#input\\_default\\_tags\\_template) | Predefined default tag template. Options: basic, cost\\_allocation, compliance, sdlc. | `string` | `null` | no |\n| \u003ca name=\"input_enable_default_tags\"\u003e\u003c/a\u003e [enable\\_default\\_tags](#input\\_enable\\_default\\_tags) | Whether to enable automatic default tags for all resources. | `bool` | `true` | no |\n| \u003ca name=\"input_enable_logging\"\u003e\u003c/a\u003e [enable\\_logging](#input\\_enable\\_logging) | Whether to enable CloudWatch logging for the repository. | `bool` | `false` | no |\n| \u003ca name=\"input_enable_monitoring\"\u003e\u003c/a\u003e [enable\\_monitoring](#input\\_enable\\_monitoring) | Whether to enable CloudWatch monitoring and alerting for the ECR repository. | `bool` | `false` | no |\n| \u003ca name=\"input_enable_pull_request_rules\"\u003e\u003c/a\u003e [enable\\_pull\\_request\\_rules](#input\\_enable\\_pull\\_request\\_rules) | Whether to enable pull request rules for enhanced governance and quality control. | `bool` | `false` | no |\n| \u003ca name=\"input_enable_pull_through_cache\"\u003e\u003c/a\u003e [enable\\_pull\\_through\\_cache](#input\\_enable\\_pull\\_through\\_cache) | Whether to create pull-through cache rules. | `bool` | `false` | no |\n| \u003ca name=\"input_enable_registry_scanning\"\u003e\u003c/a\u003e [enable\\_registry\\_scanning](#input\\_enable\\_registry\\_scanning) | Whether to enable enhanced scanning for the ECR registry. | `bool` | `false` | no |\n| \u003ca name=\"input_enable_replication\"\u003e\u003c/a\u003e [enable\\_replication](#input\\_enable\\_replication) | Whether to enable cross-region replication for the ECR registry. | `bool` | `false` | no |\n| \u003ca name=\"input_enable_secret_scanning\"\u003e\u003c/a\u003e [enable\\_secret\\_scanning](#input\\_enable\\_secret\\_scanning) | Whether to enable secret scanning. Detects secrets in container images. | `bool` | `false` | no |\n| \u003ca name=\"input_enable_tag_normalization\"\u003e\u003c/a\u003e [enable\\_tag\\_normalization](#input\\_enable\\_tag\\_normalization) | Whether to enable automatic tag normalization. | `bool` | `true` | no |\n| \u003ca name=\"input_enable_tag_validation\"\u003e\u003c/a\u003e [enable\\_tag\\_validation](#input\\_enable\\_tag\\_validation) | Whether to enable tag validation to ensure compliance with organizational standards. | `bool` | `false` | no |\n| \u003ca name=\"input_encryption_type\"\u003e\u003c/a\u003e [encryption\\_type](#input\\_encryption\\_type) | Repository encryption type. Either KMS or AES256. | `string` | `\"AES256\"` | no |\n| \u003ca name=\"input_force_delete\"\u003e\u003c/a\u003e [force\\_delete](#input\\_force\\_delete) | Whether to delete the repository even if it contains images. Use with caution. | `bool` | `false` | no |\n| \u003ca name=\"input_image_scanning_configuration\"\u003e\u003c/a\u003e [image\\_scanning\\_configuration](#input\\_image\\_scanning\\_configuration) | Image scanning configuration block. Set to null to use scan\\_on\\_push variable. | \u003cpre\u003eobject({\u003cbr/\u003e    scan_on_push = bool\u003cbr/\u003e  })\u003c/pre\u003e | `null` | no |\n| \u003ca name=\"input_image_tag_mutability\"\u003e\u003c/a\u003e [image\\_tag\\_mutability](#input\\_image\\_tag\\_mutability) | The tag mutability setting for the repository. Either MUTABLE, IMMUTABLE, IMMUTABLE\\_WITH\\_EXCLUSION, or MUTABLE\\_WITH\\_EXCLUSION. | `string` | `\"MUTABLE\"` | no |\n| \u003ca name=\"input_kms_additional_principals\"\u003e\u003c/a\u003e [kms\\_additional\\_principals](#input\\_kms\\_additional\\_principals) | List of additional IAM principals (ARNs) to grant KMS key access. | `list(string)` | `[]` | no |\n| \u003ca name=\"input_kms_alias_name\"\u003e\u003c/a\u003e [kms\\_alias\\_name](#input\\_kms\\_alias\\_name) | Custom alias name for the KMS key (without 'alias/' prefix). | `string` | `null` | no |\n| \u003ca name=\"input_kms_custom_policy\"\u003e\u003c/a\u003e [kms\\_custom\\_policy](#input\\_kms\\_custom\\_policy) | Complete custom policy JSON for the KMS key. Use with caution. | `string` | `null` | no |\n| \u003ca name=\"input_kms_custom_policy_statements\"\u003e\u003c/a\u003e [kms\\_custom\\_policy\\_statements](#input\\_kms\\_custom\\_policy\\_statements) | List of custom policy statements to add to the KMS key policy. | \u003cpre\u003elist(object({\u003cbr/\u003e    sid    = optional(string)\u003cbr/\u003e    effect = string\u003cbr/\u003e    principals = optional(object({\u003cbr/\u003e      type        = string\u003cbr/\u003e      identifiers = list(string)\u003cbr/\u003e    }))\u003cbr/\u003e    actions   = list(string)\u003cbr/\u003e    resources = optional(list(string), [\"*\"])\u003cbr/\u003e    conditions = optional(list(object({\u003cbr/\u003e      test     = string\u003cbr/\u003e      variable = string\u003cbr/\u003e      values   = list(string)\u003cbr/\u003e    })), [])\u003cbr/\u003e  }))\u003c/pre\u003e | `[]` | no |\n| \u003ca name=\"input_kms_deletion_window_in_days\"\u003e\u003c/a\u003e [kms\\_deletion\\_window\\_in\\_days](#input\\_kms\\_deletion\\_window\\_in\\_days) | Number of days to wait before deleting the KMS key (7-30 days). | `number` | `7` | no |\n| \u003ca name=\"input_kms_enable_key_rotation\"\u003e\u003c/a\u003e [kms\\_enable\\_key\\_rotation](#input\\_kms\\_enable\\_key\\_rotation) | Whether to enable automatic key rotation for the KMS key. | `bool` | `true` | no |\n| \u003ca name=\"input_kms_key\"\u003e\u003c/a\u003e [kms\\_key](#input\\_kms\\_key) | ARN of existing KMS key for repository encryption. If null, a new key is created. | `string` | `null` | no |\n| \u003ca name=\"input_kms_key_administrators\"\u003e\u003c/a\u003e [kms\\_key\\_administrators](#input\\_kms\\_key\\_administrators) | List of IAM principals (ARNs) who can administer the KMS key. | `list(string)` | `[]` | no |\n| \u003ca name=\"input_kms_key_rotation_period\"\u003e\u003c/a\u003e [kms\\_key\\_rotation\\_period](#input\\_kms\\_key\\_rotation\\_period) | Number of days between automatic key rotations (90-2555 days). | `number` | `null` | no |\n| \u003ca name=\"input_kms_key_users\"\u003e\u003c/a\u003e [kms\\_key\\_users](#input\\_kms\\_key\\_users) | List of IAM principals (ARNs) who can use the KMS key for crypto operations. | `list(string)` | `[]` | no |\n| \u003ca name=\"input_kms_multi_region\"\u003e\u003c/a\u003e [kms\\_multi\\_region](#input\\_kms\\_multi\\_region) | Whether to create a multi-region KMS key. | `bool` | `false` | no |\n| \u003ca name=\"input_kms_tags\"\u003e\u003c/a\u003e [kms\\_tags](#input\\_kms\\_tags) | Additional tags specific to KMS resources. | `map(string)` | `{}` | no |\n| \u003ca name=\"input_lifecycle_expire_tagged_after_days\"\u003e\u003c/a\u003e [lifecycle\\_expire\\_tagged\\_after\\_days](#input\\_lifecycle\\_expire\\_tagged\\_after\\_days) | Number of days after which tagged images expire (1-3650). Use with caution. | `number` | `null` | no |\n| \u003ca name=\"input_lifecycle_expire_untagged_after_days\"\u003e\u003c/a\u003e [lifecycle\\_expire\\_untagged\\_after\\_days](#input\\_lifecycle\\_expire\\_untagged\\_after\\_days) | Number of days after which untagged images expire (1-3650). Null to disable. | `number` | `null` | no |\n| \u003ca name=\"input_lifecycle_keep_latest_n_images\"\u003e\u003c/a\u003e [lifecycle\\_keep\\_latest\\_n\\_images](#input\\_lifecycle\\_keep\\_latest\\_n\\_images) | Number of latest images to keep in the repository (1-10000). Null to disable. | `number` | `null` | no |\n| \u003ca name=\"input_lifecycle_policy\"\u003e\u003c/a\u003e [lifecycle\\_policy](#input\\_lifecycle\\_policy) | JSON string representing the lifecycle policy. Takes precedence over helper variables. | `string` | `null` | no |\n| \u003ca name=\"input_lifecycle_policy_template\"\u003e\u003c/a\u003e [lifecycle\\_policy\\_template](#input\\_lifecycle\\_policy\\_template) | Predefined lifecycle policy template. Options: development, production, cost\\_optimization, compliance. | `string` | `null` | no |\n| \u003ca name=\"input_lifecycle_tag_prefixes_to_keep\"\u003e\u003c/a\u003e [lifecycle\\_tag\\_prefixes\\_to\\_keep](#input\\_lifecycle\\_tag\\_prefixes\\_to\\_keep) | List of tag prefixes for keep-latest rule. Empty list applies to all images. Max 100 prefixes. | `list(string)` | `[]` | no |\n| \u003ca name=\"input_log_retention_days\"\u003e\u003c/a\u003e [log\\_retention\\_days](#input\\_log\\_retention\\_days) | Number of days to retain ECR logs in CloudWatch. | `number` | `30` | no |\n| \u003ca name=\"input_monitoring_threshold_api_calls\"\u003e\u003c/a\u003e [monitoring\\_threshold\\_api\\_calls](#input\\_monitoring\\_threshold\\_api\\_calls) | API call volume threshold per minute to trigger CloudWatch alarm. | `number` | `1000` | no |\n| \u003ca name=\"input_monitoring_threshold_image_pull\"\u003e\u003c/a\u003e [monitoring\\_threshold\\_image\\_pull](#input\\_monitoring\\_threshold\\_image\\_pull) | Image pull frequency threshold per 5-minute period to trigger CloudWatch alarm. | `number` | `100` | no |\n| \u003ca name=\"input_monitoring_threshold_image_push\"\u003e\u003c/a\u003e [monitoring\\_threshold\\_image\\_push](#input\\_monitoring\\_threshold\\_image\\_push) | Image push frequency threshold per 5-minute period to trigger CloudWatch alarm. | `number` | `10` | no |\n| \u003ca name=\"input_monitoring_threshold_security_findings\"\u003e\u003c/a\u003e [monitoring\\_threshold\\_security\\_findings](#input\\_monitoring\\_threshold\\_security\\_findings) | Security findings threshold to trigger CloudWatch alarm. | `number` | `10` | no |\n| \u003ca name=\"input_monitoring_threshold_storage\"\u003e\u003c/a\u003e [monitoring\\_threshold\\_storage](#input\\_monitoring\\_threshold\\_storage) | Storage usage threshold in GB to trigger CloudWatch alarm. | `number` | `10` | no |\n| \u003ca name=\"input_name\"\u003e\u003c/a\u003e [name](#input\\_name) | Name of the ECR repository. This name must be unique within the AWS account and region. | `string` | n/a | yes |\n| \u003ca name=\"input_normalize_tag_values\"\u003e\u003c/a\u003e [normalize\\_tag\\_values](#input\\_normalize\\_tag\\_values) | Whether to normalize tag values by trimming whitespace. | `bool` | `true` | no |\n| \u003ca name=\"input_policy\"\u003e\u003c/a\u003e [policy](#input\\_policy) | JSON string representing the repository policy. If null, no policy is created. | `string` | `null` | no |\n| \u003ca name=\"input_prevent_destroy\"\u003e\u003c/a\u003e [prevent\\_destroy](#input\\_prevent\\_destroy) | Whether to protect the repository from being destroyed via lifecycle prevent\\_destroy. | `bool` | `false` | no |\n| \u003ca name=\"input_pull_request_rules\"\u003e\u003c/a\u003e [pull\\_request\\_rules](#input\\_pull\\_request\\_rules) | List of pull request rule configurations for enhanced governance. | \u003cpre\u003elist(object({\u003cbr/\u003e    name    = string\u003cbr/\u003e    type    = string\u003cbr/\u003e    enabled = bool\u003cbr/\u003e    conditions = optional(object({\u003cbr/\u003e      tag_patterns            = optional(list(string), [])\u003cbr/\u003e      severity_threshold      = optional(string, \"MEDIUM\")\u003cbr/\u003e      require_scan_completion = optional(bool, true)\u003cbr/\u003e      allowed_principals      = optional(list(string), [])\u003cbr/\u003e    }), {})\u003cbr/\u003e    actions = optional(object({\u003cbr/\u003e      require_approval_count = optional(number, 1)\u003cbr/\u003e      notification_topic_arn = optional(string)\u003cbr/\u003e      webhook_url            = optional(string)\u003cbr/\u003e      block_on_failure       = optional(bool, true)\u003cbr/\u003e      approval_timeout_hours = optional(number, 24)\u003cbr/\u003e    }), {})\u003cbr/\u003e  }))\u003c/pre\u003e | `[]` | no |\n| \u003ca name=\"input_pull_through_cache_rules\"\u003e\u003c/a\u003e [pull\\_through\\_cache\\_rules](#input\\_pull\\_through\\_cache\\_rules) | List of pull-through cache rules to create. | \u003cpre\u003elist(object({\u003cbr/\u003e    ecr_repository_prefix = string\u003cbr/\u003e    upstream_registry_url = string\u003cbr/\u003e    credential_arn        = optional(string)\u003cbr/\u003e  }))\u003c/pre\u003e | `[]` | no |\n| \u003ca name=\"input_registry_scan_filters\"\u003e\u003c/a\u003e [registry\\_scan\\_filters](#input\\_registry\\_scan\\_filters) | List of scan filters for filtering scan results when querying ECR findings. | \u003cpre\u003elist(object({\u003cbr/\u003e    name   = string\u003cbr/\u003e    values = list(string)\u003cbr/\u003e  }))\u003c/pre\u003e | `[]` | no |\n| \u003ca name=\"input_registry_scan_type\"\u003e\u003c/a\u003e [registry\\_scan\\_type](#input\\_registry\\_scan\\_type) | The type of scanning to configure for the registry. Either BASIC or ENHANCED. | `string` | `\"ENHANCED\"` | no |\n| \u003ca name=\"input_replication_regions\"\u003e\u003c/a\u003e [replication\\_regions](#input\\_replication\\_regions) | List of AWS regions to replicate ECR images to. | `list(string)` | `[]` | no |\n| \u003ca name=\"input_required_tags\"\u003e\u003c/a\u003e [required\\_tags](#input\\_required\\_tags) | List of tag keys that are required to be present. Empty list disables validation. | `list(string)` | `[]` | no |\n| \u003ca name=\"input_scan_on_push\"\u003e\u003c/a\u003e [scan\\_on\\_push](#input\\_scan\\_on\\_push) | Whether images should be scanned after being pushed to the repository. | `bool` | `true` | no |\n| \u003ca name=\"input_scan_repository_filters\"\u003e\u003c/a\u003e [scan\\_repository\\_filters](#input\\_scan\\_repository\\_filters) | List of repository filters to apply for registry scanning. Supports wildcards. | `list(string)` | \u003cpre\u003e[\u003cbr/\u003e  \"*\"\u003cbr/\u003e]\u003c/pre\u003e | no |\n| \u003ca name=\"input_sns_topic_name\"\u003e\u003c/a\u003e [sns\\_topic\\_name](#input\\_sns\\_topic\\_name) | Name of the SNS topic to create or use for alarm notifications. | `string` | `null` | no |\n| \u003ca name=\"input_sns_topic_subscribers\"\u003e\u003c/a\u003e [sns\\_topic\\_subscribers](#input\\_sns\\_topic\\_subscribers) | List of email addresses to subscribe to the SNS topic for alarm notifications. | `list(string)` | `[]` | no |\n| \u003ca name=\"input_tag_key_case\"\u003e\u003c/a\u003e [tag\\_key\\_case](#input\\_tag\\_key\\_case) | Enforce consistent casing for tag keys. Options: PascalCase, camelCase, snake\\_case, kebab-case. | `string` | `\"PascalCase\"` | no |\n| \u003ca name=\"input_tags\"\u003e\u003c/a\u003e [tags](#input\\_tags) | A map of tags to assign to all resources created by this module. | `map(string)` | `{}` | no |\n| \u003ca name=\"input_timeouts\"\u003e\u003c/a\u003e [timeouts](#input\\_timeouts) | Timeout configuration for repository operations. Example: { delete = \"20m\" } | \u003cpre\u003eobject({\u003cbr/\u003e    delete = optional(string)\u003cbr/\u003e  })\u003c/pre\u003e | `{}` | no |\n\n## Outputs\n\n| Name | Description |\n|------|-------------|\n| \u003ca name=\"output_applied_tags\"\u003e\u003c/a\u003e [applied\\_tags](#output\\_applied\\_tags) | The final set of tags applied to all resources after normalization and default tag application |\n| \u003ca name=\"output_cloudwatch_alarms\"\u003e\u003c/a\u003e [cloudwatch\\_alarms](#output\\_cloudwatch\\_alarms) | List of CloudWatch alarms created for ECR monitoring |\n| \u003ca name=\"output_cloudwatch_log_group_arn\"\u003e\u003c/a\u003e [cloudwatch\\_log\\_group\\_arn](#output\\_cloudwatch\\_log\\_group\\_arn) | The ARN of the CloudWatch Log Group used for ECR logs (if logging is enabled) |\n| \u003ca name=\"output_kms_alias_arn\"\u003e\u003c/a\u003e [kms\\_alias\\_arn](#output\\_kms\\_alias\\_arn) | The ARN of the KMS alias (if created by this module). |\n| \u003ca name=\"output_kms_configuration\"\u003e\u003c/a\u003e [kms\\_configuration](#output\\_kms\\_configuration) | Complete KMS configuration information. |\n| \u003ca name=\"output_kms_key_arn\"\u003e\u003c/a\u003e [kms\\_key\\_arn](#output\\_kms\\_key\\_arn) | The ARN of the KMS key used for repository encryption. |\n| \u003ca name=\"output_kms_key_id\"\u003e\u003c/a\u003e [kms\\_key\\_id](#output\\_kms\\_key\\_id) | The globally unique identifier for the KMS key (if created by this module). |\n| \u003ca name=\"output_lifecycle_policy\"\u003e\u003c/a\u003e [lifecycle\\_policy](#output\\_lifecycle\\_policy) | The lifecycle policy JSON applied to the repository (if any) |\n| \u003ca name=\"output_logging_role_arn\"\u003e\u003c/a\u003e [logging\\_role\\_arn](#output\\_logging\\_role\\_arn) | The ARN of the IAM role used for ECR logging (if logging is enabled) |\n| \u003ca name=\"output_monitoring_status\"\u003e\u003c/a\u003e [monitoring\\_status](#output\\_monitoring\\_status) | Status of CloudWatch monitoring configuration |\n| \u003ca name=\"output_pull_request_rules\"\u003e\u003c/a\u003e [pull\\_request\\_rules](#output\\_pull\\_request\\_rules) | Information about pull request rules configuration |\n| \u003ca name=\"output_pull_through_cache_role_arn\"\u003e\u003c/a\u003e [pull\\_through\\_cache\\_role\\_arn](#output\\_pull\\_through\\_cache\\_role\\_arn) | The ARN of the IAM role used for pull-through cache operations (if enabled) |\n| \u003ca name=\"output_pull_through_cache_rules\"\u003e\u003c/a\u003e [pull\\_through\\_cache\\_rules](#output\\_pull\\_through\\_cache\\_rules) | List of pull-through cache rules (if enabled) |\n| \u003ca name=\"output_registry_id\"\u003e\u003c/a\u003e [registry\\_id](#output\\_registry\\_id) | ID of the ECR registry |\n| \u003ca name=\"output_registry_scan_filters\"\u003e\u003c/a\u003e [registry\\_scan\\_filters](#output\\_registry\\_scan\\_filters) | The configured scan filters for filtering scan results (e.g., by vulnerability severity) |\n| \u003ca name=\"output_registry_scanning_configuration_arn\"\u003e\u003c/a\u003e [registry\\_scanning\\_configuration\\_arn](#output\\_registry\\_scanning\\_configuration\\_arn) | The ARN of the ECR registry scanning configuration (if enhanced scanning is enabled) |\n| \u003ca name=\"output_registry_scanning_status\"\u003e\u003c/a\u003e [registry\\_scanning\\_status](#output\\_registry\\_scanning\\_status) | Status of ECR registry scanning configuration |\n| \u003ca name=\"output_replication_configuration_arn\"\u003e\u003c/a\u003e [replication\\_configuration\\_arn](#output\\_replication\\_configuration\\_arn) | The ARN of the ECR replication configuration (if replication is enabled) |\n| \u003ca name=\"output_replication_regions\"\u003e\u003c/a\u003e [replication\\_regions](#output\\_replication\\_regions) | List of regions where ECR images are replicated to (if replication is enabled) |\n| \u003ca name=\"output_replication_status\"\u003e\u003c/a\u003e [replication\\_status](#output\\_replication\\_status) | Status of ECR replication configuration |\n| \u003ca name=\"output_repository_arn\"\u003e\u003c/a\u003e [repository\\_arn](#output\\_repository\\_arn) | ARN of the ECR repository |\n| \u003ca name=\"output_repository_name\"\u003e\u003c/a\u003e [repository\\_name](#output\\_repository\\_name) | Name of the ECR repository |\n| \u003ca name=\"output_repository_policy_exists\"\u003e\u003c/a\u003e [repository\\_policy\\_exists](#output\\_repository\\_policy\\_exists) | Whether a repository policy exists for this ECR repository |\n| \u003ca name=\"output_repository_url\"\u003e\u003c/a\u003e [repository\\_url](#output\\_repository\\_url) | URL of the ECR repository |\n| \u003ca name=\"output_security_status\"\u003e\u003c/a\u003e [security\\_status](#output\\_security\\_status) | Comprehensive security status of the ECR configuration |\n| \u003ca name=\"output_sns_topic_arn\"\u003e\u003c/a\u003e [sns\\_topic\\_arn](#output\\_sns\\_topic\\_arn) | ARN of the SNS topic used for ECR monitoring alerts (if created) |\n| \u003ca name=\"output_tag_compliance_status\"\u003e\u003c/a\u003e [tag\\_compliance\\_status](#output\\_tag\\_compliance\\_status) | Tag compliance and validation status |\n| \u003ca name=\"output_tagging_strategy\"\u003e\u003c/a\u003e [tagging","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flgallard%2Fterraform-aws-ecr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flgallard%2Fterraform-aws-ecr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flgallard%2Fterraform-aws-ecr/lists"}