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

provider/azurerm: add subnet resource #4595

Merged
merged 1 commit into from
Jan 9, 2016
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions builtin/providers/azurerm/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ func Provider() terraform.ResourceProvider {
"azurerm_network_security_group": resourceArmNetworkSecurityGroup(),
"azurerm_network_security_rule": resourceArmNetworkSecurityRule(),
"azurerm_public_ip": resourceArmPublicIp(),
"azurerm_subnet": resourceArmSubnet(),
},
ConfigureFunc: providerConfigure,
}
Expand Down
188 changes: 188 additions & 0 deletions builtin/providers/azurerm/resource_arm_subnet.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
package azurerm

import (
"fmt"
"log"
"net/http"
"time"

"github.com/Azure/azure-sdk-for-go/arm/network"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
)

func resourceArmSubnet() *schema.Resource {
return &schema.Resource{
Create: resourceArmSubnetCreate,
Read: resourceArmSubnetRead,
Update: resourceArmSubnetCreate,
Delete: resourceArmSubnetDelete,

Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"resource_group_name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"virtual_network_name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"address_prefix": &schema.Schema{
Type: schema.TypeString,
Required: true,
},

"network_security_group_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
},

"route_table_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
},

"ip_configurations": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
},
}
}

func resourceArmSubnetCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient)
subnetClient := client.subnetClient

log.Printf("[INFO] preparing arguments for Azure ARM Subnet creation.")

name := d.Get("name").(string)
vnetName := d.Get("virtual_network_name").(string)
resGroup := d.Get("resource_group_name").(string)
addressPrefix := d.Get("address_prefix").(string)

armMutexKV.Lock(vnetName)
defer armMutexKV.Unlock(vnetName)

properties := network.SubnetPropertiesFormat{
AddressPrefix: &addressPrefix,
}

if v, ok := d.GetOk("network_security_group_id"); ok {
nsgId := v.(string)
properties.NetworkSecurityGroup = &network.SecurityGroup{
ID: &nsgId,
}
}

if v, ok := d.GetOk("route_table_id"); ok {
rtId := v.(string)
properties.RouteTable = &network.RouteTable{
ID: &rtId,
}
}

subnet := network.Subnet{
Name: &name,
Properties: &properties,
}

resp, err := subnetClient.CreateOrUpdate(resGroup, vnetName, name, subnet)
if err != nil {
return err
}

d.SetId(*resp.ID)

log.Printf("[DEBUG] Waiting for Subnet (%s) to become available", name)
stateConf := &resource.StateChangeConf{
Pending: []string{"Accepted", "Updating"},
Target: "Succeeded",
Refresh: subnetRuleStateRefreshFunc(client, resGroup, vnetName, name),
Timeout: 10 * time.Minute,
}
if _, err := stateConf.WaitForState(); err != nil {
return fmt.Errorf("Error waiting for Subnet (%s) to become available: %s", name, err)
}

return resourceArmSubnetRead(d, meta)
}

func resourceArmSubnetRead(d *schema.ResourceData, meta interface{}) error {
subnetClient := meta.(*ArmClient).subnetClient

id, err := parseAzureResourceID(d.Id())
if err != nil {
return err
}
resGroup := id.ResourceGroup
vnetName := id.Path["virtualNetworks"]
name := id.Path["subnets"]

resp, err := subnetClient.Get(resGroup, vnetName, name, "")
if resp.StatusCode == http.StatusNotFound {
d.SetId("")
return nil
}
if err != nil {
return fmt.Errorf("Error making Read request on Azure Subnet %s: %s", name, err)
}

if resp.Properties.IPConfigurations != nil && len(*resp.Properties.IPConfigurations) > 0 {
ips := make([]string, 0, len(*resp.Properties.IPConfigurations))
for _, ip := range *resp.Properties.IPConfigurations {
ips = append(ips, *ip.ID)
}

if err := d.Set("ip_configurations", ips); err != nil {
return err
}
}

return nil
}

func resourceArmSubnetDelete(d *schema.ResourceData, meta interface{}) error {
subnetClient := meta.(*ArmClient).subnetClient

id, err := parseAzureResourceID(d.Id())
if err != nil {
return err
}
resGroup := id.ResourceGroup
name := id.Path["subnets"]
vnetName := id.Path["virtualNetworks"]

armMutexKV.Lock(vnetName)
defer armMutexKV.Unlock(vnetName)

_, err = subnetClient.Delete(resGroup, vnetName, name)

return err
}

func subnetRuleStateRefreshFunc(client *ArmClient, resourceGroupName string, virtualNetworkName string, subnetName string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
res, err := client.subnetClient.Get(resourceGroupName, virtualNetworkName, subnetName, "")
if err != nil {
return nil, "", fmt.Errorf("Error issuing read request in subnetRuleStateRefreshFunc to Azure ARM for subnet '%s' (RG: '%s') (VNN: '%s'): %s", subnetName, resourceGroupName, virtualNetworkName, err)
}

return res, *res.Properties.ProvisioningState, nil
}
}
104 changes: 104 additions & 0 deletions builtin/providers/azurerm/resource_arm_subnet_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package azurerm

import (
"fmt"
"net/http"
"testing"

"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)

func TestAccAzureRMSubnet_basic(t *testing.T) {

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMSubnetDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAzureRMSubnet_basic,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMSubnetExists("azurerm_subnet.test"),
),
},
},
})
}

