github.com/minamijoyo/terraform@v0.7.8-0.20161029001309-18b3736ba44b/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  			networkLink, err = getNetworkLink(d, config, prefix+".network")
   421  			if err != nil {
   422  				return nil, fmt.Errorf("Error referencing network '%s': %s",
   423  					networkName, err)
   424  			}
   425  
   426  		} else {
   427  			// lookup subnetwork link using region and subnetwork name
   428  			region, err := getRegion(d, config)
   429  			if err != nil {
   430  				return nil, err
   431  			}
   432  			subnetwork, err := config.clientCompute.Subnetworks.Get(
   433  				project, region, subnetworkName).Do()
   434  			if err != nil {
   435  				return nil, fmt.Errorf(
   436  					"Error referencing subnetwork '%s' in region '%s': %s",
   437  					subnetworkName, region, err)
   438  			}
   439  			subnetworkLink = subnetwork.SelfLink
   440  		}
   441  
   442  		// Build the networkInterface
   443  		var iface compute.NetworkInterface
   444  		iface.Network = networkLink
   445  		iface.Subnetwork = subnetworkLink
   446  
   447  		accessConfigsCount := d.Get(prefix + ".access_config.#").(int)
   448  		iface.AccessConfigs = make([]*compute.AccessConfig, accessConfigsCount)
   449  		for j := 0; j < accessConfigsCount; j++ {
   450  			acPrefix := fmt.Sprintf("%s.access_config.%d", prefix, j)
   451  			iface.AccessConfigs[j] = &compute.AccessConfig{
   452  				Type:  "ONE_TO_ONE_NAT",
   453  				NatIP: d.Get(acPrefix + ".nat_ip").(string),
   454  			}
   455  		}
   456  
   457  		networkInterfaces = append(networkInterfaces, &iface)
   458  	}
   459  	return networkInterfaces, nil
   460  }
   461  
   462  func resourceComputeInstanceTemplateCreate(d *schema.ResourceData, meta interface{}) error {
   463  	config := meta.(*Config)
   464  
   465  	project, err := getProject(d, config)
   466  	if err != nil {
   467  		return err
   468  	}
   469  
   470  	instanceProperties := &compute.InstanceProperties{}
   471  
   472  	instanceProperties.CanIpForward = d.Get("can_ip_forward").(bool)
   473  	instanceProperties.Description = d.Get("instance_description").(string)
   474  	instanceProperties.MachineType = d.Get("machine_type").(string)
   475  	disks, err := buildDisks(d, meta)
   476  	if err != nil {
   477  		return err
   478  	}
   479  	instanceProperties.Disks = disks
   480  	metadata, err := resourceInstanceMetadata(d)
   481  	if err != nil {
   482  		return err
   483  	}
   484  	instanceProperties.Metadata = metadata
   485  	networks, err := buildNetworks(d, meta)
   486  	if err != nil {
   487  		return err
   488  	}
   489  	instanceProperties.NetworkInterfaces = networks
   490  
   491  	instanceProperties.Scheduling = &compute.Scheduling{}
   492  	instanceProperties.Scheduling.OnHostMaintenance = "MIGRATE"
   493  
   494  	// Depreciated fields
   495  	if v, ok := d.GetOk("automatic_restart"); ok {
   496  		instanceProperties.Scheduling.AutomaticRestart = v.(bool)
   497  	}
   498  
   499  	if v, ok := d.GetOk("on_host_maintenance"); ok {
   500  		instanceProperties.Scheduling.OnHostMaintenance = v.(string)
   501  	}
   502  
   503  	forceSendFieldsScheduling := make([]string, 0, 3)
   504  	var hasSendMaintenance bool
   505  	hasSendMaintenance = false
   506  	if v, ok := d.GetOk("scheduling"); ok {
   507  		_schedulings := v.([]interface{})
   508  		if len(_schedulings) > 1 {
   509  			return fmt.Errorf("Error, at most one `scheduling` block can be defined")
   510  		}
   511  		_scheduling := _schedulings[0].(map[string]interface{})
   512  
   513  		if vp, okp := _scheduling["automatic_restart"]; okp {
   514  			instanceProperties.Scheduling.AutomaticRestart = vp.(bool)
   515  			forceSendFieldsScheduling = append(forceSendFieldsScheduling, "AutomaticRestart")
   516  		}
   517  
   518  		if vp, okp := _scheduling["on_host_maintenance"]; okp {
   519  			instanceProperties.Scheduling.OnHostMaintenance = vp.(string)
   520  			forceSendFieldsScheduling = append(forceSendFieldsScheduling, "OnHostMaintenance")
   521  			hasSendMaintenance = true
   522  		}
   523  
   524  		if vp, okp := _scheduling["preemptible"]; okp {
   525  			instanceProperties.Scheduling.Preemptible = vp.(bool)
   526  			forceSendFieldsScheduling = append(forceSendFieldsScheduling, "Preemptible")
   527  			if vp.(bool) && !hasSendMaintenance {
   528  				instanceProperties.Scheduling.OnHostMaintenance = "TERMINATE"
   529  				forceSendFieldsScheduling = append(forceSendFieldsScheduling, "OnHostMaintenance")
   530  			}
   531  		}
   532  	}
   533  	instanceProperties.Scheduling.ForceSendFields = forceSendFieldsScheduling
   534  
   535  	serviceAccountsCount := d.Get("service_account.#").(int)
   536  	serviceAccounts := make([]*compute.ServiceAccount, 0, serviceAccountsCount)
   537  	for i := 0; i < serviceAccountsCount; i++ {
   538  		prefix := fmt.Sprintf("service_account.%d", i)
   539  
   540  		scopesCount := d.Get(prefix + ".scopes.#").(int)
   541  		scopes := make([]string, 0, scopesCount)
   542  		for j := 0; j < scopesCount; j++ {
   543  			scope := d.Get(fmt.Sprintf(prefix+".scopes.%d", j)).(string)
   544  			scopes = append(scopes, canonicalizeServiceScope(scope))
   545  		}
   546  
   547  		email := "default"
   548  		if v := d.Get(prefix + ".email"); v != nil {
   549  			email = v.(string)
   550  		}
   551  
   552  		serviceAccount := &compute.ServiceAccount{
   553  			Email:  email,
   554  			Scopes: scopes,
   555  		}
   556  
   557  		serviceAccounts = append(serviceAccounts, serviceAccount)
   558  	}
   559  	instanceProperties.ServiceAccounts = serviceAccounts
   560  
   561  	instanceProperties.Tags = resourceInstanceTags(d)
   562  
   563  	var itName string
   564  	if v, ok := d.GetOk("name"); ok {
   565  		itName = v.(string)
   566  	} else if v, ok := d.GetOk("name_prefix"); ok {
   567  		itName = resource.PrefixedUniqueId(v.(string))
   568  	} else {
   569  		itName = resource.UniqueId()
   570  	}
   571  	instanceTemplate := compute.InstanceTemplate{
   572  		Description: d.Get("description").(string),
   573  		Properties:  instanceProperties,
   574  		Name:        itName,
   575  	}
   576  
   577  	op, err := config.clientCompute.InstanceTemplates.Insert(
   578  		project, &instanceTemplate).Do()
   579  	if err != nil {
   580  		return fmt.Errorf("Error creating instance: %s", err)
   581  	}
   582  
   583  	// Store the ID now
   584  	d.SetId(instanceTemplate.Name)
   585  
   586  	err = computeOperationWaitGlobal(config, op, project, "Creating Instance Template")
   587  	if err != nil {
   588  		return err
   589  	}
   590  
   591  	return resourceComputeInstanceTemplateRead(d, meta)
   592  }
   593  
   594  func flattenDisks(disks []*compute.AttachedDisk, d *schema.ResourceData) []map[string]interface{} {
   595  	result := make([]map[string]interface{}, 0, len(disks))
   596  	for i, disk := range disks {
   597  		diskMap := make(map[string]interface{})
   598  		if disk.InitializeParams != nil {
   599  			var source_img = fmt.Sprintf("disk.%d.source_image", i)
   600  			if d.Get(source_img) == nil || d.Get(source_img) == "" {
   601  				sourceImageUrl := strings.Split(disk.InitializeParams.SourceImage, "/")
   602  				diskMap["source_image"] = sourceImageUrl[len(sourceImageUrl)-1]
   603  			} else {
   604  				diskMap["source_image"] = d.Get(source_img)
   605  			}
   606  			diskMap["disk_type"] = disk.InitializeParams.DiskType
   607  			diskMap["disk_name"] = disk.InitializeParams.DiskName
   608  			diskMap["disk_size_gb"] = disk.InitializeParams.DiskSizeGb
   609  		}
   610  		diskMap["auto_delete"] = disk.AutoDelete
   611  		diskMap["boot"] = disk.Boot
   612  		diskMap["device_name"] = disk.DeviceName
   613  		diskMap["interface"] = disk.Interface
   614  		diskMap["source"] = disk.Source
   615  		diskMap["mode"] = disk.Mode
   616  		diskMap["type"] = disk.Type
   617  		result = append(result, diskMap)
   618  	}
   619  	return result
   620  }
   621  
   622  func flattenNetworkInterfaces(networkInterfaces []*compute.NetworkInterface) ([]map[string]interface{}, string) {
   623  	result := make([]map[string]interface{}, 0, len(networkInterfaces))
   624  	region := ""
   625  	for _, networkInterface := range networkInterfaces {
   626  		networkInterfaceMap := make(map[string]interface{})
   627  		if networkInterface.Network != "" {
   628  			networkUrl := strings.Split(networkInterface.Network, "/")
   629  			networkInterfaceMap["network"] = networkUrl[len(networkUrl)-1]
   630  		}
   631  		if networkInterface.Subnetwork != "" {
   632  			subnetworkUrl := strings.Split(networkInterface.Subnetwork, "/")
   633  			networkInterfaceMap["subnetwork"] = subnetworkUrl[len(subnetworkUrl)-1]
   634  			region = subnetworkUrl[len(subnetworkUrl)-3]
   635  		}
   636  
   637  		if networkInterface.AccessConfigs != nil {
   638  			accessConfigsMap := make([]map[string]interface{}, 0, len(networkInterface.AccessConfigs))
   639  			for _, accessConfig := range networkInterface.AccessConfigs {
   640  				accessConfigMap := make(map[string]interface{})
   641  				accessConfigMap["nat_ip"] = accessConfig.NatIP
   642  
   643  				accessConfigsMap = append(accessConfigsMap, accessConfigMap)
   644  			}
   645  			networkInterfaceMap["access_config"] = accessConfigsMap
   646  		}
   647  		result = append(result, networkInterfaceMap)
   648  	}
   649  	return result, region
   650  }
   651  
   652  func flattenScheduling(scheduling *compute.Scheduling) ([]map[string]interface{}, bool) {
   653  	result := make([]map[string]interface{}, 0, 1)
   654  	schedulingMap := make(map[string]interface{})
   655  	schedulingMap["automatic_restart"] = scheduling.AutomaticRestart
   656  	schedulingMap["on_host_maintenance"] = scheduling.OnHostMaintenance
   657  	schedulingMap["preemptible"] = scheduling.Preemptible
   658  	result = append(result, schedulingMap)
   659  	return result, scheduling.AutomaticRestart
   660  }
   661  
   662  func flattenServiceAccounts(serviceAccounts []*compute.ServiceAccount) []map[string]interface{} {
   663  	result := make([]map[string]interface{}, 0, len(serviceAccounts))
   664  	for _, serviceAccount := range serviceAccounts {
   665  		serviceAccountMap := make(map[string]interface{})
   666  		serviceAccountMap["email"] = serviceAccount.Email
   667  		serviceAccountMap["scopes"] = serviceAccount.Scopes
   668  
   669  		result = append(result, serviceAccountMap)
   670  	}
   671  	return result
   672  }
   673  
   674  func flattenMetadata(metadata *compute.Metadata) map[string]string {
   675  	metadataMap := make(map[string]string)
   676  	for _, item := range metadata.Items {
   677  		metadataMap[item.Key] = *item.Value
   678  	}
   679  	return metadataMap
   680  }
   681  
   682  func resourceComputeInstanceTemplateRead(d *schema.ResourceData, meta interface{}) error {
   683  	config := meta.(*Config)
   684  	project, err := getProject(d, config)
   685  	if err != nil {
   686  		return err
   687  	}
   688  
   689  	instanceTemplate, err := config.clientCompute.InstanceTemplates.Get(
   690  		project, d.Id()).Do()
   691  	if err != nil {
   692  		if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
   693  			log.Printf("[WARN] Removing Instance Template %q because it's gone", d.Get("name").(string))
   694  			// The resource doesn't exist anymore
   695  			d.SetId("")
   696  
   697  			return nil
   698  		}
   699  
   700  		return fmt.Errorf("Error reading instance template: %s", err)
   701  	}
   702  
   703  	// Set the metadata fingerprint if there is one.
   704  	if instanceTemplate.Properties.Metadata != nil {
   705  		d.Set("metadata_fingerprint", instanceTemplate.Properties.Metadata.Fingerprint)
   706  	}
   707  
   708  	// Set the tags fingerprint if there is one.
   709  	if instanceTemplate.Properties.Tags != nil {
   710  		d.Set("tags_fingerprint", instanceTemplate.Properties.Tags.Fingerprint)
   711  	}
   712  	d.Set("self_link", instanceTemplate.SelfLink)
   713  	d.Set("name", instanceTemplate.Name)
   714  	if instanceTemplate.Properties.Disks != nil {
   715  		d.Set("disk", flattenDisks(instanceTemplate.Properties.Disks, d))
   716  	}
   717  	d.Set("description", instanceTemplate.Description)
   718  	d.Set("machine_type", instanceTemplate.Properties.MachineType)
   719  	d.Set("can_ip_forward", instanceTemplate.Properties.CanIpForward)
   720  	if instanceTemplate.Properties.Metadata != nil {
   721  		d.Set("metadata", flattenMetadata(instanceTemplate.Properties.Metadata))
   722  	}
   723  	d.Set("instance_description", instanceTemplate.Properties.Description)
   724  	d.Set("project", project)
   725  	if instanceTemplate.Properties.NetworkInterfaces != nil {
   726  		networkInterfaces, region := flattenNetworkInterfaces(instanceTemplate.Properties.NetworkInterfaces)
   727  		d.Set("network_interface", networkInterfaces)
   728  		// region is where to look up the subnetwork if there is one attached to the instance template
   729  		if region != "" {
   730  			d.Set("region", region)
   731  		}
   732  	}
   733  	if instanceTemplate.Properties.Scheduling != nil {
   734  		scheduling, autoRestart := flattenScheduling(instanceTemplate.Properties.Scheduling)
   735  		d.Set("scheduling", scheduling)
   736  		d.Set("automatic_restart", autoRestart)
   737  	}
   738  	if instanceTemplate.Properties.Tags != nil {
   739  		d.Set("tags", instanceTemplate.Properties.Tags.Items)
   740  	}
   741  	if instanceTemplate.Properties.ServiceAccounts != nil {
   742  		d.Set("service_account", flattenServiceAccounts(instanceTemplate.Properties.ServiceAccounts))
   743  	}
   744  	return nil
   745  }
   746  
   747  func resourceComputeInstanceTemplateDelete(d *schema.ResourceData, meta interface{}) error {
   748  	config := meta.(*Config)
   749  
   750  	project, err := getProject(d, config)
   751  	if err != nil {
   752  		return err
   753  	}
   754  
   755  	op, err := config.clientCompute.InstanceTemplates.Delete(
   756  		project, d.Id()).Do()
   757  	if err != nil {
   758  		return fmt.Errorf("Error deleting instance template: %s", err)
   759  	}
   760  
   761  	err = computeOperationWaitGlobal(config, op, project, "Deleting Instance Template")
   762  	if err != nil {
   763  		return err
   764  	}
   765  
   766  	d.SetId("")
   767  	return nil
   768  }