Skip to content

Commit

Permalink
New Resource: azurerm_storage_account_network_rules (#5082)
Browse files Browse the repository at this point in the history
  • Loading branch information
mbfrahry authored Dec 6, 2019
1 parent 06254f5 commit 4e7ebdc
Show file tree
Hide file tree
Showing 7 changed files with 642 additions and 0 deletions.
1 change: 1 addition & 0 deletions azurerm/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,7 @@ func Provider() terraform.ResourceProvider {
"azurerm_sql_server": resourceArmSqlServer(),
"azurerm_sql_virtual_network_rule": resourceArmSqlVirtualNetworkRule(),
"azurerm_storage_account": resourceArmStorageAccount(),
"azurerm_storage_account_network_rules": resourceArmStorageAccountNetworkRules(),
"azurerm_storage_blob": resourceArmStorageBlob(),
"azurerm_storage_container": resourceArmStorageContainer(),
"azurerm_storage_data_lake_gen2_filesystem": resourceArmStorageDataLakeGen2FileSystem(),
Expand Down
9 changes: 9 additions & 0 deletions azurerm/resource_arm_storage_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,9 @@ func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) e
storageAccountName := d.Get("name").(string)
resourceGroupName := d.Get("resource_group_name").(string)

locks.ByName(storageAccountName, storageAccountResourceName)
defer locks.UnlockByName(storageAccountName, storageAccountResourceName)

if features.ShouldResourcesBeImported() {
existing, err := client.GetProperties(ctx, resourceGroupName, storageAccountName, "")
if err != nil {
Expand Down Expand Up @@ -795,6 +798,9 @@ func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) e
storageAccountName := id.Path["storageAccounts"]
resourceGroupName := id.ResourceGroup

locks.ByName(storageAccountName, iothubResourceName)
defer locks.UnlockByName(storageAccountName, iothubResourceName)

accountTier := d.Get("account_tier").(string)
replicationType := d.Get("account_replication_type").(string)
storageType := fmt.Sprintf("%s_%s", accountTier, replicationType)
Expand Down Expand Up @@ -1188,6 +1194,9 @@ func resourceArmStorageAccountDelete(d *schema.ResourceData, meta interface{}) e
name := id.Path["storageAccounts"]
resourceGroup := id.ResourceGroup

locks.ByName(name, storageAccountResourceName)
defer locks.UnlockByName(name, storageAccountResourceName)

read, err := client.GetProperties(ctx, resourceGroup, name, "")
if err != nil {
if utils.ResponseWasNotFound(read.Response) {
Expand Down
297 changes: 297 additions & 0 deletions azurerm/resource_arm_storage_account_network_rules.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
package azurerm

import (
"fmt"
"strings"
"time"

"github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-04-01/storage"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

var storageAccountResourceName = "azurerm_storage_account"

func resourceArmStorageAccountNetworkRules() *schema.Resource {
return &schema.Resource{
Create: resourceArmStorageAccountNetworkRulesCreateUpdate,
Read: resourceArmStorageAccountNetworkRulesRead,
Update: resourceArmStorageAccountNetworkRulesCreateUpdate,
Delete: resourceArmStorageAccountNetworkRulesDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(60 * time.Minute),
Read: schema.DefaultTimeout(5 * time.Minute),
Update: schema.DefaultTimeout(60 * time.Minute),
Delete: schema.DefaultTimeout(60 * time.Minute),
},

Schema: map[string]*schema.Schema{
"resource_group_name": azure.SchemaResourceGroupName(),

"storage_account_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateArmStorageAccountName,
},

"bypass": {
Type: schema.TypeSet,
Optional: true,
Computed: true,
ConfigMode: schema.SchemaConfigModeAttr,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.StringInSlice([]string{
string(storage.AzureServices),
string(storage.Logging),
string(storage.Metrics),
string(storage.None),
}, false),
},
Set: schema.HashString,
},

"ip_rules": {
Type: schema.TypeSet,
Optional: true,
Computed: true,
ConfigMode: schema.SchemaConfigModeAttr,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validate.IPv4Address,
},
Set: schema.HashString,
},

"virtual_network_subnet_ids": {
Type: schema.TypeSet,
Optional: true,
Computed: true,
ConfigMode: schema.SchemaConfigModeAttr,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: azure.ValidateResourceID,
},
Set: schema.HashString,
},

"default_action": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{
string(storage.DefaultActionAllow),
string(storage.DefaultActionDeny),
}, false),
},
},
}
}

