Skip to content

Commit 067d899

Browse files
authored
Merge pull request #43886 from tabito-hara/f-aws_ecr_repository_creation_template-support_image_tag_mutability_exclusion_filter
[Enhancement] aws_ecr_repository_creation_template: Support `image_tag_mutability_exclusion_filter`
2 parents 4fba752 + f23227a commit 067d899

10 files changed

+277
-0
lines changed

.changelog/43886.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
```release-note:enhancement
2+
resource/aws_ecr_repository_creation_template: Add `image_tag_mutability_exclusion_filter` configuration block
3+
```
4+
5+
```release-note:enhancement
6+
data-source/aws_ecr_repository_creation_template: Add `image_tag_mutability_exclusion_filter` attribute
7+
```
8+
9+
```release-note:enhancement
10+
data-source/aws_ecr_repository: Add `image_tag_mutability_exclusion_filter` attribute
11+
```

internal/service/ecr/repository_creation_template.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,32 @@ func resourceRepositoryCreationTemplate() *schema.Resource {
8686
Default: types.ImageTagMutabilityMutable,
8787
ValidateDiagFunc: enum.Validate[types.ImageTagMutability](),
8888
},
89+
"image_tag_mutability_exclusion_filter": {
90+
Type: schema.TypeList,
91+
Optional: true,
92+
MaxItems: 5,
93+
Elem: &schema.Resource{
94+
Schema: map[string]*schema.Schema{
95+
names.AttrFilter: {
96+
Type: schema.TypeString,
97+
Required: true,
98+
ValidateDiagFunc: validation.AllDiag(
99+
validation.ToDiagFunc(validation.StringLenBetween(1, 128)),
100+
validation.ToDiagFunc(validation.StringMatch(
101+
regexache.MustCompile(`^[a-zA-Z0-9._*-]+$`),
102+
"must contain only letters, numbers, and special characters (._*-)",
103+
)),
104+
validateImageTagMutabilityExclusionFilter(),
105+
),
106+
},
107+
"filter_type": {
108+
Type: schema.TypeString,
109+
Required: true,
110+
ValidateDiagFunc: enum.Validate[types.ImageTagMutabilityExclusionFilterType](),
111+
},
112+
},
113+
},
114+
},
89115
"lifecycle_policy": {
90116
Type: schema.TypeString,
91117
Optional: true,
@@ -141,6 +167,10 @@ func resourceRepositoryCreationTemplateCreate(ctx context.Context, d *schema.Res
141167
input.Description = aws.String(v.(string))
142168
}
143169

170+
if v, ok := d.GetOk("image_tag_mutability_exclusion_filter"); ok && len(v.([]any)) > 0 && v.([]any)[0] != nil {
171+
input.ImageTagMutabilityExclusionFilters = expandImageTagMutabilityExclusionFilters(v.([]any))
172+
}
173+
144174
if v, ok := d.GetOk("lifecycle_policy"); ok {
145175
policy, err := structure.NormalizeJsonString(v.(string))
146176
if err != nil {
@@ -198,6 +228,10 @@ func resourceRepositoryCreationTemplateRead(ctx context.Context, d *schema.Resou
198228
}
199229
d.Set("image_tag_mutability", rct.ImageTagMutability)
200230

231+
if err := d.Set("image_tag_mutability_exclusion_filter", flattenImageTagMutabilityExclusionFilters(rct.ImageTagMutabilityExclusionFilters)); err != nil {
232+
return sdkdiag.AppendErrorf(diags, "setting image_tag_mutability_exclusion_filter: %s", err)
233+
}
234+
201235
if _, err := equivalentLifecyclePolicyJSON(d.Get("lifecycle_policy").(string), aws.ToString(rct.LifecyclePolicy)); err != nil {
202236
return sdkdiag.AppendFromErr(diags, err)
203237
}
@@ -258,6 +292,12 @@ func resourceRepositoryCreationTemplateUpdate(ctx context.Context, d *schema.Res
258292
input.ImageTagMutability = types.ImageTagMutability((d.Get("image_tag_mutability").(string)))
259293
}
260294

295+
if d.HasChange("image_tag_mutability_exclusion_filter") {
296+
// To use image_tag_mutability_exclusion_filter, image_tag_mutability must be set
297+
input.ImageTagMutability = types.ImageTagMutability((d.Get("image_tag_mutability").(string)))
298+
input.ImageTagMutabilityExclusionFilters = expandImageTagMutabilityExclusionFilters(d.Get("image_tag_mutability_exclusion_filter").([]any))
299+
}
300+
261301
if d.HasChange("lifecycle_policy") {
262302
policy, err := structure.NormalizeJsonString(d.Get("lifecycle_policy").(string))
263303
if err != nil {

internal/service/ecr/repository_creation_template_data_source.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,22 @@ func dataSourceRepositoryCreationTemplate() *schema.Resource {
5959
Type: schema.TypeString,
6060
Computed: true,
6161
},
62+
"image_tag_mutability_exclusion_filter": {
63+
Type: schema.TypeList,
64+
Computed: true,
65+
Elem: &schema.Resource{
66+
Schema: map[string]*schema.Schema{
67+
names.AttrFilter: {
68+
Type: schema.TypeString,
69+
Computed: true,
70+
},
71+
"filter_type": {
72+
Type: schema.TypeString,
73+
Computed: true,
74+
},
75+
},
76+
},
77+
},
6278
"lifecycle_policy": {
6379
Type: schema.TypeString,
6480
Computed: true,
@@ -106,6 +122,9 @@ func dataSourceRepositoryCreationTemplateRead(ctx context.Context, d *schema.Res
106122
return sdkdiag.AppendErrorf(diags, "setting encryption_configuration: %s", err)
107123
}
108124
d.Set("image_tag_mutability", rct.ImageTagMutability)
125+
if err := d.Set("image_tag_mutability_exclusion_filter", flattenImageTagMutabilityExclusionFilters(rct.ImageTagMutabilityExclusionFilters)); err != nil {
126+
return sdkdiag.AppendErrorf(diags, "setting image_tag_mutability_exclusion_filter: %s", err)
127+
}
109128

110129
policy, err := structure.NormalizeJsonString(aws.ToString(rct.LifecyclePolicy))
111130
if err != nil {

internal/service/ecr/repository_creation_template_data_source_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,31 @@ func TestAccECRRepositoryCreationTemplateDataSource_root(t *testing.T) {
6666
})
6767
}
6868

69+
func TestAccECRRepositoryCreationTemplateDataSource_mutabilityWithExclusion(t *testing.T) {
70+
ctx := acctest.Context(t)
71+
repositoryPrefix := "tf-test-" + sdkacctest.RandString(8)
72+
dataSource := "data.aws_ecr_repository_creation_template.root"
73+
74+
resource.Test(t, resource.TestCase{
75+
PreCheck: func() { acctest.PreCheck(ctx, t) },
76+
ErrorCheck: acctest.ErrorCheck(t, names.ECRServiceID),
77+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
78+
Steps: []resource.TestStep{
79+
{
80+
Config: testAccRepositoryCreationTemplateDataSourceConfig_mutabilityWithExclusion(repositoryPrefix, "latest*", "prod-*"),
81+
Check: resource.ComposeAggregateTestCheckFunc(
82+
resource.TestCheckResourceAttr(dataSource, "image_tag_mutability", string(types.ImageTagMutabilityMutableWithExclusion)),
83+
resource.TestCheckResourceAttr(dataSource, "image_tag_mutability_exclusion_filter.#", "2"),
84+
resource.TestCheckResourceAttr(dataSource, "image_tag_mutability_exclusion_filter.0.filter", "latest*"),
85+
resource.TestCheckResourceAttr(dataSource, "image_tag_mutability_exclusion_filter.0.filter_type", string(types.ImageTagMutabilityExclusionFilterTypeWildcard)),
86+
resource.TestCheckResourceAttr(dataSource, "image_tag_mutability_exclusion_filter.1.filter", "prod-*"),
87+
resource.TestCheckResourceAttr(dataSource, "image_tag_mutability_exclusion_filter.1.filter_type", string(types.ImageTagMutabilityExclusionFilterTypeWildcard)),
88+
),
89+
},
90+
},
91+
})
92+
}
93+
6994
func testAccRepositoryCreationTemplateDataSourceConfig_basic(repositoryPrefix string) string {
7095
return fmt.Sprintf(`
7196
resource "aws_ecr_repository_creation_template" "test" {
@@ -101,3 +126,35 @@ data "aws_ecr_repository_creation_template" "root" {
101126
}
102127
`
103128
}
129+
130+
func testAccRepositoryCreationTemplateDataSourceConfig_mutabilityWithExclusion(repositoryPrefix, filter1, filter2 string) string {
131+
return fmt.Sprintf(`
132+
resource "aws_ecr_repository_creation_template" "test" {
133+
prefix = %[1]q
134+
135+
applied_for = [
136+
"PULL_THROUGH_CACHE",
137+
"REPLICATION",
138+
]
139+
140+
resource_tags = {
141+
Foo = "Bar"
142+
}
143+
144+
image_tag_mutability = "MUTABLE_WITH_EXCLUSION"
145+
146+
image_tag_mutability_exclusion_filter {
147+
filter = %[2]q
148+
filter_type = "WILDCARD"
149+
}
150+
151+
image_tag_mutability_exclusion_filter {
152+
filter = %[3]q
153+
filter_type = "WILDCARD"
154+
}
155+
}
156+
data "aws_ecr_repository_creation_template" "root" {
157+
prefix = aws_ecr_repository_creation_template.test.prefix
158+
}
159+
`, repositoryPrefix, filter1, filter2)
160+
}

internal/service/ecr/repository_creation_template_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,50 @@ func TestAccECRRepositoryCreationTemplate_root(t *testing.T) {
197197
})
198198
}
199199

200+
func TestAccECRRepositoryCreationTemplate_mutabilityWithExclusion(t *testing.T) {
201+
ctx := acctest.Context(t)
202+
repositoryPrefix := "tf-test-" + sdkacctest.RandString(8)
203+
resourceName := "aws_ecr_repository_creation_template.test"
204+
205+
resource.ParallelTest(t, resource.TestCase{
206+
PreCheck: func() { acctest.PreCheck(ctx, t) },
207+
ErrorCheck: acctest.ErrorCheck(t, names.ECRServiceID),
208+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
209+
CheckDestroy: testAccCheckRepositoryCreationTemplateDestroy(ctx),
210+
Steps: []resource.TestStep{
211+
{
212+
Config: testAccRepositoryCreationTemplateConfig_mutabilityWithExclusion(repositoryPrefix, "latest*", "prod-*"),
213+
Check: resource.ComposeAggregateTestCheckFunc(
214+
testAccCheckRepositoryCreationTemplateExists(ctx, resourceName),
215+
resource.TestCheckResourceAttr(resourceName, "image_tag_mutability", string(types.ImageTagMutabilityMutableWithExclusion)),
216+
resource.TestCheckResourceAttr(resourceName, "image_tag_mutability_exclusion_filter.#", "2"),
217+
resource.TestCheckResourceAttr(resourceName, "image_tag_mutability_exclusion_filter.0.filter", "latest*"),
218+
resource.TestCheckResourceAttr(resourceName, "image_tag_mutability_exclusion_filter.0.filter_type", string(types.ImageTagMutabilityExclusionFilterTypeWildcard)),
219+
resource.TestCheckResourceAttr(resourceName, "image_tag_mutability_exclusion_filter.1.filter", "prod-*"),
220+
resource.TestCheckResourceAttr(resourceName, "image_tag_mutability_exclusion_filter.1.filter_type", string(types.ImageTagMutabilityExclusionFilterTypeWildcard)),
221+
),
222+
},
223+
{
224+
ResourceName: resourceName,
225+
ImportState: true,
226+
ImportStateVerify: true,
227+
},
228+
{
229+
Config: testAccRepositoryCreationTemplateConfig_mutabilityWithExclusion(repositoryPrefix, "prod-*", "latest*"),
230+
Check: resource.ComposeAggregateTestCheckFunc(
231+
testAccCheckRepositoryCreationTemplateExists(ctx, resourceName),
232+
resource.TestCheckResourceAttr(resourceName, "image_tag_mutability", string(types.ImageTagMutabilityMutableWithExclusion)),
233+
resource.TestCheckResourceAttr(resourceName, "image_tag_mutability_exclusion_filter.#", "2"),
234+
resource.TestCheckResourceAttr(resourceName, "image_tag_mutability_exclusion_filter.0.filter", "prod-*"),
235+
resource.TestCheckResourceAttr(resourceName, "image_tag_mutability_exclusion_filter.0.filter_type", string(types.ImageTagMutabilityExclusionFilterTypeWildcard)),
236+
resource.TestCheckResourceAttr(resourceName, "image_tag_mutability_exclusion_filter.1.filter", "latest*"),
237+
resource.TestCheckResourceAttr(resourceName, "image_tag_mutability_exclusion_filter.1.filter_type", string(types.ImageTagMutabilityExclusionFilterTypeWildcard)),
238+
),
239+
},
240+
},
241+
})
242+
}
243+
200244
func testAccCheckRepositoryCreationTemplateDestroy(ctx context.Context) resource.TestCheckFunc {
201245
return func(s *terraform.State) error {
202246
conn := acctest.Provider.Meta().(*conns.AWSClient).ECRClient(ctx)
@@ -435,3 +479,32 @@ resource "aws_ecr_repository_creation_template" "root" {
435479
}
436480
`
437481
}
482+
483+
func testAccRepositoryCreationTemplateConfig_mutabilityWithExclusion(repositoryPrefix, filter1, filter2 string) string {
484+
return fmt.Sprintf(`
485+
resource "aws_ecr_repository_creation_template" "test" {
486+
prefix = %[1]q
487+
488+
applied_for = [
489+
"PULL_THROUGH_CACHE",
490+
"REPLICATION",
491+
]
492+
493+
resource_tags = {
494+
Foo = "Bar"
495+
}
496+
497+
image_tag_mutability = "MUTABLE_WITH_EXCLUSION"
498+
499+
image_tag_mutability_exclusion_filter {
500+
filter = %[2]q
501+
filter_type = "WILDCARD"
502+
}
503+
504+
image_tag_mutability_exclusion_filter {
505+
filter = %[3]q
506+
filter_type = "WILDCARD"
507+
}
508+
}
509+
`, repositoryPrefix, filter1, filter2)
510+
}

internal/service/ecr/repository_data_source.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,22 @@ func dataSourceRepository() *schema.Resource {
6161
Type: schema.TypeString,
6262
Computed: true,
6363
},
64+
"image_tag_mutability_exclusion_filter": {
65+
Type: schema.TypeList,
66+
Computed: true,
67+
Elem: &schema.Resource{
68+
Schema: map[string]*schema.Schema{
69+
names.AttrFilter: {
70+
Type: schema.TypeString,
71+
Computed: true,
72+
},
73+
"filter_type": {
74+
Type: schema.TypeString,
75+
Computed: true,
76+
},
77+
},
78+
},
79+
},
6480
"most_recent_image_tags": {
6581
Type: schema.TypeList,
6682
Computed: true,
@@ -113,6 +129,9 @@ func dataSourceRepositoryRead(ctx context.Context, d *schema.ResourceData, meta
113129
return sdkdiag.AppendErrorf(diags, "setting image_scanning_configuration: %s", err)
114130
}
115131
d.Set("image_tag_mutability", repository.ImageTagMutability)
132+
if err := d.Set("image_tag_mutability_exclusion_filter", flattenImageTagMutabilityExclusionFilters(repository.ImageTagMutabilityExclusionFilters)); err != nil {
133+
return sdkdiag.AppendErrorf(diags, "setting image_tag_mutability_exclusion_filter: %s", err)
134+
}
116135
d.Set(names.AttrName, repository.RepositoryName)
117136
d.Set("registry_id", repository.RegistryId)
118137
d.Set("repository_url", repository.RepositoryUri)

internal/service/ecr/repository_data_source_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,29 @@ func TestAccECRRepositoryDataSource_encryption(t *testing.T) {
7171
})
7272
}
7373

74+
func TestAccECRRepositoryDataSource_mutabilityWithExclusion(t *testing.T) {
75+
ctx := acctest.Context(t)
76+
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
77+
resourceName := "aws_ecr_repository.test"
78+
dataSourceName := "data.aws_ecr_repository.test"
79+
80+
resource.ParallelTest(t, resource.TestCase{
81+
PreCheck: func() { acctest.PreCheck(ctx, t) },
82+
ErrorCheck: acctest.ErrorCheck(t, names.ECRServiceID),
83+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
84+
Steps: []resource.TestStep{
85+
{
86+
Config: testAccRepositoryDataSourceConfig_mutabilityWithExclusion(rName, "test*"),
87+
Check: resource.ComposeTestCheckFunc(
88+
resource.TestCheckResourceAttr(dataSourceName, "image_tag_mutability_exclusion_filter.#", "1"),
89+
resource.TestCheckResourceAttrPair(resourceName, "image_tag_mutability_exclusion_filter.0.filter", dataSourceName, "image_tag_mutability_exclusion_filter.0.filter"),
90+
resource.TestCheckResourceAttrPair(resourceName, "image_tag_mutability_exclusion_filter.0.filter_type", dataSourceName, "image_tag_mutability_exclusion_filter.0.filter_type"),
91+
),
92+
},
93+
},
94+
})
95+
}
96+
7497
func TestAccECRRepositoryDataSource_nonExistent(t *testing.T) {
7598
ctx := acctest.Context(t)
7699
resource.ParallelTest(t, resource.TestCase{
@@ -130,3 +153,20 @@ data "aws_ecr_repository" "test" {
130153
}
131154
`, rName)
132155
}
156+
157+
func testAccRepositoryDataSourceConfig_mutabilityWithExclusion(rName, filter string) string {
158+
return fmt.Sprintf(`
159+
resource "aws_ecr_repository" "test" {
160+
name = %[1]q
161+
image_tag_mutability = "MUTABLE_WITH_EXCLUSION"
162+
163+
image_tag_mutability_exclusion_filter {
164+
filter = %[2]q
165+
filter_type = "WILDCARD"
166+
}
167+
}
168+
data "aws_ecr_repository" "test" {
169+
name = aws_ecr_repository.test.name
170+
}
171+
`, rName, filter)
172+
}

website/docs/d/ecr_repository.html.markdown

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ This data source exports the following attributes in addition to the arguments a
3434
* `encryption_configuration` - Encryption configuration for the repository. See [Encryption Configuration](#encryption-configuration) below.
3535
* `image_scanning_configuration` - Configuration block that defines image scanning configuration for the repository. See [Image Scanning Configuration](#image-scanning-configuration) below.
3636
* `image_tag_mutability` - The tag mutability setting for the repository.
37+
* `image_tag_mutability_exclusion_filter` - Block that defines filters to specify which image tags can override the default tag mutability setting.
3738
* `most_recent_image_tags` - List of image tags associated with the most recently pushed image in the repository.
3839
* `repository_url` - URL of the repository (in the form `aws_account_id.dkr.ecr.region.amazonaws.com/repositoryName`).
3940
* `tags` - Map of tags assigned to the resource.
@@ -43,6 +44,11 @@ This data source exports the following attributes in addition to the arguments a
4344
* `encryption_type` - Encryption type to use for the repository, either `AES256` or `KMS`.
4445
* `kms_key` - If `encryption_type` is `KMS`, the ARN of the KMS key used.
4546

47+
### Image Tag Mutability Exclusion Filter
48+
49+
* `filter` - The filter pattern to use for excluding image tags from the mutability setting.
50+
* `filter_type` - The type of filter to use.
51+
4652
### Image Scanning Configuration
4753

4854
* `scan_on_push` - Whether images are scanned after being pushed to the repository.

website/docs/d/ecr_repository_creation_template.html.markdown

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ This data source exports the following attributes in addition to the arguments a
3434
* `description` - The description for this template.
3535
* `encryption_configuration` - Encryption configuration for any created repositories. See [Encryption Configuration](#encryption-configuration) below.
3636
* `image_tag_mutability` - The tag mutability setting for any created repositories.
37+
* `image_tag_mutability_exclusion_filter` - Block that defines filters to specify which image tags can override the default tag mutability setting.
3738
* `lifecycle_policy` - The lifecycle policy document to apply to any created repositories.
3839
* `registry_id` - The registry ID the repository creation template applies to.
3940
* `repository_policy` - The registry policy document to apply to any created repositories.
@@ -43,3 +44,8 @@ This data source exports the following attributes in addition to the arguments a
4344

4445
* `encryption_type` - Encryption type to use for any created repositories, either `AES256` or `KMS`.
4546
* `kms_key` - If `encryption_type` is `KMS`, the ARN of the KMS key used.
47+
48+
### Image Tag Mutability Exclusion Filter
49+
50+
* `filter` - The filter pattern to use for excluding image tags from the mutability setting.
51+
* `filter_type` - The type of filter to use.

0 commit comments

Comments
 (0)