Skip to content

Commit

Permalink
Merge pull request #1334 from terraform-providers/f-storage-account-v…
Browse files Browse the repository at this point in the history
…irt-subnet

Storage Account Network Rules
  • Loading branch information
mbfrahry authored Jun 5, 2018
2 parents 8cbcad3 + 69c146d commit 3c5216f
Show file tree
Hide file tree
Showing 4 changed files with 392 additions and 0 deletions.
25 changes: 25 additions & 0 deletions azurerm/import_arm_storage_account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,28 @@ func TestAccAzureRMStorageAccount_importEnableHttpsTrafficOnly(t *testing.T) {
},
})
}

func TestAccAzureRMStorageAccount_importNetworkRules(t *testing.T) {
resourceName := "azurerm_storage_account.testsa"

ri := acctest.RandInt()
rs := acctest.RandString(4)
config := testAccAzureRMStorageAccount_networkRules(ri, rs, testLocation())

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMStorageAccountDestroy,
Steps: []resource.TestStep{
{
Config: config,
},

{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
164 changes: 164 additions & 0 deletions azurerm/resource_arm_storage_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,43 @@ func resourceArmStorageAccount() *schema.Resource {
Optional: true,
},

"network_rules": {
Type: schema.TypeList,
MaxItems: 1,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"bypass": {
Type: schema.TypeSet,
Optional: true,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.StringInSlice([]string{
string(storage.AzureServices),
string(storage.Logging),
string(storage.Metrics),
string(storage.None),
}, true),
},
Set: schema.HashString,
},
"ip_rules": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
"virtual_network_subnet_ids": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
},
},
},

"primary_location": {
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -245,6 +282,8 @@ func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) e
storageType := fmt.Sprintf("%s_%s", accountTier, replicationType)
storageAccountEncryptionSource := d.Get("account_encryption_source").(string)

networkRules := expandStorageAccountNetworkRules(d)

parameters := storage.AccountCreateParameters{
Location: &location,
Sku: &storage.Sku{
Expand All @@ -261,6 +300,7 @@ func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) e
KeySource: storage.KeySource(storageAccountEncryptionSource),
},
EnableHTTPSTrafficOnly: &enableHTTPSTrafficOnly,
NetworkRuleSet: networkRules,
},
}

Expand Down Expand Up @@ -457,6 +497,22 @@ func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) e
d.SetPartial("enable_https_traffic_only")
}

if d.HasChange("network_rules") {
networkRules := expandStorageAccountNetworkRules(d)

opts := storage.AccountUpdateParameters{
AccountPropertiesUpdateParameters: &storage.AccountPropertiesUpdateParameters{
NetworkRuleSet: networkRules,
},
}
_, err := client.Update(ctx, resourceGroupName, storageAccountName, opts)
if err != nil {
return fmt.Errorf("Error updating Azure Storage Account network_rules %q: %+v", storageAccountName, err)
}

d.SetPartial("network_rules")
}

d.Partial(false)
return nil
}
Expand Down Expand Up @@ -571,6 +627,13 @@ func resourceArmStorageAccountRead(d *schema.ResourceData, meta interface{}) err
d.Set("secondary_table_endpoint", "")
}
}

networkRules := props.NetworkRuleSet
if networkRules != nil && len(*networkRules.IPRules) > 0 && len(*networkRules.VirtualNetworkRules) > 0 {
if err := d.Set("network_rules", flattenStorageAccountNetworkRules(networkRules)); err != nil {
return fmt.Errorf("Error flattening `network_rules`: %+v", err)
}
}
}

d.Set("primary_access_key", accessKeys[0].Value)
Expand Down Expand Up @@ -626,6 +689,107 @@ func flattenStorageAccountCustomDomain(input *storage.CustomDomain) []interface{
return []interface{}{domain}
}

