Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

azurerm_subnet: Support for multiple prefixes with address_prefixes #6493

Merged
merged 1 commit into from Apr 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 15 additions & 0 deletions azurerm/internal/services/network/data_source_subnet.go
Expand Up @@ -40,6 +40,12 @@ func dataSourceArmSubnet() *schema.Resource {
Computed: true,
},

"address_prefixes": {
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
Computed: true,
},

"network_security_group_id": {
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -95,6 +101,15 @@ func dataSourceArmSubnetRead(d *schema.ResourceData, meta interface{}) error {

if props := resp.SubnetPropertiesFormat; props != nil {
d.Set("address_prefix", props.AddressPrefix)
if props.AddressPrefixes == nil {
if props.AddressPrefix != nil && len(*props.AddressPrefix) > 0 {
d.Set("address_prefixes", []string{*props.AddressPrefix})
} else {
d.Set("address_prefixes", []string{})
}
} else {
d.Set("address_prefixes", utils.FlattenStringSlice(props.AddressPrefixes))
}

d.Set("enforce_private_link_endpoint_network_policies", flattenSubnetPrivateLinkNetworkPolicy(props.PrivateEndpointNetworkPolicies))
d.Set("enforce_private_link_service_network_policies", flattenSubnetPrivateLinkNetworkPolicy(props.PrivateLinkServiceNetworkPolicies))
Expand Down
68 changes: 56 additions & 12 deletions azurerm/internal/services/network/resource_arm_subnet.go
Expand Up @@ -54,7 +54,22 @@ func resourceArmSubnet() *schema.Resource {

"address_prefix": {
Type: schema.TypeString,
Required: true,
Optional: true,
Computed: true,
// TODO Remove this in the next major version release
Deprecated: "Use the `address_prefixes` property instead.",
ExactlyOneOf: []string{"address_prefix", "address_prefixes"},
},

"address_prefixes": {
Type: schema.TypeList,
Optional: true,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.StringIsNotEmpty,
},
ExactlyOneOf: []string{"address_prefix", "address_prefixes"},
},

"service_endpoints": {
Expand Down Expand Up @@ -162,24 +177,35 @@ func resourceArmSubnetCreate(d *schema.ResourceData, meta interface{}) error {
}
}

addressPrefix := d.Get("address_prefix").(string)

locks.ByName(vnetName, VirtualNetworkResourceName)
defer locks.UnlockByName(vnetName, VirtualNetworkResourceName)

properties := network.SubnetPropertiesFormat{}
if value, ok := d.GetOk("address_prefixes"); ok {
var addressPrefixes []string
for _, item := range value.([]interface{}) {
addressPrefixes = append(addressPrefixes, item.(string))
}
properties.AddressPrefixes = &addressPrefixes
}
if value, ok := d.GetOk("address_prefix"); ok {
addressPrefix := value.(string)
properties.AddressPrefix = &addressPrefix
}
if properties.AddressPrefixes != nil && len(*properties.AddressPrefixes) == 1 {
properties.AddressPrefix = &(*properties.AddressPrefixes)[0]
properties.AddressPrefixes = nil
}

// To enable private endpoints you must disable the network policies for the subnet because
// Network policies like network security groups are not supported by private endpoints.
privateEndpointNetworkPolicies := d.Get("enforce_private_link_endpoint_network_policies").(bool)
privateLinkServiceNetworkPolicies := d.Get("enforce_private_link_service_network_policies").(bool)
properties.PrivateEndpointNetworkPolicies = expandSubnetPrivateLinkNetworkPolicy(privateEndpointNetworkPolicies)
properties.PrivateLinkServiceNetworkPolicies = expandSubnetPrivateLinkNetworkPolicy(privateLinkServiceNetworkPolicies)

serviceEndpointsRaw := d.Get("service_endpoints").([]interface{})
properties := network.SubnetPropertiesFormat{
AddressPrefix: &addressPrefix,
ServiceEndpoints: expandSubnetServiceEndpoints(serviceEndpointsRaw),

// To enable private endpoints you must disable the network policies for the subnet because
// Network policies like network security groups are not supported by private endpoints.
PrivateEndpointNetworkPolicies: expandSubnetPrivateLinkNetworkPolicy(privateEndpointNetworkPolicies),
PrivateLinkServiceNetworkPolicies: expandSubnetPrivateLinkNetworkPolicy(privateLinkServiceNetworkPolicies),
}
properties.ServiceEndpoints = expandSubnetServiceEndpoints(serviceEndpointsRaw)

delegationsRaw := d.Get("delegation").([]interface{})
properties.Delegations = expandSubnetDelegation(delegationsRaw)
Expand Down Expand Up @@ -241,6 +267,15 @@ func resourceArmSubnetUpdate(d *schema.ResourceData, meta interface{}) error {
props.AddressPrefix = utils.String(d.Get("address_prefix").(string))
}

if d.HasChange("address_prefixes") {
addressPrefixesRaw := d.Get("address_prefixes").([]interface{})
props.AddressPrefixes = utils.ExpandStringSlice(addressPrefixesRaw)
if props.AddressPrefixes != nil && len(*props.AddressPrefixes) == 1 {
props.AddressPrefix = &(*props.AddressPrefixes)[0]
props.AddressPrefixes = nil
}
}

if d.HasChange("delegation") {
delegationsRaw := d.Get("delegation").([]interface{})
props.Delegations = expandSubnetDelegation(delegationsRaw)
Expand Down Expand Up @@ -307,6 +342,15 @@ func resourceArmSubnetRead(d *schema.ResourceData, meta interface{}) error {

if props := resp.SubnetPropertiesFormat; props != nil {
d.Set("address_prefix", props.AddressPrefix)
if props.AddressPrefixes == nil {
if props.AddressPrefix != nil && len(*props.AddressPrefix) > 0 {
d.Set("address_prefixes", []string{*props.AddressPrefix})
} else {
d.Set("address_prefixes", []string{})
}
} else {
d.Set("address_prefixes", props.AddressPrefixes)
}

delegation := flattenSubnetDelegation(props.Delegations)
if err := d.Set("delegation", delegation); err != nil {
Expand Down
Expand Up @@ -442,8 +442,9 @@ func resourceAzureSubnetHash(v interface{}) int {

if m, ok := v.(map[string]interface{}); ok {
buf.WriteString(m["name"].(string))
buf.WriteString(m["address_prefix"].(string))

if v, ok := m["address_prefix"]; ok {
buf.WriteString(v.(string))
}
if v, ok := m["security_group"]; ok {
buf.WriteString(v.(string))
}
Expand Down
51 changes: 51 additions & 0 deletions azurerm/internal/services/network/tests/data_source_subnet_test.go
Expand Up @@ -30,6 +30,28 @@ func TestAccDataSourceSubnet_basic(t *testing.T) {
})
}

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

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acceptance.PreCheck(t) },
Providers: acceptance.SupportedProviders,
Steps: []resource.TestStep{
{
Config: testAccDataSourceSubnet_basic_addressPrefixes(data),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet(data.ResourceName, "name"),
resource.TestCheckResourceAttrSet(data.ResourceName, "resource_group_name"),
resource.TestCheckResourceAttrSet(data.ResourceName, "virtual_network_name"),
resource.TestCheckResourceAttrSet(data.ResourceName, "address_prefixes.#"),
resource.TestCheckResourceAttr(data.ResourceName, "network_security_group_id", ""),
resource.TestCheckResourceAttr(data.ResourceName, "route_table_id", ""),
),
},
},
})
}

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

Expand Down Expand Up @@ -127,6 +149,35 @@ data "azurerm_subnet" "test" {
`, template)
}

func testAccDataSourceSubnet_basic_addressPrefixes(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "test" {
name = "acctest%d-rg"
location = "%s"
}
resource "azurerm_virtual_network" "test" {
name = "acctest%d-vn"
address_space = ["10.0.0.0/16", "ace:cab:deca::/48"]
location = "${azurerm_resource_group.test.location}"
resource_group_name = "${azurerm_resource_group.test.name}"
}
resource "azurerm_subnet" "test" {
name = "acctest%d-private"
resource_group_name = "${azurerm_resource_group.test.name}"
virtual_network_name = "${azurerm_virtual_network.test.name}"
address_prefixes = ["10.0.0.0/24", "ace:cab:deca:deed::/64"]
}
data "azurerm_subnet" "test" {
name = "${azurerm_subnet.test.name}"
resource_group_name = "${azurerm_resource_group.test.name}"
virtual_network_name = "${azurerm_virtual_network.test.name}"
}
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger)
}

func testAccDataSourceSubnet_networkSecurityGroupDependencies(data acceptance.TestData) string {
template := testAccDataSourceSubnet_template(data)
return fmt.Sprintf(`
Expand Down
Expand Up @@ -32,6 +32,25 @@ func TestAccAzureRMSubnet_basic(t *testing.T) {
})
}

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

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acceptance.PreCheck(t) },
Providers: acceptance.SupportedProviders,
CheckDestroy: testCheckAzureRMSubnetDestroy,
Steps: []resource.TestStep{
{
Config: testAccAzureRMSubnet_basic_addressPrefixes(data),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMSubnetExists(data.ResourceName),
),
},
data.ImportStep(),
},
})
}

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

