github.com/dougneal/terraform@v0.6.15-0.20170330092735-b6a3840768a4/builtin/providers/azurerm/resource_arm_virtual_machine.go (about) 1 package azurerm 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "net/http" 8 "net/url" 9 "strings" 10 11 "github.com/Azure/azure-sdk-for-go/arm/compute" 12 "github.com/hashicorp/terraform/helper/hashcode" 13 "github.com/hashicorp/terraform/helper/schema" 14 riviera "github.com/jen20/riviera/azure" 15 ) 16 17 func resourceArmVirtualMachine() *schema.Resource { 18 return &schema.Resource{ 19 Create: resourceArmVirtualMachineCreate, 20 Read: resourceArmVirtualMachineRead, 21 Update: resourceArmVirtualMachineCreate, 22 Delete: resourceArmVirtualMachineDelete, 23 Importer: &schema.ResourceImporter{ 24 State: schema.ImportStatePassthrough, 25 }, 26 27 Schema: map[string]*schema.Schema{ 28 "name": { 29 Type: schema.TypeString, 30 Required: true, 31 ForceNew: true, 32 }, 33 34 "location": locationSchema(), 35 36 "resource_group_name": { 37 Type: schema.TypeString, 38 Required: true, 39 ForceNew: true, 40 }, 41 42 "plan": { 43 Type: schema.TypeSet, 44 Optional: true, 45 MaxItems: 1, 46 Elem: &schema.Resource{ 47 Schema: map[string]*schema.Schema{ 48 "name": { 49 Type: schema.TypeString, 50 Required: true, 51 }, 52 53 "publisher": { 54 Type: schema.TypeString, 55 Required: true, 56 }, 57 58 "product": { 59 Type: schema.TypeString, 60 Required: true, 61 }, 62 }, 63 }, 64 Set: resourceArmVirtualMachinePlanHash, 65 }, 66 67 "availability_set_id": { 68 Type: schema.TypeString, 69 Optional: true, 70 Computed: true, 71 ForceNew: true, 72 StateFunc: func(id interface{}) string { 73 return strings.ToLower(id.(string)) 74 }, 75 }, 76 77 "license_type": { 78 Type: schema.TypeString, 79 Optional: true, 80 Computed: true, 81 ValidateFunc: validateLicenseType, 82 }, 83 84 "vm_size": { 85 Type: schema.TypeString, 86 Required: true, 87 }, 88 89 "storage_image_reference": { 90 Type: schema.TypeSet, 91 Optional: true, 92 Computed: true, 93 ForceNew: true, 94 MaxItems: 1, 95 Elem: &schema.Resource{ 96 Schema: map[string]*schema.Schema{ 97 "publisher": { 98 Type: schema.TypeString, 99 Required: true, 100 ForceNew: true, 101 }, 102 103 "offer": { 104 Type: schema.TypeString, 105 Required: true, 106 ForceNew: true, 107 }, 108 109 "sku": { 110 Type: schema.TypeString, 111 Required: true, 112 ForceNew: true, 113 }, 114 115 "version": { 116 Type: schema.TypeString, 117 Optional: true, 118 Computed: true, 119 ForceNew: true, 120 }, 121 }, 122 }, 123 Set: resourceArmVirtualMachineStorageImageReferenceHash, 124 }, 125 126 "storage_os_disk": { 127 Type: schema.TypeSet, 128 Required: true, 129 MaxItems: 1, 130 Elem: &schema.Resource{ 131 Schema: map[string]*schema.Schema{ 132 "os_type": { 133 Type: schema.TypeString, 134 Optional: true, 135 }, 136 137 "name": { 138 Type: schema.TypeString, 139 Required: true, 140 }, 141 142 "vhd_uri": { 143 Type: schema.TypeString, 144 Required: true, 145 ForceNew: true, 146 }, 147 148 "image_uri": { 149 Type: schema.TypeString, 150 Optional: true, 151 }, 152 153 "caching": { 154 Type: schema.TypeString, 155 Optional: true, 156 Computed: true, 157 }, 158 159 "create_option": { 160 Type: schema.TypeString, 161 Required: true, 162 }, 163 164 "disk_size_gb": { 165 Type: schema.TypeInt, 166 Optional: true, 167 ValidateFunc: validateDiskSizeGB, 168 }, 169 }, 170 }, 171 Set: resourceArmVirtualMachineStorageOsDiskHash, 172 }, 173 174 "delete_os_disk_on_termination": { 175 Type: schema.TypeBool, 176 Optional: true, 177 Default: false, 178 }, 179 180 "storage_data_disk": { 181 Type: schema.TypeList, 182 Optional: true, 183 Elem: &schema.Resource{ 184 Schema: map[string]*schema.Schema{ 185 "name": { 186 Type: schema.TypeString, 187 Required: true, 188 }, 189 190 "vhd_uri": { 191 Type: schema.TypeString, 192 Required: true, 193 }, 194 195 "create_option": { 196 Type: schema.TypeString, 197 Required: true, 198 }, 199 200 "caching": { 201 Type: schema.TypeString, 202 Optional: true, 203 Computed: true, 204 }, 205 206 "disk_size_gb": { 207 Type: schema.TypeInt, 208 Optional: true, 209 Computed: true, 210 }, 211 212 "lun": { 213 Type: schema.TypeInt, 214 Required: true, 215 }, 216 }, 217 }, 218 }, 219 220 "delete_data_disks_on_termination": { 221 Type: schema.TypeBool, 222 Optional: true, 223 Default: false, 224 }, 225 226 "diagnostics_profile": { 227 Type: schema.TypeSet, 228 Optional: true, 229 MaxItems: 1, 230 ConflictsWith: []string{"boot_diagnostics"}, 231 Deprecated: "Use field boot_diagnostics instead", 232 Elem: &schema.Resource{ 233 Schema: map[string]*schema.Schema{ 234 "boot_diagnostics": { 235 Type: schema.TypeSet, 236 Required: true, 237 MaxItems: 1, 238 Elem: &schema.Resource{ 239 Schema: map[string]*schema.Schema{ 240 "enabled": { 241 Type: schema.TypeBool, 242 Required: true, 243 }, 244 245 "storage_uri": { 246 Type: schema.TypeString, 247 Required: true, 248 }, 249 }, 250 }, 251 }, 252 }, 253 }, 254 }, 255 256 "boot_diagnostics": { 257 Type: schema.TypeList, 258 Optional: true, 259 MaxItems: 1, 260 Elem: &schema.Resource{ 261 Schema: map[string]*schema.Schema{ 262 "enabled": { 263 Type: schema.TypeBool, 264 Required: true, 265 }, 266 267 "storage_uri": { 268 Type: schema.TypeString, 269 Required: true, 270 }, 271 }, 272 }, 273 }, 274 275 "os_profile": { 276 Type: schema.TypeSet, 277 Required: true, 278 MaxItems: 1, 279 Elem: &schema.Resource{ 280 Schema: map[string]*schema.Schema{ 281 "computer_name": { 282 Type: schema.TypeString, 283 ForceNew: true, 284 Required: true, 285 }, 286 287 "admin_username": { 288 Type: schema.TypeString, 289 Required: true, 290 }, 291 292 "admin_password": { 293 Type: schema.TypeString, 294 Required: true, 295 }, 296 297 "custom_data": { 298 Type: schema.TypeString, 299 Optional: true, 300 Computed: true, 301 StateFunc: userDataStateFunc, 302 }, 303 }, 304 }, 305 Set: resourceArmVirtualMachineStorageOsProfileHash, 306 }, 307 308 "os_profile_windows_config": { 309 Type: schema.TypeSet, 310 Optional: true, 311 MaxItems: 1, 312 Elem: &schema.Resource{ 313 Schema: map[string]*schema.Schema{ 314 "provision_vm_agent": { 315 Type: schema.TypeBool, 316 Optional: true, 317 }, 318 "enable_automatic_upgrades": { 319 Type: schema.TypeBool, 320 Optional: true, 321 }, 322 "winrm": { 323 Type: schema.TypeList, 324 Optional: true, 325 Elem: &schema.Resource{ 326 Schema: map[string]*schema.Schema{ 327 "protocol": { 328 Type: schema.TypeString, 329 Required: true, 330 }, 331 "certificate_url": { 332 Type: schema.TypeString, 333 Optional: true, 334 }, 335 }, 336 }, 337 }, 338 "additional_unattend_config": { 339 Type: schema.TypeList, 340 Optional: true, 341 Elem: &schema.Resource{ 342 Schema: map[string]*schema.Schema{ 343 "pass": { 344 Type: schema.TypeString, 345 Required: true, 346 }, 347 "component": { 348 Type: schema.TypeString, 349 Required: true, 350 }, 351 "setting_name": { 352 Type: schema.TypeString, 353 Required: true, 354 }, 355 "content": { 356 Type: schema.TypeString, 357 Required: true, 358 }, 359 }, 360 }, 361 }, 362 }, 363 }, 364 Set: resourceArmVirtualMachineStorageOsProfileWindowsConfigHash, 365 }, 366 367 "os_profile_linux_config": { 368 Type: schema.TypeSet, 369 Optional: true, 370 MaxItems: 1, 371 Elem: &schema.Resource{ 372 Schema: map[string]*schema.Schema{ 373 "disable_password_authentication": { 374 Type: schema.TypeBool, 375 Required: true, 376 }, 377 "ssh_keys": { 378 Type: schema.TypeList, 379 Optional: true, 380 Elem: &schema.Resource{ 381 Schema: map[string]*schema.Schema{ 382 "path": { 383 Type: schema.TypeString, 384 Required: true, 385 }, 386 "key_data": { 387 Type: schema.TypeString, 388 Optional: true, 389 }, 390 }, 391 }, 392 }, 393 }, 394 }, 395 Set: resourceArmVirtualMachineStorageOsProfileLinuxConfigHash, 396 }, 397 398 "os_profile_secrets": { 399 Type: schema.TypeSet, 400 Optional: true, 401 Elem: &schema.Resource{ 402 Schema: map[string]*schema.Schema{ 403 "source_vault_id": { 404 Type: schema.TypeString, 405 Required: true, 406 }, 407 408 "vault_certificates": { 409 Type: schema.TypeList, 410 Optional: true, 411 Elem: &schema.Resource{ 412 Schema: map[string]*schema.Schema{ 413 "certificate_url": { 414 Type: schema.TypeString, 415 Required: true, 416 }, 417 "certificate_store": { 418 Type: schema.TypeString, 419 Optional: true, 420 }, 421 }, 422 }, 423 }, 424 }, 425 }, 426 }, 427 428 "network_interface_ids": { 429 Type: schema.TypeSet, 430 Required: true, 431 Elem: &schema.Schema{ 432 Type: schema.TypeString, 433 }, 434 Set: schema.HashString, 435 }, 436 437 "primary_network_interface_id": { 438 Type: schema.TypeString, 439 Optional: true, 440 }, 441 442 "tags": tagsSchema(), 443 }, 444 } 445 } 446 447 func validateLicenseType(v interface{}, k string) (ws []string, errors []error) { 448 value := v.(string) 449 if value != "" && value != "Windows_Server" { 450 errors = append(errors, fmt.Errorf( 451 "[ERROR] license_type must be 'Windows_Server' or empty")) 452 } 453 return 454 } 455 456 func validateDiskSizeGB(v interface{}, k string) (ws []string, errors []error) { 457 value := v.(int) 458 if value < 1 || value > 1023 { 459 errors = append(errors, fmt.Errorf( 460 "The `disk_size_gb` can only be between 1 and 1023")) 461 } 462 return 463 } 464 465 func resourceArmVirtualMachineCreate(d *schema.ResourceData, meta interface{}) error { 466 client := meta.(*ArmClient) 467 vmClient := client.vmClient 468 469 log.Printf("[INFO] preparing arguments for Azure ARM Virtual Machine creation.") 470 471 name := d.Get("name").(string) 472 location := d.Get("location").(string) 473 resGroup := d.Get("resource_group_name").(string) 474 tags := d.Get("tags").(map[string]interface{}) 475 expandedTags := expandTags(tags) 476 477 osDisk, err := expandAzureRmVirtualMachineOsDisk(d) 478 if err != nil { 479 return err 480 } 481 storageProfile := compute.StorageProfile{ 482 OsDisk: osDisk, 483 } 484 485 if _, ok := d.GetOk("storage_image_reference"); ok { 486 imageRef, err := expandAzureRmVirtualMachineImageReference(d) 487 if err != nil { 488 return err 489 } 490 storageProfile.ImageReference = imageRef 491 } 492 493 if _, ok := d.GetOk("storage_data_disk"); ok { 494 dataDisks, err := expandAzureRmVirtualMachineDataDisk(d) 495 if err != nil { 496 return err 497 } 498 storageProfile.DataDisks = &dataDisks 499 } 500 501 networkProfile := expandAzureRmVirtualMachineNetworkProfile(d) 502 vmSize := d.Get("vm_size").(string) 503 properties := compute.VirtualMachineProperties{ 504 NetworkProfile: &networkProfile, 505 HardwareProfile: &compute.HardwareProfile{ 506 VMSize: compute.VirtualMachineSizeTypes(vmSize), 507 }, 508 StorageProfile: &storageProfile, 509 } 510 511 if v, ok := d.GetOk("license_type"); ok { 512 license := v.(string) 513 properties.LicenseType = &license 514 } 515 516 if _, ok := d.GetOk("boot_diagnostics"); ok { 517 diagnosticsProfile := expandAzureRmVirtualMachineDiagnosticsProfile(d) 518 if diagnosticsProfile != nil { 519 properties.DiagnosticsProfile = diagnosticsProfile 520 } 521 } 522 523 osProfile, err := expandAzureRmVirtualMachineOsProfile(d) 524 if err != nil { 525 return err 526 } 527 properties.OsProfile = osProfile 528 529 if v, ok := d.GetOk("availability_set_id"); ok { 530 availabilitySet := v.(string) 531 availSet := compute.SubResource{ 532 ID: &availabilitySet, 533 } 534 535 properties.AvailabilitySet = &availSet 536 } 537 538 vm := compute.VirtualMachine{ 539 Name: &name, 540 Location: &location, 541 VirtualMachineProperties: &properties, 542 Tags: expandedTags, 543 } 544 545 if _, ok := d.GetOk("plan"); ok { 546 plan, err := expandAzureRmVirtualMachinePlan(d) 547 if err != nil { 548 return err 549 } 550 551 vm.Plan = plan 552 } 553 554 _, vmErr := vmClient.CreateOrUpdate(resGroup, name, vm, make(chan struct{})) 555 if vmErr != nil { 556 return vmErr 557 } 558 559 read, err := vmClient.Get(resGroup, name, "") 560 if err != nil { 561 return err 562 } 563 if read.ID == nil { 564 return fmt.Errorf("Cannot read Virtual Machine %s (resource group %s) ID", name, resGroup) 565 } 566 567 d.SetId(*read.ID) 568 569 return resourceArmVirtualMachineRead(d, meta) 570 } 571 572 func resourceArmVirtualMachineRead(d *schema.ResourceData, meta interface{}) error { 573 vmClient := meta.(*ArmClient).vmClient 574 575 id, err := parseAzureResourceID(d.Id()) 576 if err != nil { 577 return err 578 } 579 resGroup := id.ResourceGroup 580 name := id.Path["virtualMachines"] 581 582 resp, err := vmClient.Get(resGroup, name, "") 583 584 if err != nil { 585 if resp.StatusCode == http.StatusNotFound { 586 d.SetId("") 587 return nil 588 } 589 return fmt.Errorf("Error making Read request on Azure Virtual Machine %s: %s", name, err) 590 } 591 592 d.Set("name", resp.Name) 593 d.Set("resource_group_name", resGroup) 594 d.Set("location", resp.Location) 595 596 if resp.Plan != nil { 597 if err := d.Set("plan", schema.NewSet(resourceArmVirtualMachinePlanHash, flattenAzureRmVirtualMachinePlan(resp.Plan))); err != nil { 598 return fmt.Errorf("[DEBUG] Error setting Virtual Machine Plan error: %#v", err) 599 } 600 } 601 602 if resp.VirtualMachineProperties.AvailabilitySet != nil { 603 d.Set("availability_set_id", strings.ToLower(*resp.VirtualMachineProperties.AvailabilitySet.ID)) 604 } 605 606 d.Set("vm_size", resp.VirtualMachineProperties.HardwareProfile.VMSize) 607 608 if resp.VirtualMachineProperties.StorageProfile.ImageReference != nil { 609 if err := d.Set("storage_image_reference", schema.NewSet(resourceArmVirtualMachineStorageImageReferenceHash, flattenAzureRmVirtualMachineImageReference(resp.VirtualMachineProperties.StorageProfile.ImageReference))); err != nil { 610 return fmt.Errorf("[DEBUG] Error setting Virtual Machine Storage Image Reference error: %#v", err) 611 } 612 } 613 614 if err := d.Set("storage_os_disk", schema.NewSet(resourceArmVirtualMachineStorageOsDiskHash, flattenAzureRmVirtualMachineOsDisk(resp.VirtualMachineProperties.StorageProfile.OsDisk))); err != nil { 615 return fmt.Errorf("[DEBUG] Error setting Virtual Machine Storage OS Disk error: %#v", err) 616 } 617 618 if resp.VirtualMachineProperties.StorageProfile.DataDisks != nil { 619 if err := d.Set("storage_data_disk", flattenAzureRmVirtualMachineDataDisk(resp.VirtualMachineProperties.StorageProfile.DataDisks)); err != nil { 620 return fmt.Errorf("[DEBUG] Error setting Virtual Machine Storage Data Disks error: %#v", err) 621 } 622 } 623 624 if err := d.Set("os_profile", schema.NewSet(resourceArmVirtualMachineStorageOsProfileHash, flattenAzureRmVirtualMachineOsProfile(resp.VirtualMachineProperties.OsProfile))); err != nil { 625 return fmt.Errorf("[DEBUG] Error setting Virtual Machine Storage OS Profile: %#v", err) 626 } 627 628 if resp.VirtualMachineProperties.OsProfile.WindowsConfiguration != nil { 629 if err := d.Set("os_profile_windows_config", flattenAzureRmVirtualMachineOsProfileWindowsConfiguration(resp.VirtualMachineProperties.OsProfile.WindowsConfiguration)); err != nil { 630 return fmt.Errorf("[DEBUG] Error setting Virtual Machine Storage OS Profile Windows Configuration: %#v", err) 631 } 632 } 633 634 if resp.VirtualMachineProperties.OsProfile.LinuxConfiguration != nil { 635 if err := d.Set("os_profile_linux_config", flattenAzureRmVirtualMachineOsProfileLinuxConfiguration(resp.VirtualMachineProperties.OsProfile.LinuxConfiguration)); err != nil { 636 return fmt.Errorf("[DEBUG] Error setting Virtual Machine Storage OS Profile Linux Configuration: %#v", err) 637 } 638 } 639 640 if resp.VirtualMachineProperties.OsProfile.Secrets != nil { 641 if err := d.Set("os_profile_secrets", flattenAzureRmVirtualMachineOsProfileSecrets(resp.VirtualMachineProperties.OsProfile.Secrets)); err != nil { 642 return fmt.Errorf("[DEBUG] Error setting Virtual Machine Storage OS Profile Secrets: %#v", err) 643 } 644 } 645 646 if resp.VirtualMachineProperties.DiagnosticsProfile != nil && resp.VirtualMachineProperties.DiagnosticsProfile.BootDiagnostics != nil { 647 if err := d.Set("boot_diagnostics", flattenAzureRmVirtualMachineDiagnosticsProfile(resp.VirtualMachineProperties.DiagnosticsProfile.BootDiagnostics)); err != nil { 648 return fmt.Errorf("[DEBUG] Error setting Virtual Machine Diagnostics Profile: %#v", err) 649 } 650 } 651 652 if resp.VirtualMachineProperties.NetworkProfile != nil { 653 if err := d.Set("network_interface_ids", flattenAzureRmVirtualMachineNetworkInterfaces(resp.VirtualMachineProperties.NetworkProfile)); err != nil { 654 return fmt.Errorf("[DEBUG] Error setting Virtual Machine Storage Network Interfaces: %#v", err) 655 } 656 657 if resp.VirtualMachineProperties.NetworkProfile.NetworkInterfaces != nil { 658 for _, nic := range *resp.VirtualMachineProperties.NetworkProfile.NetworkInterfaces { 659 if nic.NetworkInterfaceReferenceProperties != nil && *nic.NetworkInterfaceReferenceProperties.Primary { 660 d.Set("primary_network_interface_id", nic.ID) 661 break 662 } 663 } 664 } 665 } 666 667 flattenAndSetTags(d, resp.Tags) 668 669 return nil 670 } 671 672 func resourceArmVirtualMachineDelete(d *schema.ResourceData, meta interface{}) error { 673 vmClient := meta.(*ArmClient).vmClient 674 675 id, err := parseAzureResourceID(d.Id()) 676 if err != nil { 677 return err 678 } 679 resGroup := id.ResourceGroup 680 name := id.Path["virtualMachines"] 681 682 if _, err = vmClient.Delete(resGroup, name, make(chan struct{})); err != nil { 683 return err 684 } 685 686 // delete OS Disk if opted in 687 if deleteOsDisk := d.Get("delete_os_disk_on_termination").(bool); deleteOsDisk { 688 log.Printf("[INFO] delete_os_disk_on_termination is enabled, deleting") 689 690 osDisk, err := expandAzureRmVirtualMachineOsDisk(d) 691 if err != nil { 692 return fmt.Errorf("Error expanding OS Disk: %s", err) 693 } 694 695 if err = resourceArmVirtualMachineDeleteVhd(*osDisk.Vhd.URI, meta); err != nil { 696 return fmt.Errorf("Error deleting OS Disk VHD: %s", err) 697 } 698 } 699 700 // delete Data disks if opted in 701 if deleteDataDisks := d.Get("delete_data_disks_on_termination").(bool); deleteDataDisks { 702 log.Printf("[INFO] delete_data_disks_on_termination is enabled, deleting each data disk") 703 704 disks, err := expandAzureRmVirtualMachineDataDisk(d) 705 if err != nil { 706 return fmt.Errorf("Error expanding Data Disks: %s", err) 707 } 708 709 for _, disk := range disks { 710 if err = resourceArmVirtualMachineDeleteVhd(*disk.Vhd.URI, meta); err != nil { 711 return fmt.Errorf("Error deleting Data Disk VHD: %s", err) 712 } 713 } 714 } 715 716 return nil 717 } 718 719 func resourceArmVirtualMachineDeleteVhd(uri string, meta interface{}) error { 720 vhdURL, err := url.Parse(uri) 721 if err != nil { 722 return fmt.Errorf("Cannot parse Disk VHD URI: %s", err) 723 } 724 725 // VHD URI is in the form: https://storageAccountName.blob.core.windows.net/containerName/blobName 726 storageAccountName := strings.Split(vhdURL.Host, ".")[0] 727 path := strings.Split(strings.TrimPrefix(vhdURL.Path, "/"), "/") 728 containerName := path[0] 729 blobName := path[1] 730 731 storageAccountResourceGroupName, err := findStorageAccountResourceGroup(meta, storageAccountName) 732 if err != nil { 733 return fmt.Errorf("Error finding resource group for storage account %s: %s", storageAccountName, err) 734 } 735 736 blobClient, saExists, err := meta.(*ArmClient).getBlobStorageClientForStorageAccount(storageAccountResourceGroupName, storageAccountName) 737 if err != nil { 738 return fmt.Errorf("Error creating blob store client for VHD deletion: %s", err) 739 } 740 741 if !saExists { 742 log.Printf("[INFO] Storage Account %q in resource group %q doesn't exist so the VHD blob won't exist", storageAccountName, storageAccountResourceGroupName) 743 return nil 744 } 745 746 log.Printf("[INFO] Deleting VHD blob %s", blobName) 747 _, err = blobClient.DeleteBlobIfExists(containerName, blobName, nil) 748 if err != nil { 749 return fmt.Errorf("Error deleting VHD blob: %s", err) 750 } 751 752 return nil 753 } 754 755 func resourceArmVirtualMachinePlanHash(v interface{}) int { 756 var buf bytes.Buffer 757 m := v.(map[string]interface{}) 758 buf.WriteString(fmt.Sprintf("%s-", m["name"].(string))) 759 buf.WriteString(fmt.Sprintf("%s-", m["publisher"].(string))) 760 buf.WriteString(fmt.Sprintf("%s-", m["product"].(string))) 761 762 return hashcode.String(buf.String()) 763 } 764 765 func resourceArmVirtualMachineStorageImageReferenceHash(v interface{}) int { 766 var buf bytes.Buffer 767 m := v.(map[string]interface{}) 768 buf.WriteString(fmt.Sprintf("%s-", m["publisher"].(string))) 769 buf.WriteString(fmt.Sprintf("%s-", m["offer"].(string))) 770 buf.WriteString(fmt.Sprintf("%s-", m["sku"].(string))) 771 772 return hashcode.String(buf.String()) 773 } 774 775 func resourceArmVirtualMachineStorageOsProfileHash(v interface{}) int { 776 var buf bytes.Buffer 777 m := v.(map[string]interface{}) 778 buf.WriteString(fmt.Sprintf("%s-", m["admin_username"].(string))) 779 buf.WriteString(fmt.Sprintf("%s-", m["computer_name"].(string))) 780 return hashcode.String(buf.String()) 781 } 782 783 func resourceArmVirtualMachineStorageOsDiskHash(v interface{}) int { 784 var buf bytes.Buffer 785 m := v.(map[string]interface{}) 786 buf.WriteString(fmt.Sprintf("%s-", m["name"].(string))) 787 buf.WriteString(fmt.Sprintf("%s-", m["vhd_uri"].(string))) 788 789 return hashcode.String(buf.String()) 790 } 791 792 func resourceArmVirtualMachineStorageOsProfileLinuxConfigHash(v interface{}) int { 793 var buf bytes.Buffer 794 m := v.(map[string]interface{}) 795 buf.WriteString(fmt.Sprintf("%t-", m["disable_password_authentication"].(bool))) 796 797 return hashcode.String(buf.String()) 798 } 799 800 func resourceArmVirtualMachineStorageOsProfileWindowsConfigHash(v interface{}) int { 801 var buf bytes.Buffer 802 m := v.(map[string]interface{}) 803 if m["provision_vm_agent"] != nil { 804 buf.WriteString(fmt.Sprintf("%t-", m["provision_vm_agent"].(bool))) 805 } 806 if m["enable_automatic_upgrades"] != nil { 807 buf.WriteString(fmt.Sprintf("%t-", m["enable_automatic_upgrades"].(bool))) 808 } 809 return hashcode.String(buf.String()) 810 } 811 812 func flattenAzureRmVirtualMachinePlan(plan *compute.Plan) []interface{} { 813 result := make(map[string]interface{}) 814 result["name"] = *plan.Name 815 result["publisher"] = *plan.Publisher 816 result["product"] = *plan.Product 817 818 return []interface{}{result} 819 } 820 821 func flattenAzureRmVirtualMachineImageReference(image *compute.ImageReference) []interface{} { 822 result := make(map[string]interface{}) 823 result["offer"] = *image.Offer 824 result["publisher"] = *image.Publisher 825 result["sku"] = *image.Sku 826 827 if image.Version != nil { 828 result["version"] = *image.Version 829 } 830 831 return []interface{}{result} 832 } 833 834 func flattenAzureRmVirtualMachineDiagnosticsProfile(profile *compute.BootDiagnostics) []interface{} { 835 result := make(map[string]interface{}) 836 837 result["enabled"] = *profile.Enabled 838 if profile.StorageURI != nil { 839 result["storage_uri"] = *profile.StorageURI 840 } 841 842 return []interface{}{result} 843 } 844 845 func flattenAzureRmVirtualMachineNetworkInterfaces(profile *compute.NetworkProfile) []string { 846 result := make([]string, 0, len(*profile.NetworkInterfaces)) 847 for _, nic := range *profile.NetworkInterfaces { 848 result = append(result, *nic.ID) 849 } 850 return result 851 } 852 853 func flattenAzureRmVirtualMachineOsProfileSecrets(secrets *[]compute.VaultSecretGroup) []map[string]interface{} { 854 result := make([]map[string]interface{}, 0, len(*secrets)) 855 for _, secret := range *secrets { 856 s := map[string]interface{}{ 857 "source_vault_id": *secret.SourceVault.ID, 858 } 859 860 if secret.VaultCertificates != nil { 861 certs := make([]map[string]interface{}, 0, len(*secret.VaultCertificates)) 862 for _, cert := range *secret.VaultCertificates { 863 vaultCert := make(map[string]interface{}) 864 vaultCert["certificate_url"] = *cert.CertificateURL 865 866 if cert.CertificateStore != nil { 867 vaultCert["certificate_store"] = *cert.CertificateStore 868 } 869 870 certs = append(certs, vaultCert) 871 } 872 873 s["vault_certificates"] = certs 874 } 875 876 result = append(result, s) 877 } 878 return result 879 } 880 881 func flattenAzureRmVirtualMachineDataDisk(disks *[]compute.DataDisk) interface{} { 882 result := make([]interface{}, len(*disks)) 883 for i, disk := range *disks { 884 l := make(map[string]interface{}) 885 l["name"] = *disk.Name 886 l["vhd_uri"] = *disk.Vhd.URI 887 l["create_option"] = disk.CreateOption 888 l["caching"] = string(disk.Caching) 889 if disk.DiskSizeGB != nil { 890 l["disk_size_gb"] = *disk.DiskSizeGB 891 } 892 l["lun"] = *disk.Lun 893 894 result[i] = l 895 } 896 return result 897 } 898 899 func flattenAzureRmVirtualMachineOsProfile(osProfile *compute.OSProfile) []interface{} { 900 result := make(map[string]interface{}) 901 result["computer_name"] = *osProfile.ComputerName 902 result["admin_username"] = *osProfile.AdminUsername 903 if osProfile.CustomData != nil { 904 result["custom_data"] = *osProfile.CustomData 905 } 906 907 return []interface{}{result} 908 } 909 910 func flattenAzureRmVirtualMachineOsProfileWindowsConfiguration(config *compute.WindowsConfiguration) []interface{} { 911 result := make(map[string]interface{}) 912 913 if config.ProvisionVMAgent != nil { 914 result["provision_vm_agent"] = *config.ProvisionVMAgent 915 } 916 917 if config.EnableAutomaticUpdates != nil { 918 result["enable_automatic_upgrades"] = *config.EnableAutomaticUpdates 919 } 920 921 if config.WinRM != nil { 922 listeners := make([]map[string]interface{}, 0, len(*config.WinRM.Listeners)) 923 for _, i := range *config.WinRM.Listeners { 924 listener := make(map[string]interface{}) 925 listener["protocol"] = i.Protocol 926 927 if i.CertificateURL != nil { 928 listener["certificate_url"] = *i.CertificateURL 929 } 930 931 listeners = append(listeners, listener) 932 } 933 934 result["winrm"] = listeners 935 } 936 937 if config.AdditionalUnattendContent != nil { 938 content := make([]map[string]interface{}, 0, len(*config.AdditionalUnattendContent)) 939 for _, i := range *config.AdditionalUnattendContent { 940 c := make(map[string]interface{}) 941 c["pass"] = i.PassName 942 c["component"] = i.ComponentName 943 c["setting_name"] = i.SettingName 944 945 if i.Content != nil { 946 c["content"] = *i.Content 947 } 948 949 content = append(content, c) 950 } 951 952 result["additional_unattend_config"] = content 953 } 954 955 return []interface{}{result} 956 } 957 958 func flattenAzureRmVirtualMachineOsProfileLinuxConfiguration(config *compute.LinuxConfiguration) []interface{} { 959 960 result := make(map[string]interface{}) 961 result["disable_password_authentication"] = *config.DisablePasswordAuthentication 962 963 if config.SSH != nil && len(*config.SSH.PublicKeys) > 0 { 964 ssh_keys := make([]map[string]interface{}, len(*config.SSH.PublicKeys)) 965 for _, i := range *config.SSH.PublicKeys { 966 key := make(map[string]interface{}) 967 key["path"] = *i.Path 968 969 if i.KeyData != nil { 970 key["key_data"] = *i.KeyData 971 } 972 973 ssh_keys = append(ssh_keys, key) 974 } 975 976 result["ssh_keys"] = ssh_keys 977 } 978 979 return []interface{}{result} 980 } 981 982 func flattenAzureRmVirtualMachineOsDisk(disk *compute.OSDisk) []interface{} { 983 result := make(map[string]interface{}) 984 result["name"] = *disk.Name 985 result["vhd_uri"] = *disk.Vhd.URI 986 result["create_option"] = disk.CreateOption 987 result["caching"] = disk.Caching 988 if disk.DiskSizeGB != nil { 989 result["disk_size_gb"] = *disk.DiskSizeGB 990 } 991 992 return []interface{}{result} 993 } 994 995 func expandAzureRmVirtualMachinePlan(d *schema.ResourceData) (*compute.Plan, error) { 996 planConfigs := d.Get("plan").(*schema.Set).List() 997 998 planConfig := planConfigs[0].(map[string]interface{}) 999 1000 publisher := planConfig["publisher"].(string) 1001 name := planConfig["name"].(string) 1002 product := planConfig["product"].(string) 1003 1004 return &compute.Plan{ 1005 Publisher: &publisher, 1006 Name: &name, 1007 Product: &product, 1008 }, nil 1009 } 1010 1011 func expandAzureRmVirtualMachineOsProfile(d *schema.ResourceData) (*compute.OSProfile, error) { 1012 osProfiles := d.Get("os_profile").(*schema.Set).List() 1013 1014 osProfile := osProfiles[0].(map[string]interface{}) 1015 1016 adminUsername := osProfile["admin_username"].(string) 1017 adminPassword := osProfile["admin_password"].(string) 1018 computerName := osProfile["computer_name"].(string) 1019 1020 profile := &compute.OSProfile{ 1021 AdminUsername: &adminUsername, 1022 ComputerName: &computerName, 1023 } 1024 1025 if adminPassword != "" { 1026 profile.AdminPassword = &adminPassword 1027 } 1028 1029 if _, ok := d.GetOk("os_profile_windows_config"); ok { 1030 winConfig, err := expandAzureRmVirtualMachineOsProfileWindowsConfig(d) 1031 if err != nil { 1032 return nil, err 1033 } 1034 if winConfig != nil { 1035 profile.WindowsConfiguration = winConfig 1036 } 1037 } 1038 1039 if _, ok := d.GetOk("os_profile_linux_config"); ok { 1040 linuxConfig, err := expandAzureRmVirtualMachineOsProfileLinuxConfig(d) 1041 if err != nil { 1042 return nil, err 1043 } 1044 if linuxConfig != nil { 1045 profile.LinuxConfiguration = linuxConfig 1046 } 1047 } 1048 1049 if _, ok := d.GetOk("os_profile_secrets"); ok { 1050 secrets := expandAzureRmVirtualMachineOsProfileSecrets(d) 1051 if secrets != nil { 1052 profile.Secrets = secrets 1053 } 1054 } 1055 1056 if v := osProfile["custom_data"].(string); v != "" { 1057 v = base64Encode(v) 1058 profile.CustomData = &v 1059 } 1060 1061 return profile, nil 1062 } 1063 1064 func expandAzureRmVirtualMachineOsProfileSecrets(d *schema.ResourceData) *[]compute.VaultSecretGroup { 1065 secretsConfig := d.Get("os_profile_secrets").(*schema.Set).List() 1066 secrets := make([]compute.VaultSecretGroup, 0, len(secretsConfig)) 1067 1068 for _, secretConfig := range secretsConfig { 1069 config := secretConfig.(map[string]interface{}) 1070 sourceVaultId := config["source_vault_id"].(string) 1071 1072 vaultSecretGroup := compute.VaultSecretGroup{ 1073 SourceVault: &compute.SubResource{ 1074 ID: &sourceVaultId, 1075 }, 1076 } 1077 1078 if v := config["vault_certificates"]; v != nil { 1079 certsConfig := v.([]interface{}) 1080 certs := make([]compute.VaultCertificate, 0, len(certsConfig)) 1081 for _, certConfig := range certsConfig { 1082 config := certConfig.(map[string]interface{}) 1083 1084 certUrl := config["certificate_url"].(string) 1085 cert := compute.VaultCertificate{ 1086 CertificateURL: &certUrl, 1087 } 1088 if v := config["certificate_store"].(string); v != "" { 1089 cert.CertificateStore = &v 1090 } 1091 1092 certs = append(certs, cert) 1093 } 1094 vaultSecretGroup.VaultCertificates = &certs 1095 } 1096 1097 secrets = append(secrets, vaultSecretGroup) 1098 } 1099 1100 return &secrets 1101 } 1102 1103 func expandAzureRmVirtualMachineOsProfileLinuxConfig(d *schema.ResourceData) (*compute.LinuxConfiguration, error) { 1104 osProfilesLinuxConfig := d.Get("os_profile_linux_config").(*schema.Set).List() 1105 1106 linuxConfig := osProfilesLinuxConfig[0].(map[string]interface{}) 1107 disablePasswordAuth := linuxConfig["disable_password_authentication"].(bool) 1108 1109 config := &compute.LinuxConfiguration{ 1110 DisablePasswordAuthentication: &disablePasswordAuth, 1111 } 1112 1113 linuxKeys := linuxConfig["ssh_keys"].([]interface{}) 1114 sshPublicKeys := []compute.SSHPublicKey{} 1115 for _, key := range linuxKeys { 1116 1117 sshKey, ok := key.(map[string]interface{}) 1118 if !ok { 1119 continue 1120 } 1121 path := sshKey["path"].(string) 1122 keyData := sshKey["key_data"].(string) 1123 1124 sshPublicKey := compute.SSHPublicKey{ 1125 Path: &path, 1126 KeyData: &keyData, 1127 } 1128 1129 sshPublicKeys = append(sshPublicKeys, sshPublicKey) 1130 } 1131 1132 if len(sshPublicKeys) > 0 { 1133 config.SSH = &compute.SSHConfiguration{ 1134 PublicKeys: &sshPublicKeys, 1135 } 1136 } 1137 1138 return config, nil 1139 } 1140 1141 func expandAzureRmVirtualMachineOsProfileWindowsConfig(d *schema.ResourceData) (*compute.WindowsConfiguration, error) { 1142 osProfilesWindowsConfig := d.Get("os_profile_windows_config").(*schema.Set).List() 1143 1144 osProfileConfig := osProfilesWindowsConfig[0].(map[string]interface{}) 1145 config := &compute.WindowsConfiguration{} 1146 1147 if v := osProfileConfig["provision_vm_agent"]; v != nil { 1148 provision := v.(bool) 1149 config.ProvisionVMAgent = &provision 1150 } 1151 1152 if v := osProfileConfig["enable_automatic_upgrades"]; v != nil { 1153 update := v.(bool) 1154 config.EnableAutomaticUpdates = &update 1155 } 1156 1157 if v := osProfileConfig["winrm"]; v != nil { 1158 winRm := v.([]interface{}) 1159 if len(winRm) > 0 { 1160 winRmListners := make([]compute.WinRMListener, 0, len(winRm)) 1161 for _, winRmConfig := range winRm { 1162 config := winRmConfig.(map[string]interface{}) 1163 1164 protocol := config["protocol"].(string) 1165 winRmListner := compute.WinRMListener{ 1166 Protocol: compute.ProtocolTypes(protocol), 1167 } 1168 if v := config["certificate_url"].(string); v != "" { 1169 winRmListner.CertificateURL = &v 1170 } 1171 1172 winRmListners = append(winRmListners, winRmListner) 1173 } 1174 config.WinRM = &compute.WinRMConfiguration{ 1175 Listeners: &winRmListners, 1176 } 1177 } 1178 } 1179 if v := osProfileConfig["additional_unattend_config"]; v != nil { 1180 additionalConfig := v.([]interface{}) 1181 if len(additionalConfig) > 0 { 1182 additionalConfigContent := make([]compute.AdditionalUnattendContent, 0, len(additionalConfig)) 1183 for _, addConfig := range additionalConfig { 1184 config := addConfig.(map[string]interface{}) 1185 pass := config["pass"].(string) 1186 component := config["component"].(string) 1187 settingName := config["setting_name"].(string) 1188 content := config["content"].(string) 1189 1190 addContent := compute.AdditionalUnattendContent{ 1191 PassName: compute.PassNames(pass), 1192 ComponentName: compute.ComponentNames(component), 1193 SettingName: compute.SettingNames(settingName), 1194 Content: &content, 1195 } 1196 1197 additionalConfigContent = append(additionalConfigContent, addContent) 1198 } 1199 config.AdditionalUnattendContent = &additionalConfigContent 1200 } 1201 } 1202 return config, nil 1203 } 1204 1205 func expandAzureRmVirtualMachineDataDisk(d *schema.ResourceData) ([]compute.DataDisk, error) { 1206 disks := d.Get("storage_data_disk").([]interface{}) 1207 data_disks := make([]compute.DataDisk, 0, len(disks)) 1208 for _, disk_config := range disks { 1209 config := disk_config.(map[string]interface{}) 1210 1211 name := config["name"].(string) 1212 vhd := config["vhd_uri"].(string) 1213 createOption := config["create_option"].(string) 1214 lun := int32(config["lun"].(int)) 1215 1216 data_disk := compute.DataDisk{ 1217 Name: &name, 1218 Vhd: &compute.VirtualHardDisk{ 1219 URI: &vhd, 1220 }, 1221 Lun: &lun, 1222 CreateOption: compute.DiskCreateOptionTypes(createOption), 1223 } 1224 1225 if v := config["caching"].(string); v != "" { 1226 data_disk.Caching = compute.CachingTypes(v) 1227 } 1228 1229 if v := config["disk_size_gb"]; v != nil { 1230 diskSize := int32(config["disk_size_gb"].(int)) 1231 data_disk.DiskSizeGB = &diskSize 1232 } 1233 1234 data_disks = append(data_disks, data_disk) 1235 } 1236 1237 return data_disks, nil 1238 } 1239 1240 func expandAzureRmVirtualMachineDiagnosticsProfile(d *schema.ResourceData) *compute.DiagnosticsProfile { 1241 bootDiagnostics := d.Get("boot_diagnostics").([]interface{}) 1242 1243 diagnosticsProfile := &compute.DiagnosticsProfile{} 1244 if len(bootDiagnostics) > 0 { 1245 bootDiagnostic := bootDiagnostics[0].(map[string]interface{}) 1246 1247 diagnostic := &compute.BootDiagnostics{ 1248 Enabled: riviera.Bool(bootDiagnostic["enabled"].(bool)), 1249 StorageURI: riviera.String(bootDiagnostic["storage_uri"].(string)), 1250 } 1251 1252 diagnosticsProfile.BootDiagnostics = diagnostic 1253 1254 return diagnosticsProfile 1255 } 1256 1257 return nil 1258 } 1259 1260 func expandAzureRmVirtualMachineImageReference(d *schema.ResourceData) (*compute.ImageReference, error) { 1261 storageImageRefs := d.Get("storage_image_reference").(*schema.Set).List() 1262 1263 storageImageRef := storageImageRefs[0].(map[string]interface{}) 1264 1265 publisher := storageImageRef["publisher"].(string) 1266 offer := storageImageRef["offer"].(string) 1267 sku := storageImageRef["sku"].(string) 1268 version := storageImageRef["version"].(string) 1269 1270 return &compute.ImageReference{ 1271 Publisher: &publisher, 1272 Offer: &offer, 1273 Sku: &sku, 1274 Version: &version, 1275 }, nil 1276 } 1277 1278 func expandAzureRmVirtualMachineNetworkProfile(d *schema.ResourceData) compute.NetworkProfile { 1279 nicIds := d.Get("network_interface_ids").(*schema.Set).List() 1280 primaryNicId := d.Get("primary_network_interface_id").(string) 1281 network_interfaces := make([]compute.NetworkInterfaceReference, 0, len(nicIds)) 1282 1283 network_profile := compute.NetworkProfile{} 1284 1285 for _, nic := range nicIds { 1286 id := nic.(string) 1287 primary := id == primaryNicId 1288 1289 network_interface := compute.NetworkInterfaceReference{ 1290 ID: &id, 1291 NetworkInterfaceReferenceProperties: &compute.NetworkInterfaceReferenceProperties{ 1292 Primary: &primary, 1293 }, 1294 } 1295 network_interfaces = append(network_interfaces, network_interface) 1296 } 1297 1298 network_profile.NetworkInterfaces = &network_interfaces 1299 1300 return network_profile 1301 } 1302 1303 func expandAzureRmVirtualMachineOsDisk(d *schema.ResourceData) (*compute.OSDisk, error) { 1304 disks := d.Get("storage_os_disk").(*schema.Set).List() 1305 1306 disk := disks[0].(map[string]interface{}) 1307 1308 name := disk["name"].(string) 1309 vhdURI := disk["vhd_uri"].(string) 1310 imageURI := disk["image_uri"].(string) 1311 createOption := disk["create_option"].(string) 1312 1313 osDisk := &compute.OSDisk{ 1314 Name: &name, 1315 Vhd: &compute.VirtualHardDisk{ 1316 URI: &vhdURI, 1317 }, 1318 CreateOption: compute.DiskCreateOptionTypes(createOption), 1319 } 1320 1321 if v := disk["image_uri"].(string); v != "" { 1322 osDisk.Image = &compute.VirtualHardDisk{ 1323 URI: &imageURI, 1324 } 1325 } 1326 1327 if v := disk["os_type"].(string); v != "" { 1328 if v == "linux" { 1329 osDisk.OsType = compute.Linux 1330 } else if v == "windows" { 1331 osDisk.OsType = compute.Windows 1332 } else { 1333 return nil, fmt.Errorf("[ERROR] os_type must be 'linux' or 'windows'") 1334 } 1335 } 1336 1337 if v := disk["caching"].(string); v != "" { 1338 osDisk.Caching = compute.CachingTypes(v) 1339 } 1340 1341 if v := disk["disk_size_gb"].(int); v != 0 { 1342 diskSize := int32(v) 1343 osDisk.DiskSizeGB = &diskSize 1344 } 1345 1346 return osDisk, nil 1347 } 1348 1349 func findStorageAccountResourceGroup(meta interface{}, storageAccountName string) (string, error) { 1350 client := meta.(*ArmClient).resourceFindClient 1351 filter := fmt.Sprintf("name eq '%s' and resourceType eq 'Microsoft.Storage/storageAccounts'", storageAccountName) 1352 expand := "" 1353 var pager *int32 1354 1355 rf, err := client.List(filter, expand, pager) 1356 if err != nil { 1357 return "", fmt.Errorf("Error making resource request for query %s: %s", filter, err) 1358 } 1359 1360 results := *rf.Value 1361 if len(results) != 1 { 1362 return "", fmt.Errorf("Wrong number of results making resource request for query %s: %d", filter, len(results)) 1363 } 1364 1365 id, err := parseAzureResourceID(*results[0].ID) 1366 if err != nil { 1367 return "", err 1368 } 1369 1370 return id.ResourceGroup, nil 1371 }