github.com/anuaimi/terraform@v0.6.4-0.20150904235404-2bf9aec61da8/builtin/providers/azure/resource_azure_instance.go (about)

     1  package azure
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/base64"
     6  	"fmt"
     7  	"log"
     8  	"strings"
     9  
    10  	"github.com/Azure/azure-sdk-for-go/management"
    11  	"github.com/Azure/azure-sdk-for-go/management/hostedservice"
    12  	"github.com/Azure/azure-sdk-for-go/management/osimage"
    13  	"github.com/Azure/azure-sdk-for-go/management/virtualmachine"
    14  	"github.com/Azure/azure-sdk-for-go/management/virtualmachineimage"
    15  	"github.com/Azure/azure-sdk-for-go/management/vmutils"
    16  	"github.com/hashicorp/terraform/helper/hashcode"
    17  	"github.com/hashicorp/terraform/helper/schema"
    18  )
    19  
    20  const (
    21  	linux                = "Linux"
    22  	windows              = "Windows"
    23  	osDiskBlobStorageURL = "http://%s.blob.core.windows.net/vhds/%s.vhd"
    24  )
    25  
    26  func resourceAzureInstance() *schema.Resource {
    27  	return &schema.Resource{
    28  		Create: resourceAzureInstanceCreate,
    29  		Read:   resourceAzureInstanceRead,
    30  		Update: resourceAzureInstanceUpdate,
    31  		Delete: resourceAzureInstanceDelete,
    32  
    33  		Schema: map[string]*schema.Schema{
    34  			"name": &schema.Schema{
    35  				Type:     schema.TypeString,
    36  				Required: true,
    37  				ForceNew: true,
    38  			},
    39  
    40  			"hosted_service_name": &schema.Schema{
    41  				Type:     schema.TypeString,
    42  				Optional: true,
    43  				Computed: true,
    44  				ForceNew: true,
    45  			},
    46  
    47  			"description": &schema.Schema{
    48  				Type:     schema.TypeString,
    49  				Optional: true,
    50  				Computed: true,
    51  				ForceNew: true,
    52  			},
    53  
    54  			"image": &schema.Schema{
    55  				Type:     schema.TypeString,
    56  				Required: true,
    57  				ForceNew: true,
    58  			},
    59  
    60  			"size": &schema.Schema{
    61  				Type:     schema.TypeString,
    62  				Required: true,
    63  			},
    64  
    65  			"subnet": &schema.Schema{
    66  				Type:     schema.TypeString,
    67  				Optional: true,
    68  				Computed: true,
    69  				ForceNew: true,
    70  			},
    71  
    72  			"virtual_network": &schema.Schema{
    73  				Type:     schema.TypeString,
    74  				Optional: true,
    75  				ForceNew: true,
    76  			},
    77  
    78  			"storage_service_name": &schema.Schema{
    79  				Type:     schema.TypeString,
    80  				Optional: true,
    81  				ForceNew: true,
    82  			},
    83  
    84  			"reverse_dns": &schema.Schema{
    85  				Type:     schema.TypeString,
    86  				Optional: true,
    87  				ForceNew: true,
    88  			},
    89  
    90  			"location": &schema.Schema{
    91  				Type:     schema.TypeString,
    92  				Required: true,
    93  				ForceNew: true,
    94  			},
    95  
    96  			"automatic_updates": &schema.Schema{
    97  				Type:     schema.TypeBool,
    98  				Optional: true,
    99  				Default:  false,
   100  				ForceNew: true,
   101  			},
   102  
   103  			"time_zone": &schema.Schema{
   104  				Type:     schema.TypeString,
   105  				Optional: true,
   106  				ForceNew: true,
   107  			},
   108  
   109  			"username": &schema.Schema{
   110  				Type:     schema.TypeString,
   111  				Required: true,
   112  				ForceNew: true,
   113  			},
   114  
   115  			"password": &schema.Schema{
   116  				Type:     schema.TypeString,
   117  				Optional: true,
   118  				ForceNew: true,
   119  			},
   120  
   121  			"ssh_key_thumbprint": &schema.Schema{
   122  				Type:     schema.TypeString,
   123  				Optional: true,
   124  				ForceNew: true,
   125  			},
   126  
   127  			"endpoint": &schema.Schema{
   128  				Type:     schema.TypeSet,
   129  				Optional: true,
   130  				Computed: true,
   131  				Elem: &schema.Resource{
   132  					Schema: map[string]*schema.Schema{
   133  						"name": &schema.Schema{
   134  							Type:     schema.TypeString,
   135  							Required: true,
   136  						},
   137  
   138  						"protocol": &schema.Schema{
   139  							Type:     schema.TypeString,
   140  							Optional: true,
   141  							Default:  "tcp",
   142  						},
   143  
   144  						"public_port": &schema.Schema{
   145  							Type:     schema.TypeInt,
   146  							Required: true,
   147  						},
   148  
   149  						"private_port": &schema.Schema{
   150  							Type:     schema.TypeInt,
   151  							Required: true,
   152  						},
   153  					},
   154  				},
   155  				Set: resourceAzureEndpointHash,
   156  			},
   157  
   158  			"security_group": &schema.Schema{
   159  				Type:     schema.TypeString,
   160  				Optional: true,
   161  				Computed: true,
   162  			},
   163  
   164  			"ip_address": &schema.Schema{
   165  				Type:     schema.TypeString,
   166  				Computed: true,
   167  			},
   168  
   169  			"vip_address": &schema.Schema{
   170  				Type:     schema.TypeString,
   171  				Computed: true,
   172  			},
   173  		},
   174  	}
   175  }
   176  
   177  func resourceAzureInstanceCreate(d *schema.ResourceData, meta interface{}) (err error) {
   178  	azureClient := meta.(*Client)
   179  	mc := azureClient.mgmtClient
   180  	hostedServiceClient := azureClient.hostedServiceClient
   181  	vmClient := azureClient.vmClient
   182  
   183  	name := d.Get("name").(string)
   184  
   185  	// Compute/set the description
   186  	description := d.Get("description").(string)
   187  	if description == "" {
   188  		description = name
   189  	}
   190  
   191  	// Retrieve the needed details of the image
   192  	configureForImage, osType, err := retrieveImageDetails(
   193  		meta,
   194  		d.Get("image").(string),
   195  		name,
   196  		d.Get("storage_service_name").(string),
   197  	)
   198  	if err != nil {
   199  		return err
   200  	}
   201  
   202  	// Verify if we have all required parameters
   203  	if err := verifyInstanceParameters(d, osType); err != nil {
   204  		return err
   205  	}
   206  
   207  	var hostedServiceName string
   208  	// check if hosted service name parameter was given:
   209  	if serviceName, ok := d.GetOk("hosted_service_name"); !ok {
   210  		// if not provided; just use the name of the instance to create a new one:
   211  		hostedServiceName = name
   212  		d.Set("hosted_service_name", hostedServiceName)
   213  
   214  		p := hostedservice.CreateHostedServiceParameters{
   215  			ServiceName:    hostedServiceName,
   216  			Label:          base64.StdEncoding.EncodeToString([]byte(name)),
   217  			Description:    fmt.Sprintf("Cloud Service created automatically for instance %s", name),
   218  			Location:       d.Get("location").(string),
   219  			ReverseDNSFqdn: d.Get("reverse_dns").(string),
   220  		}
   221  
   222  		log.Printf("[DEBUG] Creating Cloud Service for instance: %s", name)
   223  		err = hostedServiceClient.CreateHostedService(p)
   224  		if err != nil {
   225  			return fmt.Errorf("Error creating Cloud Service for instance %s: %s", name, err)
   226  		}
   227  	} else {
   228  		// else; use the provided hosted service name:
   229  		hostedServiceName = serviceName.(string)
   230  	}
   231  
   232  	// Create a new role for the instance
   233  	role := vmutils.NewVMConfiguration(name, d.Get("size").(string))
   234  
   235  	log.Printf("[DEBUG] Configuring deployment from image...")
   236  	err = configureForImage(&role)
   237  	if err != nil {
   238  		return fmt.Errorf("Error configuring the deployment for %s: %s", name, err)
   239  	}
   240  
   241  	if osType == linux {
   242  		// This is pretty ugly, but the Azure SDK leaves me no other choice...
   243  		if tp, ok := d.GetOk("ssh_key_thumbprint"); ok {
   244  			err = vmutils.ConfigureForLinux(
   245  				&role,
   246  				name,
   247  				d.Get("username").(string),
   248  				d.Get("password").(string),
   249  				tp.(string),
   250  			)
   251  		} else {
   252  			err = vmutils.ConfigureForLinux(
   253  				&role,
   254  				name,
   255  				d.Get("username").(string),
   256  				d.Get("password").(string),
   257  			)
   258  		}
   259  		if err != nil {
   260  			return fmt.Errorf("Error configuring %s for Linux: %s", name, err)
   261  		}
   262  	}
   263  
   264  	if osType == windows {
   265  		err = vmutils.ConfigureForWindows(
   266  			&role,
   267  			name,
   268  			d.Get("username").(string),
   269  			d.Get("password").(string),
   270  			d.Get("automatic_updates").(bool),
   271  			d.Get("time_zone").(string),
   272  		)
   273  		if err != nil {
   274  			return fmt.Errorf("Error configuring %s for Windows: %s", name, err)
   275  		}
   276  	}
   277  
   278  	if s := d.Get("endpoint").(*schema.Set); s.Len() > 0 {
   279  		for _, v := range s.List() {
   280  			m := v.(map[string]interface{})
   281  			err := vmutils.ConfigureWithExternalPort(
   282  				&role,
   283  				m["name"].(string),
   284  				m["private_port"].(int),
   285  				m["public_port"].(int),
   286  				endpointProtocol(m["protocol"].(string)),
   287  			)
   288  			if err != nil {
   289  				return fmt.Errorf(
   290  					"Error adding endpoint %s for instance %s: %s", m["name"].(string), name, err)
   291  			}
   292  		}
   293  	}
   294  
   295  	if subnet, ok := d.GetOk("subnet"); ok {
   296  		err = vmutils.ConfigureWithSubnet(&role, subnet.(string))
   297  		if err != nil {
   298  			return fmt.Errorf(
   299  				"Error associating subnet %s with instance %s: %s", d.Get("subnet").(string), name, err)
   300  		}
   301  	}
   302  
   303  	if sg, ok := d.GetOk("security_group"); ok {
   304  		err = vmutils.ConfigureWithSecurityGroup(&role, sg.(string))
   305  		if err != nil {
   306  			return fmt.Errorf(
   307  				"Error associating security group %s with instance %s: %s", sg.(string), name, err)
   308  		}
   309  	}
   310  
   311  	options := virtualmachine.CreateDeploymentOptions{
   312  		VirtualNetworkName: d.Get("virtual_network").(string),
   313  	}
   314  
   315  	log.Printf("[DEBUG] Creating the new instance...")
   316  	req, err := vmClient.CreateDeployment(role, hostedServiceName, options)
   317  	if err != nil {
   318  		return fmt.Errorf("Error creating instance %s: %s", name, err)
   319  	}
   320  
   321  	log.Printf("[DEBUG] Waiting for the new instance to be created...")
   322  	if err := mc.WaitForOperation(req, nil); err != nil {
   323  		return fmt.Errorf(
   324  			"Error waiting for instance %s to be created: %s", name, err)
   325  	}
   326  
   327  	d.SetId(name)
   328  
   329  	return resourceAzureInstanceRead(d, meta)
   330  }
   331  
   332  func resourceAzureInstanceRead(d *schema.ResourceData, meta interface{}) error {
   333  	azureClient := meta.(*Client)
   334  	hostedServiceClient := azureClient.hostedServiceClient
   335  	vmClient := azureClient.vmClient
   336  
   337  	name := d.Get("name").(string)
   338  
   339  	// check if the instance belongs to an independent hosted service
   340  	// or it had one created for it.
   341  	var hostedServiceName string
   342  	if serviceName, ok := d.GetOk("hosted_service_name"); ok {
   343  		// if independent; use that hosted service name:
   344  		hostedServiceName = serviceName.(string)
   345  	} else {
   346  		// else; suppose it's the instance's name:
   347  		hostedServiceName = name
   348  	}
   349  
   350  	log.Printf("[DEBUG] Retrieving Cloud Service for instance: %s", name)
   351  	cs, err := hostedServiceClient.GetHostedService(hostedServiceName)
   352  	if err != nil {
   353  		return fmt.Errorf("Error retrieving Cloud Service of instance %s (%q): %s", name, hostedServiceName, err)
   354  	}
   355  
   356  	d.Set("reverse_dns", cs.ReverseDNSFqdn)
   357  	d.Set("location", cs.Location)
   358  
   359  	log.Printf("[DEBUG] Retrieving instance: %s", name)
   360  	dpmt, err := vmClient.GetDeployment(hostedServiceName, name)
   361  	if err != nil {
   362  		if management.IsResourceNotFoundError(err) {
   363  			d.SetId("")
   364  			return nil
   365  		}
   366  		return fmt.Errorf("Error retrieving instance %s: %s", name, err)
   367  	}
   368  
   369  	if len(dpmt.RoleList) != 1 {
   370  		return fmt.Errorf(
   371  			"Instance %s has an unexpected number of roles: %d", name, len(dpmt.RoleList))
   372  	}
   373  
   374  	d.Set("size", dpmt.RoleList[0].RoleSize)
   375  
   376  	if len(dpmt.RoleInstanceList) != 1 {
   377  		return fmt.Errorf(
   378  			"Instance %s has an unexpected number of role instances: %d",
   379  			name, len(dpmt.RoleInstanceList))
   380  	}
   381  	d.Set("ip_address", dpmt.RoleInstanceList[0].IPAddress)
   382  
   383  	if len(dpmt.RoleInstanceList[0].InstanceEndpoints) > 0 {
   384  		d.Set("vip_address", dpmt.RoleInstanceList[0].InstanceEndpoints[0].Vip)
   385  	}
   386  
   387  	// Find the network configuration set
   388  	for _, c := range dpmt.RoleList[0].ConfigurationSets {
   389  		if c.ConfigurationSetType == virtualmachine.ConfigurationSetTypeNetwork {
   390  			// Create a new set to hold all configured endpoints
   391  			endpoints := &schema.Set{
   392  				F: resourceAzureEndpointHash,
   393  			}
   394  
   395  			// Loop through all endpoints
   396  			for _, ep := range c.InputEndpoints {
   397  				endpoint := map[string]interface{}{}
   398  
   399  				// Update the values
   400  				endpoint["name"] = ep.Name
   401  				endpoint["protocol"] = string(ep.Protocol)
   402  				endpoint["public_port"] = ep.Port
   403  				endpoint["private_port"] = ep.LocalPort
   404  				endpoints.Add(endpoint)
   405  			}
   406  			d.Set("endpoint", endpoints)
   407  
   408  			// Update the subnet
   409  			switch len(c.SubnetNames) {
   410  			case 1:
   411  				d.Set("subnet", c.SubnetNames[0])
   412  			case 0:
   413  				d.Set("subnet", "")
   414  			default:
   415  				return fmt.Errorf(
   416  					"Instance %s has an unexpected number of associated subnets %d",
   417  					name, len(dpmt.RoleInstanceList))
   418  			}
   419  
   420  			// Update the security group
   421  			d.Set("security_group", c.NetworkSecurityGroup)
   422  		}
   423  	}
   424  
   425  	connType := "ssh"
   426  	if dpmt.RoleList[0].OSVirtualHardDisk.OS == windows {
   427  		connType = "winrm"
   428  	}
   429  
   430  	// Set the connection info for any configured provisioners
   431  	d.SetConnInfo(map[string]string{
   432  		"type":     connType,
   433  		"host":     dpmt.VirtualIPs[0].Address,
   434  		"user":     d.Get("username").(string),
   435  		"password": d.Get("password").(string),
   436  	})
   437  
   438  	return nil
   439  }
   440  
   441  func resourceAzureInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
   442  	azureClient := meta.(*Client)
   443  	mc := azureClient.mgmtClient
   444  	vmClient := azureClient.vmClient
   445  
   446  	// First check if anything we can update changed, and if not just return
   447  	if !d.HasChange("size") && !d.HasChange("endpoint") && !d.HasChange("security_group") {
   448  		return nil
   449  	}
   450  
   451  	name := d.Get("name").(string)
   452  	hostedServiceName := d.Get("hosted_service_name").(string)
   453  
   454  	// Get the current role
   455  	role, err := vmClient.GetRole(hostedServiceName, name, name)
   456  	if err != nil {
   457  		return fmt.Errorf("Error retrieving role of instance %s: %s", name, err)
   458  	}
   459  
   460  	// Verify if we have all required parameters
   461  	if err := verifyInstanceParameters(d, role.OSVirtualHardDisk.OS); err != nil {
   462  		return err
   463  	}
   464  
   465  	if d.HasChange("size") {
   466  		role.RoleSize = d.Get("size").(string)
   467  	}
   468  
   469  	if d.HasChange("endpoint") {
   470  		_, n := d.GetChange("endpoint")
   471  
   472  		// Delete the existing endpoints
   473  		for i, c := range role.ConfigurationSets {
   474  			if c.ConfigurationSetType == virtualmachine.ConfigurationSetTypeNetwork {
   475  				c.InputEndpoints = nil
   476  				role.ConfigurationSets[i] = c
   477  			}
   478  		}
   479  
   480  		// And add the ones we still want
   481  		if s := n.(*schema.Set); s.Len() > 0 {
   482  			for _, v := range s.List() {
   483  				m := v.(map[string]interface{})
   484  				err := vmutils.ConfigureWithExternalPort(
   485  					role,
   486  					m["name"].(string),
   487  					m["private_port"].(int),
   488  					m["public_port"].(int),
   489  					endpointProtocol(m["protocol"].(string)),
   490  				)
   491  				if err != nil {
   492  					return fmt.Errorf(
   493  						"Error adding endpoint %s for instance %s: %s", m["name"].(string), name, err)
   494  				}
   495  			}
   496  		}
   497  	}
   498  
   499  	if d.HasChange("security_group") {
   500  		sg := d.Get("security_group").(string)
   501  		err := vmutils.ConfigureWithSecurityGroup(role, sg)
   502  		if err != nil {
   503  			return fmt.Errorf(
   504  				"Error associating security group %s with instance %s: %s", sg, name, err)
   505  		}
   506  	}
   507  
   508  	// Update the adjusted role
   509  	req, err := vmClient.UpdateRole(hostedServiceName, name, name, *role)
   510  	if err != nil {
   511  		return fmt.Errorf("Error updating role of instance %s: %s", name, err)
   512  	}
   513  
   514  	if err := mc.WaitForOperation(req, nil); err != nil {
   515  		return fmt.Errorf(
   516  			"Error waiting for role of instance %s to be updated: %s", name, err)
   517  	}
   518  
   519  	return resourceAzureInstanceRead(d, meta)
   520  }
   521  
   522  func resourceAzureInstanceDelete(d *schema.ResourceData, meta interface{}) error {
   523  	azureClient := meta.(*Client)
   524  	mc := azureClient.mgmtClient
   525  	vmClient := azureClient.vmClient
   526  	hostedServiceClient := azureClient.hostedServiceClient
   527  
   528  	name := d.Get("name").(string)
   529  	hostedServiceName := d.Get("hosted_service_name").(string)
   530  
   531  	log.Printf("[DEBUG] Deleting instance: %s", name)
   532  
   533  	// check if the instance had a hosted service created especially for it:
   534  	if name == hostedServiceName {
   535  		// if so; we must delete the associated hosted service as well:
   536  		req, err := hostedServiceClient.DeleteHostedService(name, true)
   537  		if err != nil {
   538  			return fmt.Errorf("Error deleting instance and hosted service %s: %s", name, err)
   539  		}
   540  
   541  		// Wait until the hosted service and the instance it contains is deleted:
   542  		if err := mc.WaitForOperation(req, nil); err != nil {
   543  			return fmt.Errorf(
   544  				"Error waiting for instance %s to be deleted: %s", name, err)
   545  		}
   546  	} else {
   547  		// else; just delete the instance:
   548  		reqID, err := vmClient.DeleteDeployment(hostedServiceName, name)
   549  		if err != nil {
   550  			return fmt.Errorf("Error deleting instance %s off hosted service %s: %s", name, hostedServiceName, err)
   551  		}
   552  
   553  		// and wait for the deletion:
   554  		if err := mc.WaitForOperation(reqID, nil); err != nil {
   555  			return fmt.Errorf("Error waiting for intance %s to be deleted off the hosted service %s: %s",
   556  				name, hostedServiceName, err)
   557  		}
   558  	}
   559  
   560  	return nil
   561  }
   562  
   563  func resourceAzureEndpointHash(v interface{}) int {
   564  	var buf bytes.Buffer
   565  	m := v.(map[string]interface{})
   566  	buf.WriteString(fmt.Sprintf("%s-", m["name"].(string)))
   567  	buf.WriteString(fmt.Sprintf("%s-", m["protocol"].(string)))
   568  	buf.WriteString(fmt.Sprintf("%d-", m["public_port"].(int)))
   569  	buf.WriteString(fmt.Sprintf("%d-", m["private_port"].(int)))
   570  
   571  	return hashcode.String(buf.String())
   572  }
   573  
   574  func retrieveImageDetails(
   575  	meta interface{},
   576  	label string,
   577  	name string,
   578  	storage string) (func(*virtualmachine.Role) error, string, error) {
   579  
   580  	azureClient := meta.(*Client)
   581  	vmImageClient := azureClient.vmImageClient
   582  	osImageClient := azureClient.osImageClient
   583  
   584  	configureForImage, osType, VMLabels, err := retrieveVMImageDetails(vmImageClient, label)
   585  	if err == nil {
   586  		return configureForImage, osType, nil
   587  	}
   588  
   589  	configureForImage, osType, OSLabels, err := retrieveOSImageDetails(osImageClient, label, name, storage)
   590  	if err == nil {
   591  		return configureForImage, osType, nil
   592  	}
   593  
   594  	if err == PlatformStorageError {
   595  		return nil, "", err
   596  	}
   597  
   598  	return nil, "", fmt.Errorf("Could not find image with label '%s'. Available images are: %s",
   599  		label, strings.Join(append(VMLabels, OSLabels...), ", "))
   600  }
   601  
   602  func retrieveVMImageDetails(
   603  	vmImageClient virtualmachineimage.Client,
   604  	label string) (func(*virtualmachine.Role) error, string, []string, error) {
   605  	imgs, err := vmImageClient.ListVirtualMachineImages()
   606  	if err != nil {
   607  		return nil, "", nil, fmt.Errorf("Error retrieving image details: %s", err)
   608  	}
   609  
   610  	var labels []string
   611  	for _, img := range imgs.VMImages {
   612  		if img.Label == label {
   613  			if img.OSDiskConfiguration.OS != linux && img.OSDiskConfiguration.OS != windows {
   614  				return nil, "", nil, fmt.Errorf("Unsupported image OS: %s", img.OSDiskConfiguration.OS)
   615  			}
   616  
   617  			configureForImage := func(role *virtualmachine.Role) error {
   618  				return vmutils.ConfigureDeploymentFromVMImage(
   619  					role,
   620  					img.Name,
   621  					"",
   622  					true,
   623  				)
   624  			}
   625  
   626  			return configureForImage, img.OSDiskConfiguration.OS, nil, nil
   627  		}
   628  
   629  		labels = append(labels, img.Label)
   630  	}
   631  
   632  	return nil, "", labels, fmt.Errorf("Could not find image with label '%s'", label)
   633  }
   634  
   635  func retrieveOSImageDetails(
   636  	osImageClient osimage.OSImageClient,
   637  	label string,
   638  	name string,
   639  	storage string) (func(*virtualmachine.Role) error, string, []string, error) {
   640  	imgs, err := osImageClient.ListOSImages()
   641  	if err != nil {
   642  		return nil, "", nil, fmt.Errorf("Error retrieving image details: %s", err)
   643  	}
   644  
   645  	var labels []string
   646  	for _, img := range imgs.OSImages {
   647  		if img.Label == label {
   648  			if img.OS != linux && img.OS != windows {
   649  				return nil, "", nil, fmt.Errorf("Unsupported image OS: %s", img.OS)
   650  			}
   651  			if img.MediaLink == "" {
   652  				if storage == "" {
   653  					return nil, "", nil, PlatformStorageError
   654  				}
   655  				img.MediaLink = fmt.Sprintf(osDiskBlobStorageURL, storage, name)
   656  			}
   657  
   658  			configureForImage := func(role *virtualmachine.Role) error {
   659  				return vmutils.ConfigureDeploymentFromPlatformImage(
   660  					role,
   661  					img.Name,
   662  					img.MediaLink,
   663  					label,
   664  				)
   665  			}
   666  
   667  			return configureForImage, img.OS, nil, nil
   668  		}
   669  
   670  		labels = append(labels, img.Label)
   671  	}
   672  
   673  	return nil, "", labels, fmt.Errorf("Could not find image with label '%s'", label)
   674  }
   675  
   676  func endpointProtocol(p string) virtualmachine.InputEndpointProtocol {
   677  	if p == "tcp" {
   678  		return virtualmachine.InputEndpointProtocolTCP
   679  	}
   680  
   681  	return virtualmachine.InputEndpointProtocolUDP
   682  }
   683  
   684  func verifyInstanceParameters(d *schema.ResourceData, osType string) error {
   685  	if osType == linux {
   686  		_, pass := d.GetOk("password")
   687  		_, key := d.GetOk("ssh_key_thumbprint")
   688  
   689  		if !pass && !key {
   690  			return fmt.Errorf(
   691  				"You must supply a 'password' and/or a 'ssh_key_thumbprint' when using a Linux image")
   692  		}
   693  	}
   694  
   695  	if osType == windows {
   696  		if _, ok := d.GetOk("password"); !ok {
   697  			return fmt.Errorf("You must supply a 'password' when using a Windows image")
   698  		}
   699  
   700  		if _, ok := d.GetOk("time_zone"); !ok {
   701  			return fmt.Errorf("You must supply a 'time_zone' when using a Windows image")
   702  		}
   703  	}
   704  
   705  	if _, ok := d.GetOk("subnet"); ok {
   706  		if _, ok := d.GetOk("virtual_network"); !ok {
   707  			return fmt.Errorf("You must also supply a 'virtual_network' when supplying a 'subnet'")
   708  		}
   709  	}
   710  
   711  	if s := d.Get("endpoint").(*schema.Set); s.Len() > 0 {
   712  		for _, v := range s.List() {
   713  			protocol := v.(map[string]interface{})["protocol"].(string)
   714  
   715  			if protocol != "tcp" && protocol != "udp" {
   716  				return fmt.Errorf(
   717  					"Invalid endpoint protocol %s! Valid options are 'tcp' and 'udp'.", protocol)
   718  			}
   719  		}
   720  	}
   721  
   722  	return nil
   723  }