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