diff --git a/azurerm/internal/services/containers/container_registry_resource.go b/azurerm/internal/services/containers/container_registry_resource.go index a45e1632ec16..76680fe30965 100644 --- a/azurerm/internal/services/containers/container_registry_resource.go +++ b/azurerm/internal/services/containers/container_registry_resource.go @@ -71,11 +71,13 @@ func resourceContainerRegistry() *schema.Resource { Optional: true, Default: false, }, - + // TODO 3.0 - Remove below property "georeplication_locations": { - Type: schema.TypeSet, - MinItems: 1, - Optional: true, + Type: schema.TypeSet, + Optional: true, + Deprecated: "Deprecated in favour of `georeplications`", + Computed: true, + ConflictsWith: []string{"georeplications"}, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringIsNotEmpty, @@ -83,6 +85,27 @@ func resourceContainerRegistry() *schema.Resource { Set: location.HashCode, }, + "georeplications": { + Type: schema.TypeSet, + Optional: true, + Computed: true, // TODO -- remove this when deprecation resolves + ConflictsWith: []string{"georeplication_locations"}, + ConfigMode: schema.SchemaConfigModeAttr, // TODO -- remove in 3.0, because this property is optional and computed, it has to be declared as empty array to remove existed values + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "location": { + Type: schema.TypeString, + Required: true, + ValidateFunc: location.EnhancedValidate, + StateFunc: location.StateFunc, + DiffSuppressFunc: location.DiffSuppressFunc, + }, + + "tags": tags.Schema(), + }, + }, + }, + "public_network_access_enabled": { Type: schema.TypeBool, Optional: true, @@ -227,8 +250,10 @@ func resourceContainerRegistry() *schema.Resource { CustomizeDiff: func(d *schema.ResourceDiff, v interface{}) error { sku := d.Get("sku").(string) geoReplicationLocations := d.Get("georeplication_locations").(*schema.Set) + geoReplications := d.Get("georeplications").(*schema.Set) + hasGeoReplicationsApplied := geoReplicationLocations.Len() > 0 || geoReplications.Len() > 0 // if locations have been specified for geo-replication then, the SKU has to be Premium - if geoReplicationLocations != nil && geoReplicationLocations.Len() > 0 && !strings.EqualFold(sku, string(containerregistry.Premium)) { + if hasGeoReplicationsApplied && !strings.EqualFold(sku, string(containerregistry.Premium)) { return fmt.Errorf("ACR geo-replication can only be applied when using the Premium Sku.") } @@ -292,6 +317,7 @@ func resourceContainerRegistryCreate(d *schema.ResourceData, meta interface{}) e adminUserEnabled := d.Get("admin_enabled").(bool) t := d.Get("tags").(map[string]interface{}) geoReplicationLocations := d.Get("georeplication_locations").(*schema.Set) + geoReplications := d.Get("georeplications").(*schema.Set) networkRuleSet := expandNetworkRuleSet(d.Get("network_rule_set").([]interface{})) if networkRuleSet != nil && !strings.EqualFold(sku, string(containerregistry.Premium)) { @@ -351,11 +377,16 @@ func resourceContainerRegistryCreate(d *schema.ResourceData, meta interface{}) e return fmt.Errorf("Error waiting for creation of Container Registry %q (Resource Group %q): %+v", name, resourceGroup, err) } - // locations have been specified for geo-replication + // the ACR is being created so no previous geo-replication locations + var oldGeoReplicationLocations, newGeoReplicationLocations []*containerregistry.Replication if geoReplicationLocations != nil && geoReplicationLocations.Len() > 0 { - // the ACR is being created so no previous geo-replication locations - oldGeoReplicationLocations := []interface{}{} - err = applyGeoReplicationLocations(d, meta, resourceGroup, name, oldGeoReplicationLocations, geoReplicationLocations.List()) + newGeoReplicationLocations = expandReplicationsFromLocations(geoReplicationLocations.List()) + } else { + newGeoReplicationLocations = expandReplications(geoReplications.List()) + } + // geo replications have been specified + if len(newGeoReplicationLocations) > 0 { + err = applyGeoReplicationLocations(d, meta, resourceGroup, name, oldGeoReplicationLocations, newGeoReplicationLocations) if err != nil { return fmt.Errorf("Error applying geo replications for Container Registry %q (Resource Group %q): %+v", name, resourceGroup, err) } @@ -394,10 +425,15 @@ func resourceContainerRegistryUpdate(d *schema.ResourceData, meta interface{}) e t := d.Get("tags").(map[string]interface{}) old, new := d.GetChange("georeplication_locations") - hasGeoReplicationChanges := d.HasChange("georeplication_locations") + hasGeoReplicationLocationsChanges := d.HasChange("georeplication_locations") oldGeoReplicationLocations := old.(*schema.Set) newGeoReplicationLocations := new.(*schema.Set) + oldReplicationsRaw, newReplicationsRaw := d.GetChange("georeplications") + hasGeoReplicationsChanges := d.HasChange("georeplications") + oldReplications := oldReplicationsRaw.(*schema.Set) + newReplications := newReplicationsRaw.(*schema.Set) + // handle upgrade to Premium SKU first if skuChange && isPremiumSku { if err := applyContainerRegistrySku(d, meta, sku, resourceGroup, name); err != nil { @@ -434,13 +470,18 @@ func resourceContainerRegistryUpdate(d *schema.ResourceData, meta interface{}) e } // geo replication is only supported by Premium Sku - if hasGeoReplicationChanges && newGeoReplicationLocations.Len() > 0 && !strings.EqualFold(sku, string(containerregistry.Premium)) { + hasGeoReplicationsApplied := newGeoReplicationLocations.Len() > 0 || newReplications.Len() > 0 + if hasGeoReplicationsApplied && !strings.EqualFold(sku, string(containerregistry.Premium)) { return fmt.Errorf("ACR geo-replication can only be applied when using the Premium Sku.") } - // if the registry had replications and is updated to another Sku than premium - remove old locations - if !strings.EqualFold(sku, string(containerregistry.Premium)) && oldGeoReplicationLocations != nil && oldGeoReplicationLocations.Len() > 0 { - err := applyGeoReplicationLocations(d, meta, resourceGroup, name, oldGeoReplicationLocations.List(), newGeoReplicationLocations.List()) + if hasGeoReplicationsChanges { + err := applyGeoReplicationLocations(d, meta, resourceGroup, name, expandReplications(oldReplications.List()), expandReplications(newReplications.List())) + if err != nil { + return fmt.Errorf("Error applying geo replications for Container Registry %q (Resource Group %q): %+v", name, resourceGroup, err) + } + } else if hasGeoReplicationLocationsChanges { + err := applyGeoReplicationLocations(d, meta, resourceGroup, name, expandReplicationsFromLocations(oldGeoReplicationLocations.List()), expandReplicationsFromLocations(newGeoReplicationLocations.List())) if err != nil { return fmt.Errorf("Error applying geo replications for Container Registry %q (Resource Group %q): %+v", name, resourceGroup, err) } @@ -455,13 +496,6 @@ func resourceContainerRegistryUpdate(d *schema.ResourceData, meta interface{}) e return fmt.Errorf("Error waiting for update of Container Registry %q (Resource Group %q): %+v", name, resourceGroup, err) } - if strings.EqualFold(sku, string(containerregistry.Premium)) && hasGeoReplicationChanges { - err = applyGeoReplicationLocations(d, meta, resourceGroup, name, oldGeoReplicationLocations.List(), newGeoReplicationLocations.List()) - if err != nil { - return fmt.Errorf("Error applying geo replications for Container Registry %q (Resource Group %q): %+v", name, resourceGroup, err) - } - } - // downgrade to Basic or Standard SKU if skuChange && (isBasicSku || isStandardSku) { if err := applyContainerRegistrySku(d, meta, sku, resourceGroup, name); err != nil { @@ -507,70 +541,41 @@ func applyContainerRegistrySku(d *schema.ResourceData, meta interface{}, sku str return nil } -func applyGeoReplicationLocations(d *schema.ResourceData, meta interface{}, resourceGroup string, name string, oldGeoReplicationLocations []interface{}, newGeoReplicationLocations []interface{}) error { +func applyGeoReplicationLocations(d *schema.ResourceData, meta interface{}, resourceGroup string, name string, oldGeoReplications []*containerregistry.Replication, newGeoReplications []*containerregistry.Replication) error { replicationClient := meta.(*clients.Client).Containers.ReplicationsClient ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() log.Printf("[INFO] preparing to apply geo-replications for Container Registry.") - createLocations := make(map[string]bool) - - // loop on the new location values - for _, nl := range newGeoReplicationLocations { - newLocation := azure.NormalizeLocation(nl) - createLocations[newLocation] = true // the location needs to be created - } - - // loop on the old location values - for _, ol := range oldGeoReplicationLocations { - // oldLocation was created from a previous deployment - oldLocation := azure.NormalizeLocation(ol) - - // if the list of locations to create already contains the location - if _, ok := createLocations[oldLocation]; ok { - createLocations[oldLocation] = false // the location do not need to be created, it already exists - } - } - - // create new geo-replication locations - for locationToCreate := range createLocations { - // if false, the location does not need to be created, continue - if !createLocations[locationToCreate] { + // delete previously deployed locations + for _, replication := range oldGeoReplications { + if replication == nil || len(*replication.Location) == 0 { continue } + oldLocation := azure.NormalizeLocation(*replication.Location) - // create the new replication location - replication := containerregistry.Replication{ - Location: &locationToCreate, - Name: &locationToCreate, - } - - future, err := replicationClient.Create(ctx, resourceGroup, name, locationToCreate, replication) + future, err := replicationClient.Delete(ctx, resourceGroup, name, oldLocation) if err != nil { - return fmt.Errorf("Error creating Container Registry Replication %q (Resource Group %q, Location %q): %+v", name, resourceGroup, locationToCreate, err) + return fmt.Errorf("Error deleting Container Registry Replication %q (Resource Group %q, Location %q): %+v", name, resourceGroup, oldLocation, err) } - if err = future.WaitForCompletionRef(ctx, replicationClient.Client); err != nil { - return fmt.Errorf("Error waiting for creation of Container Registry Replication %q (Resource Group %q, Location %q): %+v", name, resourceGroup, locationToCreate, err) + return fmt.Errorf("Error waiting for deletion of Container Registry Replication %q (Resource Group %q, Location %q): %+v", name, resourceGroup, oldLocation, err) } } - // loop on the list of previously deployed locations - for _, ol := range oldGeoReplicationLocations { - oldLocation := azure.NormalizeLocation(ol) - // if the old location is still in the list of locations, then continue - if _, ok := createLocations[oldLocation]; ok { + // create new geo-replication locations + for _, replication := range newGeoReplications { + if replication == nil || len(*replication.Location) == 0 { continue } - - // the old location is not in the list of locations, delete it - future, err := replicationClient.Delete(ctx, resourceGroup, name, oldLocation) + locationToCreate := azure.NormalizeLocation(*replication.Location) + future, err := replicationClient.Create(ctx, resourceGroup, name, locationToCreate, *replication) if err != nil { - return fmt.Errorf("Error deleting Container Registry Replication %q (Resource Group %q, Location %q): %+v", name, resourceGroup, oldLocation, err) + return fmt.Errorf("Error creating Container Registry Replication %q (Resource Group %q, Location %q): %+v", name, resourceGroup, locationToCreate, err) } if err = future.WaitForCompletionRef(ctx, replicationClient.Client); err != nil { - return fmt.Errorf("Error waiting for deletion of Container Registry Replication %q (Resource Group %q, Location %q): %+v", name, resourceGroup, oldLocation, err) + return fmt.Errorf("Error waiting for creation of Container Registry Replication %q (Resource Group %q, Location %q): %+v", name, resourceGroup, locationToCreate, err) } } @@ -658,24 +663,23 @@ func resourceContainerRegistryRead(d *schema.ResourceData, meta interface{}) err return fmt.Errorf("Error making Read request on Azure Container Registry %s for replications: %s", name, err) } - replicationValues := replications.Values() - - // if there is more than one location (the main one and the replicas) - if replicationValues != nil || len(replicationValues) > 1 { - georeplication_locations := &schema.Set{F: schema.HashString} - - for _, value := range replicationValues { - if value.Location != nil { - valueLocation := azure.NormalizeLocation(*value.Location) - if location != nil && valueLocation != azure.NormalizeLocation(*location) { - georeplication_locations.Add(valueLocation) - } + geoReplicationLocations := make([]interface{}, 0) + geoReplications := make([]interface{}, 0) + for _, value := range replications.Values() { + if value.Location != nil { + valueLocation := azure.NormalizeLocation(*value.Location) + if location != nil && valueLocation != azure.NormalizeLocation(*location) { + geoReplicationLocations = append(geoReplicationLocations, *value.Location) + replication := make(map[string]interface{}) + replication["location"] = valueLocation + replication["tags"] = tags.Flatten(value.Tags) + geoReplications = append(geoReplications, replication) } } - - d.Set("georeplication_locations", georeplication_locations) } + d.Set("georeplication_locations", geoReplicationLocations) + d.Set("georeplications", geoReplications) return tags.FlattenAndSet(d, resp.Tags) } @@ -811,6 +815,36 @@ func expandTrustPolicy(p []interface{}) *containerregistry.TrustPolicy { return &trustPolicy } +func expandReplicationsFromLocations(p []interface{}) []*containerregistry.Replication { + replications := make([]*containerregistry.Replication, 0) + for _, value := range p { + location := azure.NormalizeLocation(value) + replications = append(replications, &containerregistry.Replication{ + Location: &location, + Name: &location, + }) + } + return replications +} + +func expandReplications(p []interface{}) []*containerregistry.Replication { + replications := make([]*containerregistry.Replication, 0) + if p == nil { + return replications + } + for _, v := range p { + value := v.(map[string]interface{}) + location := azure.NormalizeLocation(value["location"]) + tags := tags.Expand(value["tags"].(map[string]interface{})) + replications = append(replications, &containerregistry.Replication{ + Location: &location, + Name: &location, + Tags: tags, + }) + } + return replications +} + func flattenNetworkRuleSet(networkRuleSet *containerregistry.NetworkRuleSet) []interface{} { if networkRuleSet == nil { return []interface{}{} diff --git a/azurerm/internal/services/containers/container_registry_resource_test.go b/azurerm/internal/services/containers/container_registry_resource_test.go index 46eadd8ef578..7359783bea7d 100644 --- a/azurerm/internal/services/containers/container_registry_resource_test.go +++ b/azurerm/internal/services/containers/container_registry_resource_test.go @@ -204,7 +204,7 @@ func TestAccContainerRegistry_update(t *testing.T) { }) } -func TestAccContainerRegistry_geoReplication(t *testing.T) { +func TestAccContainerRegistry_geoReplicationLocation(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_container_registry", "test") r := ContainerRegistryResource{} @@ -217,7 +217,7 @@ func TestAccContainerRegistry_geoReplication(t *testing.T) { data.ResourceTest(t, r, []resource.TestStep{ // first config creates an ACR with locations { - Config: r.geoReplication(data, skuPremium, []string{secondaryLocation}), + Config: r.geoReplicationLocation(data, []string{secondaryLocation}), Check: resource.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("sku").HasValue(skuPremium), @@ -227,7 +227,7 @@ func TestAccContainerRegistry_geoReplication(t *testing.T) { }, // second config updates the ACR with updated locations { - Config: r.geoReplication(data, skuPremium, []string{ternaryLocation}), + Config: r.geoReplicationLocation(data, []string{ternaryLocation}), Check: resource.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("sku").HasValue(skuPremium), @@ -235,25 +235,27 @@ func TestAccContainerRegistry_geoReplication(t *testing.T) { check.That(data.ResourceName).Key("georeplication_locations.0").HasValue(ternaryLocation), ), }, - // third config updates the ACR with no location + // third config updates the ACR with updated locations { - Config: r.geoReplicationUpdateWithNoLocation(data, skuPremium), + Config: r.geoReplicationLocation(data, []string{secondaryLocation, ternaryLocation}), Check: resource.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("sku").HasValue(skuPremium), - check.That(data.ResourceName).Key("georeplication_locations.#").HasValue("0"), + check.That(data.ResourceName).Key("georeplication_locations.#").HasValue("2"), ), }, - // fourth config updates an ACR with replicas + data.ImportStep(), + // For compatibility, downgrade from Premium to Basic should remove all replications first, but it's unnecessary. Once georeplication_locations is deprecated, this can be done in single update. + // fourth config updates the ACR with no location. { - Config: r.geoReplication(data, skuPremium, []string{secondaryLocation}), + Config: r.geoReplicationUpdateWithNoLocation(data), Check: resource.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), - check.That(data.ResourceName).Key("georeplication_locations.#").HasValue("1"), - check.That(data.ResourceName).Key("georeplication_locations.0").HasValue(secondaryLocation), + check.That(data.ResourceName).Key("sku").HasValue(skuPremium), + check.That(data.ResourceName).Key("georeplication_locations.#").HasValue("0"), ), }, - // fifth config updates the SKU to basic and no replicas (should remove the existing replicas if any) + // fifth config updates the SKU to basic. { Config: r.geoReplicationUpdateWithNoLocation_basic(data), Check: resource.ComposeTestCheckFunc( @@ -265,6 +267,111 @@ func TestAccContainerRegistry_geoReplication(t *testing.T) { }) } +func TestAccContainerRegistry_geoReplication(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_container_registry", "test") + r := ContainerRegistryResource{} + + skuPremium := "Premium" + skuBasic := "Basic" + + secondaryLocation := location.Normalize(data.Locations.Secondary) + ternaryLocation := location.Normalize(data.Locations.Ternary) + + data.ResourceTest(t, r, []resource.TestStep{ + // first config creates an ACR with locations + { + Config: r.geoReplication(data, secondaryLocation), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("sku").HasValue(skuPremium), + check.That(data.ResourceName).Key("georeplications.#").HasValue("1"), + check.That(data.ResourceName).Key("georeplications.0.location").HasValue(secondaryLocation), + check.That(data.ResourceName).Key("georeplications.0.tags.%").HasValue("1"), + check.That(data.ResourceName).Key("georeplications.0.tags.Environment").HasValue("Production"), + ), + }, + data.ImportStep(), + // second config updates the ACR with updated locations + { + Config: r.geoReplication(data, ternaryLocation), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("sku").HasValue(skuPremium), + check.That(data.ResourceName).Key("georeplications.#").HasValue("1"), + check.That(data.ResourceName).Key("georeplications.0.location").HasValue(ternaryLocation), + check.That(data.ResourceName).Key("georeplications.0.tags.%").HasValue("1"), + check.That(data.ResourceName).Key("georeplications.0.tags.Environment").HasValue("Production"), + ), + }, + data.ImportStep(), + // third config updates the ACR with updated locations + { + Config: r.geoReplicationMultipleLocations(data, secondaryLocation, ternaryLocation), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("sku").HasValue(skuPremium), + check.That(data.ResourceName).Key("georeplications.#").HasValue("2"), + check.That(data.ResourceName).Key("georeplications.0.tags.%").HasValue("0"), + check.That(data.ResourceName).Key("georeplications.1.tags.%").HasValue("0"), + ), + }, + data.ImportStep(), + // For compatibility, downgrade from Premium to Basic should remove all replications first, but it's unnecessary. Once georeplication_locations is deprecated, this can be done in single update. + // fourth config updates the ACR with no location + { + Config: r.geoReplicationUpdateWithNoReplication(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("sku").HasValue(skuPremium), + check.That(data.ResourceName).Key("georeplications.#").HasValue("0"), + ), + }, + data.ImportStep(), + // fifth config updates the SKU to basic. + { + Config: r.geoReplicationUpdateWithNoReplication_basic(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("sku").HasValue(skuBasic), + check.That(data.ResourceName).Key("georeplications.#").HasValue("0"), + ), + }, + }) +} + +func TestAccContainerRegistry_geoReplicationSwitch(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_container_registry", "test") + r := ContainerRegistryResource{} + + skuPremium := "Premium" + + secondaryLocation := location.Normalize(data.Locations.Secondary) + ternaryLocation := location.Normalize(data.Locations.Ternary) + + data.ResourceTest(t, r, []resource.TestStep{ + // first config creates an ACR using georeplication_locations + { + Config: r.geoReplicationLocation(data, []string{secondaryLocation, ternaryLocation}), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("sku").HasValue(skuPremium), + check.That(data.ResourceName).Key("georeplication_locations.#").HasValue("2"), + ), + }, + data.ImportStep(), + // second config updates the ACR using georeplications + { + Config: r.geoReplicationMultipleLocations(data, secondaryLocation, ternaryLocation), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("sku").HasValue(skuPremium), + check.That(data.ResourceName).Key("georeplications.#").HasValue("2"), + ), + }, + data.ImportStep(), + }) +} + func TestAccContainerRegistry_networkAccessProfileIp(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_container_registry", "test") r := ContainerRegistryResource{} @@ -406,7 +513,7 @@ provider "azurerm" { } resource "azurerm_resource_group" "test" { - name = "acctestRG-aks-%d" + name = "acctestRG-acr-%d" location = "%s" } @@ -417,7 +524,7 @@ resource "azurerm_container_registry" "test" { sku = "Basic" # make sure network_rule_set is empty for basic SKU - # premiuim SKU will automaticcally populate network_rule_set.default_action to allow + # premiuim SKU will automatically populate network_rule_set.default_action to allow network_rule_set = [] } `, data.RandomInteger, data.Locations.Primary, data.RandomInteger) @@ -430,7 +537,7 @@ provider "azurerm" { } resource "azurerm_resource_group" "test" { - name = "acctestRG-aks-%d" + name = "acctestRG-acr-%d" location = "%s" } @@ -463,7 +570,7 @@ provider "azurerm" { } resource "azurerm_resource_group" "test" { - name = "acctestRG-aks-%d" + name = "acctestRG-acr-%d" location = "%s" } @@ -488,7 +595,7 @@ provider "azurerm" { } resource "azurerm_resource_group" "test" { - name = "acctestRG-aks-%d" + name = "acctestRG-acr-%d" location = "%s" } @@ -507,7 +614,7 @@ resource "azurerm_container_registry" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomInteger) } -func (ContainerRegistryResource) geoReplication(data acceptance.TestData, sku string, replicationRegions []string) string { +func (ContainerRegistryResource) geoReplicationLocation(data acceptance.TestData, replicationRegions []string) string { regions := make([]string, 0) for _, region := range replicationRegions { // ensure they're quoted @@ -519,7 +626,7 @@ provider "azurerm" { } resource "azurerm_resource_group" "test" { - name = "acctestRG-aks-%d" + name = "acctestRG-acr-%d" location = "%s" } @@ -527,20 +634,20 @@ resource "azurerm_container_registry" "test" { name = "testacccr%d" resource_group_name = azurerm_resource_group.test.name location = azurerm_resource_group.test.location - sku = "%s" + sku = "Premium" georeplication_locations = [%s] } -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, sku, strings.Join(regions, ",")) +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, strings.Join(regions, ",")) } -func (ContainerRegistryResource) geoReplicationUpdateWithNoLocation(data acceptance.TestData, sku string) string { +func (ContainerRegistryResource) geoReplication(data acceptance.TestData, replication string) string { return fmt.Sprintf(` provider "azurerm" { features {} } resource "azurerm_resource_group" "test" { - name = "acctestRG-aks-%d" + name = "acctestRG-acr-%d" location = "%s" } @@ -548,11 +655,84 @@ resource "azurerm_container_registry" "test" { name = "testacccr%d" resource_group_name = azurerm_resource_group.test.name location = azurerm_resource_group.test.location - sku = "%s" + sku = "Premium" + georeplications { + location = "%s" + tags = { + Environment = "Production" + } + } } -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, sku) +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, replication) +} + +func (ContainerRegistryResource) geoReplicationMultipleLocations(data acceptance.TestData, primaryLocation string, secondaryLocation string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-acr-%d" + location = "%s" +} + +resource "azurerm_container_registry" "test" { + name = "testacccr%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + sku = "Premium" + georeplications { + location = "%s" + } + georeplications { + location = "%s" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, primaryLocation, secondaryLocation) +} + +func (ContainerRegistryResource) geoReplicationUpdateWithNoLocation(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-acr-%d" + location = "%s" } +resource "azurerm_container_registry" "test" { + name = "testacccr%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + sku = "Premium" + georeplication_locations = [] +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} + +func (ContainerRegistryResource) geoReplicationUpdateWithNoReplication(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-acr-%d" + location = "%s" +} + +resource "azurerm_container_registry" "test" { + name = "testacccr%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + sku = "Premium" + georeplications = [] +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} func (ContainerRegistryResource) geoReplicationUpdateWithNoLocation_basic(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { @@ -560,7 +740,7 @@ provider "azurerm" { } resource "azurerm_resource_group" "test" { - name = "acctestRG-aks-%d" + name = "acctestRG-acr-%d" location = "%s" } @@ -571,12 +751,38 @@ resource "azurerm_container_registry" "test" { sku = "Basic" # make sure network_rule_set is empty for basic SKU - # premiuim SKU will automaticcally populate network_rule_set.default_action to allow + # premiuim SKU will automatically populate network_rule_set.default_action to allow network_rule_set = [] } `, data.RandomInteger, data.Locations.Primary, data.RandomInteger) } +func (ContainerRegistryResource) geoReplicationUpdateWithNoReplication_basic(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-acr-%d" + location = "%s" +} + +resource "azurerm_container_registry" "test" { + name = "testacccr%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + sku = "Basic" + + # make sure network_rule_set is empty for basic SKU + # premiuim SKU will automatically populate network_rule_set.default_action to allow + network_rule_set = [] + + georeplications = [] +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} + func (ContainerRegistryResource) networkAccessProfile_ip(data acceptance.TestData, sku string) string { return fmt.Sprintf(` provider "azurerm" { diff --git a/website/docs/r/container_registry.html.markdown b/website/docs/r/container_registry.html.markdown index 39dafaf47d3e..d38068168ec4 100644 --- a/website/docs/r/container_registry.html.markdown +++ b/website/docs/r/container_registry.html.markdown @@ -52,12 +52,20 @@ The following arguments are supported: * `tags` - (Optional) A mapping of tags to assign to the resource. -* `georeplication_locations` - (Optional) A list of Azure locations where the container registry should be geo-replicated. +* `georeplication_locations` - (Optional / **Deprecated in favor of `georeplications`**) A list of Azure locations where the container registry should be geo-replicated. ~> **NOTE:** The `georeplication_locations` is only supported on new resources with the `Premium` SKU. ~> **NOTE:** The `georeplication_locations` list cannot contain the location where the Container Registry exists. +~> **NOTE:** The `georeplication_locations` is deprecated, use `georeplications` instead. + +* `georeplications` - (Optional) A `georeplications` block as documented below. + +~> **NOTE:** The `georeplications` is only supported on new resources with the `Premium` SKU. + +~> **NOTE:** The `georeplications` list cannot contain the location where the Container Registry exists. + * `network_rule_set` - (Optional) A `network_rule_set` block as documented below. * `public_network_access_enabled` - (Optional) Whether public network access is allowed for the container registry. Defaults to `true`. @@ -70,6 +78,12 @@ The following arguments are supported: ~> **NOTE:** `quarantine_policy_enabled`, `retention_policy` and `trust_policy` are only supported on resources with the `Premium` SKU. +`georeplications` supports the following: + +* `location` - (Required) A location where the container registry should be geo-replicated. + +* `tags` - (Optional) A mapping of tags to assign to this replication location. + `network_rule_set` supports the following: * `default_action` - (Optional) The behaviour for requests matching no rules. Either `Allow` or `Deny`. Defaults to `Allow`