github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/builtin/providers/azure/resource_azure_virtual_network.go (about)

     1  package azure
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  
     7  	"github.com/Azure/azure-sdk-for-go/management"
     8  	"github.com/Azure/azure-sdk-for-go/management/virtualnetwork"
     9  	"github.com/hashicorp/terraform/helper/hashcode"
    10  	"github.com/hashicorp/terraform/helper/schema"
    11  )
    12  
    13  const (
    14  	virtualNetworkRetrievalError = "Error retrieving Virtual Network Configuration: %s"
    15  )
    16  
    17  func resourceAzureVirtualNetwork() *schema.Resource {
    18  	return &schema.Resource{
    19  		Create: resourceAzureVirtualNetworkCreate,
    20  		Read:   resourceAzureVirtualNetworkRead,
    21  		Update: resourceAzureVirtualNetworkUpdate,
    22  		Delete: resourceAzureVirtualNetworkDelete,
    23  
    24  		Schema: map[string]*schema.Schema{
    25  			"name": &schema.Schema{
    26  				Type:     schema.TypeString,
    27  				Required: true,
    28  				ForceNew: true,
    29  			},
    30  
    31  			"address_space": &schema.Schema{
    32  				Type:     schema.TypeList,
    33  				Required: true,
    34  				Elem:     &schema.Schema{Type: schema.TypeString},
    35  			},
    36  
    37  			"dns_servers_names": &schema.Schema{
    38  				Type:     schema.TypeList,
    39  				Optional: true,
    40  				Elem: &schema.Schema{
    41  					Type: schema.TypeString,
    42  				},
    43  			},
    44  
    45  			"subnet": &schema.Schema{
    46  				Type:     schema.TypeSet,
    47  				Required: true,
    48  				Elem: &schema.Resource{
    49  					Schema: map[string]*schema.Schema{
    50  						"name": &schema.Schema{
    51  							Type:     schema.TypeString,
    52  							Required: true,
    53  						},
    54  						"address_prefix": &schema.Schema{
    55  							Type:     schema.TypeString,
    56  							Required: true,
    57  						},
    58  						"security_group": &schema.Schema{
    59  							Type:     schema.TypeString,
    60  							Optional: true,
    61  						},
    62  					},
    63  				},
    64  				Set: resourceAzureSubnetHash,
    65  			},
    66  
    67  			"location": &schema.Schema{
    68  				Type:     schema.TypeString,
    69  				Required: true,
    70  				ForceNew: true,
    71  			},
    72  		},
    73  	}
    74  }
    75  
    76  func resourceAzureVirtualNetworkCreate(d *schema.ResourceData, meta interface{}) error {
    77  	ac := meta.(*Client)
    78  	mc := ac.mgmtClient
    79  	vnetClient := ac.vnetClient
    80  
    81  	name := d.Get("name").(string)
    82  
    83  	// Lock the client just before we get the virtual network configuration and immediately
    84  	// set an defer to unlock the client again whenever this function exits
    85  	ac.vnetMutex.Lock()
    86  	defer ac.vnetMutex.Unlock()
    87  
    88  	nc, err := vnetClient.GetVirtualNetworkConfiguration()
    89  	if err != nil {
    90  		if management.IsResourceNotFoundError(err) {
    91  			// if no network config exists yet; create a new one now:
    92  			nc = virtualnetwork.NetworkConfiguration{}
    93  		} else {
    94  			return fmt.Errorf(virtualNetworkRetrievalError, err)
    95  		}
    96  	}
    97  
    98  	for _, n := range nc.Configuration.VirtualNetworkSites {
    99  		if n.Name == name {
   100  			return fmt.Errorf("Virtual Network %s already exists!", name)
   101  		}
   102  	}
   103  
   104  	network := createVirtualNetwork(d)
   105  	nc.Configuration.VirtualNetworkSites = append(nc.Configuration.VirtualNetworkSites, network)
   106  
   107  	req, err := vnetClient.SetVirtualNetworkConfiguration(nc)
   108  	if err != nil {
   109  		return fmt.Errorf("Error creating Virtual Network %s: %s", name, err)
   110  	}
   111  
   112  	// Wait until the virtual network is created
   113  	if err := mc.WaitForOperation(req, nil); err != nil {
   114  		return fmt.Errorf("Error waiting for Virtual Network %s to be created: %s", name, err)
   115  	}
   116  
   117  	d.SetId(name)
   118  
   119  	if err := associateSecurityGroups(d, meta); err != nil {
   120  		return err
   121  	}
   122  
   123  	return resourceAzureVirtualNetworkRead(d, meta)
   124  }
   125  
   126  func resourceAzureVirtualNetworkRead(d *schema.ResourceData, meta interface{}) error {
   127  	ac := meta.(*Client)
   128  	vnetClient := ac.vnetClient
   129  	secGroupClient := ac.secGroupClient
   130  
   131  	nc, err := vnetClient.GetVirtualNetworkConfiguration()
   132  	if err != nil {
   133  		return fmt.Errorf(virtualNetworkRetrievalError, err)
   134  	}
   135  
   136  	for _, n := range nc.Configuration.VirtualNetworkSites {
   137  		if n.Name == d.Id() {
   138  			d.Set("address_space", n.AddressSpace.AddressPrefix)
   139  			d.Set("location", n.Location)
   140  
   141  			// Create a new set to hold all configured subnets
   142  			subnets := &schema.Set{
   143  				F: resourceAzureSubnetHash,
   144  			}
   145  
   146  			// Loop through all endpoints
   147  			for _, s := range n.Subnets {
   148  				subnet := map[string]interface{}{}
   149  
   150  				// Get the associated (if any) security group
   151  				sg, err := secGroupClient.GetNetworkSecurityGroupForSubnet(s.Name, d.Id())
   152  				if err != nil && !management.IsResourceNotFoundError(err) {
   153  					return fmt.Errorf(
   154  						"Error retrieving Network Security Group associations of subnet %s: %s", s.Name, err)
   155  				}
   156  
   157  				// Update the values
   158  				subnet["name"] = s.Name
   159  				subnet["address_prefix"] = s.AddressPrefix
   160  				subnet["security_group"] = sg.Name
   161  
   162  				subnets.Add(subnet)
   163  			}
   164  
   165  			d.Set("subnet", subnets)
   166  
   167  			return nil
   168  		}
   169  	}
   170  
   171  	log.Printf("[DEBUG] Virtual Network %s does no longer exist", d.Id())
   172  	d.SetId("")
   173  
   174  	return nil
   175  }
   176  
   177  func resourceAzureVirtualNetworkUpdate(d *schema.ResourceData, meta interface{}) error {
   178  	ac := meta.(*Client)
   179  	mc := ac.mgmtClient
   180  	vnetClient := ac.vnetClient
   181  
   182  	// Lock the client just before we get the virtual network configuration and immediately
   183  	// set an defer to unlock the client again whenever this function exits
   184  	ac.vnetMutex.Lock()
   185  	defer ac.vnetMutex.Unlock()
   186  
   187  	nc, err := vnetClient.GetVirtualNetworkConfiguration()
   188  	if err != nil {
   189  		return fmt.Errorf(virtualNetworkRetrievalError, err)
   190  	}
   191  
   192  	found := false
   193  	for i, n := range nc.Configuration.VirtualNetworkSites {
   194  		if n.Name == d.Id() {
   195  			network := createVirtualNetwork(d)
   196  			nc.Configuration.VirtualNetworkSites[i] = network
   197  
   198  			found = true
   199  		}
   200  	}
   201  
   202  	if !found {
   203  		return fmt.Errorf("Virtual Network %s does not exists!", d.Id())
   204  	}
   205  
   206  	req, err := vnetClient.SetVirtualNetworkConfiguration(nc)
   207  	if err != nil {
   208  		return fmt.Errorf("Error updating Virtual Network %s: %s", d.Id(), err)
   209  	}
   210  
   211  	// Wait until the virtual network is updated
   212  	if err := mc.WaitForOperation(req, nil); err != nil {
   213  		return fmt.Errorf("Error waiting for Virtual Network %s to be updated: %s", d.Id(), err)
   214  	}
   215  
   216  	if err := associateSecurityGroups(d, meta); err != nil {
   217  		return err
   218  	}
   219  
   220  	return resourceAzureVirtualNetworkRead(d, meta)
   221  }
   222  
   223  func resourceAzureVirtualNetworkDelete(d *schema.ResourceData, meta interface{}) error {
   224  	ac := meta.(*Client)
   225  	mc := ac.mgmtClient
   226  	vnetClient := ac.vnetClient
   227  
   228  	// Lock the client just before we get the virtual network configuration and immediately
   229  	// set an defer to unlock the client again whenever this function exits
   230  	ac.vnetMutex.Lock()
   231  	defer ac.vnetMutex.Unlock()
   232  
   233  	nc, err := vnetClient.GetVirtualNetworkConfiguration()
   234  	if err != nil {
   235  		return fmt.Errorf(virtualNetworkRetrievalError, err)
   236  	}
   237  
   238  	filtered := nc.Configuration.VirtualNetworkSites[:0]
   239  	for _, n := range nc.Configuration.VirtualNetworkSites {
   240  		if n.Name != d.Id() {
   241  			filtered = append(filtered, n)
   242  		}
   243  	}
   244  
   245  	nc.Configuration.VirtualNetworkSites = filtered
   246  
   247  	req, err := vnetClient.SetVirtualNetworkConfiguration(nc)
   248  	if err != nil {
   249  		return fmt.Errorf("Error deleting Virtual Network %s: %s", d.Id(), err)
   250  	}
   251  
   252  	// Wait until the virtual network is deleted
   253  	if err := mc.WaitForOperation(req, nil); err != nil {
   254  		return fmt.Errorf("Error waiting for Virtual Network %s to be deleted: %s", d.Id(), err)
   255  	}
   256  
   257  	d.SetId("")
   258  
   259  	return nil
   260  }
   261  
   262  func resourceAzureSubnetHash(v interface{}) int {
   263  	m := v.(map[string]interface{})
   264  	subnet := m["name"].(string) + m["address_prefix"].(string) + m["security_group"].(string)
   265  	return hashcode.String(subnet)
   266  }
   267  
   268  func createVirtualNetwork(d *schema.ResourceData) virtualnetwork.VirtualNetworkSite {
   269  	// fetch address spaces:
   270  	var prefixes []string
   271  	for _, prefix := range d.Get("address_space").([]interface{}) {
   272  		prefixes = append(prefixes, prefix.(string))
   273  	}
   274  
   275  	// fetch DNS references:
   276  	var dnsRefs []virtualnetwork.DNSServerRef
   277  	for _, dns := range d.Get("dns_servers_names").([]interface{}) {
   278  		dnsRefs = append(dnsRefs, virtualnetwork.DNSServerRef{
   279  			Name: dns.(string),
   280  		})
   281  	}
   282  
   283  	// Add all subnets that are configured
   284  	var subnets []virtualnetwork.Subnet
   285  	if rs := d.Get("subnet").(*schema.Set); rs.Len() > 0 {
   286  		for _, subnet := range rs.List() {
   287  			subnet := subnet.(map[string]interface{})
   288  			subnets = append(subnets, virtualnetwork.Subnet{
   289  				Name:          subnet["name"].(string),
   290  				AddressPrefix: subnet["address_prefix"].(string),
   291  			})
   292  		}
   293  	}
   294  
   295  	return virtualnetwork.VirtualNetworkSite{
   296  		Name:     d.Get("name").(string),
   297  		Location: d.Get("location").(string),
   298  		AddressSpace: virtualnetwork.AddressSpace{
   299  			AddressPrefix: prefixes,
   300  		},
   301  		DNSServersRef: dnsRefs,
   302  		Subnets:       subnets,
   303  	}
   304  }
   305  
   306  func associateSecurityGroups(d *schema.ResourceData, meta interface{}) error {
   307  	azureClient := meta.(*Client)
   308  	mc := azureClient.mgmtClient
   309  	secGroupClient := azureClient.secGroupClient
   310  
   311  	virtualNetwork := d.Get("name").(string)
   312  
   313  	if rs := d.Get("subnet").(*schema.Set); rs.Len() > 0 {
   314  		for _, subnet := range rs.List() {
   315  			subnet := subnet.(map[string]interface{})
   316  			securityGroup := subnet["security_group"].(string)
   317  			subnetName := subnet["name"].(string)
   318  
   319  			// Get the associated (if any) security group
   320  			sg, err := secGroupClient.GetNetworkSecurityGroupForSubnet(subnetName, d.Id())
   321  			if err != nil && !management.IsResourceNotFoundError(err) {
   322  				return fmt.Errorf(
   323  					"Error retrieving Network Security Group associations of subnet %s: %s", subnetName, err)
   324  			}
   325  
   326  			// If the desired and actual security group are the same, were done so can just continue
   327  			if sg.Name == securityGroup {
   328  				continue
   329  			}
   330  
   331  			// If there is an associated security group, make sure we first remove it from the subnet
   332  			if sg.Name != "" {
   333  				req, err := secGroupClient.RemoveNetworkSecurityGroupFromSubnet(sg.Name, subnetName, virtualNetwork)
   334  				if err != nil {
   335  					return fmt.Errorf("Error removing Network Security Group %s from subnet %s: %s",
   336  						securityGroup, subnetName, err)
   337  				}
   338  
   339  				// Wait until the security group is associated
   340  				if err := mc.WaitForOperation(req, nil); err != nil {
   341  					return fmt.Errorf(
   342  						"Error waiting for Network Security Group %s to be removed from subnet %s: %s",
   343  						securityGroup, subnetName, err)
   344  				}
   345  			}
   346  
   347  			// If the desired security group is not empty, assign the security group to the subnet
   348  			if securityGroup != "" {
   349  				req, err := secGroupClient.AddNetworkSecurityToSubnet(securityGroup, subnetName, virtualNetwork)
   350  				if err != nil {
   351  					return fmt.Errorf("Error associating Network Security Group %s to subnet %s: %s",
   352  						securityGroup, subnetName, err)
   353  				}
   354  
   355  				// Wait until the security group is associated
   356  				if err := mc.WaitForOperation(req, nil); err != nil {
   357  					return fmt.Errorf(
   358  						"Error waiting for Network Security Group %s to be associated with subnet %s: %s",
   359  						securityGroup, subnetName, err)
   360  				}
   361  			}
   362  
   363  		}
   364  	}
   365  
   366  	return nil
   367  }