func resourceArmStorageAccountNetworkRulesCreateUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).Storage.AccountsClient
ctx, cancel := timeouts.ForCreateUpdate(meta.(*ArmClient).StopContext, d)
defer cancel()

storageAccountName := d.Get("storage_account_name").(string)
resourceGroup := d.Get("resource_group_name").(string)

locks.ByName(storageAccountName, storageAccountResourceName)
defer locks.UnlockByName(storageAccountName, storageAccountResourceName)

storageAccount, err := client.GetProperties(ctx, resourceGroup, storageAccountName, "")
if err != nil {
if utils.ResponseWasNotFound(storageAccount.Response) {
return fmt.Errorf("Storage Account %q (Resource Group %q) was not found", storageAccountName, resourceGroup)
}

return fmt.Errorf("Error loading Storage Account %q (Resource Group %q): %+v", storageAccountName, resourceGroup, err)
}

if features.ShouldResourcesBeImported() {
if checkForNonDefaultStorageAccountNetworkRule(storageAccount.NetworkRuleSet) {
return tf.ImportAsExistsError("azurerm_storage_account_network_rule", *storageAccount.ID)
}
}

rules := storageAccount.NetworkRuleSet
if rules == nil {
rules = &storage.NetworkRuleSet{}
}

rules.DefaultAction = storage.DefaultAction(d.Get("default_action").(string))

if v, ok := d.GetOk("bypass"); ok {
rules.Bypass = expandStorageAccountNetworkRuleBypass(v.(*schema.Set).List())
}

if v, ok := d.GetOk("ip_rules"); ok {
rules.IPRules = expandStorageAccountNetworkRuleIpRules(v.(*schema.Set).List())
}

if v, ok := d.GetOk("virtual_network_subnet_ids"); ok {
rules.VirtualNetworkRules = expandStorageAccountNetworkRuleVirtualRules(v.(*schema.Set).List())
}

opts := storage.AccountUpdateParameters{
AccountPropertiesUpdateParameters: &storage.AccountPropertiesUpdateParameters{
NetworkRuleSet: rules,
},
}

if _, err := client.Update(ctx, resourceGroup, storageAccountName, opts); err != nil {
return fmt.Errorf("Error updating Azure Storage Account Network Rules %q (Resource Group %q): %+v", storageAccountName, resourceGroup, err)
}

d.SetId(*storageAccount.ID)

return resourceArmStorageAccountNetworkRulesRead(d, meta)
}

func resourceArmStorageAccountNetworkRulesRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).Storage.AccountsClient
ctx, cancel := timeouts.ForRead(meta.(*ArmClient).StopContext, d)
defer cancel()

id, err := azure.ParseAzureResourceID(d.Id())
if err != nil {
return err
}

resourceGroup := id.ResourceGroup
storageAccountName := id.Path["storageAccounts"]

storageAccount, err := client.GetProperties(ctx, resourceGroup, storageAccountName, "")
if err != nil {
return fmt.Errorf("Error reading Storage Account Network Rules %q (Resource Group %q): %+v", storageAccountName, resourceGroup, err)
}

d.Set("storage_account_name", storageAccountName)
d.Set("resource_group_name", resourceGroup)

if rules := storageAccount.NetworkRuleSet; rules != nil {
if err := d.Set("ip_rules", schema.NewSet(schema.HashString, flattenStorageAccountIPRules(rules.IPRules))); err != nil {
return fmt.Errorf("Error setting `ip_rules`: %+v", err)
}
if err := d.Set("virtual_network_subnet_ids", schema.NewSet(schema.HashString, flattenStorageAccountVirtualNetworks(rules.VirtualNetworkRules))); err != nil {
return fmt.Errorf("Error setting `virtual_network_subnet_ids`: %+v", err)
}
if err := d.Set("bypass", schema.NewSet(schema.HashString, flattenStorageAccountBypass(rules.Bypass))); err != nil {
return fmt.Errorf("Error setting `bypass`: %+v", err)
}
d.Set("default_action", string(rules.DefaultAction))
}

