Skip to content

Commit

Permalink
Merge pull request #8698 from notchairmk/acr_policy
Browse files Browse the repository at this point in the history
Adds policies block to azurerm_container_registry for setting trust_policy and retention_policy.

Azure doesn't support upgrading to Premium SKU and setting attributes specific to Premium SKUs simultaneously. This PR breaks the client update calls into two separate areas.

Closes #8485
  • Loading branch information
katbyte committed Oct 15, 2020
2 parents 2c57b2c + 0eac96d commit b6432e5
Show file tree
Hide file tree
Showing 3 changed files with 301 additions and 5 deletions.
184 changes: 179 additions & 5 deletions azurerm/internal/services/containers/container_registry_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,46 @@ func resourceArmContainerRegistry() *schema.Resource {
},
},

"retention_policy": {
Type: schema.TypeList,
MaxItems: 1,
Optional: true,
Computed: true,
ConfigMode: schema.SchemaConfigModeAttr,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"days": {
Type: schema.TypeInt,
Optional: true,
Default: 7,
},

"enabled": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
},
},
},

"trust_policy": {
Type: schema.TypeList,
MaxItems: 1,
Optional: true,
Computed: true,
ConfigMode: schema.SchemaConfigModeAttr,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"enabled": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
},
},
},

"tags": tags.Schema(),
},

Expand All @@ -181,6 +221,16 @@ func resourceArmContainerRegistry() *schema.Resource {
return fmt.Errorf("ACR geo-replication can only be applied when using the Premium Sku.")
}

retentionPolicyEnabled, ok := d.GetOk("retention_policy.0.enabled")
if ok && retentionPolicyEnabled.(bool) && !strings.EqualFold(sku, string(containerregistry.Premium)) {
return fmt.Errorf("ACR retention policy can only be applied when using the Premium Sku. If you are downgrading from a Premium SKU please set retention_policy {}")
}

trustPolicyEnabled, ok := d.GetOk("trust_policy.0.enabled")
if ok && trustPolicyEnabled.(bool) && !strings.EqualFold(sku, string(containerregistry.Premium)) {
return fmt.Errorf("ACR trust policy can only be applied when using the Premium Sku. If you are downgrading from a Premium SKU please set trust_policy {}")
}

return nil
},
}
Expand Down Expand Up @@ -232,6 +282,12 @@ func resourceArmContainerRegistryCreate(d *schema.ResourceData, meta interface{}
return fmt.Errorf("`network_rule_set_set` can only be specified for a Premium Sku. If you are reverting from a Premium to Basic SKU plese set network_rule_set = []")
}

retentionPolicyRaw := d.Get("retention_policy").([]interface{})
retentionPolicy := expandRetentionPolicy(retentionPolicyRaw)

trustPolicyRaw := d.Get("trust_policy").([]interface{})
trustPolicy := expandTrustPolicy(trustPolicyRaw)

