github.com/aspring/terraform@v0.8.2-0.20161216122603-6a8619a5db2e/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_startup_script": &schema.Schema{
   177  				Type:     schema.TypeString,
   178  				Optional: true,
   179  				ForceNew: true,
   180  			},
   181  
   182  			"metadata_fingerprint": &schema.Schema{
   183  				Type:     schema.TypeString,
   184  				Computed: true,
   185  			},
   186  
   187  			"network_interface": &schema.Schema{
   188  				Type:     schema.TypeList,
   189  				Optional: true,
   190  				ForceNew: true,
   191  				Elem: &schema.Resource{
   192  					Schema: map[string]*schema.Schema{
   193  						"network": &schema.Schema{
   194  							Type:     schema.TypeString,
   195  							Optional: true,
   196  							ForceNew: true,
   197  							Computed: true,
   198  						},
   199  
   200  						"subnetwork": &schema.Schema{
   201  							Type:     schema.TypeString,
   202  							Optional: true,
   203  							ForceNew: true,
   204  						},
   205  
   206  						"access_config": &schema.Schema{
   207  							Type:     schema.TypeList,
   208  							Optional: true,
   209  							Elem: &schema.Resource{
   210  								Schema: map[string]*schema.Schema{
   211  									"nat_ip": &schema.Schema{
   212  										Type:     schema.TypeString,
   213  										Computed: true,
   214  										Optional: true,
   215  									},
   216  								},
   217  							},
   218  						},
   219  					},
   220  				},
   221  			},
   222  
   223  			"on_host_maintenance": &schema.Schema{
   224  				Type:       schema.TypeString,
   225  				Optional:   true,
   226  				ForceNew:   true,
   227  				Deprecated: "Please use `scheduling.on_host_maintenance` instead",
   228  			},
   229  
   230  			"project": &schema.Schema{
   231  				Type:     schema.TypeString,
   232  				Optional: true,
   233  				ForceNew: true,
   234  				Computed: true,
   235  			},
   236  
   237  			"region": &schema.Schema{
   238  				Type:     schema.TypeString,
   239  				Optional: true,
   240  				ForceNew: true,
   241  			},
   242  
   243  			"scheduling": &schema.Schema{
   244  				Type:     schema.TypeList,
   245  				Optional: true,
   246  				Computed: true,
   247  				ForceNew: true,
   248  				Elem: &schema.Resource{
   249  					Schema: map[string]*schema.Schema{
   250  						"preemptible": &schema.Schema{
   251  							Type:     schema.TypeBool,
   252  							Optional: true,
   253  							Default:  false,
   254  							ForceNew: true,
   255  						},
   256  
   257  						"automatic_restart": &schema.Schema{
   258  							Type:     schema.TypeBool,
   259  							Optional: true,
   260  							Default:  true,
   261  							ForceNew: true,
   262  						},
   263  
   264  						"on_host_maintenance": &schema.Schema{
   265  							Type:     schema.TypeString,
   266  							Optional: true,
   267  							Computed: true,
   268  							ForceNew: true,
   269  						},
   270  					},
   271  				},
   272  			},
   273  
   274  			"self_link": &schema.Schema{
   275  				Type:     schema.TypeString,
   276  				Computed: true,
   277  			},
   278  
   279  			"service_account": &schema.Schema{
   280  				Type:     schema.TypeList,
   281  				MaxItems: 1,
   282  				Optional: true,
   283  				ForceNew: true,
   284  				Elem: &schema.Resource{
   285  					Schema: map[string]*schema.Schema{
   286  						"email": &schema.Schema{
   287  							Type:     schema.TypeString,
   288  							Optional: true,
   289  							Computed: true,
   290  							ForceNew: true,
   291  						},
   292  
   293  						"scopes": &schema.Schema{
   294  							Type:     schema.TypeList,
   295  							Required: true,
   296  							ForceNew: true,
   297  							Elem: &schema.Schema{
   298  								Type: schema.TypeString,
   299  								StateFunc: func(v interface{}) string {
   300  									return canonicalizeServiceScope(v.(string))
   301  								},
   302  							},
   303  						},
   304  					},
   305  				},
   306  			},
   307  
   308  			"tags": &schema.Schema{
   309  				Type:     schema.TypeSet,
   310  				Optional: true,
   311  				ForceNew: true,
   312  				Elem:     &schema.Schema{Type: schema.TypeString},
   313  				Set:      schema.HashString,
   314  			},
   315  
   316  			"tags_fingerprint": &schema.Schema{
   317  				Type:     schema.TypeString,
   318  				Computed: true,
   319  			},
   320  		},
   321  	}
   322  }
   323  
   324  func buildDisks(d *schema.ResourceData, meta interface{}) ([]*compute.AttachedDisk, error) {
   325  	config := meta.(*Config)
   326  
   327  	disksCount := d.Get("disk.#").(int)
   328  
   329  	disks := make([]*compute.AttachedDisk, 0, disksCount)
   330  	for i := 0; i < disksCount; i++ {
   331  		prefix := fmt.Sprintf("disk.%d", i)
   332  
   333  		// Build the disk
   334  		var disk compute.AttachedDisk
   335  		disk.Type = "PERSISTENT"
   336  		disk.Mode = "READ_WRITE"
   337  		disk.Interface = "SCSI"
   338  		disk.Boot = i == 0
   339  		disk.AutoDelete = d.Get(prefix + ".auto_delete").(bool)
   340  
   341  		if v, ok := d.GetOk(prefix + ".boot"); ok {
   342  			disk.Boot = v.(bool)
   343  		}
   344  
   345  		if v, ok := d.GetOk(prefix + ".device_name"); ok {
   346  			disk.DeviceName = v.(string)
   347  		}
   348  
   349  		if v, ok := d.GetOk(prefix + ".source"); ok {
   350  			disk.Source = v.(string)
   351  		} else {
   352  			disk.InitializeParams = &compute.AttachedDiskInitializeParams{}
   353  
   354  			if v, ok := d.GetOk(prefix + ".disk_name"); ok {
   355  				disk.InitializeParams.DiskName = v.(string)
   356  			}
   357  			if v, ok := d.GetOk(prefix + ".disk_size_gb"); ok {
   358  				disk.InitializeParams.DiskSizeGb = int64(v.(int))
   359  			}
   360  			disk.InitializeParams.DiskType = "pd-standard"
   361  			if v, ok := d.GetOk(prefix + ".disk_type"); ok {
   362  				disk.InitializeParams.DiskType = v.(string)
   363  			}
   364  
   365  			if v, ok := d.GetOk(prefix + ".source_image"); ok {
   366  				imageName := v.(string)
   367  				imageUrl, err := resolveImage(config, imageName)
   368  				if err != nil {
   369  					return nil, fmt.Errorf(
   370  						"Error resolving image name '%s': %s",
   371  						imageName, err)
   372  				}
   373  				disk.InitializeParams.SourceImage = imageUrl
   374  			}
   375  		}
   376  
   377  		if v, ok := d.GetOk(prefix + ".interface"); ok {
   378  			disk.Interface = v.(string)
   379  		}
   380  
   381  		if v, ok := d.GetOk(prefix + ".mode"); ok {
   382  			disk.Mode = v.(string)
   383  		}
   384  
   385  		if v, ok := d.GetOk(prefix + ".type"); ok {
   386  			disk.Type = v.(string)
   387  		}
   388  
   389  		disks = append(disks, &disk)
   390  	}
   391  
   392  	return disks, nil
   393  }
   394  
   395  func buildNetworks(d *schema.ResourceData, meta interface{}) ([]*compute.NetworkInterface, error) {
   396  	// Build up the list of networks
   397  	config := meta.(*Config)
   398  
   399  	project, err := getProject(d, config)
   400  	if err != nil {
   401  		return nil, err
   402  	}
   403  
   404  	networksCount := d.Get("network_interface.#").(int)
   405  	networkInterfaces := make([]*compute.NetworkInterface, 0, networksCount)
   406  	for i := 0; i < networksCount; i++ {
   407  		prefix := fmt.Sprintf("network_interface.%d", i)
   408  
   409  		var networkName, subnetworkName string
   410  		if v, ok := d.GetOk(prefix + ".network"); ok {
   411  			networkName = v.(string)
   412  		}
   413  		if v, ok := d.GetOk(prefix + ".subnetwork"); ok {
   414  			subnetworkName = v.(string)
   415  		}
   416  
   417  		if networkName == "" && subnetworkName == "" {
   418  			return nil, fmt.Errorf("network or subnetwork must be provided")
   419  		}
   420  		if networkName != "" && subnetworkName != "" {
   421  			return nil, fmt.Errorf("network or subnetwork must not both be provided")
   422  		}
   423  
   424  		var networkLink, subnetworkLink string
   425  		if networkName != "" {
   426  			networkLink, err = getNetworkLink(d, config, prefix+".network")
   427  			if err != nil {
   428  				return nil, fmt.Errorf("Error referencing network '%s': %s",
   429  					networkName, err)
   430  			}
   431  
   432  		} else {
   433  			// lookup subnetwork link using region and subnetwork name
   434  			region, err := getRegion(d, config)
   435  			if err != nil {
   436  				return nil, err
   437  			}
   438  			subnetwork, err := config.clientCompute.Subnetworks.Get(
   439  				project, region, subnetworkName).Do()
   440  			if err != nil {
   441  				return nil, fmt.Errorf(
   442  					"Error referencing subnetwork '%s' in region '%s': %s",
   443  					subnetworkName, region, err)
   444  			}
   445  			subnetworkLink = subnetwork.SelfLink
   446  		}
   447  
   448  		// Build the networkInterface
   449  		var iface compute.NetworkInterface
   450  		iface.Network = networkLink
   451  		iface.Subnetwork = subnetworkLink
   452  
   453  		accessConfigsCount := d.Get(prefix + ".access_config.#").(int)
   454  		iface.AccessConfigs = make([]*compute.AccessConfig, accessConfigsCount)
   455  		for j := 0; j < accessConfigsCount; j++ {
   456  			acPrefix := fmt.Sprintf("%s.access_config.%d", prefix, j)
   457  			iface.AccessConfigs[j] = &compute.AccessConfig{
   458  				Type:  "ONE_TO_ONE_NAT",
   459  				NatIP: d.Get(acPrefix + ".nat_ip").(string),
   460  			}
   461  		}
   462  
   463  		networkInterfaces = append(networkInterfaces, &iface)
   464  	}
   465  	return networkInterfaces, nil
   466  }
   467  
   468  func resourceComputeInstanceTemplateCreate(d *schema.ResourceData, meta interface{}) error {
   469  	config := meta.(*Config)
   470  
   471  	project, err := getProject(d, config)
   472  	if err != nil {
   473  		return err
   474  	}
   475  
   476  	instanceProperties := &compute.InstanceProperties{}
   477  
   478  	instanceProperties.CanIpForward = d.Get("can_ip_forward").(bool)
   479  	instanceProperties.Description = d.Get("instance_description").(string)
   480  	instanceProperties.MachineType = d.Get("machine_type").(string)
   481  	disks, err := buildDisks(d, meta)
   482  	if err != nil {
   483  		return err
   484  	}
   485  	instanceProperties.Disks = disks
   486  
   487  	metadata, err := resourceInstanceMetadata(d)
   488  	if err != nil {
   489  		return err
   490  	}
   491  	instanceProperties.Metadata = metadata
   492  	networks, err := buildNetworks(d, meta)
   493  	if err != nil {
   494  		return err
   495  	}
   496  	instanceProperties.NetworkInterfaces = networks
   497  
   498  	instanceProperties.Scheduling = &compute.Scheduling{}
   499  	instanceProperties.Scheduling.OnHostMaintenance = "MIGRATE"
   500  
   501  	// Depreciated fields
   502  	if v, ok := d.GetOk("automatic_restart"); ok {
   503  		instanceProperties.Scheduling.AutomaticRestart = v.(bool)
   504  	}
   505  
   506  	if v, ok := d.GetOk("on_host_maintenance"); ok {
   507  		instanceProperties.Scheduling.OnHostMaintenance = v.(string)
   508  	}
   509  
   510  	forceSendFieldsScheduling := make([]string, 0, 3)
   511  	var hasSendMaintenance bool
   512  	hasSendMaintenance = false
   513  	if v, ok := d.GetOk("scheduling"); ok {
   514  		_schedulings := v.([]interface{})
   515  		if len(_schedulings) > 1 {
   516  			return fmt.Errorf("Error, at most one `scheduling` block can be defined")
   517  		}
   518  		_scheduling := _schedulings[0].(map[string]interface{})
   519  
   520  		if vp, okp := _scheduling["automatic_restart"]; okp {
   521  			instanceProperties.Scheduling.AutomaticRestart = vp.(bool)
   522  			forceSendFieldsScheduling = append(forceSendFieldsScheduling, "AutomaticRestart")
   523  		}
   524  
   525  		if vp, okp := _scheduling["on_host_maintenance"]; okp {
   526  			instanceProperties.Scheduling.OnHostMaintenance = vp.(string)
   527  			forceSendFieldsScheduling = append(forceSendFieldsScheduling, "OnHostMaintenance")
   528  			hasSendMaintenance = true
   529  		}
   530  
   531  		if vp, okp := _scheduling["preemptible"]; okp {
   532  			instanceProperties.Scheduling.Preemptible = vp.(bool)
   533  			forceSendFieldsScheduling = append(forceSendFieldsScheduling, "Preemptible")
   534  			if vp.(bool) && !hasSendMaintenance {
   535  				instanceProperties.Scheduling.OnHostMaintenance = "TERMINATE"
   536  				forceSendFieldsScheduling = append(forceSendFieldsScheduling, "OnHostMaintenance")
   537  			}
   538  		}
   539  	}
   540  	instanceProperties.Scheduling.ForceSendFields = forceSendFieldsScheduling
   541  
   542  	serviceAccountsCount := d.Get("service_account.#").(int)
   543  	serviceAccounts := make([]*compute.ServiceAccount, 0, serviceAccountsCount)
   544  	for i := 0; i < serviceAccountsCount; i++ {
   545  		prefix := fmt.Sprintf("service_account.%d", i)
   546  
   547  		scopesCount := d.Get(prefix + ".scopes.#").(int)
   548  		scopes := make([]string, 0, scopesCount)
   549  		for j := 0; j < scopesCount; j++ {
   550  			scope := d.Get(fmt.Sprintf(prefix+".scopes.%d", j)).(string)
   551  			scopes = append(scopes, canonicalizeServiceScope(scope))
   552  		}
   553  
   554  		email := "default"
   555  		if v := d.Get(prefix + ".email"); v != nil {
   556  			email = v.(string)
   557  		}
   558  
   559  		serviceAccount := &compute.ServiceAccount{
   560  			Email:  email,
   561  			Scopes: scopes,
   562  		}
   563  
   564  		serviceAccounts = append(serviceAccounts, serviceAccount)
   565  	}
   566  	instanceProperties.ServiceAccounts = serviceAccounts
   567  
   568  	instanceProperties.Tags = resourceInstanceTags(d)
   569  
   570  	var itName string
   571  	if v, ok := d.GetOk("name"); ok {
   572  		itName = v.(string)
   573  	} else if v, ok := d.GetOk("name_prefix"); ok {
   574  		itName = resource.PrefixedUniqueId(v.(string))
   575  	} else {
   576  		itName = resource.UniqueId()
   577  	}
   578  	instanceTemplate := compute.InstanceTemplate{
   579  		Description: d.Get("description").(string),
   580  		Properties:  instanceProperties,
   581  		Name:        itName,
   582  	}
   583  
   584  	op, err := config.clientCompute.InstanceTemplates.Insert(
   585  		project, &instanceTemplate).Do()
   586  	if err != nil {
   587  		return fmt.Errorf("Error creating instance: %s", err)
   588  	}
   589  
   590  	// Store the ID now
   591  	d.SetId(instanceTemplate.Name)
   592  
   593  	err = computeOperationWaitGlobal(config, op, project, "Creating Instance Template")
   594  	if err != nil {
   595  		return err
   596  	}
   597  
   598  	return resourceComputeInstanceTemplateRead(d, meta)
   599  }
   600  
   601  func flattenDisks(disks []*compute.AttachedDisk, d *schema.ResourceData) []map[string]interface{} {
   602  	result := make([]map[string]interface{}, 0, len(disks))
   603  	for i, disk := range disks {
   604  		diskMap := make(map[string]interface{})
   605  		if disk.InitializeParams != nil {
   606  			var source_img = fmt.Sprintf("disk.%d.source_image", i)
   607  			if d.Get(source_img) == nil || d.Get(source_img) == "" {
   608  				sourceImageUrl := strings.Split(disk.InitializeParams.SourceImage, "/")
   609  				diskMap["source_image"] = sourceImageUrl[len(sourceImageUrl)-1]
   610  			} else {
   611  				diskMap["source_image"] = d.Get(source_img)
   612  			}
   613  			diskMap["disk_type"] = disk.InitializeParams.DiskType
   614  			diskMap["disk_name"] = disk.InitializeParams.DiskName
   615  			diskMap["disk_size_gb"] = disk.InitializeParams.DiskSizeGb
   616  		}
   617  		diskMap["auto_delete"] = disk.AutoDelete
   618  		diskMap["boot"] = disk.Boot
   619  		diskMap["device_name"] = disk.DeviceName
   620  		diskMap["interface"] = disk.Interface
   621  		diskMap["source"] = disk.Source
   622  		diskMap["mode"] = disk.Mode
   623  		diskMap["type"] = disk.Type
   624  		result = append(result, diskMap)
   625  	}
   626  	return result
   627  }
   628  
   629  func flattenNetworkInterfaces(networkInterfaces []*compute.NetworkInterface) ([]map[string]interface{}, string) {
   630  	result := make([]map[string]interface{}, 0, len(networkInterfaces))
   631  	region := ""
   632  	for _, networkInterface := range networkInterfaces {
   633  		networkInterfaceMap := make(map[string]interface{})
   634  		if networkInterface.Network != "" {
   635  			networkUrl := strings.Split(networkInterface.Network, "/")
   636  			networkInterfaceMap["network"] = networkUrl[len(networkUrl)-1]
   637  		}
   638  		if networkInterface.Subnetwork != "" {
   639  			subnetworkUrl := strings.Split(networkInterface.Subnetwork, "/")
   640  			networkInterfaceMap["subnetwork"] = subnetworkUrl[len(subnetworkUrl)-1]
   641  			region = subnetworkUrl[len(subnetworkUrl)-3]
   642  		}
   643  
   644  		if networkInterface.AccessConfigs != nil {
   645  			accessConfigsMap := make([]map[string]interface{}, 0, len(networkInterface.AccessConfigs))
   646  			for _, accessConfig := range networkInterface.AccessConfigs {
   647  				accessConfigMap := make(map[string]interface{})
   648  				accessConfigMap["nat_ip"] = accessConfig.NatIP
   649  
   650  				accessConfigsMap = append(accessConfigsMap, accessConfigMap)
   651  			}
   652  			networkInterfaceMap["access_config"] = accessConfigsMap
   653  		}
   654  		result = append(result, networkInterfaceMap)
   655  	}
   656  	return result, region
   657  }
   658  
   659  func flattenScheduling(scheduling *compute.Scheduling) ([]map[string]interface{}, bool) {
   660  	result := make([]map[string]interface{}, 0, 1)
   661  	schedulingMap := make(map[string]interface{})
   662  	schedulingMap["automatic_restart"] = scheduling.AutomaticRestart
   663  	schedulingMap["on_host_maintenance"] = scheduling.OnHostMaintenance
   664  	schedulingMap["preemptible"] = scheduling.Preemptible
   665  	result = append(result, schedulingMap)
   666  	return result, scheduling.AutomaticRestart
   667  }
   668  
   669  func flattenServiceAccounts(serviceAccounts []*compute.ServiceAccount) []map[string]interface{} {
   670  	result := make([]map[string]interface{}, 0, len(serviceAccounts))
   671  	for _, serviceAccount := range serviceAccounts {
   672  		serviceAccountMap := make(map[string]interface{})
   673  		serviceAccountMap["email"] = serviceAccount.Email
   674  		serviceAccountMap["scopes"] = serviceAccount.Scopes
   675  
   676  		result = append(result, serviceAccountMap)
   677  	}
   678  	return result
   679  }
   680  
   681  func flattenMetadata(metadata *compute.Metadata) map[string]string {
   682  	metadataMap := make(map[string]string)
   683  	for _, item := range metadata.Items {
   684  		metadataMap[item.Key] = *item.Value
   685  	}
   686  	return metadataMap
   687  }
   688  
   689  func resourceComputeInstanceTemplateRead(d *schema.ResourceData, meta interface{}) error {
   690  	config := meta.(*Config)
   691  	project, err := getProject(d, config)
   692  	if err != nil {
   693  		return err
   694  	}
   695  
   696  	instanceTemplate, err := config.clientCompute.InstanceTemplates.Get(
   697  		project, d.Id()).Do()
   698  	if err != nil {
   699  		if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
   700  			log.Printf("[WARN] Removing Instance Template %q because it's gone", d.Get("name").(string))
   701  			// The resource doesn't exist anymore
   702  			d.SetId("")
   703  			return nil
   704  		}
   705  
   706  		return fmt.Errorf("Error reading instance template: %s", err)
   707  	}
   708  
   709  	// Set the metadata fingerprint if there is one.
   710  	if instanceTemplate.Properties.Metadata != nil {
   711  		if err = d.Set("metadata_fingerprint", instanceTemplate.Properties.Metadata.Fingerprint); err != nil {
   712  			return fmt.Errorf("Error setting metadata_fingerprint: %s", err)
   713  		}
   714  
   715  		md := instanceTemplate.Properties.Metadata
   716  
   717  		_md := flattenMetadata(md)
   718  
   719  		if script, scriptExists := d.GetOk("metadata_startup_script"); scriptExists {
   720  			if err = d.Set("metadata_startup_script", script); err != nil {
   721  				return fmt.Errorf("Error setting metadata_startup_script: %s", err)
   722  			}
   723  			delete(_md, "startup-script")
   724  		}
   725  		if err = d.Set("metadata", _md); err != nil {
   726  			return fmt.Errorf("Error setting metadata: %s", err)
   727  		}
   728  	}
   729  
   730  	// Set the tags fingerprint if there is one.
   731  	if instanceTemplate.Properties.Tags != nil {
   732  		if err = d.Set("tags_fingerprint", instanceTemplate.Properties.Tags.Fingerprint); err != nil {
   733  			return fmt.Errorf("Error setting tags_fingerprint: %s", err)
   734  		}
   735  	}
   736  	if err = d.Set("self_link", instanceTemplate.SelfLink); err != nil {
   737  		return fmt.Errorf("Error setting self_link: %s", err)
   738  	}
   739  	if err = d.Set("name", instanceTemplate.Name); err != nil {
   740  		return fmt.Errorf("Error setting name: %s", err)
   741  	}
   742  	if instanceTemplate.Properties.Disks != nil {
   743  		if err = d.Set("disk", flattenDisks(instanceTemplate.Properties.Disks, d)); err != nil {
   744  			return fmt.Errorf("Error setting disk: %s", err)
   745  		}
   746  	}
   747  	if err = d.Set("description", instanceTemplate.Description); err != nil {
   748  		return fmt.Errorf("Error setting description: %s", err)
   749  	}
   750  	if err = d.Set("machine_type", instanceTemplate.Properties.MachineType); err != nil {
   751  		return fmt.Errorf("Error setting machine_type: %s", err)
   752  	}
   753  
   754  	if err = d.Set("can_ip_forward", instanceTemplate.Properties.CanIpForward); err != nil {
   755  		return fmt.Errorf("Error setting can_ip_forward: %s", err)
   756  	}
   757  
   758  	if err = d.Set("instance_description", instanceTemplate.Properties.Description); err != nil {
   759  		return fmt.Errorf("Error setting instance_description: %s", err)
   760  	}
   761  	if err = d.Set("project", project); err != nil {
   762  		return fmt.Errorf("Error setting project: %s", err)
   763  	}
   764  	if instanceTemplate.Properties.NetworkInterfaces != nil {
   765  		networkInterfaces, region := flattenNetworkInterfaces(instanceTemplate.Properties.NetworkInterfaces)
   766  		if err = d.Set("network_interface", networkInterfaces); err != nil {
   767  			return fmt.Errorf("Error setting network_interface: %s", err)
   768  		}
   769  		// region is where to look up the subnetwork if there is one attached to the instance template
   770  		if region != "" {
   771  			if err = d.Set("region", region); err != nil {
   772  				return fmt.Errorf("Error setting region: %s", err)
   773  			}
   774  		}
   775  	}
   776  	if instanceTemplate.Properties.Scheduling != nil {
   777  		scheduling, autoRestart := flattenScheduling(instanceTemplate.Properties.Scheduling)
   778  		if err = d.Set("scheduling", scheduling); err != nil {
   779  			return fmt.Errorf("Error setting scheduling: %s", err)
   780  		}
   781  		if err = d.Set("automatic_restart", autoRestart); err != nil {
   782  			return fmt.Errorf("Error setting automatic_restart: %s", err)
   783  		}
   784  	}
   785  	if instanceTemplate.Properties.Tags != nil {
   786  		if err = d.Set("tags", instanceTemplate.Properties.Tags.Items); err != nil {
   787  			return fmt.Errorf("Error setting tags: %s", err)
   788  		}
   789  	}
   790  	if instanceTemplate.Properties.ServiceAccounts != nil {
   791  		if err = d.Set("service_account", flattenServiceAccounts(instanceTemplate.Properties.ServiceAccounts)); err != nil {
   792  			return fmt.Errorf("Error setting service_account: %s", err)
   793  		}
   794  	}
   795  	return nil
   796  }
   797  
   798  func resourceComputeInstanceTemplateDelete(d *schema.ResourceData, meta interface{}) error {
   799  	config := meta.(*Config)
   800  
   801  	project, err := getProject(d, config)
   802  	if err != nil {
   803  		return err
   804  	}
   805  
   806  	op, err := config.clientCompute.InstanceTemplates.Delete(
   807  		project, d.Id()).Do()
   808  	if err != nil {
   809  		return fmt.Errorf("Error deleting instance template: %s", err)
   810  	}
   811  
   812  	err = computeOperationWaitGlobal(config, op, project, "Deleting Instance Template")
   813  	if err != nil {
   814  		return err
   815  	}
   816  
   817  	d.SetId("")
   818  	return nil
   819  }