github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/builtin/providers/azurerm/resource_arm_virtual_network.go (about)

     1  package azurerm
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"net/http"
     8  
     9  	"github.com/Azure/azure-sdk-for-go/arm/network"
    10  	"github.com/hashicorp/terraform/helper/hashcode"
    11  	"github.com/hashicorp/terraform/helper/schema"
    12  )
    13  
    14  func resourceArmVirtualNetwork() *schema.Resource {
    15  	return &schema.Resource{
    16  		Create: resourceArmVirtualNetworkCreate,
    17  		Read:   resourceArmVirtualNetworkRead,
    18  		Update: resourceArmVirtualNetworkCreate,
    19  		Delete: resourceArmVirtualNetworkDelete,
    20  		Importer: &schema.ResourceImporter{
    21  			State: schema.ImportStatePassthrough,
    22  		},
    23  
    24  		Schema: map[string]*schema.Schema{
    25  			"name": {
    26  				Type:     schema.TypeString,
    27  				Required: true,
    28  				ForceNew: true,
    29  			},
    30  
    31  			"address_space": {
    32  				Type:     schema.TypeList,
    33  				Required: true,
    34  				Elem: &schema.Schema{
    35  					Type: schema.TypeString,
    36  				},
    37  			},
    38  
    39  			"dns_servers": {
    40  				Type:     schema.TypeList,
    41  				Optional: true,
    42  				Elem: &schema.Schema{
    43  					Type: schema.TypeString,
    44  				},
    45  			},
    46  
    47  			"subnet": {
    48  				Type:     schema.TypeSet,
    49  				Optional: true,
    50  				Computed: true,
    51  				Elem: &schema.Resource{
    52  					Schema: map[string]*schema.Schema{
    53  						"name": {
    54  							Type:     schema.TypeString,
    55  							Required: true,
    56  						},
    57  						"address_prefix": {
    58  							Type:     schema.TypeString,
    59  							Required: true,
    60  						},
    61  						"security_group": {
    62  							Type:     schema.TypeString,
    63  							Optional: true,
    64  						},
    65  					},
    66  				},
    67  				Set: resourceAzureSubnetHash,
    68  			},
    69  
    70  			"location": locationSchema(),
    71  
    72  			"resource_group_name": {
    73  				Type:     schema.TypeString,
    74  				Required: true,
    75  				ForceNew: true,
    76  			},
    77  
    78  			"tags": tagsSchema(),
    79  		},
    80  	}
    81  }
    82  
    83  func resourceArmVirtualNetworkCreate(d *schema.ResourceData, meta interface{}) error {
    84  	client := meta.(*ArmClient)
    85  	vnetClient := client.vnetClient
    86  
    87  	log.Printf("[INFO] preparing arguments for Azure ARM virtual network creation.")
    88  
    89  	name := d.Get("name").(string)
    90  	location := d.Get("location").(string)
    91  	resGroup := d.Get("resource_group_name").(string)
    92  	tags := d.Get("tags").(map[string]interface{})
    93  	vnetProperties, vnetPropsErr := getVirtualNetworkProperties(d, meta)
    94  	if vnetPropsErr != nil {
    95  		return vnetPropsErr
    96  	}
    97  
    98  	vnet := network.VirtualNetwork{
    99  		Name:                           &name,
   100  		Location:                       &location,
   101  		VirtualNetworkPropertiesFormat: vnetProperties,
   102  		Tags: expandTags(tags),
   103  	}
   104  
   105  	networkSecurityGroupNames := make([]string, 0)
   106  	for _, subnet := range *vnet.VirtualNetworkPropertiesFormat.Subnets {
   107  		if subnet.NetworkSecurityGroup != nil {
   108  			nsgName, err := parseNetworkSecurityGroupName(*subnet.NetworkSecurityGroup.ID)
   109  			if err != nil {
   110  				return err
   111  			}
   112  
   113  			networkSecurityGroupNames = append(networkSecurityGroupNames, nsgName)
   114  		}
   115  	}
   116  
   117  	azureRMLockMultiple(&networkSecurityGroupNames)
   118  	defer azureRMUnlockMultiple(&networkSecurityGroupNames)
   119  
   120  	_, error := vnetClient.CreateOrUpdate(resGroup, name, vnet, make(chan struct{}))
   121  	err := <-error
   122  	if err != nil {
   123  		return err
   124  	}
   125  
   126  	read, err := vnetClient.Get(resGroup, name, "")
   127  	if err != nil {
   128  		return err
   129  	}
   130  	if read.ID == nil {
   131  		return fmt.Errorf("Cannot read Virtual Network %s (resource group %s) ID", name, resGroup)
   132  	}
   133  
   134  	d.SetId(*read.ID)
   135  
   136  	return resourceArmVirtualNetworkRead(d, meta)
   137  }
   138  
   139  func resourceArmVirtualNetworkRead(d *schema.ResourceData, meta interface{}) error {
   140  	vnetClient := meta.(*ArmClient).vnetClient
   141  
   142  	id, err := parseAzureResourceID(d.Id())
   143  	if err != nil {
   144  		return err
   145  	}
   146  	resGroup := id.ResourceGroup
   147  	name := id.Path["virtualNetworks"]
   148  
   149  	resp, err := vnetClient.Get(resGroup, name, "")
   150  	if err != nil {
   151  		if resp.StatusCode == http.StatusNotFound {
   152  			d.SetId("")
   153  			return nil
   154  		}
   155  		return fmt.Errorf("Error making Read request on Azure virtual network %s: %s", name, err)
   156  	}
   157  
   158  	vnet := *resp.VirtualNetworkPropertiesFormat
   159  
   160  	// update appropriate values
   161  	d.Set("resource_group_name", resGroup)
   162  	d.Set("name", resp.Name)
   163  	d.Set("location", resp.Location)
   164  	d.Set("address_space", vnet.AddressSpace.AddressPrefixes)
   165  
   166  	subnets := &schema.Set{
   167  		F: resourceAzureSubnetHash,
   168  	}
   169  
   170  	for _, subnet := range *vnet.Subnets {
   171  		s := map[string]interface{}{}
   172  
   173  		s["name"] = *subnet.Name
   174  		s["address_prefix"] = *subnet.SubnetPropertiesFormat.AddressPrefix
   175  		if subnet.SubnetPropertiesFormat.NetworkSecurityGroup != nil {
   176  			s["security_group"] = *subnet.SubnetPropertiesFormat.NetworkSecurityGroup.ID
   177  		}
   178  
   179  		subnets.Add(s)
   180  	}
   181  	d.Set("subnet", subnets)
   182  
   183  	if vnet.DhcpOptions != nil && vnet.DhcpOptions.DNSServers != nil {
   184  		dnses := []string{}
   185  		for _, dns := range *vnet.DhcpOptions.DNSServers {
   186  			dnses = append(dnses, dns)
   187  		}
   188  		d.Set("dns_servers", dnses)
   189  	}
   190  
   191  	flattenAndSetTags(d, resp.Tags)
   192  
   193  	return nil
   194  }
   195  
   196  func resourceArmVirtualNetworkDelete(d *schema.ResourceData, meta interface{}) error {
   197  	vnetClient := meta.(*ArmClient).vnetClient
   198  
   199  	id, err := parseAzureResourceID(d.Id())
   200  	if err != nil {
   201  		return err
   202  	}
   203  	resGroup := id.ResourceGroup
   204  	name := id.Path["virtualNetworks"]
   205  
   206  	nsgNames, err := expandAzureRmVirtualNetworkVirtualNetworkSecurityGroupNames(d)
   207  	if err != nil {
   208  		return fmt.Errorf("[ERROR] Error parsing Network Security Group ID's: %+v", err)
   209  	}
   210  
   211  	azureRMLockMultiple(&nsgNames)
   212  	defer azureRMUnlockMultiple(&nsgNames)
   213  
   214  	_, error := vnetClient.Delete(resGroup, name, make(chan struct{}))
   215  	err = <-error
   216  
   217  	return err
   218  }
   219  
   220  func getVirtualNetworkProperties(d *schema.ResourceData, meta interface{}) (*network.VirtualNetworkPropertiesFormat, error) {
   221  	// first; get address space prefixes:
   222  	prefixes := []string{}
   223  	for _, prefix := range d.Get("address_space").([]interface{}) {
   224  		prefixes = append(prefixes, prefix.(string))
   225  	}
   226  
   227  	// then; the dns servers:
   228  	dnses := []string{}
   229  	for _, dns := range d.Get("dns_servers").([]interface{}) {
   230  		dnses = append(dnses, dns.(string))
   231  	}
   232  
   233  	// then; the subnets:
   234  	subnets := []network.Subnet{}
   235  	if subs := d.Get("subnet").(*schema.Set); subs.Len() > 0 {
   236  		for _, subnet := range subs.List() {
   237  			subnet := subnet.(map[string]interface{})
   238  
   239  			name := subnet["name"].(string)
   240  			log.Printf("[INFO] setting subnets inside vNet, processing %q", name)
   241  			//since subnets can also be created outside of vNet definition (as root objects)
   242  			// do a GET on subnet properties from the server before setting them
   243  			resGroup := d.Get("resource_group_name").(string)
   244  			vnetName := d.Get("name").(string)
   245  			subnetObj, err := getExistingSubnet(resGroup, vnetName, name, meta)
   246  			if err != nil {
   247  				return nil, err
   248  			}
   249  			log.Printf("[INFO] Completed GET of Subnet props ")
   250  
   251  			prefix := subnet["address_prefix"].(string)
   252  			secGroup := subnet["security_group"].(string)
   253  
   254  			//set the props from config and leave the rest intact
   255  			subnetObj.Name = &name
   256  			if subnetObj.SubnetPropertiesFormat == nil {
   257  				subnetObj.SubnetPropertiesFormat = &network.SubnetPropertiesFormat{}
   258  			}
   259  
   260  			subnetObj.SubnetPropertiesFormat.AddressPrefix = &prefix
   261  
   262  			if secGroup != "" {
   263  				subnetObj.SubnetPropertiesFormat.NetworkSecurityGroup = &network.SecurityGroup{
   264  					ID: &secGroup,
   265  				}
   266  			} else {
   267  				subnetObj.SubnetPropertiesFormat.NetworkSecurityGroup = nil
   268  			}
   269  
   270  			subnets = append(subnets, *subnetObj)
   271  		}
   272  	}
   273  
   274  	properties := &network.VirtualNetworkPropertiesFormat{
   275  		AddressSpace: &network.AddressSpace{
   276  			AddressPrefixes: &prefixes,
   277  		},
   278  		DhcpOptions: &network.DhcpOptions{
   279  			DNSServers: &dnses,
   280  		},
   281  		Subnets: &subnets,
   282  	}
   283  	// finally; return the struct:
   284  	return properties, nil
   285  }
   286  
   287  func resourceAzureSubnetHash(v interface{}) int {
   288  	var buf bytes.Buffer
   289  	m := v.(map[string]interface{})
   290  	buf.WriteString(fmt.Sprintf("%s", m["name"].(string)))
   291  	buf.WriteString(fmt.Sprintf("%s", m["address_prefix"].(string)))
   292  	if v, ok := m["security_group"]; ok {
   293  		buf.WriteString(v.(string))
   294  	}
   295  	return hashcode.String(buf.String())
   296  }
   297  
   298  func getExistingSubnet(resGroup string, vnetName string, subnetName string, meta interface{}) (*network.Subnet, error) {
   299  	//attempt to retrieve existing subnet from the server
   300  	existingSubnet := network.Subnet{}
   301  	subnetClient := meta.(*ArmClient).subnetClient
   302  	resp, err := subnetClient.Get(resGroup, vnetName, subnetName, "")
   303  
   304  	if err != nil {
   305  		if resp.StatusCode == http.StatusNotFound {
   306  			return &existingSubnet, nil
   307  		}
   308  		//raise an error if there was an issue other than 404 in getting subnet properties
   309  		return nil, err
   310  	}
   311  
   312  	existingSubnet.SubnetPropertiesFormat = &network.SubnetPropertiesFormat{}
   313  	existingSubnet.SubnetPropertiesFormat.AddressPrefix = resp.SubnetPropertiesFormat.AddressPrefix
   314  
   315  	if resp.SubnetPropertiesFormat.NetworkSecurityGroup != nil {
   316  		existingSubnet.SubnetPropertiesFormat.NetworkSecurityGroup = resp.SubnetPropertiesFormat.NetworkSecurityGroup
   317  	}
   318  
   319  	if resp.SubnetPropertiesFormat.RouteTable != nil {
   320  		existingSubnet.SubnetPropertiesFormat.RouteTable = resp.SubnetPropertiesFormat.RouteTable
   321  	}
   322  
   323  	if resp.SubnetPropertiesFormat.IPConfigurations != nil {
   324  		ips := make([]string, 0, len(*resp.SubnetPropertiesFormat.IPConfigurations))
   325  		for _, ip := range *resp.SubnetPropertiesFormat.IPConfigurations {
   326  			ips = append(ips, *ip.ID)
   327  		}
   328  
   329  		existingSubnet.SubnetPropertiesFormat.IPConfigurations = resp.SubnetPropertiesFormat.IPConfigurations
   330  	}
   331  
   332  	return &existingSubnet, nil
   333  }
   334  
   335  func expandAzureRmVirtualNetworkVirtualNetworkSecurityGroupNames(d *schema.ResourceData) ([]string, error) {
   336  	nsgNames := make([]string, 0)
   337  
   338  	if v, ok := d.GetOk("subnet"); ok {
   339  		subnets := v.(*schema.Set).List()
   340  		for _, subnet := range subnets {
   341  			subnet, ok := subnet.(map[string]interface{})
   342  			if !ok {
   343  				return nil, fmt.Errorf("[ERROR] Subnet should be a Hash - was '%+v'", subnet)
   344  			}
   345  
   346  			networkSecurityGroupId := subnet["security_group"].(string)
   347  			if networkSecurityGroupId != "" {
   348  				nsgName, err := parseNetworkSecurityGroupName(networkSecurityGroupId)
   349  				if err != nil {
   350  					return nil, err
   351  				}
   352  
   353  				nsgNames = append(nsgNames, nsgName)
   354  			}
   355  		}
   356  	}
   357  
   358  	return nsgNames, nil
   359  }