github.com/ggiamarchi/terraform@v0.3.7-0.20150607194748-ed2a66a46a71/builtin/providers/azure/resource_azure_virtual_network.go (about)

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