diff --git a/.changelog/21231.txt b/.changelog/21231.txt new file mode 100644 index 00000000000..80d6553af73 --- /dev/null +++ b/.changelog/21231.txt @@ -0,0 +1,4 @@ + +```release-note:enhancement +resource/aws_ecr_replication_configuration: Add `repository_filter` to `replication_configuration` block +``` \ No newline at end of file diff --git a/internal/service/ecr/replication_configuration.go b/internal/service/ecr/replication_configuration.go index 02faae98982..dcb0ce7516a 100644 --- a/internal/service/ecr/replication_configuration.go +++ b/internal/service/ecr/replication_configuration.go @@ -7,6 +7,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ecr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/verify" ) @@ -44,8 +45,9 @@ func ResourceReplicationConfiguration() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "region": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: verify.ValidRegionName, }, "registry_id": { Type: schema.TypeString, @@ -55,6 +57,25 @@ func ResourceReplicationConfiguration() *schema.Resource { }, }, }, + "repository_filter": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 100, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "filter": { + Type: schema.TypeString, + Required: true, + }, + "filter_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(ecr.RepositoryFilterType_Values(), false), + }, + }, + }, + }, }, }, }, @@ -153,7 +174,8 @@ func expandEcrReplicationConfigurationReplicationConfigurationRules(data []inter for _, rule := range data { ec := rule.(map[string]interface{}) config := &ecr.ReplicationRule{ - Destinations: expandEcrReplicationConfigurationReplicationConfigurationRulesDestinations(ec["destination"].([]interface{})), + Destinations: expandEcrReplicationConfigurationReplicationConfigurationRulesDestinations(ec["destination"].([]interface{})), + RepositoryFilters: expandEcrReplicationConfigurationReplicationConfigurationRulesRepositoryFilters(ec["repository_filter"].([]interface{})), } rules = append(rules, config) @@ -171,7 +193,8 @@ func flattenEcrReplicationConfigurationReplicationConfigurationRules(ec []*ecr.R for _, apiObject := range ec { tfMap := map[string]interface{}{ - "destination": flattenEcrReplicationConfigurationReplicationConfigurationRulesDestinations(apiObject.Destinations), + "destination": flattenEcrReplicationConfigurationReplicationConfigurationRulesDestinations(apiObject.Destinations), + "repository_filter": flattenEcrReplicationConfigurationReplicationConfigurationRulesRepositoryFilters(apiObject.RepositoryFilters), } tfList = append(tfList, tfMap) @@ -217,3 +240,41 @@ func flattenEcrReplicationConfigurationReplicationConfigurationRulesDestinations return tfList } + +func expandEcrReplicationConfigurationReplicationConfigurationRulesRepositoryFilters(data []interface{}) []*ecr.RepositoryFilter { + if len(data) == 0 || data[0] == nil { + return nil + } + + var filters []*ecr.RepositoryFilter + + for _, filter := range data { + ec := filter.(map[string]interface{}) + config := &ecr.RepositoryFilter{ + Filter: aws.String(ec["filter"].(string)), + FilterType: aws.String(ec["filter_type"].(string)), + } + + filters = append(filters, config) + } + return filters +} + +func flattenEcrReplicationConfigurationReplicationConfigurationRulesRepositoryFilters(ec []*ecr.RepositoryFilter) []interface{} { + if len(ec) == 0 { + return nil + } + + var tfList []interface{} + + for _, apiObject := range ec { + tfMap := map[string]interface{}{ + "filter": aws.StringValue(apiObject.Filter), + "filter_type": aws.StringValue(apiObject.FilterType), + } + + tfList = append(tfList, tfMap) + } + + return tfList +} diff --git a/internal/service/ecr/replication_configuration_test.go b/internal/service/ecr/replication_configuration_test.go index 1ee9eb193fa..114cbfe5641 100644 --- a/internal/service/ecr/replication_configuration_test.go +++ b/internal/service/ecr/replication_configuration_test.go @@ -11,10 +11,25 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/conns" ) -func TestAccECRReplicationConfiguration_basic(t *testing.T) { +func TestAccECRReplicationConfiguration_serial(t *testing.T) { + testFuncs := map[string]func(t *testing.T){ + "basic": testAccReplicationConfiguration_basic, + "repositoryFilter": testAccReplicationConfiguration_repositoryFilter, + } + + for name, testFunc := range testFuncs { + testFunc := testFunc + + t.Run(name, func(t *testing.T) { + testFunc(t) + }) + } +} + +func testAccReplicationConfiguration_basic(t *testing.T) { resourceName := "aws_ecr_replication_configuration.test" - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, ecr.EndpointsID), Providers: acctest.Providers, @@ -30,6 +45,7 @@ func TestAccECRReplicationConfiguration_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.destination.#", "1"), resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.destination.0.region", acctest.AlternateRegion()), acctest.CheckResourceAttrAccountID(resourceName, "replication_configuration.0.rule.0.destination.0.registry_id"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.repository_filter.#", "0"), ), }, { @@ -49,6 +65,7 @@ func TestAccECRReplicationConfiguration_basic(t *testing.T) { acctest.CheckResourceAttrAccountID(resourceName, "replication_configuration.0.rule.0.destination.0.registry_id"), resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.destination.1.region", acctest.ThirdRegion()), acctest.CheckResourceAttrAccountID(resourceName, "replication_configuration.0.rule.0.destination.1.registry_id"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.repository_filter.#", "0"), ), }, { @@ -61,6 +78,65 @@ func TestAccECRReplicationConfiguration_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.destination.#", "1"), resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.destination.0.region", acctest.AlternateRegion()), acctest.CheckResourceAttrAccountID(resourceName, "replication_configuration.0.rule.0.destination.0.registry_id"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.repository_filter.#", "0"), + ), + }, + }, + }) +} + +func testAccReplicationConfiguration_repositoryFilter(t *testing.T) { + resourceName := "aws_ecr_replication_configuration.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, ecr.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckReplicationConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccReplicationConfigurationRepositoryFilter(acctest.AlternateRegion()), + Check: resource.ComposeTestCheckFunc( + testAccCheckReplicationConfigurationExists(resourceName), + acctest.CheckResourceAttrAccountID(resourceName, "registry_id"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.destination.#", "1"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.repository_filter.#", "1"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.repository_filter.0.filter", "a-prefix"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.repository_filter.0.filter_type", "PREFIX_MATCH"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccReplicationConfigurationRepositoryFilterMultiple(acctest.AlternateRegion()), + Check: resource.ComposeTestCheckFunc( + testAccCheckReplicationConfigurationExists(resourceName), + acctest.CheckResourceAttrAccountID(resourceName, "registry_id"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.repository_filter.#", "2"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.repository_filter.0.filter", "a-prefix"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.repository_filter.0.filter_type", "PREFIX_MATCH"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.repository_filter.1.filter", "a-second-prefix"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.repository_filter.1.filter_type", "PREFIX_MATCH"), + ), + }, + { + Config: testAccReplicationConfigurationRepositoryFilter(acctest.AlternateRegion()), + Check: resource.ComposeTestCheckFunc( + testAccCheckReplicationConfigurationExists(resourceName), + acctest.CheckResourceAttrAccountID(resourceName, "registry_id"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.destination.#", "1"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.repository_filter.#", "1"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.repository_filter.0.filter", "a-prefix"), + resource.TestCheckResourceAttr(resourceName, "replication_configuration.0.rule.0.repository_filter.0.filter_type", "PREFIX_MATCH"), ), }, }, @@ -148,3 +224,52 @@ resource "aws_ecr_replication_configuration" "test" { } `, region1, region2) } + +func testAccReplicationConfigurationRepositoryFilter(region string) string { + return fmt.Sprintf(` +data "aws_caller_identity" "current" {} + +resource "aws_ecr_replication_configuration" "test" { + replication_configuration { + rule { + destination { + region = %[1]q + registry_id = data.aws_caller_identity.current.account_id + } + + repository_filter { + filter = "a-prefix" + filter_type = "PREFIX_MATCH" + } + } + } +} +`, region) +} + +func testAccReplicationConfigurationRepositoryFilterMultiple(region string) string { + return fmt.Sprintf(` +data "aws_caller_identity" "current" {} + +resource "aws_ecr_replication_configuration" "test" { + replication_configuration { + rule { + destination { + region = %[1]q + registry_id = data.aws_caller_identity.current.account_id + } + + repository_filter { + filter = "a-prefix" + filter_type = "PREFIX_MATCH" + } + + repository_filter { + filter = "a-second-prefix" + filter_type = "PREFIX_MATCH" + } + } + } +} +`, region) +} diff --git a/website/docs/r/ecr_replication_configuration.html.markdown b/website/docs/r/ecr_replication_configuration.html.markdown index ac0db4c05f0..7603b1011ea 100644 --- a/website/docs/r/ecr_replication_configuration.html.markdown +++ b/website/docs/r/ecr_replication_configuration.html.markdown @@ -53,6 +53,30 @@ resource "aws_ecr_replication_configuration" "example" { } ``` +## Repository Filter Usage + +```terraform +data "aws_caller_identity" "current" {} + +data "aws_regions" "example" {} + +resource "aws_ecr_replication_configuration" "example" { + replication_configuration { + rule { + destination { + region = data.aws_regions.example.names[0] + registry_id = data.aws_caller_identity.current.account_id + } + + repository_filter { + filter = "prod-microservice" + filter_type = "PREFIX_MATCH" + } + } + } +} +``` + ## Argument Reference The following arguments are supported: @@ -66,12 +90,18 @@ The following arguments are supported: ### Rule * `destination` - (Required) the details of a replication destination. See [Destination](#destination). +* `repository_filter` - (Optional) filters for a replication rule. See [Repository Filter](#repository-filter). ### Destination * `region` - (Required) A Region to replicate to. * `registry_id` - (Required) The account ID of the destination registry to replicate to. +### Repository Filter + +* `filter` - (Required) The repository filter details. +* `filter_type` - (Required) The repository filter type. The only supported value is `PREFIX_MATCH`, which is a repository name prefix specified with the filter parameter. + ## Attributes Reference In addition to all arguments above, the following attributes are exported: @@ -84,4 +114,4 @@ ECR Replication Configuration can be imported using the `registry_id`, e.g., ``` $ terraform import aws_ecr_replication_configuration.service 012345678912 -``` +``` \ No newline at end of file