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