func testCheckAzureRMSubnetExists(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
// Ensure we have enough information in state to look up in API
rs, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Not found: %s", name)
}

name := rs.Primary.Attributes["name"]
vnetName := rs.Primary.Attributes["virtual_network_name"]
resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"]
if !hasResourceGroup {
return fmt.Errorf("Bad: no resource group found in state for subnet: %s", name)
}

conn := testAccProvider.Meta().(*ArmClient).subnetClient

resp, err := conn.Get(resourceGroup, vnetName, name, "")
if err != nil {
return fmt.Errorf("Bad: Get on subnetClient: %s", err)
}

if resp.StatusCode == http.StatusNotFound {
return fmt.Errorf("Bad: Subnet %q (resource group: %q) does not exist", name, resourceGroup)
}

return nil
}
}

func testCheckAzureRMSubnetDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*ArmClient).subnetClient

for _, rs := range s.RootModule().Resources {
if rs.Type != "azurerm_subnet" {
continue
}

name := rs.Primary.Attributes["name"]
vnetName := rs.Primary.Attributes["virtual_network_name"]
resourceGroup := rs.Primary.Attributes["resource_group_name"]

resp, err := conn.Get(resourceGroup, vnetName, name, "")

if err != nil {
return nil
}

if resp.StatusCode != http.StatusNotFound {
return fmt.Errorf("Subnet still exists:\n%#v", resp.Properties)
}
}

return nil
}

var testAccAzureRMSubnet_basic = `
resource "azurerm_resource_group" "test" {
name = "acceptanceTestResourceGroup1"
location = "West US"
}

resource "azurerm_virtual_network" "test" {
name = "acceptanceTestVirtualNetwork1"
address_space = ["10.0.0.0/16"]
location = "West US"
resource_group_name = "${azurerm_resource_group.test.name}"
}

resource "azurerm_subnet" "test" {
name = "testsubnet"
resource_group_name = "${azurerm_resource_group.test.name}"
virtual_network_name = "${azurerm_virtual_network.test.name}"
address_prefix = "10.0.2.0/24"
}
`
3 changes: 2 additions & 1 deletion builtin/providers/azurerm/resource_arm_virtual_network.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ func resourceArmVirtualNetwork() *schema.Resource {

"subnet": &schema.Schema{
Type: schema.TypeSet,
Required: true,
Optional: true,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Expand Down
61 changes: 61 additions & 0 deletions website/source/docs/providers/azurerm/r/subnet.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
layout: "azurerm"
page_title: "Azure Resource Manager: azure_subnet"
sidebar_current: "docs-azurerm-resource-subnet"
description: |-
Creates a new subnet. Subnets represent network segments within the IP space defined by the virtual network.
---

# azurerm\_subnet

Creates a new subnet. Subnets represent network segments within the IP space defined by the virtual network.

## Example Usage

```
resource "azurerm_resource_group" "test" {
name = "acceptanceTestResourceGroup1"
location = "West US"
}

resource "azurerm_virtual_network" "test" {
name = "acceptanceTestVirtualNetwork1"
address_space = ["10.0.0.0/16"]
location = "West US"
resource_group_name = "${azurerm_resource_group.test.name}"
}

resource "azurerm_subnet" "test" {
name = "testsubnet"
resource_group_name = "${azurerm_resource_group.test.name}"
virtual_network_name = "${azurerm_virtual_network.test.name}"
address_prefix = "10.0.1.0/24"
}
```

## Argument Reference

The following arguments are supported:

* `name` - (Required) The name of the virtual network. 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.

* `virtual_network_name` - (Required) The name of the virtual network to which to attach the subnet.

* `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.

* `route_table_id` - (Optional) The ID of the Route Table to associate with
the subnet.

## Attributes Reference

The following attributes are exported:

* `id` - The subnet ID.
* `ip_configurations` - The collection of IP Configurations with IPs within this subnet.
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ The following arguments are supported:
* `dns_servers` - (Optional) List of names of DNS servers previously registered
on Azure.

* `subnet` - (Required) Can be specified multiple times to define multiple
* `subnet` - (Optional) Can be specified multiple times to define multiple
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary here to use either all inline subnets or all separate resources? If so we should document this here and in the subnet doc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jen20 this is exactly the same behaviour as for Network Security Group / Network Security Rule

From Slack (with @phinze):


stack72 [2:01 PM] 
oh they can’t be mixed?

phinze [2:01 PM] 
right because they'll always fight

stack72 [2:01 PM] 
ah so you would set up a basic network_acl and ​*then add*​ the rules?

​[2:01] 
now i see

​[2:01] 
that makes more sense

phinze [2:01 PM] 
yep, all nested or all _rule is the key

subnets. Each `subnet` block supports fields documented below.

The `subnet` block supports:
Expand Down
4 changes: 4 additions & 0 deletions website/source/layouts/azurerm.erb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@
<a href="/docs/providers/azurerm/r/public_ip.html">azurerm_public_ip</a>
</li>

<li<%= sidebar_current("docs-azurerm-resource-subnet") %>>
<a href="/docs/providers/azurerm/r/subnet.html">azurerm_subnet</a>
</li>

</ul>
</li>
</ul>
Expand Down