func expandStorageAccountNetworkRules(d *schema.ResourceData) *storage.NetworkRuleSet {
networkRules := d.Get("network_rules").([]interface{})
if networkRules == nil || len(networkRules) == 0 {
// Default access is enabled when no network rules are set.
return &storage.NetworkRuleSet{DefaultAction: storage.DefaultActionAllow}
}

networkRule := networkRules[0].(map[string]interface{})
networkRuleSet := &storage.NetworkRuleSet{}

networkRuleSet.IPRules = expandStorageAccountIPRules(networkRule)
networkRuleSet.VirtualNetworkRules = expandStorageAccountVirtualNetworks(networkRule)
networkRuleSet.Bypass = expandStorageAccountBypass(networkRule)
// Default Access is disabled when network rules are set.
networkRuleSet.DefaultAction = storage.DefaultActionDeny

return networkRuleSet
}

func expandStorageAccountIPRules(networkRule map[string]interface{}) *[]storage.IPRule {
ipRulesInfo := networkRule["ip_rules"].(*schema.Set).List()
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 expandStorageAccountVirtualNetworks(networkRule map[string]interface{}) *[]storage.VirtualNetworkRule {
virtualNetworkInfo := networkRule["virtual_network_subnet_ids"].(*schema.Set).List()
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
}

func expandStorageAccountBypass(networkRule map[string]interface{}) storage.Bypass {
bypassInfo := networkRule["bypass"].(*schema.Set).List()

var bypassValues []string
for _, bypassConfig := range bypassInfo {
bypassValues = append(bypassValues, bypassConfig.(string))
}

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

func flattenStorageAccountNetworkRules(input *storage.NetworkRuleSet) []interface{} {
networkRules := make(map[string]interface{}, 0)

networkRules["ip_rules"] = schema.NewSet(schema.HashString, flattenStorageAccountIPRules(input.IPRules))
networkRules["virtual_network_subnet_ids"] = schema.NewSet(schema.HashString, flattenStorageAccountVirtualNetworks(input.VirtualNetworkRules))
networkRules["bypass"] = schema.NewSet(schema.HashString, flattenStorageAccountBypass(input.Bypass))

return []interface{}{networkRules}
}

func flattenStorageAccountIPRules(input *[]storage.IPRule) []interface{} {
ipRules := make([]interface{}, len(*input))
for i, ipRule := range *input {
ipRules[i] = *ipRule.IPAddressOrRange
}

return ipRules
}

func flattenStorageAccountVirtualNetworks(input *[]storage.VirtualNetworkRule) []interface{} {
virtualNetworks := make([]interface{}, len(*input))
for i, virtualNetwork := range *input {
virtualNetworks[i] = *virtualNetwork.VirtualNetworkResourceID
}

return virtualNetworks
}

func flattenStorageAccountBypass(input storage.Bypass) []interface{} {
bypassValues := strings.Split(string(input), ", ")
bypass := make([]interface{}, len(bypassValues))

for i, value := range bypassValues {
bypass[i] = value
}

return bypass
}

func validateArmStorageAccountName(v interface{}, k string) (ws []string, es []error) {
input := v.(string)

Expand Down
150 changes: 150 additions & 0 deletions azurerm/resource_arm_storage_account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,72 @@ func TestAccAzureRMStorageAccount_NonStandardCasing(t *testing.T) {
})
}

func TestAccAzureRMStorageAccount_networkRules(t *testing.T) {
resourceName := "azurerm_storage_account.testsa"
ri := acctest.RandInt()
rs := acctest.RandString(4)
location := testLocation()
preConfig := testAccAzureRMStorageAccount_networkRules(ri, rs, location)
postConfig := testAccAzureRMStorageAccount_networkRulesUpdate(ri, rs, location)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMStorageAccountDestroy,
Steps: []resource.TestStep{
{
Config: preConfig,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMStorageAccountExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "network_rules.0.ip_rules.#", "1"),
resource.TestCheckResourceAttr(resourceName, "network_rules.0.virtual_network_subnet_ids.#", "1"),
),
},
{
Config: postConfig,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMStorageAccountExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "network_rules.0.ip_rules.#", "2"),
resource.TestCheckResourceAttr(resourceName, "network_rules.0.virtual_network_subnet_ids.#", "0"),
resource.TestCheckResourceAttr(resourceName, "network_rules.0.bypass.#", "2"),
),
},
},
})
}

