github.com/daveadams/terraform@v0.6.4-0.20160830094355-13ce74975936/builtin/providers/google/resource_compute_instance_template.go (about)

     1  package google
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  
     8  	"github.com/hashicorp/terraform/helper/resource"
     9  	"github.com/hashicorp/terraform/helper/schema"
    10  	"google.golang.org/api/compute/v1"
    11  	"google.golang.org/api/googleapi"
    12  )
    13  
    14  func resourceComputeInstanceTemplate() *schema.Resource {
    15  	return &schema.Resource{
    16  		Create: resourceComputeInstanceTemplateCreate,
    17  		Read:   resourceComputeInstanceTemplateRead,
    18  		Delete: resourceComputeInstanceTemplateDelete,
    19  		Importer: &schema.ResourceImporter{
    20  			State: schema.ImportStatePassthrough,
    21  		},
    22  
    23  		Schema: map[string]*schema.Schema{
    24  			"name": &schema.Schema{
    25  				Type:          schema.TypeString,
    26  				Optional:      true,
    27  				Computed:      true,
    28  				ForceNew:      true,
    29  				ConflictsWith: []string{"name_prefix"},
    30  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    31  					// https://cloud.google.com/compute/docs/reference/latest/instanceTemplates#resource
    32  					value := v.(string)
    33  					if len(value) > 63 {
    34  						errors = append(errors, fmt.Errorf(
    35  							"%q cannot be longer than 63 characters", k))
    36  					}
    37  					return
    38  				},
    39  			},
    40  
    41  			"name_prefix": &schema.Schema{
    42  				Type:     schema.TypeString,
    43  				Optional: true,
    44  				ForceNew: true,
    45  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    46  					// https://cloud.google.com/compute/docs/reference/latest/instanceTemplates#resource
    47  					// uuid is 26 characters, limit the prefix to 37.
    48  					value := v.(string)
    49  					if len(value) > 37 {
    50  						errors = append(errors, fmt.Errorf(
    51  							"%q cannot be longer than 37 characters, name is limited to 63", k))
    52  					}
    53  					return
    54  				},
    55  			},
    56  			"disk": &schema.Schema{
    57  				Type:     schema.TypeList,
    58  				Required: true,
    59  				ForceNew: true,
    60  				Elem: &schema.Resource{
    61  					Schema: map[string]*schema.Schema{
    62  						"auto_delete": &schema.Schema{
    63  							Type:     schema.TypeBool,
    64  							Optional: true,
    65  							Default:  true,
    66  							ForceNew: true,
    67  						},
    68  
    69  						"boot": &schema.Schema{
    70  							Type:     schema.TypeBool,
    71  							Optional: true,
    72  							ForceNew: true,
    73  							Computed: true,
    74  						},
    75  
    76  						"device_name": &schema.Schema{
    77  							Type:     schema.TypeString,
    78  							Optional: true,
    79  							ForceNew: true,
    80  						},
    81  
    82  						"disk_name": &schema.Schema{
    83  							Type:     schema.TypeString,
    84  							Optional: true,
    85  							ForceNew: true,
    86  						},
    87  
    88  						"disk_size_gb": &schema.Schema{
    89  							Type:     schema.TypeInt,
    90  							Optional: true,
    91  							ForceNew: true,
    92  						},
    93  
    94  						"disk_type": &schema.Schema{
    95  							Type:     schema.TypeString,
    96  							Optional: true,
    97  							ForceNew: true,
    98  							Computed: true,
    99  						},
   100  
   101  						"source_image": &schema.Schema{
   102  							Type:     schema.TypeString,
   103  							Optional: true,
   104  							ForceNew: true,
   105  						},
   106  
   107  						"interface": &schema.Schema{
   108  							Type:     schema.TypeString,
   109  							Optional: true,
   110  							ForceNew: true,
   111  							Computed: true,
   112  						},
   113  
   114  						"mode": &schema.Schema{
   115  							Type:     schema.TypeString,
   116  							Optional: true,
   117  							ForceNew: true,
   118  							Computed: true,
   119  						},
   120  
   121  						"source": &schema.Schema{
   122  							Type:     schema.TypeString,
   123  							Optional: true,
   124  							ForceNew: true,
   125  						},
   126  
   127  						"type": &schema.Schema{
   128  							Type:     schema.TypeString,
   129  							Optional: true,
   130  							ForceNew: true,
   131  							Computed: true,
   132  						},
   133  					},
   134  				},
   135  			},
   136  
   137  			"machine_type": &schema.Schema{
   138  				Type:     schema.TypeString,
   139  				Required: true,
   140  				ForceNew: true,
   141  			},
   142  
   143  			"automatic_restart": &schema.Schema{
   144  				Type:       schema.TypeBool,
   145  				Optional:   true,
   146  				Default:    true,
   147  				ForceNew:   true,
   148  				Deprecated: "Please use `scheduling.automatic_restart` instead",
   149  			},
   150  
   151  			"can_ip_forward": &schema.Schema{
   152  				Type:     schema.TypeBool,
   153  				Optional: true,
   154  				Default:  false,
   155  				ForceNew: true,
   156  			},
   157  
   158  			"description": &schema.Schema{
   159  				Type:     schema.TypeString,
   160  				Optional: true,
   161  				ForceNew: true,
   162  			},
   163  
   164  			"instance_description": &schema.Schema{
   165  				Type:     schema.TypeString,
   166  				Optional: true,
   167  				ForceNew: true,
   168  			},
   169  
   170  			"metadata": &schema.Schema{
   171  				Type:     schema.TypeMap,
   172  				Optional: true,
   173  				ForceNew: true,
   174  			},
   175  
   176  			"metadata_fingerprint": &schema.Schema{
   177  				Type:     schema.TypeString,
   178  				Computed: true,
   179  			},
   180  
   181  			"network_interface": &schema.Schema{
   182  				Type:     schema.TypeList,
   183  				Optional: true,
   184  				ForceNew: true,
   185  				Elem: &schema.Resource{
   186  					Schema: map[string]*schema.Schema{
   187  						"network": &schema.Schema{
   188  							Type:     schema.TypeString,
   189  							Optional: true,
   190  							ForceNew: true,
   191  							Computed: true,
   192  						},
   193  
   194  						"subnetwork": &schema.Schema{
   195  							Type:     schema.TypeString,
   196  							Optional: true,
   197  							ForceNew: true,
   198  						},
   199  
   200  						"access_config": &schema.Schema{
   201  							Type:     schema.TypeList,
   202  							Optional: true,
   203  							Elem: &schema.Resource{
   204  								Schema: map[string]*schema.Schema{
   205  									"nat_ip": &schema.Schema{
   206  										Type:     schema.TypeString,
   207  										Computed: true,
   208  										Optional: true,
   209  									},
   210  								},
   211  							},
   212  						},
   213  					},
   214  				},
   215  			},
   216  
   217  			"on_host_maintenance": &schema.Schema{
   218  				Type:       schema.TypeString,
   219  				Optional:   true,
   220  				ForceNew:   true,
   221  				Deprecated: "Please use `scheduling.on_host_maintenance` instead",
   222  			},
   223  
   224  			"project": &schema.Schema{
   225  				Type:     schema.TypeString,
   226  				Optional: true,
   227  				ForceNew: true,
   228  				Computed: true,
   229  			},
   230  
   231  			"region": &schema.Schema{
   232  				Type:     schema.TypeString,
   233  				Optional: true,
   234  				ForceNew: true,
   235  			},
   236  
   237  			"scheduling": &schema.Schema{
   238  				Type:     schema.TypeList,
   239  				Optional: true,
   240  				Computed: true,
   241  				ForceNew: true,
   242  				Elem: &schema.Resource{
   243  					Schema: map[string]*schema.Schema{
   244  						"preemptible": &schema.Schema{
   245  							Type:     schema.TypeBool,
   246  							Optional: true,
   247  							Default:  false,
   248  							ForceNew: true,
   249  						},
   250  
   251  						"automatic_restart": &schema.Schema{
   252  							Type:     schema.TypeBool,
   253  							Optional: true,
   254  							Default:  true,
   255  							ForceNew: true,
   256  						},
   257  
   258  						"on_host_maintenance": &schema.Schema{
   259  							Type:     schema.TypeString,
   260  							Optional: true,
   261  							Computed: true,
   262  							ForceNew: true,
   263  						},
   264  					},
   265  				},
   266  			},
   267  
   268  			"self_link": &schema.Schema{
   269  				Type:     schema.TypeString,
   270  				Computed: true,
   271  			},
   272  
   273  			"service_account": &schema.Schema{
   274  				Type:     schema.TypeList,
   275  				MaxItems: 1,
   276  				Optional: true,
   277  				ForceNew: true,
   278  				Elem: &schema.Resource{
   279  					Schema: map[string]*schema.Schema{
   280  						"email": &schema.Schema{
   281  							Type:     schema.TypeString,
   282  							Optional: true,
   283  							Computed: true,
   284  							ForceNew: true,
   285  						},
   286  
   287  						"scopes": &schema.Schema{
   288  							Type:     schema.TypeList,
   289  							Required: true,
   290  							ForceNew: true,
   291  							Elem: &schema.Schema{
   292  								Type: schema.TypeString,
   293  								StateFunc: func(v interface{}) string {
   294  									return canonicalizeServiceScope(v.(string))
   295  								},
   296  							},
   297  						},
   298  					},
   299  				},
   300  			},
   301  
   302  			"tags": &schema.Schema{
   303  				Type:     schema.TypeSet,
   304  				Optional: true,
   305  				ForceNew: true,
   306  				Elem:     &schema.Schema{Type: schema.TypeString},
   307  				Set:      schema.HashString,
   308  			},
   309  
   310  			"tags_fingerprint": &schema.Schema{
   311  				Type:     schema.TypeString,
   312  				Computed: true,
   313  			},
   314  		},
   315  	}
   316  }
   317  
   318  func buildDisks(d *schema.ResourceData, meta interface{}) ([]*compute.AttachedDisk, error) {
   319  	config := meta.(*Config)
   320  
   321  	disksCount := d.Get("disk.#").(int)
   322  
   323  	disks := make([]*compute.AttachedDisk, 0, disksCount)
   324  	for i := 0; i < disksCount; i++ {
   325  		prefix := fmt.Sprintf("disk.%d", i)
   326  
   327  		// Build the disk
   328  		var disk compute.AttachedDisk
   329  		disk.Type = "PERSISTENT"
   330  		disk.Mode = "READ_WRITE"
   331  		disk.Interface = "SCSI"
   332  		disk.Boot = i == 0
   333  		disk.AutoDelete = d.Get(prefix + ".auto_delete").(bool)
   334  
   335  		if v, ok := d.GetOk(prefix + ".boot"); ok {
   336  			disk.Boot = v.(bool)
   337  		}
   338  
   339  		if v, ok := d.GetOk(prefix + ".device_name"); ok {
   340  			disk.DeviceName = v.(string)
   341  		}
   342  
   343  		if v, ok := d.GetOk(prefix + ".source"); ok {
   344  			disk.Source = v.(string)
   345  		} else {
   346  			disk.InitializeParams = &compute.AttachedDiskInitializeParams{}
   347  
   348  			if v, ok := d.GetOk(prefix + ".disk_name"); ok {
   349  				disk.InitializeParams.DiskName = v.(string)
   350  			}
   351  			if v, ok := d.GetOk(prefix + ".disk_size_gb"); ok {
   352  				disk.InitializeParams.DiskSizeGb = int64(v.(int))
   353  			}
   354  			disk.InitializeParams.DiskType = "pd-standard"
   355  			if v, ok := d.GetOk(prefix + ".disk_type"); ok {
   356  				disk.InitializeParams.DiskType = v.(string)
   357  			}
   358  
   359  			if v, ok := d.GetOk(prefix + ".source_image"); ok {
   360  				imageName := v.(string)
   361  				imageUrl, err := resolveImage(config, imageName)
   362  				if err != nil {
   363  					return nil, fmt.Errorf(
   364  						"Error resolving image name '%s': %s",
   365  						imageName, err)
   366  				}
   367  				disk.InitializeParams.SourceImage = imageUrl
   368  			}
   369  		}
   370  
   371  		if v, ok := d.GetOk(prefix + ".interface"); ok {
   372  			disk.Interface = v.(string)
   373  		}
   374  
   375  		if v, ok := d.GetOk(prefix + ".mode"); ok {
   376  			disk.Mode = v.(string)
   377  		}
   378  
   379  		if v, ok := d.GetOk(prefix + ".type"); ok {
   380  			disk.Type = v.(string)
   381  		}
   382  
   383  		disks = append(disks, &disk)
   384  	}
   385  
   386  	return disks, nil
   387  }
   388  
   389  func buildNetworks(d *schema.ResourceData, meta interface{}) ([]*compute.NetworkInterface, error) {
   390  	// Build up the list of networks
   391  	config := meta.(*Config)
   392  
   393  	project, err := getProject(d, config)
   394  	if err != nil {
   395  		return nil, err
   396  	}
   397  
   398  	networksCount := d.Get("network_interface.#").(int)
   399  	networkInterfaces := make([]*compute.NetworkInterface, 0, networksCount)
   400  	for i := 0; i < networksCount; i++ {
   401  		prefix := fmt.Sprintf("network_interface.%d", i)
   402  
   403  		var networkName, subnetworkName string
   404  		if v, ok := d.GetOk(prefix + ".network"); ok {
   405  			networkName = v.(string)
   406  		}
   407  		if v, ok := d.GetOk(prefix + ".subnetwork"); ok {
   408  			subnetworkName = v.(string)
   409  		}
   410  
   411  		if networkName == "" && subnetworkName == "" {
   412  			return nil, fmt.Errorf("network or subnetwork must be provided")
   413  		}
   414  		if networkName != "" && subnetworkName != "" {
   415  			return nil, fmt.Errorf("network or subnetwork must not both be provided")
   416  		}
   417  
   418  		var networkLink, subnetworkLink string
   419  		if networkName != "" {
   420  			network, err := config.clientCompute.Networks.Get(
   421  				project, networkName).Do()
   422  			if err != nil {
   423  				return nil, fmt.Errorf("Error referencing network '%s': %s",
   424  					networkName, err)
   425  			}
   426  			networkLink = network.SelfLink
   427  		} else {
   428  			// lookup subnetwork link using region and subnetwork name
   429  			region, err := getRegion(d, config)
   430  			if err != nil {
   431  				return nil, err
   432  			}
   433  			subnetwork, err := config.clientCompute.Subnetworks.Get(
   434  				project, region, subnetworkName).Do()
   435  			if err != nil {
   436  				return nil, fmt.Errorf(
   437  					"Error referencing subnetwork '%s' in region '%s': %s",
   438  					subnetworkName, region, err)
   439  			}
   440  			subnetworkLink = subnetwork.SelfLink
   441  		}
   442  
   443  		// Build the networkInterface
   444  		var iface compute.NetworkInterface
   445  		iface.Network = networkLink
   446  		iface.Subnetwork = subnetworkLink
   447  
   448  		accessConfigsCount := d.Get(prefix + ".access_config.#").(int)
   449  		iface.AccessConfigs = make([]*compute.AccessConfig, accessConfigsCount)
   450  		for j := 0; j < accessConfigsCount; j++ {
   451  			acPrefix := fmt.Sprintf("%s.access_config.%d", prefix, j)
   452  			iface.AccessConfigs[j] = &compute.AccessConfig{
   453  				Type:  "ONE_TO_ONE_NAT",
   454  				NatIP: d.Get(acPrefix + ".nat_ip").(string),
   455  			}
   456  		}
   457  
   458  		networkInterfaces = append(networkInterfaces, &iface)
   459  	}
   460  	return networkInterfaces, nil
   461  }
   462  
   463  func resourceComputeInstanceTemplateCreate(d *schema.ResourceData, meta interface{}) error {
   464  	config := meta.(*Config)
   465  
   466  	project, err := getProject(d, config)
   467  	if err != nil {
   468  		return err
   469  	}
   470  
   471  	instanceProperties := &compute.InstanceProperties{}
   472  
   473  	instanceProperties.CanIpForward = d.Get("can_ip_forward").(bool)
   474  	instanceProperties.Description = d.Get("instance_description").(string)
   475  	instanceProperties.MachineType = d.Get("machine_type").(string)
   476  	disks, err := buildDisks(d, meta)
   477  	if err != nil {
   478  		return err
   479  	}
   480  	instanceProperties.Disks = disks
   481  	metadata, err := resourceInstanceMetadata(d)
   482  	if err != nil {
   483  		return err
   484  	}
   485  	instanceProperties.Metadata = metadata
   486  	networks, err := buildNetworks(d, meta)
   487  	if err != nil {
   488  		return err
   489  	}
   490  	instanceProperties.NetworkInterfaces = networks
   491  
   492  	instanceProperties.Scheduling = &compute.Scheduling{}
   493  	instanceProperties.Scheduling.OnHostMaintenance = "MIGRATE"
   494  
   495  	// Depreciated fields
   496  	if v, ok := d.GetOk("automatic_restart"); ok {
   497  		instanceProperties.Scheduling.AutomaticRestart = v.(bool)
   498  	}
   499  
   500  	if v, ok := d.GetOk("on_host_maintenance"); ok {
   501  		instanceProperties.Scheduling.OnHostMaintenance = v.(string)
   502  	}
   503  
   504  	forceSendFieldsScheduling := make([]string, 0, 3)
   505  	var hasSendMaintenance bool
   506  	hasSendMaintenance = false
   507  	if v, ok := d.GetOk("scheduling"); ok {
   508  		_schedulings := v.([]interface{})
   509  		if len(_schedulings) > 1 {
   510  			return fmt.Errorf("Error, at most one `scheduling` block can be defined")
   511  		}
   512  		_scheduling := _schedulings[0].(map[string]interface{})
   513  
   514  		if vp, okp := _scheduling["automatic_restart"]; okp {
   515  			instanceProperties.Scheduling.AutomaticRestart = vp.(bool)
   516  			forceSendFieldsScheduling = append(forceSendFieldsScheduling, "AutomaticRestart")
   517  		}
   518  
   519  		if vp, okp := _scheduling["on_host_maintenance"]; okp {
   520  			instanceProperties.Scheduling.OnHostMaintenance = vp.(string)
   521  			forceSendFieldsScheduling = append(forceSendFieldsScheduling, "OnHostMaintenance")
   522  			hasSendMaintenance = true
   523  		}
   524  
   525  		if vp, okp := _scheduling["preemptible"]; okp {
   526  			instanceProperties.Scheduling.Preemptible = vp.(bool)
   527  			forceSendFieldsScheduling = append(forceSendFieldsScheduling, "Preemptible")
   528  			if vp.(bool) && !hasSendMaintenance {
   529  				instanceProperties.Scheduling.OnHostMaintenance = "TERMINATE"
   530  				forceSendFieldsScheduling = append(forceSendFieldsScheduling, "OnHostMaintenance")
   531  			}
   532  		}
   533  	}
   534  	instanceProperties.Scheduling.ForceSendFields = forceSendFieldsScheduling
   535  
   536  	serviceAccountsCount := d.Get("service_account.#").(int)
   537  	serviceAccounts := make([]*compute.ServiceAccount, 0, serviceAccountsCount)
   538  	for i := 0; i < serviceAccountsCount; i++ {
   539  		prefix := fmt.Sprintf("service_account.%d", i)
   540  
   541  		scopesCount := d.Get(prefix + ".scopes.#").(int)
   542  		scopes := make([]string, 0, scopesCount)
   543  		for j := 0; j < scopesCount; j++ {
   544  			scope := d.Get(fmt.Sprintf(prefix+".scopes.%d", j)).(string)
   545  			scopes = append(scopes, canonicalizeServiceScope(scope))
   546  		}
   547  
   548  		email := "default"
   549  		if v := d.Get(prefix + ".email"); v != nil {
   550  			email = v.(string)
   551  		}
   552  
   553  		serviceAccount := &compute.ServiceAccount{
   554  			Email:  email,
   555  			Scopes: scopes,
   556  		}
   557  
   558  		serviceAccounts = append(serviceAccounts, serviceAccount)
   559  	}
   560  	instanceProperties.ServiceAccounts = serviceAccounts
   561  
   562  	instanceProperties.Tags = resourceInstanceTags(d)
   563  
   564  	var itName string
   565  	if v, ok := d.GetOk("name"); ok {
   566  		itName = v.(string)
   567  	} else if v, ok := d.GetOk("name_prefix"); ok {
   568  		itName = resource.PrefixedUniqueId(v.(string))
   569  	} else {
   570  		itName = resource.UniqueId()
   571  	}
   572  	instanceTemplate := compute.InstanceTemplate{
   573  		Description: d.Get("description").(string),
   574  		Properties:  instanceProperties,
   575  		Name:        itName,
   576  	}
   577  
   578  	op, err := config.clientCompute.InstanceTemplates.Insert(
   579  		project, &instanceTemplate).Do()
   580  	if err != nil {
   581  		return fmt.Errorf("Error creating instance: %s", err)
   582  	}
   583  
   584  	// Store the ID now
   585  	d.SetId(instanceTemplate.Name)
   586  
   587  	err = computeOperationWaitGlobal(config, op, project, "Creating Instance Template")
   588  	if err != nil {
   589  		return err
   590  	}
   591  
   592  	return resourceComputeInstanceTemplateRead(d, meta)
   593  }
   594  
   595  func flattenDisks(disks []*compute.AttachedDisk, d *schema.ResourceData) []map[string]interface{} {
   596  	result := make([]map[string]interface{}, 0, len(disks))
   597  	for i, disk := range disks {
   598  		diskMap := make(map[string]interface{})
   599  		if disk.InitializeParams != nil {
   600  			var source_img = fmt.Sprintf("disk.%d.source_image", i)
   601  			if d.Get(source_img) == nil || d.Get(source_img) == "" {
   602  				sourceImageUrl := strings.Split(disk.InitializeParams.SourceImage, "/")
   603  				diskMap["source_image"] = sourceImageUrl[len(sourceImageUrl)-1]
   604  			} else {
   605  				diskMap["source_image"] = d.Get(source_img)
   606  			}
   607  			diskMap["disk_type"] = disk.InitializeParams.DiskType
   608  			diskMap["disk_name"] = disk.InitializeParams.DiskName
   609  			diskMap["disk_size_gb"] = disk.InitializeParams.DiskSizeGb
   610  		}
   611  		diskMap["auto_delete"] = disk.AutoDelete
   612  		diskMap["boot"] = disk.Boot
   613  		diskMap["device_name"] = disk.DeviceName
   614  		diskMap["interface"] = disk.Interface
   615  		diskMap["source"] = disk.Source
   616  		diskMap["mode"] = disk.Mode
   617  		diskMap["type"] = disk.Type
   618  		result = append(result, diskMap)
   619  	}
   620  	return result
   621  }
   622  
   623  func flattenNetworkInterfaces(networkInterfaces []*compute.NetworkInterface) ([]map[string]interface{}, string) {
   624  	result := make([]map[string]interface{}, 0, len(networkInterfaces))
   625  	region := ""
   626  	for _, networkInterface := range networkInterfaces {
   627  		networkInterfaceMap := make(map[string]interface{})
   628  		if networkInterface.Network != "" {
   629  			networkUrl := strings.Split(networkInterface.Network, "/")
   630  			networkInterfaceMap["network"] = networkUrl[len(networkUrl)-1]
   631  		}
   632  		if networkInterface.Subnetwork != "" {
   633  			subnetworkUrl := strings.Split(networkInterface.Subnetwork, "/")
   634  			networkInterfaceMap["subnetwork"] = subnetworkUrl[len(subnetworkUrl)-1]
   635  			region = subnetworkUrl[len(subnetworkUrl)-3]
   636  		}
   637  
   638  		if networkInterface.AccessConfigs != nil {
   639  			accessConfigsMap := make([]map[string]interface{}, 0, len(networkInterface.AccessConfigs))
   640  			for _, accessConfig := range networkInterface.AccessConfigs {
   641  				accessConfigMap := make(map[string]interface{})
   642  				accessConfigMap["nat_ip"] = accessConfig.NatIP
   643  
   644  				accessConfigsMap = append(accessConfigsMap, accessConfigMap)
   645  			}
   646  			networkInterfaceMap["access_config"] = accessConfigsMap
   647  		}
   648  		result = append(result, networkInterfaceMap)
   649  	}
   650  	return result, region
   651  }
   652  
   653  func flattenScheduling(scheduling *compute.Scheduling) ([]map[string]interface{}, bool) {
   654  	result := make([]map[string]interface{}, 0, 1)
   655  	schedulingMap := make(map[string]interface{})
   656  	schedulingMap["automatic_restart"] = scheduling.AutomaticRestart
   657  	schedulingMap["on_host_maintenance"] = scheduling.OnHostMaintenance
   658  	schedulingMap["preemptible"] = scheduling.Preemptible
   659  	result = append(result, schedulingMap)
   660  	return result, scheduling.AutomaticRestart
   661  }
   662  
   663  func flattenServiceAccounts(serviceAccounts []*compute.ServiceAccount) []map[string]interface{} {
   664  	result := make([]map[string]interface{}, 0, len(serviceAccounts))
   665  	for _, serviceAccount := range serviceAccounts {
   666  		serviceAccountMap := make(map[string]interface{})
   667  		serviceAccountMap["email"] = serviceAccount.Email
   668  		serviceAccountMap["scopes"] = serviceAccount.Scopes
   669  
   670  		result = append(result, serviceAccountMap)
   671  	}
   672  	return result
   673  }
   674  
   675  func flattenMetadata(metadata *compute.Metadata) map[string]string {
   676  	metadataMap := make(map[string]string)
   677  	for _, item := range metadata.Items {
   678  		metadataMap[item.Key] = *item.Value
   679  	}
   680  	return metadataMap
   681  }
   682  
   683  func resourceComputeInstanceTemplateRead(d *schema.ResourceData, meta interface{}) error {
   684  	config := meta.(*Config)
   685  	project, err := getProject(d, config)
   686  	if err != nil {
   687  		return err
   688  	}
   689  
   690  	instanceTemplate, err := config.clientCompute.InstanceTemplates.Get(
   691  		project, d.Id()).Do()
   692  	if err != nil {
   693  		if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
   694  			log.Printf("[WARN] Removing Instance Template %q because it's gone", d.Get("name").(string))
   695  			// The resource doesn't exist anymore
   696  			d.SetId("")
   697  
   698  			return nil
   699  		}
   700  
   701  		return fmt.Errorf("Error reading instance template: %s", err)
   702  	}
   703  
   704  	// Set the metadata fingerprint if there is one.
   705  	if instanceTemplate.Properties.Metadata != nil {
   706  		d.Set("metadata_fingerprint", instanceTemplate.Properties.Metadata.Fingerprint)
   707  	}
   708  
   709  	// Set the tags fingerprint if there is one.
   710  	if instanceTemplate.Properties.Tags != nil {
   711  		d.Set("tags_fingerprint", instanceTemplate.Properties.Tags.Fingerprint)
   712  	}
   713  	d.Set("self_link", instanceTemplate.SelfLink)
   714  	d.Set("name", instanceTemplate.Name)
   715  	if instanceTemplate.Properties.Disks != nil {
   716  		d.Set("disk", flattenDisks(instanceTemplate.Properties.Disks, d))
   717  	}
   718  	d.Set("description", instanceTemplate.Description)
   719  	d.Set("machine_type", instanceTemplate.Properties.MachineType)
   720  	d.Set("can_ip_forward", instanceTemplate.Properties.CanIpForward)
   721  	if instanceTemplate.Properties.Metadata != nil {
   722  		d.Set("metadata", flattenMetadata(instanceTemplate.Properties.Metadata))
   723  	}
   724  	d.Set("instance_description", instanceTemplate.Properties.Description)
   725  	d.Set("project", project)
   726  	if instanceTemplate.Properties.NetworkInterfaces != nil {
   727  		networkInterfaces, region := flattenNetworkInterfaces(instanceTemplate.Properties.NetworkInterfaces)
   728  		d.Set("network_interface", networkInterfaces)
   729  		// region is where to look up the subnetwork if there is one attached to the instance template
   730  		if region != "" {
   731  			d.Set("region", region)
   732  		}
   733  	}
   734  	if instanceTemplate.Properties.Scheduling != nil {
   735  		scheduling, autoRestart := flattenScheduling(instanceTemplate.Properties.Scheduling)
   736  		d.Set("scheduling", scheduling)
   737  		d.Set("automatic_restart", autoRestart)
   738  	}
   739  	if instanceTemplate.Properties.Tags != nil {
   740  		d.Set("tags", instanceTemplate.Properties.Tags.Items)
   741  	}
   742  	if instanceTemplate.Properties.ServiceAccounts != nil {
   743  		d.Set("service_account", flattenServiceAccounts(instanceTemplate.Properties.ServiceAccounts))
   744  	}
   745  	return nil
   746  }
   747  
   748  func resourceComputeInstanceTemplateDelete(d *schema.ResourceData, meta interface{}) error {
   749  	config := meta.(*Config)
   750  
   751  	project, err := getProject(d, config)
   752  	if err != nil {
   753  		return err
   754  	}
   755  
   756  	op, err := config.clientCompute.InstanceTemplates.Delete(
   757  		project, d.Id()).Do()
   758  	if err != nil {
   759  		return fmt.Errorf("Error deleting instance template: %s", err)
   760  	}
   761  
   762  	err = computeOperationWaitGlobal(config, op, project, "Deleting Instance Template")
   763  	if err != nil {
   764  		return err
   765  	}
   766  
   767  	d.SetId("")
   768  	return nil
   769  }