return nil
}

func resourceArmStorageAccountNetworkRulesDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).Storage.AccountsClient
ctx, cancel := timeouts.ForDelete(meta.(*ArmClient).StopContext, d)
defer cancel()

parsedStorageAccountNetworkRuleId, err := azure.ParseAzureResourceID(d.Id())
if err != nil {
return err
}

resourceGroup := parsedStorageAccountNetworkRuleId.ResourceGroup
storageAccountName := parsedStorageAccountNetworkRuleId.Path["storageAccounts"]

locks.ByName(storageAccountName, storageAccountResourceName)
defer locks.UnlockByName(storageAccountName, storageAccountResourceName)

storageAccount, err := client.GetProperties(ctx, resourceGroup, storageAccountName, "")
if err != nil {
if utils.ResponseWasNotFound(storageAccount.Response) {
return fmt.Errorf("Storage Account %q (Resource Group %q) was not found", storageAccountName, resourceGroup)
}

return fmt.Errorf("Error loading Storage Account %q (Resource Group %q): %+v", storageAccountName, resourceGroup, err)
}

if storageAccount.NetworkRuleSet == nil {
return nil
}

// We can't delete a network rule set so we'll just update it back to the default instead
opts := storage.AccountUpdateParameters{
AccountPropertiesUpdateParameters: &storage.AccountPropertiesUpdateParameters{
NetworkRuleSet: &storage.NetworkRuleSet{
Bypass: storage.AzureServices,
DefaultAction: storage.DefaultActionDeny,
},
},
}

if _, err := client.Update(ctx, resourceGroup, storageAccountName, opts); err != nil {
return fmt.Errorf("Error deleting Azure Storage Account Network Rule %q (Resource Group %q): %+v", storageAccountName, resourceGroup, err)
}

return nil
}

// To make sure that someone isn't overriding their existing network rules, we'll check for a non default network rule
func checkForNonDefaultStorageAccountNetworkRule(rule *storage.NetworkRuleSet) bool {
if rule == nil {
return false
}

if rule.IPRules != nil || len(*rule.IPRules) != 0 ||
rule.VirtualNetworkRules != nil || len(*rule.VirtualNetworkRules) == 0 ||
rule.Bypass != "AzureServices" || rule.DefaultAction != "Allow" {
return true
}

return false
}

func expandStorageAccountNetworkRuleBypass(bypass []interface{}) storage.Bypass {
var bypassValues []string
for _, bypassConfig := range bypass {
bypassValues = append(bypassValues, bypassConfig.(string))
}

return storage.Bypass(strings.Join(bypassValues, ", "))
}

func expandStorageAccountNetworkRuleIpRules(ipRulesInfo []interface{}) *[]storage.IPRule {
ipRules := make([]storage.IPRule, len(ipRulesInfo))

for i, ipRuleConfig := range ipRulesInfo {
attrs := ipRuleConfig.(string)
ipRule := storage.IPRule{
IPAddressOrRange: utils.String(attrs),
Action: storage.Allow,
}
ipRules[i] = ipRule
}

return &ipRules
}

func expandStorageAccountNetworkRuleVirtualRules(virtualNetworkInfo []interface{}) *[]storage.VirtualNetworkRule {
virtualNetworks := make([]storage.VirtualNetworkRule, len(virtualNetworkInfo))

for i, virtualNetworkConfig := range virtualNetworkInfo {
attrs := virtualNetworkConfig.(string)
virtualNetwork := storage.VirtualNetworkRule{
VirtualNetworkResourceID: utils.String(attrs),
Action: storage.Allow,
}
virtualNetworks[i] = virtualNetwork
}

return &virtualNetworks
}
Loading

0 comments on commit 4e7ebdc

Please sign in to comment.