diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl new file mode 100644 index 0000000..e646fb1 --- /dev/null +++ b/.terraform.lock.hcl @@ -0,0 +1,33 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.93.0" + constraints = ">= 3.0.0" + hashes = [ + "h1:Gix6sLHGKwqKg4L1V/gBa5tcjIj5UrqH4kW9AX/agl0=", + "zh:00e1b15e6f02cdc788fe855232b63ccce6652930080eac3ba4b8a2e35db02b23", + "zh:3a77ee12e4f5ab2e7b320a0f507389c9171ab82c50d39ae7caa5a1fb2bd95cb3", + "zh:3e32d58e139d098d867eef37914fef01fffb08504d828e0f384c2ffc18d71f80", + "zh:41cf69a525f0fbe0fdb71d26be7ff5e20bb90ccdf5af32c83ed53f0ca2f071b5", + "zh:43055bdd0786855cf7242638a74b579f74f4f1a8e7c7e5e0e50230c8f6b908cb", + "zh:4ac4c29aa0de842ad91145c5a5fba21338531ffca13a510927d445e007a24938", + "zh:57e510498b3aeb6d6155c10fa195e1d5502e763899251057e59e73f653d1e262", + "zh:8f749645b27dba1a07d06aaf9d5596fc4213123f12f3808d68539e78ab16996e", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:aaca5934ac6273d48922ad7685c5fc2aa7ef5275346a9e70366b7a180a788d41", + "zh:b7585b720a97467302f2e29f0688a5a746778f7b73c30eb085c25831decba1e1", + "zh:c16ae0a46d796858c49a89dd90e5ca92f793e646474fadeafaf701def4a4aa83", + "zh:d66bdc9cd5108452d9dba44082e504ff5e3a3001c8f853bbcaff850cb2127a21", + "zh:ee1aec6c44b117a6c8b7159ee7dc82f1ddac6ba434b4e6c493717738326f0a99", + "zh:f0da48692e00ecacea72d7104714d9721f6be40ba094490c442bb3e68d2e2604", + ] +} + +provider "registry.terraform.io/hashicorp/null" { + version = "3.2.3" + constraints = ">= 1.0.0" + hashes = [ + "h1:+AnORRgFbRO6qqcfaQyeX80W0eX3VmjadjnUFUJTiXo=", + ] +} diff --git a/.tflint.hcl b/.tflint.hcl index fcc2fa8..a4029d7 100644 --- a/.tflint.hcl +++ b/.tflint.hcl @@ -1,15 +1,15 @@ config { - module = true - force = false + module = true + force = false disabled_by_default = false -# ignore_module = { -# "terraform-aws-modules/vpc/aws" = true -# "terraform-aws-modules/security-group/aws" = true -# } + # ignore_module = { + # "terraform-aws-modules/vpc/aws" = true + # "terraform-aws-modules/security-group/aws" = true + # } -# varfile = ["example1.tfvars", "example2.tfvars"] -# variables = ["foo=bar", "bar=[\"baz\"]"] + # varfile = ["example1.tfvars", "example2.tfvars"] + # variables = ["foo=bar", "bar=[\"baz\"]"] } rule "aws_instance_invalid_type" { diff --git a/CHANGELOG.md b/CHANGELOG.md index 298df4f..d67d45f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,3 +39,6 @@ * 2.2.2 -- 2024-07-29 - only process image_config if enabled (default) + +* 2.2.3 -- 2025-03-27 + - add lifecycle time_out policy to expire images older than 365 days (default) diff --git a/README.md b/README.md index 7438a8c..eb710cd 100644 --- a/README.md +++ b/README.md @@ -206,8 +206,8 @@ Currently, a destroy of the images (null\_resources) does **NOT** remove the rep | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 3.0 | -| [null](#provider\_null) | >= 1.0 | +| [aws](#provider\_aws) | 5.93.0 | +| [null](#provider\_null) | 3.2.3 | ## Modules @@ -223,6 +223,7 @@ No modules. | [aws_ecr_lifecycle_policy.images_all](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_lifecycle_policy) | resource | | [aws_ecr_lifecycle_policy.patterns](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_lifecycle_policy) | resource | | [aws_ecr_lifecycle_policy.prefixes](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_lifecycle_policy) | resource | +| [aws_ecr_lifecycle_policy.time_out](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_lifecycle_policy) | resource | | [aws_ecr_repository.apps_repos](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_repository) | resource | | [aws_ecr_repository.image_repos](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_repository) | resource | | [null_resource.copy_images](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | @@ -235,6 +236,7 @@ No modules. | [aws_ecr_lifecycle_policy_document.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ecr_lifecycle_policy_document) | data source | | [aws_ecr_lifecycle_policy_document.patterns](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ecr_lifecycle_policy_document) | data source | | [aws_ecr_lifecycle_policy_document.prefixes](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ecr_lifecycle_policy_document) | data source | +| [aws_ecr_lifecycle_policy_document.time_out_lifecycle](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ecr_lifecycle_policy_document) | data source | | [aws_iam_account_alias.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_account_alias) | data source | | [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source | @@ -251,13 +253,14 @@ No modules. | [enable\_lifecycle\_policy](#input\_enable\_lifecycle\_policy) | Flag to enable/disable ECR lifecycle policy. If enabled, default is 5 most recent images (count) | `bool` | `false` | no | | [enable\_lifecycle\_policy\_image\_config](#input\_enable\_lifecycle\_policy\_image\_config) | Flag to enable/disable ECR lifecycle policy for images in the image\_config. If enabled, it will keep lifecycle\_policy\_keep\_count (default: 5) images | `bool` | `true` | no | | [force\_delete](#input\_force\_delete) | Flag to force delete of a repository even if contains images (warning!) | `bool` | `false` | no | -| [image\_config](#input\_image\_config) | List of image configuration objects to copy from SOURCE to DESTINATION |
list(object({
name = string,
tag = string,
dest_path = string,
source_registry = string,
source_image = string,
source_tag = string,
enabled = bool,
})) | `[]` | no |
+| [image\_config](#input\_image\_config) | List of image configuration objects to copy from SOURCE to DESTINATION | list(object({
name = string,
tag = string,
dest_path = string,
source_registry = string,
source_image = string,
source_tag = string,
enabled = bool,
})) | `[]` | no |
+| [lifecycle\_days](#input\_lifecycle\_days) | number of days after which we automatically expire images | `string` | `365` | no |
| [lifecycle\_policy\_all](#input\_lifecycle\_policy\_all) | Flag to enable the same default policy (any, count of 5) if true | `bool` | `false` | no |
-| [lifecycle\_policy\_default](#input\_lifecycle\_policy\_default) | Object with settings for selecting repositories to apply a policy for 'any'. Select repo list and number of images to keep (default: 5). | object({
repos = list(string)
count = optional(number, 5)
}) | {
"count": 5,
"repos": []
} | no |
-| [lifecycle\_policy\_explicit](#input\_lifecycle\_policy\_explicit) | Object with settings for selecting repositories to apply a policy for an explicit policy. Select repo list and number of images to keep (default: 5), and a policy defined using `data.aws_ecr_lifecycle_policy_document.{name}.json'` | object({
repos = list(string)
policy = string
}) | {
"policy": null,
"repos": []
} | no |
+| [lifecycle\_policy\_default](#input\_lifecycle\_policy\_default) | Object with settings for selecting repositories to apply a policy for 'any'. Select repo list and number of images to keep (default: 5). | object({
repos = list(string)
count = optional(number, 5)
}) | {
"count": 5,
"repos": []
} | no |
+| [lifecycle\_policy\_explicit](#input\_lifecycle\_policy\_explicit) | Object with settings for selecting repositories to apply a policy for an explicit policy. Select repo list and number of images to keep (default: 5), and a policy defined using `data.aws_ecr_lifecycle_policy_document.{name}.json'` | object({
repos = list(string)
policy = string
}) | {
"policy": null,
"repos": []
} | no |
| [lifecycle\_policy\_keep\_count](#input\_lifecycle\_policy\_keep\_count) | Number of images to keep when not specified per type of policy (default: 5) | `number` | `5` | no |
-| [lifecycle\_policy\_pattern](#input\_lifecycle\_policy\_pattern) | Object with settings for selecting repositories to apply a policy for 'pattern'. Select repo list and number of images to keep (default: 5), and a list of patterns (will create one rule per pattern). | object({
count = optional(number, 5)
repos = list(string)
values = list(string)
}) | {
"count": 5,
"repos": [],
"values": []
} | no |
-| [lifecycle\_policy\_prefix](#input\_lifecycle\_policy\_prefix) | Object with settings for selecting repositories to apply a policy for 'prefix'. Select repo list and number of images to keep (default: 5), and a list of prefixes (will create one rule per prefix). | object({
count = optional(number, 5)
repos = list(string)
values = list(string)
}) | {
"count": 5,
"repos": [],
"values": []
} | no |
+| [lifecycle\_policy\_pattern](#input\_lifecycle\_policy\_pattern) | Object with settings for selecting repositories to apply a policy for 'pattern'. Select repo list and number of images to keep (default: 5), and a list of patterns (will create one rule per pattern). | object({
count = optional(number, 5)
repos = list(string)
values = list(string)
}) | {
"count": 5,
"repos": [],
"values": []
} | no |
+| [lifecycle\_policy\_prefix](#input\_lifecycle\_policy\_prefix) | Object with settings for selecting repositories to apply a policy for 'prefix'. Select repo list and number of images to keep (default: 5), and a list of prefixes (will create one rule per prefix). | object({
count = optional(number, 5)
repos = list(string)
values = list(string)
}) | {
"count": 5,
"repos": [],
"values": []
} | no |
| [override\_prefixes](#input\_override\_prefixes) | Override built-in prefixes by component. This should be used primarily for common infrastructure things | `map(string)` | `{}` | no |
| [profile](#input\_profile) | AWS Profile Name, used generating key rotation file | `string` | n/a | yes |
| [region](#input\_region) | Region in which to create the ECR repositories (default of current region) | `string` | `null` | no |
diff --git a/ecr-lifecycle.tf b/ecr-lifecycle.tf
index edd9e6c..0622f9c 100644
--- a/ecr-lifecycle.tf
+++ b/ecr-lifecycle.tf
@@ -1,6 +1,6 @@
locals {
- default_count = var.lifecycle_policy_keep_count
-
+ default_count = var.lifecycle_policy_keep_count
+ default_lifecycle_days = var.lifecycle_days
lifecycle_policy_specific_repos = compact(distinct(concat(
try(var.lifecycle_policy_default.repos, []),
try(var.lifecycle_policy_prefix.repos, []),
@@ -21,6 +21,18 @@ data "aws_ecr_lifecycle_policy_document" "any" {
}
}
+data "aws_ecr_lifecycle_policy_document" "time_out_lifecycle" {
+ rule {
+ priority = 9
+ description = format("expire images older than %v", try(var.lifecycle_days, local.default_lifecycle_days))
+ selection {
+ tag_status = "any"
+ count_type = "sinceImagePushed"
+ count_unit = "days"
+ count_number = var.lifecycle_days
+ }
+ }
+}
data "aws_ecr_lifecycle_policy_document" "default" {
count = length(var.lifecycle_policy_default.repos) > 0 ? 1 : 0
@@ -92,7 +104,7 @@ data "aws_ecr_lifecycle_policy_document" "patterns" {
# set policies
resource "aws_ecr_lifecycle_policy" "all" {
- for_each = var.lifecycle_policy_all ? { for k, v in aws_ecr_repository.apps_repos : k => v if ! contains(local.lifecycle_policy_specific_repos, k) } : {}
+ for_each = var.lifecycle_policy_all ? { for k, v in aws_ecr_repository.apps_repos : k => v if !contains(local.lifecycle_policy_specific_repos, k) } : {}
repository = each.value.name
policy = data.aws_ecr_lifecycle_policy_document.any.json
}
@@ -103,6 +115,12 @@ resource "aws_ecr_lifecycle_policy" "images_all" {
policy = data.aws_ecr_lifecycle_policy_document.any.json
}
+resource "aws_ecr_lifecycle_policy" "time_out" {
+ for_each = var.lifecycle_policy_all ? { for k, v in aws_ecr_repository.apps_repos : k => v if !contains(local.lifecycle_policy_specific_repos, k) } : {}
+ repository = each.value.name
+ policy = data.aws_ecr_lifecycle_policy_document.any.json
+}
+
resource "aws_ecr_lifecycle_policy" "default" {
for_each = toset(try(var.lifecycle_policy_default.repos, []))
repository = aws_ecr_repository.apps_repos[each.key].name
diff --git a/examples/lifecycle-policy-eks-cluster/images.tf b/examples/lifecycle-policy-eks-cluster/images.tf
index e903db6..652f387 100644
--- a/examples/lifecycle-policy-eks-cluster/images.tf
+++ b/examples/lifecycle-policy-eks-cluster/images.tf
@@ -16,6 +16,7 @@ module "images" {
lifecycle_policy_all = true
enable_lifecycle_policy_image_config = true
lifecycle_policy_keep_count = 5
+ lifecycle_days = 365
image_config = local.image_config
tags = merge(
local.base_tags,
diff --git a/variables.tf b/variables.tf
index 2ef6dca..311a5f6 100644
--- a/variables.tf
+++ b/variables.tf
@@ -142,3 +142,9 @@ variable "force_delete" {
type = bool
default = false
}
+
+variable "lifecycle_days" {
+ description = "number of days after which we automatically expire images"
+ type = string
+ default = 365
+}