Skip to content

Commit

Permalink
subnet: Support address_prefixes for multiple prefixes (ipv6)
Browse files Browse the repository at this point in the history
The azure subnet resource supports addressPrefix when one prefix is
available and addressPrefixes when two or or more are set (for
instance, when configuring an ipv4 and ipv6 dual stack configuration
for a network). Update `azurerm_subnet` to allow either
`address_prefix` or `address_prefixes` as input, where the latter
is a list of string. Check that at least one is set, and mark
the older address_prefix as deprecated.

The data source for subnet is modified to always return address_prefixes
to simplify retrieval (although this could be changed to always return
the first address prefix as address_prefix).

Today, if two prefixes are specified via CLI or Azure UI the data
source returns no address_prefix and it is not possible to create a
new subnet with two prefixes via Terraform.
  • Loading branch information
smarterclayton committed Jan 31, 2020
1 parent 134b510 commit c729756
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 11 deletions.
19 changes: 18 additions & 1 deletion azurerm/internal/services/network/data_source_subnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,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 @@ -102,7 +108,18 @@ func dataSourceArmSubnetRead(d *schema.ResourceData, meta interface{}) error {
d.Set("virtual_network_name", virtualNetworkName)

if props := resp.SubnetPropertiesFormat; props != nil {
d.Set("address_prefix", props.AddressPrefix)
if props.AddressPrefix != 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))
}

if pe := props.PrivateEndpointNetworkPolicies; pe != nil {
d.Set("enforce_private_link_endpoint_network_policies", strings.EqualFold("Disabled", *pe))
Expand Down
54 changes: 44 additions & 10 deletions azurerm/internal/services/network/resource_arm_subnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,17 @@ func resourceArmSubnet() *schema.Resource {
},

"address_prefix": {
Type: schema.TypeString,
Required: true,
Type: schema.TypeString,
Optional: true,
Deprecated: "Use the `address_prefixes` property instead.",
ConflictsWith: []string{"address_prefixes"},
},

"address_prefixes": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
ConflictsWith: []string{"address_prefix"},
},

"network_security_group_id": {
Expand Down Expand Up @@ -179,13 +188,27 @@ func resourceArmSubnetCreateUpdate(d *schema.ResourceData, meta interface{}) err
}
}

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

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

properties := network.SubnetPropertiesFormat{
AddressPrefix: &addressPrefix,
var prefixSet bool
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
prefixSet = len(addressPrefixes) > 0
}
if value, ok := d.GetOk("address_prefix"); ok {
addressPrefix := value.(string)
properties.AddressPrefix = &addressPrefix
prefixSet = len(addressPrefix) > 0
}
if properties.AddressPrefixes != nil && len(*properties.AddressPrefixes) == 1 {
properties.AddressPrefix = &(*properties.AddressPrefixes)[0]
properties.AddressPrefixes = nil
}
if !prefixSet {
return fmt.Errorf("[ERROR] either address_prefix or address_prefixes is required")
}

if v, ok := d.GetOk("enforce_private_link_service_network_policies"); ok {
Expand Down Expand Up @@ -305,7 +328,18 @@ func resourceArmSubnetRead(d *schema.ResourceData, meta interface{}) error {
d.Set("virtual_network_name", vnetName)

if props := resp.SubnetPropertiesFormat; props != nil {
d.Set("address_prefix", props.AddressPrefix)
if props.AddressPrefix != 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)
}

if p := props.PrivateLinkServiceNetworkPolicies; p != nil {
// To enable private endpoints you must disable the network policies for the
Expand Down
51 changes: 51 additions & 0 deletions azurerm/internal/services/network/tests/data_source_subnet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,28 @@ func TestAccDataSourceAzureRMSubnet_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: testAccDataSourceAzureRMSubnet_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 TestAccDataSourceAzureRMSubnet_networkSecurityGroup(t *testing.T) {
data := acceptance.BuildTestData(t, "data.azurerm_subnet", "test")

Expand Down Expand Up @@ -128,6 +150,35 @@ data "azurerm_subnet" "test" {
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger)
}

func testAccDataSourceAzureRMSubnet_basic_addressPrefixes(data acceptance.TestData) string {
return fmt.Sprintf(`
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"]
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", "fd00::/48""]
}
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 testAccDataSourceAzureRMSubnet_networkSecurityGroup(data acceptance.TestData) string {
return fmt.Sprintf(`
resource "azurerm_resource_group" "test" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,24 @@ 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) {
if !features.ShouldResourcesBeImported() {
t.Skip("Skipping since resources aren't required to be imported")
Expand Down Expand Up @@ -426,6 +444,29 @@ resource "azurerm_subnet" "test" {
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger)
}

func testAccAzureRMSubnet_basic_addressPrefixes(data acceptance.TestData) string {
return fmt.Sprintf(`
resource "azurerm_resource_group" "test" {
name = "acctestRG-%d"
location = "%s"
}
resource "azurerm_virtual_network" "test" {
name = "acctestvirtnet%d"
address_space = ["10.0.0.0/16"]
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.2.0/24", "fd00::/48"]
}
`, 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

0 comments on commit c729756

Please sign in to comment.