From 081d44075bf39d79568811606be0fad63b549ebe Mon Sep 17 00:00:00 2001 From: Eugene Chuvyrov Date: Mon, 12 Jun 2017 18:52:05 -0700 Subject: [PATCH 01/10] Adding support for Image Resources and Custom Images with Managed Disks --- azurerm/config.go | 7 + azurerm/import_arm_image_test.go | 49 + azurerm/provider.go | 1 + azurerm/resource_arm_image.go | 377 ++++++++ azurerm/resource_arm_image_test.go | 954 +++++++++++++++++++ azurerm/resource_arm_virtual_machine.go | 80 +- website/azurerm.erb | 9 + website/docs/r/image.html.markdown | 92 ++ website/docs/r/virtual_machine.html.markdown | 8 +- 9 files changed, 1551 insertions(+), 26 deletions(-) create mode 100644 azurerm/import_arm_image_test.go create mode 100644 azurerm/resource_arm_image.go create mode 100644 azurerm/resource_arm_image_test.go create mode 100644 website/docs/r/image.html.markdown diff --git a/azurerm/config.go b/azurerm/config.go index 8fdb95d19a7a..85bdfc83f866 100644 --- a/azurerm/config.go +++ b/azurerm/config.go @@ -49,6 +49,7 @@ type ArmClient struct { vmScaleSetClient compute.VirtualMachineScaleSetsClient vmImageClient compute.VirtualMachineImagesClient vmClient compute.VirtualMachinesClient + imageClient compute.ImagesClient diskClient disk.DisksClient @@ -260,6 +261,12 @@ func (c *Config) getArmClient() (*ArmClient, error) { dkc.Sender = autorest.CreateSender(withRequestLogging()) client.diskClient = dkc + img := compute.NewImagesClientWithBaseURI(endpoint, c.SubscriptionID) + setUserAgent(&img.Client) + img.Authorizer = auth + img.Sender = autorest.CreateSender(withRequestLogging()) + client.imageClient = img + ehc := eventhub.NewEventHubsClientWithBaseURI(endpoint, c.SubscriptionID) setUserAgent(&ehc.Client) ehc.Authorizer = auth diff --git a/azurerm/import_arm_image_test.go b/azurerm/import_arm_image_test.go new file mode 100644 index 000000000000..cfbf89b12460 --- /dev/null +++ b/azurerm/import_arm_image_test.go @@ -0,0 +1,49 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccAzureRMImage_importStandalone(t *testing.T) { + ri := acctest.RandInt() + userName := "testadmin" + password := "Password1234!" + hostName := "tftestcustomimagesrc" + dnsName := fmt.Sprintf("%[1]s.westcentralus.cloudapp.azure.com", hostName) + sshPort := "22" + preConfig := fmt.Sprintf(testAccAzureRMImage_standaloneImage_setup, ri, userName, password, hostName) + postConfig := fmt.Sprintf(testAccAzureRMImage_standaloneImage_provision, ri, userName, password, hostName) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMImageDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + //need to create a vm and then reference it in the image creation + Config: preConfig, + Destroy: false, + Check: resource.ComposeTestCheckFunc( + testCheckAzureVMExists("azurerm_virtual_machine.testsource", true), + testGeneralizeVMImage(fmt.Sprintf("acctestRG-%[1]d", ri), "testsource", + userName, password, dnsName, sshPort), + ), + }, + resource.TestStep{ + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMImageExists("azurerm_image.test", true), + ), + }, + resource.TestStep{ + ResourceName: "azurerm_image.test", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/azurerm/provider.go b/azurerm/provider.go index 05c10505cfd9..020ab1d25479 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -90,6 +90,7 @@ func Provider() terraform.ResourceProvider { "azurerm_lb_rule": resourceArmLoadBalancerRule(), "azurerm_managed_disk": resourceArmManagedDisk(), + "azurerm_image": resourceArmImage(), "azurerm_key_vault": resourceArmKeyVault(), "azurerm_local_network_gateway": resourceArmLocalNetworkGateway(), diff --git a/azurerm/resource_arm_image.go b/azurerm/resource_arm_image.go new file mode 100644 index 000000000000..8b8b54c8c2c8 --- /dev/null +++ b/azurerm/resource_arm_image.go @@ -0,0 +1,377 @@ +package azurerm + +import ( + "fmt" + "log" + "net/http" + + "github.com/Azure/azure-sdk-for-go/arm/compute" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" +) + +func resourceArmImage() *schema.Resource { + return &schema.Resource{ + Create: resourceArmImageCreateUpdate, + Read: resourceArmImageRead, + Update: resourceArmImageCreateUpdate, + Delete: resourceArmImageDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "location": locationSchema(), + + "resource_group_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "source_virtual_machine_id": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"os_disk_os_type"}, + }, + + "os_disk_os_type": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(compute.Linux), + string(compute.Windows), + }, true), + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + }, + + "os_disk_os_state": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(compute.Generalized), + string(compute.Specialized), + }, true), + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + }, + + "os_disk_managed_disk_id": { + Type: schema.TypeString, + Optional: true, + }, + + "os_disk_blob_uri": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "os_disk_caching": { + Type: schema.TypeString, + Optional: true, + Default: "None", + ValidateFunc: validation.StringInSlice([]string{ + string(compute.None), + string(compute.ReadOnly), + string(compute.ReadWrite), + }, true), + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + }, + + "os_disk_size_gb": { + Type: schema.TypeInt, + Optional: true, + }, + + "data_disk": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + + "lun": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + }, + + "managed_disk_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "blob_uri": { + Type: schema.TypeString, + Optional: true, + }, + + "caching": { + Type: schema.TypeString, + Optional: true, + Default: "None", + ValidateFunc: validation.StringInSlice([]string{ + string(compute.None), + string(compute.ReadOnly), + string(compute.ReadWrite), + }, true), + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + }, + + "size_gb": { + Type: schema.TypeInt, + Optional: true, + }, + }, + }, + }, + + "tags": tagsSchema(), + }, + } +} + +func resourceArmImageCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient) + imageClient := client.imageClient + + log.Printf("[INFO] preparing arguments for Azure ARM Image creation.") + + name := d.Get("name").(string) + location := d.Get("location").(string) + resGroup := d.Get("resource_group_name").(string) + tags := d.Get("tags").(map[string]interface{}) + expandedTags := expandTags(tags) + + osDisk := &compute.ImageOSDisk{} + if v, ok := d.Get("os_disk_os_type").(string); ok { + osType := compute.OperatingSystemTypes(v) + osDisk.OsType = osType + } + + if v, ok := d.Get("os_disk_os_state").(string); ok { + osState := compute.OperatingSystemStateTypes(v) + osDisk.OsState = osState + } + + managedDisk := &compute.SubResource{} + if managedDiskID, _ := d.Get("os_disk_managed_disk_id").(string); managedDiskID != "" { + managedDisk.ID = &managedDiskID + osDisk.ManagedDisk = managedDisk + } + + blobURI := d.Get("os_disk_blob_uri").(string) + if blobURI != "" { + osDisk.BlobURI = &blobURI + } + if v := d.Get("os_disk_caching").(string); v != "" { + caching := compute.CachingTypes(v) + osDisk.Caching = caching + } + + diskSize := int32(0) + if size := d.Get("os_disk_size_gb"); size != 0 { + diskSize = int32(size.(int)) + osDisk.DiskSizeGB = &diskSize + } + + dataDisks, err := expandAzureRmImageDataDisks(d) + if err != nil { + return err + } + + storageProfile := compute.ImageStorageProfile{ + OsDisk: osDisk, + DataDisks: &dataDisks, + } + + sourceVM := compute.SubResource{} + if v, ok := d.GetOk("source_virtual_machine_id"); ok { + vmID := v.(string) + sourceVM = compute.SubResource{ + ID: &vmID, + } + } + + properties := compute.ImageProperties{} + //either source VM or storage profile can be specified, but not both + if (compute.SubResource{}) == sourceVM { + properties = compute.ImageProperties{ + StorageProfile: &storageProfile, + } + } else { + properties = compute.ImageProperties{ + SourceVirtualMachine: &sourceVM, + } + } + + createImage := compute.Image{ + Name: &name, + Location: &location, + Tags: expandedTags, + ImageProperties: &properties, + } + + _, imageErr := imageClient.CreateOrUpdate(resGroup, name, createImage, make(chan struct{})) + err = <-imageErr + if err != nil { + return err + } + + read, err := imageClient.Get(resGroup, name, "") + if err != nil { + return err + } + if read.ID == nil { + return fmt.Errorf("[ERROR] Cannot read Image %s (resource group %s) ID", name, resGroup) + } + + d.SetId(*read.ID) + + return resourceArmImageRead(d, meta) +} + +func resourceArmImageRead(d *schema.ResourceData, meta interface{}) error { + imageClient := meta.(*ArmClient).imageClient + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resGroup := id.ResourceGroup + name := id.Path["images"] + + resp, err := imageClient.Get(resGroup, name, "") + if err != nil { + if resp.StatusCode == http.StatusNotFound { + d.SetId("") + return nil + } + return fmt.Errorf("[ERROR] Error making Read request on AzureRM Image %s (resource group %s): %s", name, resGroup, err) + } + + d.Set("name", resp.Name) + d.Set("resource_group_name", resGroup) + d.Set("location", resp.Location) + + //either source VM or storage profile can be specified, but not both + if resp.SourceVirtualMachine != nil { + flattenAzureRmSourceVMProperties(d, resp.SourceVirtualMachine) + } else if resp.StorageProfile != nil { + if resp.StorageProfile.OsDisk != nil { + d.Set("os_disk_os_type", resp.StorageProfile.OsDisk.OsType) + d.Set("os_disk_os_state", resp.StorageProfile.OsDisk.OsState) + + if resp.StorageProfile.OsDisk.ManagedDisk != nil { + d.Set("os_disk_managed_disk_id", *resp.StorageProfile.OsDisk.ManagedDisk.ID) + } + if resp.StorageProfile.OsDisk.BlobURI != nil { + d.Set("os_disk_blob_uri", *resp.StorageProfile.OsDisk.BlobURI) + } + d.Set("os_disk_caching", resp.StorageProfile.OsDisk.Caching) + if resp.StorageProfile.OsDisk.DiskSizeGB != nil { + d.Set("os_disk_size_gb", *resp.StorageProfile.OsDisk.DiskSizeGB) + } + } + + if resp.StorageProfile.DataDisks != nil { + if err := d.Set("data_disk", flattenAzureRmStorageProfileDataDisks(d, resp.StorageProfile)); err != nil { + return fmt.Errorf("[DEBUG] Error setting Managed Images Data Disks error: %#v", err) + } + } + } + + flattenAndSetTags(d, resp.Tags) + + return nil +} + +func resourceArmImageDelete(d *schema.ResourceData, meta interface{}) error { + imageClient := meta.(*ArmClient).imageClient + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resGroup := id.ResourceGroup + name := id.Path["images"] + + _, deleteErr := imageClient.Delete(resGroup, name, make(chan struct{})) + err = <-deleteErr + if err != nil { + return err + } + + return nil +} + +func flattenAzureRmSourceVMProperties(d *schema.ResourceData, properties *compute.SubResource) { + if properties.ID != nil { + d.Set("source_virtual_machine_id", properties.ID) + } +} + +func flattenAzureRmStorageProfileDataDisks(d *schema.ResourceData, storageProfile *compute.ImageStorageProfile) []interface{} { + disks := storageProfile.DataDisks + result := make([]interface{}, len(*disks)) + for i, disk := range *disks { + l := make(map[string]interface{}) + if disk.ManagedDisk != nil { + l["managed_disk_id"] = *disk.ManagedDisk.ID + } + l["blob_uri"] = disk.BlobURI + l["caching"] = string(disk.Caching) + if disk.DiskSizeGB != nil { + l["size_gb"] = *disk.DiskSizeGB + } + l["lun"] = *disk.Lun + + result[i] = l + } + return result +} + +func expandAzureRmImageDataDisks(d *schema.ResourceData) ([]compute.ImageDataDisk, error) { + + disks := d.Get("data_disk").([]interface{}) + + dataDisks := make([]compute.ImageDataDisk, 0, len(disks)) + for _, diskConfig := range disks { + config := diskConfig.(map[string]interface{}) + + managedDiskID := d.Get("managed_disk_id").(string) + blobURI := d.Get("blob_uri").(string) + lun := int32(config["lun"].(int)) + + dataDisk := compute.ImageDataDisk{ + Lun: &lun, + BlobURI: &blobURI, + } + + if size := d.Get("size_gb"); size != 0 { + diskSize := int32(size.(int)) + dataDisk.DiskSizeGB = &diskSize + } + + if v := d.Get("caching").(string); v != "" { + caching := compute.CachingTypes(v) + dataDisk.Caching = caching + } + + if managedDiskID != "" { + managedDisk := &compute.SubResource{} + managedDisk.ID = &managedDiskID + dataDisk.ManagedDisk = managedDisk + } + + dataDisks = append(dataDisks, dataDisk) + } + + return dataDisks, nil + +} diff --git a/azurerm/resource_arm_image_test.go b/azurerm/resource_arm_image_test.go new file mode 100644 index 000000000000..c816c5ab3750 --- /dev/null +++ b/azurerm/resource_arm_image_test.go @@ -0,0 +1,954 @@ +package azurerm + +import ( + "bytes" + "fmt" + "log" + "net/http" + "strings" + "testing" + + "golang.org/x/crypto/ssh" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAzureRMImage_standaloneImage(t *testing.T) { + ri := acctest.RandInt() + userName := "testadmin" + password := "Password1234!" + hostName := "tftestcustomimagesrc" + dnsName := fmt.Sprintf("%[1]s.westcentralus.cloudapp.azure.com", hostName) + sshPort := "22" + preConfig := fmt.Sprintf(testAccAzureRMImage_standaloneImage_setup, ri, userName, password, hostName) + postConfig := fmt.Sprintf(testAccAzureRMImage_standaloneImage_provision, ri, userName, password, hostName) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMImageDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + //need to create a vm and then reference it in the image creation + Config: preConfig, + Destroy: false, + Check: resource.ComposeTestCheckFunc( + testCheckAzureVMExists("azurerm_virtual_machine.testsource", true), + testGeneralizeVMImage(fmt.Sprintf("acctestRG-%[1]d", ri), "testsource", + userName, password, dnsName, sshPort), + ), + }, + resource.TestStep{ + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMImageExists("azurerm_image.test", true), + ), + }, + }, + }) +} + +func TestAccAzureRMImage_customImageVMFromVHD(t *testing.T) { + ri := acctest.RandInt() + userName := "testadmin" + password := "Password1234!" + hostName := "tftestcustomimagesrc" + dnsName := fmt.Sprintf("%[1]s.westcentralus.cloudapp.azure.com", hostName) + sshPort := "22" + preConfig := fmt.Sprintf(testAccAzureRMImage_customImage_fromVHD_setup, ri, userName, password, hostName) + postConfig := fmt.Sprintf(testAccAzureRMImage_customImage_fromVHD_provision, ri, userName, password, hostName) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMImageDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + //need to create a vm and then reference it in the image creation + Config: preConfig, + Destroy: false, + Check: resource.ComposeTestCheckFunc( + testCheckAzureVMExists("azurerm_virtual_machine.testsource", true), + testGeneralizeVMImage(fmt.Sprintf("acctestRG-%[1]d", ri), "testsource", + userName, password, dnsName, sshPort), + ), + }, + resource.TestStep{ + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureVMExists("azurerm_virtual_machine.testdestination", true), + ), + }, + }, + }) +} + +func TestAccAzureRMImage_customImageVMFromVM(t *testing.T) { + ri := acctest.RandInt() + userName := "testadmin" + password := "Password1234!" + hostName := "tftestcustomimagesrc" + dnsName := fmt.Sprintf("%[1]s.westcentralus.cloudapp.azure.com", hostName) + sshPort := "22" + preConfig := fmt.Sprintf(testAccAzureRMImage_customImage_fromVM_sourceVM, ri, userName, password, hostName) + postConfig := fmt.Sprintf(testAccAzureRMImage_customImage_fromVM_destinationVM, ri, userName, password, hostName) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMImageDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + //need to create a vm and then reference it in the image creation + Config: preConfig, + Destroy: false, + Check: resource.ComposeTestCheckFunc( + testCheckAzureVMExists("azurerm_virtual_machine.testsource", true), + testGeneralizeVMImage(fmt.Sprintf("acctestRG-%[1]d", ri), "testsource", + userName, password, dnsName, sshPort), + ), + }, + resource.TestStep{ + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureVMExists("azurerm_virtual_machine.testdestination", true), + ), + }, + }, + }) +} + +func testGeneralizeVMImage(groupName string, vmName string, userName string, password string, hostName string, port string) resource.TestCheckFunc { + return func(s *terraform.State) error { + vmClient := testAccProvider.Meta().(*ArmClient).vmClient + + deprovisionErr := deprovisionVM(userName, password, hostName, port) + if deprovisionErr != nil { + return fmt.Errorf("Bad: Deprovisioning error %s", deprovisionErr) + } + + _, deallocateErr := vmClient.Deallocate(groupName, vmName, nil) + err := <-deallocateErr + if err != nil { + return fmt.Errorf("Bad: Deallocating error %s", err) + } + + _, generalizeErr := vmClient.Generalize(groupName, vmName) + if generalizeErr != nil { + return fmt.Errorf("Bad: Generalizing error %s", generalizeErr) + } + + return nil + } +} + +func deprovisionVM(userName string, password string, hostName string, port string) error { + //SSH into the machine and execute a waagent deprovisioning command + var b bytes.Buffer + cmd := "sudo waagent -verbose -deprovision+user -force" + + config := &ssh.ClientConfig{ + User: userName, + Auth: []ssh.AuthMethod{ + ssh.Password(password), + }, + //line below for the newer versions of SSH Client + //HostKeyCallback: ssh.InsecureIgnoreHostKey(), + } + log.Printf("[INFO] Connecting to %s:%v remote server...", hostName, port) + + hostAddress := strings.Join([]string{hostName, port}, ":") + client, err := ssh.Dial("tcp", hostAddress, config) + if err != nil { + return fmt.Errorf("Bad: deprovisioning error %s", err.Error()) + } + + session, err := client.NewSession() + if err != nil { + return fmt.Errorf("Bad: deprovisioning error, failure creating session %s", err.Error()) + } + defer session.Close() + + session.Stdout = &b + if err := session.Run(cmd); err != nil { + return fmt.Errorf("Bad: deprovisioning error, failure running command %s", err.Error()) + } + + return nil +} + +func testCheckAzureRMImageExists(name string, shouldExist bool) resource.TestCheckFunc { + return func(s *terraform.State) error { + + log.Printf("[INFO] testing MANAGED IMAGE EXISTS - BEGIN.") + + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + dName := rs.Primary.Attributes["name"] + resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] + if !hasResourceGroup { + return fmt.Errorf("Bad: no resource group found in state for image: %s", dName) + } + + conn := testAccProvider.Meta().(*ArmClient).imageClient + + resp, err := conn.Get(resourceGroup, dName, "") + if err != nil { + return fmt.Errorf("Bad: Get on imageClient: %s", err) + } + + if resp.StatusCode == http.StatusNotFound && shouldExist { + return fmt.Errorf("Bad: Image %q (resource group %q) does not exist", dName, resourceGroup) + } + if resp.StatusCode != http.StatusNotFound && !shouldExist { + return fmt.Errorf("Bad: Image %q (resource group %q) still exists", dName, resourceGroup) + } + + return nil + } +} + +func testCheckAzureVMExists(sourceVM string, shouldExist bool) resource.TestCheckFunc { + return func(s *terraform.State) error { + log.Printf("[INFO] testing MANAGED IMAGE VM EXISTS - BEGIN.") + + vmClient := testAccProvider.Meta().(*ArmClient).vmClient + vmRs, vmOk := s.RootModule().Resources[sourceVM] + if !vmOk { + return fmt.Errorf("VM Not found: %s", sourceVM) + } + vmName := vmRs.Primary.Attributes["name"] + + resourceGroup, hasResourceGroup := vmRs.Primary.Attributes["resource_group_name"] + if !hasResourceGroup { + return fmt.Errorf("Bad: no resource group found in state for VM: %s", vmName) + } + + resp, err := vmClient.Get(resourceGroup, vmName, "") + if err != nil { + return fmt.Errorf("Bad: Get on vmClient: %s", err) + } + + if resp.StatusCode == http.StatusNotFound && shouldExist { + return fmt.Errorf("Bad: VM %q (resource group %q) does not exist", vmName, resourceGroup) + } + if resp.StatusCode != http.StatusNotFound && !shouldExist { + return fmt.Errorf("Bad: VM %q (resource group %q) still exists", vmName, resourceGroup) + } + + log.Printf("[INFO] testing MANAGED IMAGE VM EXISTS - END.") + + return nil + } +} + +func testCheckAzureRMImageDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*ArmClient).diskClient + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_image" { + continue + } + + name := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + resp, err := conn.Get(resourceGroup, name) + + if err != nil { + return nil + } + + if resp.StatusCode != http.StatusNotFound { + return fmt.Errorf("Managed Disk still exists: \n%#v", resp.Properties) + } + } + + return nil +} + +var testAccAzureRMImage_standaloneImage_setup = ` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]d" + location = "West Central US" +} + +resource "azurerm_virtual_network" "test" { + name = "acctvn-%[1]d" + address_space = ["10.0.0.0/16"] + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test" { + name = "acctsub-%[1]d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" +} + +resource "azurerm_public_ip" "test" { + name = "acctpip-%[1]d" + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" + public_ip_address_allocation = "Dynamic" + domain_name_label = "%[4]s" +} + +resource "azurerm_network_interface" "testsource" { + name = "acctnicsource-%[1]d" + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "testconfigurationsource" + subnet_id = "${azurerm_subnet.test.id}" + private_ip_address_allocation = "dynamic" + public_ip_address_id = "${azurerm_public_ip.test.id}" + } +} + +resource "azurerm_storage_account" "test" { + name = "accsa%[1]d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "West Central US" + account_type = "Standard_LRS" + + tags { + environment = "Dev" + } +} + +resource "azurerm_storage_container" "test" { + name = "vhds" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + container_access_type = "blob" +} + +resource "azurerm_virtual_machine" "testsource" { + name = "testsource" + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" + network_interface_ids = ["${azurerm_network_interface.testsource.id}"] + vm_size = "Standard_D1_v2" + + storage_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } + + storage_os_disk { + name = "myosdisk1" + vhd_uri = "${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}/myosdisk1.vhd" + caching = "ReadWrite" + create_option = "FromImage" + disk_size_gb = "30" + } + + os_profile { + computer_name = "mdimagetestsource" + admin_username = "%[2]s" + admin_password = "%[3]s" + } + + os_profile_linux_config { + disable_password_authentication = false + } + + tags { + environment = "Dev" + cost-center = "Ops" + } +} +` + +var testAccAzureRMImage_standaloneImage_provision = ` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]d" + location = "West Central US" +} + +resource "azurerm_virtual_network" "test" { + name = "acctvn-%[1]d" + address_space = ["10.0.0.0/16"] + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test" { + name = "acctsub-%[1]d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" +} + +resource "azurerm_public_ip" "test" { + name = "acctpip-%[1]d" + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" + public_ip_address_allocation = "Dynamic" + domain_name_label = "%[4]s" +} + +resource "azurerm_network_interface" "testsource" { + name = "acctnicsource-%[1]d" + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "testconfigurationsource" + subnet_id = "${azurerm_subnet.test.id}" + private_ip_address_allocation = "dynamic" + public_ip_address_id = "${azurerm_public_ip.test.id}" + } +} + +resource "azurerm_storage_account" "test" { + name = "accsa%[1]d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "West Central US" + account_type = "Standard_LRS" + + tags { + environment = "Dev" + } +} + +resource "azurerm_storage_container" "test" { + name = "vhds" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + container_access_type = "blob" +} + +resource "azurerm_virtual_machine" "testsource" { + name = "testsource" + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" + network_interface_ids = ["${azurerm_network_interface.testsource.id}"] + vm_size = "Standard_D1_v2" + + storage_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } + + storage_os_disk { + name = "myosdisk1" + vhd_uri = "${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}/myosdisk1.vhd" + caching = "ReadWrite" + create_option = "FromImage" + disk_size_gb = "45" + } + + os_profile { + computer_name = "mdimagetestsource" + admin_username = "%[2]s" + admin_password = "%[3]s" + } + + os_profile_linux_config { + disable_password_authentication = false + } + + tags { + environment = "Dev" + cost-center = "Ops" + } +} + +resource "azurerm_image" "test" { + name = "accteste" + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" + os_disk_os_type = "Linux" + os_disk_os_state = "Generalized" + os_disk_blob_uri = "${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}/myosdisk1.vhd" + os_disk_size_gb = 30 + os_disk_caching = "None" + + tags { + environment = "Dev" + cost-center = "Ops" + } +} +` + +var testAccAzureRMImage_customImage_fromVHD_setup = ` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]d" + location = "West Central US" +} + +resource "azurerm_virtual_network" "test" { + name = "acctvn-%[1]d" + address_space = ["10.0.0.0/16"] + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test" { + name = "acctsub-%[1]d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" +} + +resource "azurerm_public_ip" "test" { + name = "acctpip-%[1]d" + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" + public_ip_address_allocation = "Dynamic" + domain_name_label = "%[4]s" +} + +resource "azurerm_network_interface" "testsource" { + name = "acctnicsource-%[1]d" + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "testconfigurationsource" + subnet_id = "${azurerm_subnet.test.id}" + private_ip_address_allocation = "dynamic" + public_ip_address_id = "${azurerm_public_ip.test.id}" + } +} + +resource "azurerm_storage_account" "test" { + name = "accsa%[1]d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "West Central US" + account_type = "Standard_LRS" + + tags { + environment = "Dev" + } +} + +resource "azurerm_storage_container" "test" { + name = "vhds" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + container_access_type = "blob" +} + +resource "azurerm_virtual_machine" "testsource" { + name = "testsource" + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" + network_interface_ids = ["${azurerm_network_interface.testsource.id}"] + vm_size = "Standard_D1_v2" + + storage_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } + + storage_os_disk { + name = "myosdisk1" + vhd_uri = "${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}/myosdisk1.vhd" + caching = "ReadWrite" + create_option = "FromImage" + disk_size_gb = "30" + } + + os_profile { + computer_name = "mdimagetestsource" + admin_username = "%[2]s" + admin_password = "%[3]s" + } + + os_profile_linux_config { + disable_password_authentication = false + } + + tags { + environment = "Dev" + cost-center = "Ops" + } +} +` + +var testAccAzureRMImage_customImage_fromVHD_provision = ` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]d" + location = "West Central US" +} + +resource "azurerm_virtual_network" "test" { + name = "acctvn-%[1]d" + address_space = ["10.0.0.0/16"] + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test" { + name = "acctsub-%[1]d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" +} + +resource "azurerm_public_ip" "test" { + name = "acctpip-%[1]d" + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" + public_ip_address_allocation = "Dynamic" + domain_name_label = "%[4]s" +} + +resource "azurerm_network_interface" "testsource" { + name = "acctnicsource-%[1]d" + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "testconfigurationsource" + subnet_id = "${azurerm_subnet.test.id}" + private_ip_address_allocation = "dynamic" + public_ip_address_id = "${azurerm_public_ip.test.id}" + } +} + +resource "azurerm_storage_account" "test" { + name = "accsa%[1]d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "West Central US" + account_type = "Standard_LRS" + + tags { + environment = "Dev" + } +} + +resource "azurerm_storage_container" "test" { + name = "vhds" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + container_access_type = "blob" +} + +resource "azurerm_virtual_machine" "testsource" { + name = "testsource" + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" + network_interface_ids = ["${azurerm_network_interface.testsource.id}"] + vm_size = "Standard_D1_v2" + + storage_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } + + storage_os_disk { + name = "myosdisk1" + vhd_uri = "${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}/myosdisk1.vhd" + caching = "ReadWrite" + create_option = "FromImage" + disk_size_gb = "45" + } + + os_profile { + computer_name = "mdimagetestsource" + admin_username = "%[2]s" + admin_password = "%[3]s" + } + + os_profile_linux_config { + disable_password_authentication = false + } + + tags { + environment = "Dev" + cost-center = "Ops" + } +} + +resource "azurerm_image" "testdestination" { + name = "accteste" + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" + os_disk_os_type = "Linux" + os_disk_os_state = "Generalized" + os_disk_blob_uri = "${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}/myosdisk1.vhd" + os_disk_size_gb = 30 + os_disk_caching = "None" + + tags { + environment = "Dev" + cost-center = "Ops" + } +} + +resource "azurerm_network_interface" "testdestination" { + name = "acctnicdest-%[1]d" + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "testconfiguration2" + subnet_id = "${azurerm_subnet.test.id}" + private_ip_address_allocation = "dynamic" + } +} + +resource "azurerm_virtual_machine" "testdestination" { + name = "acctvm" + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" + network_interface_ids = ["${azurerm_network_interface.testdestination.id}"] + vm_size = "Standard_D1_v2" + + storage_image_reference { + image_id = "${azurerm_image.testdestination.id}" + } + + storage_os_disk { + name = "myosdisk1" + caching = "ReadWrite" + create_option = "FromImage" + } + + os_profile { + computer_name = "mdimagetestsource" + admin_username = "%[2]s" + admin_password = "%[3]s" + } + + os_profile_linux_config { + disable_password_authentication = false + } + + tags { + environment = "Dev" + cost-center = "Ops" + } +} +` + +var testAccAzureRMImage_customImage_fromVM_sourceVM = ` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]d" + location = "West Central US" +} + +resource "azurerm_virtual_network" "test" { + name = "acctvn-%[1]d" + address_space = ["10.0.0.0/16"] + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test" { + name = "acctsub-%[1]d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" +} + +resource "azurerm_public_ip" "test" { + name = "acctpip-%[1]d" + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" + public_ip_address_allocation = "Dynamic" + domain_name_label = "%[4]s" +} + +resource "azurerm_network_interface" "testsource" { + name = "acctnicsource-%[1]d" + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "testconfigurationsource" + subnet_id = "${azurerm_subnet.test.id}" + private_ip_address_allocation = "dynamic" + public_ip_address_id = "${azurerm_public_ip.test.id}" + } +} + +resource "azurerm_virtual_machine" "testsource" { + name = "testsource" + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" + network_interface_ids = ["${azurerm_network_interface.testsource.id}"] + vm_size = "Standard_D1_v2" + + storage_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } + + storage_os_disk { + name = "myosdisk1" + caching = "ReadWrite" + create_option = "FromImage" + } + + os_profile { + computer_name = "mdimagetestsource" + admin_username = "%[2]s" + admin_password = "%[3]s" + } + + os_profile_linux_config { + disable_password_authentication = false + } + + tags { + environment = "Dev" + cost-center = "Ops" + } +} +` + +var testAccAzureRMImage_customImage_fromVM_destinationVM = ` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]d" + location = "West Central US" +} + +resource "azurerm_virtual_network" "test" { + name = "acctvn-%[1]d" + address_space = ["10.0.0.0/16"] + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test" { + name = "acctsub-%[1]d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" +} + +resource "azurerm_public_ip" "test" { + name = "acctpip-%[1]d" + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" + public_ip_address_allocation = "Dynamic" + domain_name_label = "%[4]s" +} + +resource "azurerm_network_interface" "testsource" { + name = "acctnicsource-%[1]d" + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "testconfigurationsource" + subnet_id = "${azurerm_subnet.test.id}" + private_ip_address_allocation = "dynamic" + public_ip_address_id = "${azurerm_public_ip.test.id}" + } +} + +resource "azurerm_virtual_machine" "testsource" { + name = "testsource" + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" + network_interface_ids = ["${azurerm_network_interface.testsource.id}"] + vm_size = "Standard_D1_v2" + + storage_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } + + storage_os_disk { + name = "myosdisk1" + caching = "ReadWrite" + create_option = "FromImage" + } + + os_profile { + computer_name = "mdimagetestsource" + admin_username = "%[2]s" + admin_password = "%[3]s" + } + + os_profile_linux_config { + disable_password_authentication = false + } + + tags { + environment = "Dev" + cost-center = "Ops" + } +} + +resource "azurerm_image" "testdestination" { + name = "acctestdest-%[1]d" + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" + source_virtual_machine_id = "${azurerm_virtual_machine.testsource.id}" + tags { + environment = "acctest" + cost-center = "ops" + } +} + +resource "azurerm_network_interface" "testdestination" { + name = "acctnicdest-%[1]d" + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "testconfiguration2" + subnet_id = "${azurerm_subnet.test.id}" + private_ip_address_allocation = "dynamic" + } +} + +resource "azurerm_virtual_machine" "testdestination" { + name = "testdestination" + location = "West Central US" + resource_group_name = "${azurerm_resource_group.test.name}" + network_interface_ids = ["${azurerm_network_interface.testdestination.id}"] + vm_size = "Standard_D1_v2" + + storage_image_reference { + image_id = "${azurerm_image.testdestination.id}" + } + + storage_os_disk { + name = "myosdisk2" + caching = "ReadWrite" + create_option = "FromImage" + } + + os_profile { + computer_name = "mdimagetestdest" + admin_username = "%[2]s" + admin_password = "%[3]s" + } + + os_profile_linux_config { + disable_password_authentication = false + } + + tags { + environment = "Dev" + cost-center = "Ops" + } +} +` diff --git a/azurerm/resource_arm_virtual_machine.go b/azurerm/resource_arm_virtual_machine.go index 8e0f56b24ec2..4b3604a2c57d 100644 --- a/azurerm/resource_arm_virtual_machine.go +++ b/azurerm/resource_arm_virtual_machine.go @@ -96,21 +96,27 @@ func resourceArmVirtualMachine() *schema.Resource { MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + "image_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "publisher": { Type: schema.TypeString, - Required: true, + Optional: true, ForceNew: true, }, "offer": { Type: schema.TypeString, - Required: true, + Optional: true, ForceNew: true, }, "sku": { Type: schema.TypeString, - Required: true, + Optional: true, ForceNew: true, }, @@ -845,10 +851,18 @@ func resourceArmVirtualMachinePlanHash(v interface{}) int { func resourceArmVirtualMachineStorageImageReferenceHash(v interface{}) int { var buf bytes.Buffer m := v.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%s-", m["publisher"].(string))) - buf.WriteString(fmt.Sprintf("%s-", m["offer"].(string))) - buf.WriteString(fmt.Sprintf("%s-", m["sku"].(string))) - + if m["publisher"] != nil { + buf.WriteString(fmt.Sprintf("%s-", m["publisher"].(string))) + } + if m["offer"] != nil { + buf.WriteString(fmt.Sprintf("%s-", m["offer"].(string))) + } + if m["sku"] != nil { + buf.WriteString(fmt.Sprintf("%s-", m["sku"].(string))) + } + if m["image_id"] != nil { + buf.WriteString(fmt.Sprintf("%s-", m["image_id"].(string))) + } return hashcode.String(buf.String()) } @@ -901,13 +915,21 @@ func flattenAzureRmVirtualMachinePlan(plan *compute.Plan) []interface{} { func flattenAzureRmVirtualMachineImageReference(image *compute.ImageReference) []interface{} { result := make(map[string]interface{}) - result["offer"] = *image.Offer - result["publisher"] = *image.Publisher - result["sku"] = *image.Sku - + if image.Publisher != nil { + result["publisher"] = *image.Publisher + } + if image.Offer != nil { + result["offer"] = *image.Offer + } + if image.Sku != nil { + result["sku"] = *image.Sku + } if image.Version != nil { result["version"] = *image.Version } + if image.ID != nil { + result["image_id"] = *image.ID + } return []interface{}{result} } @@ -1332,14 +1354,12 @@ func expandAzureRmVirtualMachineDataDisk(d *schema.ResourceData) ([]compute.Data data_disk.ManagedDisk = managedDisk } - //BEGIN: code to be removed after GH-13016 is merged if vhdURI != "" && managedDiskID != "" { return nil, fmt.Errorf("[ERROR] Conflict between `vhd_uri` and `managed_disk_id` (only one or the other can be used)") } if vhdURI != "" && managedDiskType != "" { return nil, fmt.Errorf("[ERROR] Conflict between `vhd_uri` and `managed_disk_type` (only one or the other can be used)") } - //END: code to be removed after GH-13016 is merged if managedDiskID == "" && strings.EqualFold(string(data_disk.CreateOption), string(compute.Attach)) { return nil, fmt.Errorf("[ERROR] Must specify which disk to attach") } @@ -1383,18 +1403,32 @@ func expandAzureRmVirtualMachineImageReference(d *schema.ResourceData) (*compute storageImageRefs := d.Get("storage_image_reference").(*schema.Set).List() storageImageRef := storageImageRefs[0].(map[string]interface{}) + imageReference := compute.ImageReference{} - publisher := storageImageRef["publisher"].(string) - offer := storageImageRef["offer"].(string) - sku := storageImageRef["sku"].(string) - version := storageImageRef["version"].(string) + if storageImageRef["image_id"] != "" { + imageID := storageImageRef["image_id"].(string) + imageReference = compute.ImageReference{ + ID: &imageID, + } + } else { + publisher := storageImageRef["publisher"].(string) + offer := storageImageRef["offer"].(string) + sku := storageImageRef["sku"].(string) + version := storageImageRef["version"].(string) + + imageReference = compute.ImageReference{ + Publisher: &publisher, + Offer: &offer, + Sku: &sku, + Version: &version, + } + } - return &compute.ImageReference{ - Publisher: &publisher, - Offer: &offer, - Sku: &sku, - Version: &version, - }, nil + if imageReference.ID != nil && imageReference.Publisher != nil { + return nil, fmt.Errorf("[ERROR] Conflict between `image_id` and `publisher` (only one or the other can be used)") + } + + return &imageReference, nil } func expandAzureRmVirtualMachineNetworkProfile(d *schema.ResourceData) compute.NetworkProfile { diff --git a/website/azurerm.erb b/website/azurerm.erb index 95c5b39791cf..cc7530e056d7 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -174,6 +174,15 @@ + > + Image Resources + + + > Network Resources - - - > - Image Resources - - + > Network Resources diff --git a/website/docs/r/image.html.markdown b/website/docs/r/image.html.markdown index e63a102e9554..85ac8cd603a2 100644 --- a/website/docs/r/image.html.markdown +++ b/website/docs/r/image.html.markdown @@ -57,8 +57,8 @@ The following arguments are supported: * `location` - (Required) Specified the supported Azure location where the resource exists. Changing this forces a new resource to be created. * `source_virtual_machine_id` - (Optional) The Virtual Machine ID from which to create the image. -* `os_disk` - (Optional) One or more os_disk elements as defined below. -* `data_disk` - (Optional) One or more data_disk elements as defined below. +* `os_disk` - (Optional) One or more `os_disk` elements as defined below. +* `data_disk` - (Optional) One or more `data_disk` elements as defined below. * `tags` - (Optional) A mapping of tags to assign to the resource. `os_disk` supports the following: From af84909994a76229342414076acc1d86af598f8f Mon Sep 17 00:00:00 2001 From: Eugene Chuvyrov Date: Tue, 11 Jul 2017 14:16:09 -0700 Subject: [PATCH 07/10] Minor changes to Image properties --- azurerm/resource_arm_image.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/azurerm/resource_arm_image.go b/azurerm/resource_arm_image.go index f55d3cb8badf..0a1303673afd 100644 --- a/azurerm/resource_arm_image.go +++ b/azurerm/resource_arm_image.go @@ -51,7 +51,6 @@ func resourceArmImage() *schema.Resource { "os_type": { Type: schema.TypeString, Optional: true, - ForceNew: true, ValidateFunc: validation.StringInSlice([]string{ string(compute.Linux), string(compute.Windows), @@ -62,7 +61,6 @@ func resourceArmImage() *schema.Resource { "os_state": { Type: schema.TypeString, Optional: true, - ForceNew: true, ValidateFunc: validation.StringInSlice([]string{ string(compute.Generalized), string(compute.Specialized), @@ -79,13 +77,14 @@ func resourceArmImage() *schema.Resource { "blob_uri": { Type: schema.TypeString, Optional: true, + Computed: true, ForceNew: true, }, "caching": { Type: schema.TypeString, Optional: true, - Default: string(compute.None), + Computed: true, ValidateFunc: validation.StringInSlice([]string{ string(compute.None), string(compute.ReadOnly), @@ -123,12 +122,13 @@ func resourceArmImage() *schema.Resource { "blob_uri": { Type: schema.TypeString, Optional: true, + Computed: true, }, "caching": { Type: schema.TypeString, Optional: true, - Default: "None", + Computed: true, ValidateFunc: validation.StringInSlice([]string{ string(compute.None), string(compute.ReadOnly), @@ -140,6 +140,7 @@ func resourceArmImage() *schema.Resource { "size_gb": { Type: schema.TypeInt, Optional: true, + Computed: true, }, }, }, @@ -252,11 +253,10 @@ func resourceArmImageRead(d *schema.ResourceData, meta interface{}) error { d.Set("resource_group_name", resGroup) d.Set("location", resp.Location) + //either source VM or storage profile can be specified, but not both if resp.SourceVirtualMachine != nil { d.Set("source_virtual_machine_id", resp.SourceVirtualMachine.ID) - } - - if resp.StorageProfile != nil { + } else if resp.StorageProfile != nil { if err := d.Set("os_disk", flattenAzureRmStorageProfileOsDisk(d, resp.StorageProfile)); err != nil { return fmt.Errorf("[DEBUG] Error setting AzureRM Image OS Disk error: %#v", err) } From 2790ca2a98036eb16cff0604e225c83c7235219a Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Wed, 19 Jul 2017 14:18:16 +0100 Subject: [PATCH 08/10] Fixing the Creating from VHD example --- website/docs/r/image.html.markdown | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/website/docs/r/image.html.markdown b/website/docs/r/image.html.markdown index 85ac8cd603a2..e841db04843a 100644 --- a/website/docs/r/image.html.markdown +++ b/website/docs/r/image.html.markdown @@ -23,10 +23,12 @@ resource "azurerm_image" "test" { location = "West US" resource_group_name = "${azurerm_resource_group.test.name}" - os_disk_os_type = "Linux" - os_disk_os_state = "Generalized" - os_disk_blob_uri = "{blob_uri}" - os_disk_size_gb = 30 + os_disk { + os_type = "Linux" + os_state = "Generalized" + blob_uri = "{blob_uri}" + size_gb = 30 + } } ``` @@ -67,7 +69,7 @@ The following arguments are supported: * `os_state` - (Required) Specifies the state of the operating system contained in the blob. Currently, the only value is Generalized. * `managed_disk_id` - (Optional) Specifies the ID of the managed disk resource that you want to use to create the image. * `blob_uri` - (Optional) Specifies the URI in Azure storage of the blob that you want to use to create the image. -* `caching` - (Optional) Specifies the caching mode as 'readonly', 'readwrite', or 'none'. The default is none. +* `caching` - (Optional) Specifies the caching mode as `ReadOnly`, `ReadWrite`, or `None`. The default is `None`. `data_disk` supports the following: From bf1adf3c8ebd31191de373f66b2b48ed2a7ab916 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Wed, 19 Jul 2017 14:21:32 +0100 Subject: [PATCH 09/10] Managed Disk -> Managed Image --- azurerm/resource_arm_image_test.go | 38 +++++++++++++++--------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/azurerm/resource_arm_image_test.go b/azurerm/resource_arm_image_test.go index 4398a2ca9773..36d74a5c9170 100644 --- a/azurerm/resource_arm_image_test.go +++ b/azurerm/resource_arm_image_test.go @@ -261,7 +261,7 @@ func testCheckAzureRMImageDestroy(s *terraform.State) error { } if resp.StatusCode != http.StatusNotFound { - return fmt.Errorf("Managed Disk still exists: \n%#v", resp.Properties) + return fmt.Errorf("Managed Image still exists: \n%#v", resp.Properties) } } @@ -341,7 +341,7 @@ resource "azurerm_virtual_machine" "testsource" { sku = "16.04-LTS" version = "latest" } - + storage_os_disk { name = "myosdisk1" vhd_uri = "${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}/myosdisk1.vhd" @@ -349,7 +349,7 @@ resource "azurerm_virtual_machine" "testsource" { create_option = "FromImage" disk_size_gb = "30" } - + os_profile { computer_name = "mdimagetestsource" admin_username = "%[2]s" @@ -441,7 +441,7 @@ resource "azurerm_virtual_machine" "testsource" { sku = "16.04-LTS" version = "latest" } - + storage_os_disk { name = "myosdisk1" vhd_uri = "${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}/myosdisk1.vhd" @@ -449,7 +449,7 @@ resource "azurerm_virtual_machine" "testsource" { create_option = "FromImage" disk_size_gb = "30" } - + os_profile { computer_name = "mdimagetestsource" admin_username = "%[2]s" @@ -470,13 +470,13 @@ resource "azurerm_image" "test" { name = "accteste" location = "West US" resource_group_name = "${azurerm_resource_group.test.name}" - + os_disk { os_type = "Linux" os_state = "Generalized" blob_uri = "${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}/myosdisk1.vhd" size_gb = 30 - caching = "None" + caching = "None" } tags { @@ -560,7 +560,7 @@ resource "azurerm_virtual_machine" "testsource" { sku = "16.04-LTS" version = "latest" } - + storage_os_disk { name = "myosdisk1" vhd_uri = "${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}/myosdisk1.vhd" @@ -568,7 +568,7 @@ resource "azurerm_virtual_machine" "testsource" { create_option = "FromImage" disk_size_gb = "30" } - + os_profile { computer_name = "mdimagetestsource" admin_username = "%[2]s" @@ -668,7 +668,7 @@ resource "azurerm_virtual_machine" "testsource" { create_option = "FromImage" disk_size_gb = "45" } - + os_profile { computer_name = "mdimagetestsource" admin_username = "%[2]s" @@ -689,14 +689,14 @@ resource "azurerm_image" "testdestination" { name = "accteste" location = "West US" resource_group_name = "${azurerm_resource_group.test.name}" - os_disk { + os_disk { os_type = "Linux" os_state = "Generalized" blob_uri = "${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}/myosdisk1.vhd" size_gb = 30 - caching = "None" + caching = "None" } - + tags { environment = "Dev" cost-center = "Ops" @@ -731,7 +731,7 @@ resource "azurerm_virtual_machine" "testdestination" { caching = "ReadWrite" create_option = "FromImage" } - + os_profile { computer_name = "mdimagetestsource" admin_username = "%[2]s" @@ -811,7 +811,7 @@ resource "azurerm_virtual_machine" "testsource" { caching = "ReadWrite" create_option = "FromImage" } - + os_profile { computer_name = "mdimagetestsource" admin_username = "%[2]s" @@ -868,7 +868,7 @@ resource "azurerm_network_interface" "testsource" { name = "testconfigurationsource" subnet_id = "${azurerm_subnet.test.id}" private_ip_address_allocation = "dynamic" - public_ip_address_id = "${azurerm_public_ip.test.id}" + public_ip_address_id = "${azurerm_public_ip.test.id}" } } @@ -891,7 +891,7 @@ resource "azurerm_virtual_machine" "testsource" { caching = "ReadWrite" create_option = "FromImage" } - + os_profile { computer_name = "mdimagetestsource" admin_username = "%[2]s" @@ -912,7 +912,7 @@ resource "azurerm_image" "testdestination" { name = "acctestdest-%[1]d" location = "West US" resource_group_name = "${azurerm_resource_group.test.name}" - source_virtual_machine_id = "${azurerm_virtual_machine.testsource.id}" + source_virtual_machine_id = "${azurerm_virtual_machine.testsource.id}" tags { environment = "acctest" cost-center = "ops" @@ -947,7 +947,7 @@ resource "azurerm_virtual_machine" "testdestination" { caching = "ReadWrite" create_option = "FromImage" } - + os_profile { computer_name = "mdimagetestdest" admin_username = "%[2]s" From 8f91395db4ae6de3b161c3095784413ab178904e Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Wed, 19 Jul 2017 14:39:14 +0100 Subject: [PATCH 10/10] Updating the casings to match azure --- website/docs/r/image.html.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/r/image.html.markdown b/website/docs/r/image.html.markdown index e841db04843a..4538e74740b5 100644 --- a/website/docs/r/image.html.markdown +++ b/website/docs/r/image.html.markdown @@ -69,14 +69,14 @@ The following arguments are supported: * `os_state` - (Required) Specifies the state of the operating system contained in the blob. Currently, the only value is Generalized. * `managed_disk_id` - (Optional) Specifies the ID of the managed disk resource that you want to use to create the image. * `blob_uri` - (Optional) Specifies the URI in Azure storage of the blob that you want to use to create the image. -* `caching` - (Optional) Specifies the caching mode as `ReadOnly`, `ReadWrite`, or `None`. The default is `None`. +* `caching` - (Optional) Specifies the caching mode as `ReadWrite`, `ReadOnly`, or `None`. The default is `None`. `data_disk` supports the following: * `lun` - (Required) Specifies the logical unit number of the data disk. * `managed_disk_id` - (Optional) Specifies the ID of the managed disk resource that you want to use to create the image. * `blob_uri` - (Optional) Specifies the URI in Azure storage of the blob that you want to use to create the image. -* `caching` - (Optional) Specifies the caching mode as readonly, readwrite, or none. The default is none. +* `caching` - (Optional) Specifies the caching mode as `ReadWrite`, `ReadOnly`, or `None`. The default is `None`. * `size_gb` - (Optional) Specifies the size of the image to be created. The target size can't be smaller than the source size. ## Attributes Reference