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