Expand Down Expand Up @@ -436,6 +455,27 @@ resource "azurerm_subnet" "test" {
`, template, enabled)
}

func testAccAzureRMSubnet_basic_addressPrefixes(data acceptance.TestData) string {
return fmt.Sprintf(`
resource "azurerm_resource_group" "test" {
name = "acctestRG-n-%d"
location = "%s"
}
resource "azurerm_virtual_network" "test" {
name = "acctestvirtnet%d"
address_space = ["10.0.0.0/16", "ace:cab:deca::/48"]
location = "${azurerm_resource_group.test.location}"
resource_group_name = "${azurerm_resource_group.test.name}"
}
resource "azurerm_subnet" "test" {
name = "acctestsubnet%d"
resource_group_name = "${azurerm_resource_group.test.name}"
virtual_network_name = "${azurerm_virtual_network.test.name}"
address_prefixes = ["10.0.0.0/24", "ace:cab:deca:deed::/64"]
}
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger)
}

func testAccAzureRMSubnet_requiresImport(data acceptance.TestData) string {
template := testAccAzureRMSubnet_basic(data)
return fmt.Sprintf(`
Expand Down
3 changes: 2 additions & 1 deletion website/docs/d/subnet.html.markdown
Expand Up @@ -33,7 +33,8 @@ output "subnet_id" {
## Attributes Reference

* `id` - The ID of the Subnet.
* `address_prefix` - The address prefix used for the subnet.
* `address_prefix` - (Deprecated) The address prefix used for the subnet.
* `address_prefixes` - The address prefixes for the subnet.
* `enforce_private_link_service_network_policies` - Enable or Disable network policies on private link service in the subnet.
* `network_security_group_id` - The ID of the Network Security Group associated with the subnet.
* `route_table_id` - The ID of the Route Table associated with this subnet.
Expand Down
11 changes: 8 additions & 3 deletions website/docs/r/subnet.html.markdown
Expand Up @@ -34,7 +34,7 @@ resource "azurerm_subnet" "example" {
name = "testsubnet"
resource_group_name = azurerm_resource_group.example.name
virtual_network_name = azurerm_virtual_network.example.name
address_prefix = "10.0.1.0/24"
address_prefixes = ["10.0.1.0/24"]

delegation {
name = "acctestdelegation"
Expand All @@ -57,7 +57,11 @@ The following arguments are supported:

* `virtual_network_name` - (Required) The name of the virtual network to which to attach the subnet. Changing this forces a new resource to be created.

* `address_prefix` - (Required) The address prefix to use for the subnet.
* `address_prefix` - (Optional / **Deprecated in favour of `address_prefixes`**) The address prefix to use for the subnet.

* `address_prefixes` - (Optional) The address prefixes to use for the subnet.

-> **NOTE:** One of `address_prefix` or `address_prefixes` is required.

---

Expand Down Expand Up @@ -101,7 +105,8 @@ The following attributes are exported:
* `name` - The name of the subnet.
* `resource_group_name` - The name of the resource group in which the subnet is created in.
* `virtual_network_name` - The name of the virtual network in which the subnet is created in
* `address_prefix` - The address prefix for the subnet
* `address_prefix` - (Deprecated) The address prefix for the subnet
* `address_prefixes` - The address prefixes for the subnet

## Timeouts

Expand Down