func TestAccAzureRMStorageAccount_networkRulesDeleted(t *testing.T) {
resourceName := "azurerm_storage_account.testsa"
ri := acctest.RandInt()
rs := acctest.RandString(4)
location := testLocation()
preConfig := testAccAzureRMStorageAccount_networkRules(ri, rs, location)
postConfig := testAccAzureRMStorageAccount_basic(ri, rs, location)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMStorageAccountDestroy,
Steps: []resource.TestStep{
{
Config: preConfig,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMStorageAccountExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "network_rules.0.ip_rules.#", "1"),
resource.TestCheckResourceAttr(resourceName, "network_rules.0.virtual_network_subnet_ids.#", "1"),
),
},
{
Config: postConfig,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMStorageAccountExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "network_rules.#", "0"),
),
},
},
})
}

func testCheckAzureRMStorageAccountExists(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
// Ensure we have enough information in state to look up in API
Expand Down Expand Up @@ -738,3 +804,87 @@ resource "azurerm_storage_account" "testsa" {
}
`, rInt, location, rString)
}

func testAccAzureRMStorageAccount_networkRules(rInt int, rString string, location string) string {
return fmt.Sprintf(`
resource "azurerm_resource_group" "testrg" {
name = "testAccAzureRMSA-%d"
location = "%s"
}
resource "azurerm_virtual_network" "test" {
name = "acctestvirtnet%d"
address_space = ["10.0.0.0/16"]
location = "${azurerm_resource_group.testrg.location}"
resource_group_name = "${azurerm_resource_group.testrg.name}"
}
resource "azurerm_subnet" "test" {
name = "acctestsubnet%d"
resource_group_name = "${azurerm_resource_group.testrg.name}"
virtual_network_name = "${azurerm_virtual_network.test.name}"
address_prefix = "10.0.2.0/24"
service_endpoints = ["Microsoft.Storage"]
}
resource "azurerm_storage_account" "testsa" {
name = "unlikely23exst2acct%s"
resource_group_name = "${azurerm_resource_group.testrg.name}"
location = "${azurerm_resource_group.testrg.location}"
account_tier = "Standard"
account_replication_type = "LRS"
network_rules {
ip_rules = ["127.0.0.1"]
virtual_network_subnet_ids = ["${azurerm_subnet.test.id}"]
}
tags {
environment = "production"
}
}
`, rInt, location, rInt, rInt, rString)
}

func testAccAzureRMStorageAccount_networkRulesUpdate(rInt int, rString string, location string) string {
return fmt.Sprintf(`
resource "azurerm_resource_group" "testrg" {
name = "testAccAzureRMSA-%d"
location = "%s"
}
resource "azurerm_virtual_network" "test" {
name = "acctestvirtnet%d"
address_space = ["10.0.0.0/16"]
location = "${azurerm_resource_group.testrg.location}"
resource_group_name = "${azurerm_resource_group.testrg.name}"
}
resource "azurerm_subnet" "test" {
name = "acctestsubnet%d"
resource_group_name = "${azurerm_resource_group.testrg.name}"
virtual_network_name = "${azurerm_virtual_network.test.name}"
address_prefix = "10.0.2.0/24"
service_endpoints = ["Microsoft.Storage"]
}
resource "azurerm_storage_account" "testsa" {
name = "unlikely23exst2acct%s"
resource_group_name = "${azurerm_resource_group.testrg.name}"
location = "${azurerm_resource_group.testrg.location}"
account_tier = "Standard"
account_replication_type = "LRS"
network_rules {
ip_rules = ["127.0.0.1", "127.0.0.2"]
bypass = ["Logging", "Metrics"]
}
tags {
environment = "production"
}
}
`, rInt, location, rInt, rInt, rString)
}
Loading

0 comments on commit 3c5216f

Please sign in to comment.