parameters := containerregistry.Registry{
Location: &location,
Sku: &containerregistry.Sku{
Expand All @@ -241,6 +297,10 @@ func resourceArmContainerRegistryCreate(d *schema.ResourceData, meta interface{}
RegistryProperties: &containerregistry.RegistryProperties{
AdminUserEnabled: utils.Bool(adminUserEnabled),
NetworkRuleSet: networkRuleSet,
Policies: &containerregistry.Policies{
RetentionPolicy: retentionPolicy,
TrustPolicy: trustPolicy,
},
},

Tags: tags.Expand(t),
Expand Down Expand Up @@ -301,6 +361,11 @@ func resourceArmContainerRegistryUpdate(d *schema.ResourceData, meta interface{}
name := d.Get("name").(string)

sku := d.Get("sku").(string)
skuChange := d.HasChange("sku")
isBasicSku := strings.EqualFold(sku, string(containerregistry.Basic))
isPremiumSku := strings.EqualFold(sku, string(containerregistry.Premium))
isStandardSku := strings.EqualFold(sku, string(containerregistry.Standard))

adminUserEnabled := d.Get("admin_enabled").(bool)
t := d.Get("tags").(map[string]interface{})

Expand All @@ -309,19 +374,28 @@ func resourceArmContainerRegistryUpdate(d *schema.ResourceData, meta interface{}
oldGeoReplicationLocations := old.(*schema.Set)
newGeoReplicationLocations := new.(*schema.Set)

// handle upgrade to Premium SKU first
if skuChange && isPremiumSku {
if err := applyContainerRegistrySku(d, meta, sku, resourceGroup, name); err != nil {
return fmt.Errorf("Error applying sku %q for Container Registry %q (Resource Group %q): %+v", sku, name, resourceGroup, err)
}
}

networkRuleSet := expandNetworkRuleSet(d.Get("network_rule_set").([]interface{}))
if networkRuleSet != nil && !strings.EqualFold(sku, string(containerregistry.Premium)) {
if networkRuleSet != nil && isBasicSku {
return fmt.Errorf("`network_rule_set_set` can only be specified for a Premium Sku. If you are reverting from a Premium to Basic SKU plese set network_rule_set = []")
}

retentionPolicy := expandRetentionPolicy(d.Get("retention_policy").([]interface{}))
trustPolicy := expandTrustPolicy(d.Get("trust_policy").([]interface{}))
parameters := containerregistry.RegistryUpdateParameters{
RegistryPropertiesUpdateParameters: &containerregistry.RegistryPropertiesUpdateParameters{
AdminUserEnabled: utils.Bool(adminUserEnabled),
NetworkRuleSet: networkRuleSet,
},
Sku: &containerregistry.Sku{
Name: containerregistry.SkuName(sku),
Tier: containerregistry.SkuTier(sku),
Policies: &containerregistry.Policies{
RetentionPolicy: retentionPolicy,
TrustPolicy: trustPolicy,
},
},
Tags: tags.Expand(t),
}
Expand Down Expand Up @@ -355,6 +429,13 @@ func resourceArmContainerRegistryUpdate(d *schema.ResourceData, meta interface{}
}
}

// downgrade to Basic or Standard SKU
if skuChange && (isBasicSku || isStandardSku) {
if err := applyContainerRegistrySku(d, meta, sku, resourceGroup, name); err != nil {
return fmt.Errorf("Error applying sku %q for Container Registry %q (Resource Group %q): %+v", sku, name, resourceGroup, err)
}
}

read, err := client.Get(ctx, resourceGroup, name)
if err != nil {
return fmt.Errorf("Error retrieving Container Registry %q (Resource Group %q): %+v", name, resourceGroup, err)
Expand All @@ -369,6 +450,30 @@ func resourceArmContainerRegistryUpdate(d *schema.ResourceData, meta interface{}
return resourceArmContainerRegistryRead(d, meta)
}

func applyContainerRegistrySku(d *schema.ResourceData, meta interface{}, sku string, resourceGroup string, name string) error {
client := meta.(*clients.Client).Containers.RegistriesClient
ctx, cancel := timeouts.ForUpdate(meta.(*clients.Client).StopContext, d)
defer cancel()

parameters := containerregistry.RegistryUpdateParameters{
Sku: &containerregistry.Sku{
Name: containerregistry.SkuName(sku),
Tier: containerregistry.SkuTier(sku),
},
}

future, err := client.Update(ctx, resourceGroup, name, parameters)
if err != nil {
return fmt.Errorf("Error updating Container Registry %q (Resource Group %q): %+v", name, resourceGroup, err)
}

if err = future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("Error waiting for update of Container Registry %q (Resource Group %q): %+v", name, resourceGroup, err)
}

return nil
}

func applyGeoReplicationLocations(d *schema.ResourceData, meta interface{}, resourceGroup string, name string, oldGeoReplicationLocations []interface{}, newGeoReplicationLocations []interface{}) error {
replicationClient := meta.(*clients.Client).Containers.ReplicationsClient
ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d)
Expand Down Expand Up @@ -478,6 +583,15 @@ func resourceArmContainerRegistryRead(d *schema.ResourceData, meta interface{})
return fmt.Errorf("Error setting `network_rule_set`: %+v", err)
}

if properties := resp.RegistryProperties; properties != nil {
if err := d.Set("retention_policy", flattenRetentionPolicy(properties.Policies)); err != nil {
return fmt.Errorf("Error setting `retention_policy`: %+v", err)
}
if err := d.Set("trust_policy", flattenTrustPolicy(properties.Policies)); err != nil {
return fmt.Errorf("Error setting `trust_policy`: %+v", err)
}
}

if sku := resp.Sku; sku != nil {
d.Set("sku", string(sku.Tier))
}
Expand Down Expand Up @@ -613,6 +727,41 @@ func expandNetworkRuleSet(profiles []interface{}) *containerregistry.NetworkRule
return &networkRuleSet
}

func expandRetentionPolicy(p []interface{}) *containerregistry.RetentionPolicy {
retentionPolicy := containerregistry.RetentionPolicy{
Status: containerregistry.Disabled,
}

if len(p) > 0 {
v := p[0].(map[string]interface{})
days := int32(v["days"].(int))
enabled := v["enabled"].(bool)
if enabled {
retentionPolicy.Status = containerregistry.Enabled
}
retentionPolicy.Days = utils.Int32(days)
}

return &retentionPolicy
}

func expandTrustPolicy(p []interface{}) *containerregistry.TrustPolicy {
trustPolicy := containerregistry.TrustPolicy{
Status: containerregistry.Disabled,
}

if len(p) > 0 {
v := p[0].(map[string]interface{})
enabled := v["enabled"].(bool)
if enabled {
trustPolicy.Status = containerregistry.Enabled
}
trustPolicy.Type = containerregistry.Notary
}

return &trustPolicy
}

func flattenNetworkRuleSet(networkRuleSet *containerregistry.NetworkRuleSet) []interface{} {
if networkRuleSet == nil {
return []interface{}{}
Expand Down Expand Up @@ -654,3 +803,28 @@ func flattenNetworkRuleSet(networkRuleSet *containerregistry.NetworkRuleSet) []i

return []interface{}{values}
}

func flattenRetentionPolicy(p *containerregistry.Policies) []interface{} {
if p == nil || p.RetentionPolicy == nil {
return nil
}

r := *p.RetentionPolicy
retentionPolicy := make(map[string]interface{})
retentionPolicy["days"] = r.Days
enabled := strings.EqualFold(string(r.Status), string(containerregistry.Enabled))
retentionPolicy["enabled"] = utils.Bool(enabled)
return []interface{}{retentionPolicy}
}

func flattenTrustPolicy(p *containerregistry.Policies) []interface{} {
if p == nil || p.TrustPolicy == nil {
return nil
}

t := *p.TrustPolicy
trustPolicy := make(map[string]interface{})
enabled := strings.EqualFold(string(t.Status), string(containerregistry.Enabled))
trustPolicy["enabled"] = utils.Bool(enabled)
return []interface{}{trustPolicy}
}
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,50 @@ func TestAccAzureRMContainerRegistry_networkAccessProfileVnet(t *testing.T) {
})
}

func TestAccAzureRMContainerRegistry_policies(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_container_registry", "test")

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acceptance.PreCheck(t) },
Providers: acceptance.SupportedProviders,
CheckDestroy: testCheckAzureRMContainerRegistryDestroy,
Steps: []resource.TestStep{
{
Config: testAccAzureRMContainerRegistry_policies(data, 10),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMContainerRegistryExists(data.ResourceName),
resource.TestCheckResourceAttr(data.ResourceName, "network_rule_set.0.default_action", "Allow"),
resource.TestCheckResourceAttr(data.ResourceName, "network_rule_set.0.virtual_network.#", "0"),
resource.TestCheckResourceAttr(data.ResourceName, "retention_policy.0.days", "10"),
resource.TestCheckResourceAttr(data.ResourceName, "retention_policy.0.enabled", "true"),
resource.TestCheckResourceAttr(data.ResourceName, "trust_policy.0.enabled", "true"),
),
},
{
Config: testAccAzureRMContainerRegistry_policies(data, 20),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMContainerRegistryExists(data.ResourceName),
resource.TestCheckResourceAttr(data.ResourceName, "network_rule_set.0.default_action", "Allow"),
resource.TestCheckResourceAttr(data.ResourceName, "network_rule_set.0.virtual_network.#", "0"),
resource.TestCheckResourceAttr(data.ResourceName, "retention_policy.0.days", "20"),
resource.TestCheckResourceAttr(data.ResourceName, "retention_policy.0.enabled", "true"),
resource.TestCheckResourceAttr(data.ResourceName, "trust_policy.0.enabled", "true"),
),
},
{
Config: testAccAzureRMContainerRegistry_policies_downgradeUpdate(data),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMContainerRegistryExists(data.ResourceName),
resource.TestCheckResourceAttr(data.ResourceName, "network_rule_set.#", "0"),
resource.TestCheckResourceAttr(data.ResourceName, "retention_policy.0.enabled", "false"),
resource.TestCheckResourceAttr(data.ResourceName, "trust_policy.0.enabled", "false"),
),
},
data.ImportStep(),
},
})
}

