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