From 902dad0eae1b02a9205187f02c9733ba64c6ab28 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Tue, 10 Oct 2017 18:11:42 +0100 Subject: [PATCH 1/2] Refactoring Subnets Fixing an issue where it wasn't possible to remove a NSG / Route Table attachment --- azurerm/import_arm_subnet_test.go | 23 +++ azurerm/resource_arm_subnet.go | 52 +++-- azurerm/resource_arm_subnet_test.go | 289 +++++++++++++++++++++------- website/docs/r/subnet.html.markdown | 23 +-- 4 files changed, 275 insertions(+), 112 deletions(-) diff --git a/azurerm/import_arm_subnet_test.go b/azurerm/import_arm_subnet_test.go index 077a08d943f0..5f6c294eb6b6 100644 --- a/azurerm/import_arm_subnet_test.go +++ b/azurerm/import_arm_subnet_test.go @@ -52,3 +52,26 @@ func TestAccAzureRMSubnet_importWithRouteTable(t *testing.T) { }, }) } + +func TestAccAzureRMSubnet_importWithNetworkSecurityGroup(t *testing.T) { + resourceName := "azurerm_subnet.test" + + ri := acctest.RandInt() + config := testAccAzureRMSubnet_networkSecurityGroup(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMSubnetDestroy, + Steps: []resource.TestStep{ + { + Config: config, + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/azurerm/resource_arm_subnet.go b/azurerm/resource_arm_subnet.go index fafca054ca78..93559a758a49 100644 --- a/azurerm/resource_arm_subnet.go +++ b/azurerm/resource_arm_subnet.go @@ -44,13 +44,11 @@ func resourceArmSubnet() *schema.Resource { "network_security_group_id": { Type: schema.TypeString, Optional: true, - Computed: true, }, "route_table_id": { Type: schema.TypeString, Optional: true, - Computed: true, }, "ip_configurations": { @@ -65,8 +63,7 @@ func resourceArmSubnet() *schema.Resource { } func resourceArmSubnetCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient) - subnetClient := client.subnetClient + client := meta.(*ArmClient).subnetClient log.Printf("[INFO] preparing arguments for Azure ARM Subnet creation.") @@ -117,13 +114,13 @@ func resourceArmSubnetCreate(d *schema.ResourceData, meta interface{}) error { SubnetPropertiesFormat: &properties, } - _, error := subnetClient.CreateOrUpdate(resGroup, vnetName, name, subnet, make(chan struct{})) - err := <-error + _, createErr := client.CreateOrUpdate(resGroup, vnetName, name, subnet, make(chan struct{})) + err := <-createErr if err != nil { return err } - read, err := subnetClient.Get(resGroup, vnetName, name, "") + read, err := client.Get(resGroup, vnetName, name, "") if err != nil { return err } @@ -137,7 +134,7 @@ func resourceArmSubnetCreate(d *schema.ResourceData, meta interface{}) error { } func resourceArmSubnetRead(d *schema.ResourceData, meta interface{}) error { - subnetClient := meta.(*ArmClient).subnetClient + client := meta.(*ArmClient).subnetClient id, err := parseAzureResourceID(d.Id()) if err != nil { @@ -147,7 +144,7 @@ func resourceArmSubnetRead(d *schema.ResourceData, meta interface{}) error { vnetName := id.Path["virtualNetworks"] name := id.Path["subnets"] - resp, err := subnetClient.Get(resGroup, vnetName, name, "") + resp, err := client.Get(resGroup, vnetName, name, "") if err != nil { if utils.ResponseWasNotFound(resp.Response) { @@ -160,38 +157,35 @@ func resourceArmSubnetRead(d *schema.ResourceData, meta interface{}) error { d.Set("name", name) d.Set("resource_group_name", resGroup) d.Set("virtual_network_name", vnetName) - d.Set("address_prefix", resp.SubnetPropertiesFormat.AddressPrefix) - if resp.SubnetPropertiesFormat.NetworkSecurityGroup != nil { - d.Set("network_security_group_id", resp.SubnetPropertiesFormat.NetworkSecurityGroup.ID) - } else { - d.Set("network_security_group_id", "") - } + if props := resp.SubnetPropertiesFormat; props != nil { + d.Set("address_prefix", props.AddressPrefix) - if resp.SubnetPropertiesFormat.RouteTable != nil { - d.Set("route_table_id", resp.SubnetPropertiesFormat.RouteTable.ID) - } else { - d.Set("route_table_id", "") - } + if props.NetworkSecurityGroup != nil { + d.Set("network_security_group_id", props.NetworkSecurityGroup.ID) + } + + if props.RouteTable != nil { + d.Set("route_table_id", props.RouteTable.ID) + } - if resp.SubnetPropertiesFormat.IPConfigurations != nil { - ips := make([]string, 0, len(*resp.SubnetPropertiesFormat.IPConfigurations)) - for _, ip := range *resp.SubnetPropertiesFormat.IPConfigurations { - ips = append(ips, *ip.ID) + ips := make([]string, 0) + if props.IPConfigurations != nil { + for _, ip := range *props.IPConfigurations { + ips = append(ips, *ip.ID) + } } if err := d.Set("ip_configurations", ips); err != nil { return err } - } else { - d.Set("ip_configurations", []string{}) } return nil } func resourceArmSubnetDelete(d *schema.ResourceData, meta interface{}) error { - subnetClient := meta.(*ArmClient).subnetClient + client := meta.(*ArmClient).subnetClient id, err := parseAzureResourceID(d.Id()) if err != nil { @@ -229,8 +223,8 @@ func resourceArmSubnetDelete(d *schema.ResourceData, meta interface{}) error { azureRMLockByName(name, subnetResourceName) defer azureRMUnlockByName(name, subnetResourceName) - _, error := subnetClient.Delete(resGroup, vnetName, name, make(chan struct{})) - err = <-error + _, deleteErr := client.Delete(resGroup, vnetName, name, make(chan struct{})) + err = <-deleteErr return err } diff --git a/azurerm/resource_arm_subnet_test.go b/azurerm/resource_arm_subnet_test.go index 33a0ca215fee..b93ed0df12e7 100644 --- a/azurerm/resource_arm_subnet_test.go +++ b/azurerm/resource_arm_subnet_test.go @@ -3,13 +3,13 @@ package azurerm import ( "fmt" "log" - "net/http" "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) func TestAccAzureRMSubnet_basic(t *testing.T) { @@ -33,7 +33,6 @@ func TestAccAzureRMSubnet_basic(t *testing.T) { } func TestAccAzureRMSubnet_routeTableUpdate(t *testing.T) { - ri := acctest.RandInt() location := testLocation() initConfig := testAccAzureRMSubnet_routeTable(ri, location) @@ -61,6 +60,68 @@ func TestAccAzureRMSubnet_routeTableUpdate(t *testing.T) { }) } +func TestAccAzureRMSubnet_routeTableRemove(t *testing.T) { + resourceName := "azurerm_subnet.test" + ri := acctest.RandInt() + location := testLocation() + initConfig := testAccAzureRMSubnet_routeTable(ri, location) + updatedConfig := testAccAzureRMSubnet_routeTableUnlinked(ri, location) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMSubnetDestroy, + Steps: []resource.TestStep{ + { + Config: initConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSubnetExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "route_table_id"), + ), + }, + + { + Config: updatedConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSubnetExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "route_table_id", ""), + ), + }, + }, + }) +} + +func TestAccAzureRMSubnet_removeNetworkSecurityGroup(t *testing.T) { + resourceName := "azurerm_subnet.test" + ri := acctest.RandInt() + location := testLocation() + initConfig := testAccAzureRMSubnet_networkSecurityGroup(ri, location) + updatedConfig := testAccAzureRMSubnet_networkSecurityGroupDetached(ri, location) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMSubnetDestroy, + Steps: []resource.TestStep{ + { + Config: initConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSubnetExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "network_security_group_id"), + ), + }, + + { + Config: updatedConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSubnetExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "network_security_group_id", ""), + ), + }, + }, + }) +} + func TestAccAzureRMSubnet_bug7986(t *testing.T) { ri := acctest.RandInt() initConfig := testAccAzureRMSubnet_bug7986(ri, testLocation()) @@ -101,7 +162,6 @@ func TestAccAzureRMSubnet_bug15204(t *testing.T) { } func TestAccAzureRMSubnet_disappears(t *testing.T) { - ri := acctest.RandInt() config := testAccAzureRMSubnet_basic(ri, testLocation()) @@ -143,15 +203,11 @@ func testCheckAzureRMSubnetExists(name string) resource.TestCheckFunc { resp, err := conn.Get(resourceGroup, vnetName, name, "") if err != nil { - return fmt.Errorf("Bad: Get on subnetClient: %+v", err) - } + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Subnet %q (resource group: %q) does not exist", name, resourceGroup) + } - if resp.StatusCode == http.StatusNotFound { - return fmt.Errorf("Bad: Subnet %q (resource group: %q) does not exist", name, resourceGroup) - } - - if resp.RouteTable == nil { - return fmt.Errorf("Bad: Subnet %q (resource group: %q) does not contain route tables after add", name, resourceGroup) + return fmt.Errorf("Bad: Get on subnetClient: %+v", err) } return nil @@ -189,11 +245,11 @@ func testCheckAzureRMSubnetRouteTableExists(subnetName string, routeTableId stri resp, err := conn.Get(resourceGroup, vnetName, name, "") if err != nil { - return fmt.Errorf("Bad: Get on subnetClient: %+v", err) - } + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Subnet %q (resource group: %q) does not exist", subnetName, resourceGroup) + } - if resp.StatusCode == http.StatusNotFound { - return fmt.Errorf("Bad: Subnet %q (resource group: %q) does not exist", subnetName, resourceGroup) + return fmt.Errorf("Bad: Get on subnetClient: %+v", err) } if resp.RouteTable == nil { @@ -223,12 +279,14 @@ func testCheckAzureRMSubnetDisappears(name string) resource.TestCheckFunc { return fmt.Errorf("Bad: no resource group found in state for subnet: %s", name) } - conn := testAccProvider.Meta().(*ArmClient).subnetClient - - _, error := conn.Delete(resourceGroup, vnetName, name, make(chan struct{})) - err := <-error + client := testAccProvider.Meta().(*ArmClient).subnetClient + deleteResp, deleteErr := client.Delete(resourceGroup, vnetName, name, make(chan struct{})) + resp := <-deleteResp + err := <-deleteErr if err != nil { - return fmt.Errorf("Bad: Delete on subnetClient: %+v", err) + if !utils.ResponseWasNotFound(resp) { + return fmt.Errorf("Bad: Delete on subnetClient: %+v", err) + } } return nil @@ -236,7 +294,7 @@ func testCheckAzureRMSubnetDisappears(name string) resource.TestCheckFunc { } func testCheckAzureRMSubnetDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).subnetClient + client := testAccProvider.Meta().(*ArmClient).subnetClient for _, rs := range s.RootModule().Resources { if rs.Type != "azurerm_subnet" { @@ -247,15 +305,13 @@ func testCheckAzureRMSubnetDestroy(s *terraform.State) error { vnetName := rs.Primary.Attributes["virtual_network_name"] resourceGroup := rs.Primary.Attributes["resource_group_name"] - resp, err := conn.Get(resourceGroup, vnetName, name, "") - + resp, err := client.Get(resourceGroup, vnetName, name, "") if err != nil { + if !utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Subnet still exists:\n%#v", resp.SubnetPropertiesFormat) + } return nil } - - if resp.StatusCode != http.StatusNotFound { - return fmt.Errorf("Subnet still exists:\n%#v", resp.SubnetPropertiesFormat) - } } return nil @@ -280,28 +336,48 @@ resource "azurerm_subnet" "test" { resource_group_name = "${azurerm_resource_group.test.name}" virtual_network_name = "${azurerm_virtual_network.test.name}" address_prefix = "10.0.2.0/24" - route_table_id = "${azurerm_route_table.test.id}" +} +`, rInt, location, rInt, rInt) } -resource "azurerm_route_table" "test" { - name = "acctestroutetable%d" - resource_group_name = "${azurerm_resource_group.test.name}" +func testAccAzureRMSubnet_routeTable(rInt int, location string) 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_route" "test" { - name = "acctestroute%d" +resource "azurerm_subnet" "test" { + name = "acctestsubnet%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" + route_table_id = "${azurerm_route_table.test.id}" +} + +resource "azurerm_route_table" "test" { + name = "acctest-%d" + location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" - route_table_name = "${azurerm_route_table.test.name}" - address_prefix = "10.100.0.0/14" - next_hop_type = "VirtualAppliance" - next_hop_in_ip_address = "10.10.1.1" + route { + name = "acctest-%d" + address_prefix = "10.100.0.0/14" + next_hop_type = "VirtualAppliance" + next_hop_in_ip_address = "10.10.1.1" + } } `, rInt, location, rInt, rInt, rInt, rInt) } -func testAccAzureRMSubnet_routeTable(rInt int, location string) string { +func testAccAzureRMSubnet_routeTableUnlinked(rInt int, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { name = "acctestRG-%d" @@ -320,23 +396,19 @@ resource "azurerm_subnet" "test" { resource_group_name = "${azurerm_resource_group.test.name}" virtual_network_name = "${azurerm_virtual_network.test.name}" address_prefix = "10.0.2.0/24" - route_table_id = "${azurerm_route_table.test.id}" } resource "azurerm_route_table" "test" { name = "acctest-%d" location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" -} - -resource "azurerm_route" "route_a" { - name = "acctest-%d" - resource_group_name = "${azurerm_resource_group.test.name}" - route_table_name = "${azurerm_route_table.test.name}" - address_prefix = "10.100.0.0/14" - next_hop_type = "VirtualAppliance" - next_hop_in_ip_address = "10.10.1.1" + route { + name = "acctest-%d" + address_prefix = "10.100.0.0/14" + next_hop_type = "VirtualAppliance" + next_hop_in_ip_address = "10.10.1.1" + } } `, rInt, location, rInt, rInt, rInt, rInt) } @@ -398,30 +470,87 @@ resource "azurerm_route_table" "test" { location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" + route { + name = "acctest-%d" + address_prefix = "10.100.0.0/14" + next_hop_type = "VirtualAppliance" + next_hop_in_ip_address = "10.10.1.1" + } + tags { environment = "Testing" } } +`, rInt, location, rInt, rInt, rInt, rInt, rInt, rInt) +} -resource "azurerm_route" "route_a" { - name = "acctest-%d" +func testAccAzureRMSubnet_networkSecurityGroup(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctest%d-rg" + location = "%s" +} + +resource "azurerm_network_security_group" "test" { + name = "acctestnsg%d" + location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" - route_table_name = "${azurerm_route_table.test.name}" - address_prefix = "10.100.0.0/14" - next_hop_type = "VirtualAppliance" - next_hop_in_ip_address = "10.10.1.1" + security_rule { + name = "test123" + priority = 100 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = "*" + source_address_prefix = "*" + destination_address_prefix = "*" + } } -`, rInt, location, rInt, rInt, rInt, rInt, rInt, rInt) + +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}" } -func testAccAzureRMSubnet_bug7986(rInt int, location string) string { +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_prefix = "10.0.0.0/24" + network_security_group_id = "${azurerm_network_security_group.test.id}" +} +`, rInt, location, rInt, rInt, rInt) +} + +func testAccAzureRMSubnet_networkSecurityGroupDetached(rInt int, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { name = "acctest%d-rg" location = "%s" } +resource "azurerm_network_security_group" "test" { + name = "acctestnsg%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + security_rule { + name = "test123" + priority = 100 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = "*" + source_address_prefix = "*" + destination_address_prefix = "*" + } +} + resource "azurerm_virtual_network" "test" { name = "acctest%d-vn" address_space = ["10.0.0.0/16"] @@ -429,18 +558,39 @@ resource "azurerm_virtual_network" "test" { resource_group_name = "${azurerm_resource_group.test.name}" } -resource "azurerm_route_table" "first" { - name = "acctest%d-private-1" +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_prefix = "10.0.0.0/24" +} +`, rInt, location, rInt, rInt, rInt) +} + +func testAccAzureRMSubnet_bug7986(rInt int, location string) 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_route" "first" { +resource "azurerm_route_table" "first" { name = "acctest%d-private-1" + location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" - route_table_name = "${azurerm_route_table.first.name}" - address_prefix = "0.0.0.0/0" - next_hop_type = "None" + + route { + name = "acctest%d-private-1" + address_prefix = "0.0.0.0/0" + next_hop_type = "None" + } } resource "azurerm_subnet" "first" { @@ -455,14 +605,12 @@ resource "azurerm_route_table" "second" { name = "acctest%d-private-2" location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" -} -resource "azurerm_route" "second" { - name = "acctest%d-private-2" - resource_group_name = "${azurerm_resource_group.test.name}" - route_table_name = "${azurerm_route_table.second.name}" - address_prefix = "0.0.0.0/0" - next_hop_type = "None" + route { + name = "acctest%d-private-2" + address_prefix = "0.0.0.0/0" + next_hop_type = "None" + } } resource "azurerm_subnet" "second" { @@ -471,7 +619,8 @@ resource "azurerm_subnet" "second" { virtual_network_name = "${azurerm_virtual_network.test.name}" address_prefix = "10.0.1.0/24" route_table_id = "${azurerm_route_table.second.id}" -}`, rInt, location, rInt, rInt, rInt, rInt, rInt, rInt, rInt) +} +`, rInt, location, rInt, rInt, rInt, rInt, rInt, rInt, rInt) } func testAccAzureRMSubnet_bug15204(rInt int, location string) string { diff --git a/website/docs/r/subnet.html.markdown b/website/docs/r/subnet.html.markdown index 72b8a87f520d..503485d40d69 100644 --- a/website/docs/r/subnet.html.markdown +++ b/website/docs/r/subnet.html.markdown @@ -3,12 +3,13 @@ layout: "azurerm" page_title: "Azure Resource Manager: azure_subnet" sidebar_current: "docs-azurerm-resource-network-subnet" description: |- - Creates a new subnet. Subnets represent network segments within the IP space defined by the virtual network. + Manages a subnet. Subnets represent network segments within the IP space defined by the virtual network. + --- -# azurerm\_subnet +# azurerm_subnet -Creates a new subnet. Subnets represent network segments within the IP space defined by the virtual network. +Manages a subnet. Subnets represent network segments within the IP space defined by the virtual network. ~> **NOTE on Virtual Networks and Subnet's:** Terraform currently provides both a standalone [Subnet resource](subnet.html), and allows for Subnets to be defined in-line within the [Virtual Network resource](virtual_network.html). @@ -25,7 +26,7 @@ resource "azurerm_resource_group" "test" { resource "azurerm_virtual_network" "test" { name = "acceptanceTestVirtualNetwork1" address_space = ["10.0.0.0/16"] - location = "West US" + location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" } @@ -41,21 +42,17 @@ resource "azurerm_subnet" "test" { The following arguments are supported: -* `name` - (Required) The name of the subnet. Changing this forces a - new resource to be created. +* `name` - (Required) The name of the subnet. Changing this forces a new resource to be created. -* `resource_group_name` - (Required) The name of the resource group in which to - create the subnet. +* `resource_group_name` - (Required) The name of the resource group in which to create the subnet. Changing this forces a new resource to be created. -* `virtual_network_name` - (Required) The name of the virtual network to which to attach the subnet. +* `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. -* `network_security_group_id` - (Optional) The ID of the Network Security Group to associate with - the subnet. +* `network_security_group_id` - (Optional) The ID of the Network Security Group to associate with the subnet. -* `route_table_id` - (Optional) The ID of the Route Table to associate with - the subnet. +* `route_table_id` - (Optional) The ID of the Route Table to associate with the subnet. ## Attributes Reference From a9a001725edd1750899674ce64ba38f73f836df0 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Tue, 10 Oct 2017 18:53:37 +0100 Subject: [PATCH 2/2] Adding a data source for Subnets --- azurerm/data_source_subnet.go | 102 ++++++++++++++ azurerm/data_source_subnet_test.go | 198 ++++++++++++++++++++++++++++ azurerm/provider.go | 1 + azurerm/resource_arm_subnet.go | 22 ++-- website/docs/d/subnet.html.markdown | 39 ++++++ 5 files changed, 354 insertions(+), 8 deletions(-) create mode 100644 azurerm/data_source_subnet.go create mode 100644 azurerm/data_source_subnet_test.go create mode 100644 website/docs/d/subnet.html.markdown diff --git a/azurerm/data_source_subnet.go b/azurerm/data_source_subnet.go new file mode 100644 index 000000000000..2a8d7c3e740b --- /dev/null +++ b/azurerm/data_source_subnet.go @@ -0,0 +1,102 @@ +package azurerm + +import ( + "fmt" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func dataSourceArmSubnet() *schema.Resource { + return &schema.Resource{ + Read: dataSourceArmSubnetRead, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + + "virtual_network_name": { + Type: schema.TypeString, + Required: true, + }, + + "resource_group_name": { + Type: schema.TypeString, + Required: true, + }, + + "address_prefix": { + Type: schema.TypeString, + Computed: true, + }, + + "network_security_group_id": { + Type: schema.TypeString, + Computed: true, + }, + + "route_table_id": { + Type: schema.TypeString, + Computed: true, + }, + + "ip_configurations": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + }, + } +} + +func dataSourceArmSubnetRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).subnetClient + + name := d.Get("name").(string) + virtualNetworkName := d.Get("virtual_network_name").(string) + resourceGroupName := d.Get("resource_group_name").(string) + + resp, err := client.Get(resourceGroupName, virtualNetworkName, name, "") + if err != nil { + return fmt.Errorf("Error reading Subnet: %+v", err) + } + + d.SetId(*resp.ID) + + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error making Read request on Azure Subnet %q: %+v", name, err) + } + + d.Set("name", name) + d.Set("resource_group_name", resourceGroupName) + d.Set("virtual_network_name", virtualNetworkName) + + if props := resp.SubnetPropertiesFormat; props != nil { + d.Set("address_prefix", props.AddressPrefix) + + if props.NetworkSecurityGroup != nil { + d.Set("network_security_group_id", props.NetworkSecurityGroup.ID) + } else { + d.Set("network_security_group_id", "") + } + + if props.RouteTable != nil { + d.Set("route_table_id", props.RouteTable.ID) + } else { + d.Set("route_table_id", "") + } + + ips := flattenSubnetIPConfigurations(props.IPConfigurations) + if err := d.Set("ip_configurations", ips); err != nil { + return err + } + } + + return nil +} diff --git a/azurerm/data_source_subnet_test.go b/azurerm/data_source_subnet_test.go new file mode 100644 index 000000000000..e1d0810dd033 --- /dev/null +++ b/azurerm/data_source_subnet_test.go @@ -0,0 +1,198 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccDataSourceAzureRMSubnet_basic(t *testing.T) { + resourceName := "data.azurerm_subnet.test" + ri := acctest.RandInt() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAzureRMSubnet_basic(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "name"), + resource.TestCheckResourceAttrSet(resourceName, "resource_group_name"), + resource.TestCheckResourceAttrSet(resourceName, "virtual_network_name"), + resource.TestCheckResourceAttrSet(resourceName, "address_prefix"), + resource.TestCheckResourceAttr(resourceName, "network_security_group_id", ""), + resource.TestCheckResourceAttr(resourceName, "route_table_id", ""), + ), + }, + }, + }) +} + +func TestAccDataSourceAzureRMSubnet_networkSecurityGroup(t *testing.T) { + dataSourceName := "data.azurerm_subnet.test" + ri := acctest.RandInt() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAzureRMSubnet_networkSecurityGroup(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(dataSourceName, "name"), + resource.TestCheckResourceAttrSet(dataSourceName, "resource_group_name"), + resource.TestCheckResourceAttrSet(dataSourceName, "virtual_network_name"), + resource.TestCheckResourceAttrSet(dataSourceName, "address_prefix"), + resource.TestCheckResourceAttrSet(dataSourceName, "network_security_group_id"), + resource.TestCheckResourceAttr(dataSourceName, "route_table_id", ""), + ), + }, + }, + }) +} + +func TestAccDataSourceAzureRMSubnet_routeTable(t *testing.T) { + dataSourceName := "data.azurerm_subnet.test" + ri := acctest.RandInt() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAzureRMSubnet_routeTable(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(dataSourceName, "name"), + resource.TestCheckResourceAttrSet(dataSourceName, "resource_group_name"), + resource.TestCheckResourceAttrSet(dataSourceName, "virtual_network_name"), + resource.TestCheckResourceAttrSet(dataSourceName, "address_prefix"), + resource.TestCheckResourceAttr(dataSourceName, "network_security_group_id", ""), + resource.TestCheckResourceAttrSet(dataSourceName, "route_table_id"), + ), + }, + }, + }) +} + +func testAccDataSourceAzureRMSubnet_basic(rInt int, location string) 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_prefix = "10.0.0.0/24" +} + +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}" +} +`, rInt, location, rInt, rInt) +} + +func testAccDataSourceAzureRMSubnet_networkSecurityGroup(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctest%d-rg" + location = "%s" +} + +resource "azurerm_network_security_group" "test" { + name = "acctestnsg%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + security_rule { + name = "test123" + priority = 100 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = "*" + source_address_prefix = "*" + destination_address_prefix = "*" + } +} + +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_prefix = "10.0.0.0/24" + network_security_group_id = "${azurerm_network_security_group.test.id}" +} + +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}" +} +`, rInt, location, rInt, rInt, rInt) +} + +func testAccDataSourceAzureRMSubnet_routeTable(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_route_table" "test" { + name = "acctest-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + route { + name = "acctest-%d" + address_prefix = "10.100.0.0/14" + next_hop_type = "VirtualAppliance" + next_hop_in_ip_address = "10.10.1.1" + } +} + +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_prefix = "10.0.2.0/24" + route_table_id = "${azurerm_route_table.test.id}" +} + +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}" +} +`, rInt, location, rInt, rInt, rInt, rInt) +} diff --git a/azurerm/provider.go b/azurerm/provider.go index 6299163bec90..ec5776437fcf 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -75,6 +75,7 @@ func Provider() terraform.ResourceProvider { "azurerm_platform_image": dataSourceArmPlatformImage(), "azurerm_public_ip": dataSourceArmPublicIP(), "azurerm_resource_group": dataSourceArmResourceGroup(), + "azurerm_subnet": dataSourceArmSubnet(), "azurerm_subscription": dataSourceArmSubscription(), }, diff --git a/azurerm/resource_arm_subnet.go b/azurerm/resource_arm_subnet.go index 93559a758a49..6d73fffe4aba 100644 --- a/azurerm/resource_arm_subnet.go +++ b/azurerm/resource_arm_subnet.go @@ -151,7 +151,7 @@ func resourceArmSubnetRead(d *schema.ResourceData, meta interface{}) error { d.SetId("") return nil } - return fmt.Errorf("Error making Read request on Azure Subnet %s: %+v", name, err) + return fmt.Errorf("Error making Read request on Azure Subnet %q: %+v", name, err) } d.Set("name", name) @@ -169,13 +169,7 @@ func resourceArmSubnetRead(d *schema.ResourceData, meta interface{}) error { d.Set("route_table_id", props.RouteTable.ID) } - ips := make([]string, 0) - if props.IPConfigurations != nil { - for _, ip := range *props.IPConfigurations { - ips = append(ips, *ip.ID) - } - } - + ips := flattenSubnetIPConfigurations(props.IPConfigurations) if err := d.Set("ip_configurations", ips); err != nil { return err } @@ -228,3 +222,15 @@ func resourceArmSubnetDelete(d *schema.ResourceData, meta interface{}) error { return err } + +func flattenSubnetIPConfigurations(ipConfigurations *[]network.IPConfiguration) []string { + ips := make([]string, 0) + + if ipConfigurations != nil { + for _, ip := range *ipConfigurations { + ips = append(ips, *ip.ID) + } + } + + return ips +} diff --git a/website/docs/d/subnet.html.markdown b/website/docs/d/subnet.html.markdown new file mode 100644 index 000000000000..7fd8faba5341 --- /dev/null +++ b/website/docs/d/subnet.html.markdown @@ -0,0 +1,39 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_subnet" +sidebar_current: "docs-azurerm-datasource-subnet" +description: |- + Get information about the specified Subnet located within a Virtual Network. +--- + +# azurerm_subnet + +Use this data source to access the properties of an Azure Subnet located within a Virtual Network. + +## Example Usage + +```hcl +data "azurerm_subnet" "test" { + name = "backend" + virtual_network_name = "production" + resource_group_name = "networking" +} + +output "subnet_id" { + value = "${data.azurerm_subnet.test.id}" +} +``` + +## Argument Reference + +* `name` - (Required) Specifies the name of the Subnet. +* `virtual_network_name` - (Required) Specifies the name of the Virtual Network this Subnet is located within. +* `resource_group_name` - (Required) Specifies the name of the resource group the Virtual Network is located in. + +## Attributes Reference + +* `id` - The ID of the Subnet. +* `address_prefix` - The address prefix used for 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. +* `ip_configurations` - The collection of IP Configurations with IPs within this subnet.