func testCheckAzureRMContainerRegistryDestroy(s *terraform.State) error {
conn := acceptance.AzureProvider.Meta().(*clients.Client).Containers.RegistriesClient
ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext
Expand Down Expand Up @@ -781,3 +825,66 @@ resource "azurerm_container_registry" "test" {
}
`, data.RandomInteger, data.Locations.Primary)
}

func testAccAzureRMContainerRegistry_policies(data acceptance.TestData, days int) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "test" {
name = "acctestRG-acr-%d"
location = "%s"
}
resource "azurerm_container_registry" "test" {
name = "acctestACR%d"
resource_group_name = azurerm_resource_group.test.name
location = azurerm_resource_group.test.location
admin_enabled = false
sku = "Premium"
retention_policy {
days = %d
enabled = true
}
trust_policy {
enabled = true
}
tags = {
Environment = "Production"
}
}
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, days)
}

func testAccAzureRMContainerRegistry_policies_downgradeUpdate(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 = "acctestACR%d"
resource_group_name = azurerm_resource_group.test.name
location = azurerm_resource_group.test.location
admin_enabled = false
sku = "Basic"
network_rule_set = []
retention_policy {}
trust_policy {}
tags = {
Environment = "Production"
}
}
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger)
}

0 comments on commit b6432e5

Please sign in to comment.