github.com/meteor/terraform@v0.6.15-0.20210412225145-79ec4bc057c6/builtin/providers/google/resource_compute_instance_template.go (about)

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