diff --git a/builtin/providers/vsphere/resource_vsphere_virtual_machine.go b/builtin/providers/vsphere/resource_vsphere_virtual_machine.go
index 1c90d6176b..73712b345a 100644
--- a/builtin/providers/vsphere/resource_vsphere_virtual_machine.go
+++ b/builtin/providers/vsphere/resource_vsphere_virtual_machine.go
@@ -53,6 +53,11 @@ type windowsOptConfig struct {
domainUserPassword string
}
+type cdrom struct {
+ datastore string
+ path string
+}
+
type virtualMachine struct {
name string
folder string
@@ -65,6 +70,7 @@ type virtualMachine struct {
template string
networkInterfaces []networkInterface
hardDisks []hardDisk
+ cdroms []cdrom
gateway string
domain string
timeZone string
@@ -328,6 +334,27 @@ func resourceVSphereVirtualMachine() *schema.Resource {
},
},
+ "cdrom": &schema.Schema{
+ Type: schema.TypeList,
+ Optional: true,
+ ForceNew: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "datastore": &schema.Schema{
+ Type: schema.TypeString,
+ Required: true,
+ ForceNew: true,
+ },
+
+ "path": &schema.Schema{
+ Type: schema.TypeString,
+ Required: true,
+ ForceNew: true,
+ },
+ },
+ },
+ },
+
"boot_delay": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
@@ -492,6 +519,25 @@ func resourceVSphereVirtualMachineCreate(d *schema.ResourceData, meta interface{
log.Printf("[DEBUG] disk init: %v", disks)
}
+ if vL, ok := d.GetOk("cdrom"); ok {
+ cdroms := make([]cdrom, len(vL.([]interface{})))
+ for i, v := range vL.([]interface{}) {
+ c := v.(map[string]interface{})
+ if v, ok := c["datastore"].(string); ok && v != "" {
+ cdroms[i].datastore = v
+ } else {
+ return fmt.Errorf("Datastore argument must be specified when attaching a cdrom image.")
+ }
+ if v, ok := c["path"].(string); ok && v != "" {
+ cdroms[i].path = v
+ } else {
+ return fmt.Errorf("Path argument must be specified when attaching a cdrom image.")
+ }
+ }
+ vm.cdroms = cdroms
+ log.Printf("[DEBUG] cdrom init: %v", cdroms)
+ }
+
if vm.template != "" {
err := vm.deployVirtualMachine(client)
if err != nil {
@@ -749,6 +795,31 @@ func addHardDisk(vm *object.VirtualMachine, size, iops int64, diskType string) e
}
}
+// addCdrom adds a new virtual cdrom drive to the VirtualMachine and attaches an image (ISO) to it from a datastore path.
+func addCdrom(vm *object.VirtualMachine, datastore, path string) error {
+ devices, err := vm.Device(context.TODO())
+ if err != nil {
+ return err
+ }
+ log.Printf("[DEBUG] vm devices: %#v", devices)
+
+ controller, err := devices.FindIDEController("")
+ if err != nil {
+ return err
+ }
+ log.Printf("[DEBUG] ide controller: %#v", controller)
+
+ c, err := devices.CreateCdrom(controller)
+ if err != nil {
+ return err
+ }
+
+ c = devices.InsertIso(c, fmt.Sprintf("[%s] %s", datastore, path))
+ log.Printf("[DEBUG] addCdrom: %#v", c)
+
+ return vm.AddDevice(context.TODO(), c)
+}
+
// buildNetworkDevice builds VirtualDeviceConfigSpec for Network Device.
func buildNetworkDevice(f *find.Finder, label, adapterType string) (*types.VirtualDeviceConfigSpec, error) {
network, err := f.Network(context.TODO(), "*"+label)
@@ -940,6 +1011,21 @@ func findDatastore(c *govmomi.Client, sps types.StoragePlacementSpec) (*object.D
return datastore, nil
}
+// createCdroms is a helper function to attach virtual cdrom devices (and their attached disk images) to a virtual IDE controller.
+func createCdroms(vm *object.VirtualMachine, cdroms []cdrom) error {
+ log.Printf("[DEBUG] add cdroms: %v", cdroms)
+ for _, cd := range cdroms {
+ log.Printf("[DEBUG] add cdrom (datastore): %v", cd.datastore)
+ log.Printf("[DEBUG] add cdrom (cd path): %v", cd.path)
+ err := addCdrom(vm, cd.datastore, cd.path)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
// createVirtualMachine creates a new VirtualMachine.
func (vm *virtualMachine) createVirtualMachine(c *govmomi.Client) error {
dc, err := getDatacenter(c, vm.datacenter)
@@ -1077,6 +1163,7 @@ func (vm *virtualMachine) createVirtualMachine(c *govmomi.Client) error {
Operation: types.VirtualDeviceConfigSpecOperationAdd,
Device: scsi,
})
+
configSpec.Files = &types.VirtualMachineFileInfo{VmPathName: fmt.Sprintf("[%s]", mds.Name)}
task, err := folder.CreateVM(context.TODO(), configSpec, resourcePool, nil)
@@ -1104,6 +1191,12 @@ func (vm *virtualMachine) createVirtualMachine(c *govmomi.Client) error {
return err
}
}
+
+ // Create the cdroms if needed.
+ if err := createCdroms(newVM, vm.cdroms); err != nil {
+ return err
+ }
+
return nil
}
@@ -1255,6 +1348,7 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error {
NumCoresPerSocket: 1,
MemoryMB: vm.memoryMb,
}
+
log.Printf("[DEBUG] virtual machine config spec: %v", configSpec)
log.Printf("[DEBUG] starting extra custom config spec: %v", vm.customConfigurations)
@@ -1407,6 +1501,11 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error {
}
}
+ // Create the cdroms if needed.
+ if err := createCdroms(newVM, vm.cdroms); err != nil {
+ return err
+ }
+
taskb, err := newVM.Customize(context.TODO(), customSpec)
if err != nil {
return err
@@ -1416,7 +1515,7 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error {
if err != nil {
return err
}
- log.Printf("[DEBUG]VM customization finished")
+ log.Printf("[DEBUG] VM customization finished")
for i := 1; i < len(vm.hardDisks); i++ {
err = addHardDisk(newVM, vm.hardDisks[i].size, vm.hardDisks[i].iops, vm.hardDisks[i].initType)
@@ -1424,6 +1523,7 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error {
return err
}
}
+
log.Printf("[DEBUG] virtual machine config spec: %v", configSpec)
newVM.PowerOn(context.TODO())
diff --git a/builtin/providers/vsphere/resource_vsphere_virtual_machine_test.go b/builtin/providers/vsphere/resource_vsphere_virtual_machine_test.go
index 17197f63d5..264884b391 100644
--- a/builtin/providers/vsphere/resource_vsphere_virtual_machine_test.go
+++ b/builtin/providers/vsphere/resource_vsphere_virtual_machine_test.go
@@ -388,6 +388,71 @@ func TestAccVSphereVirtualMachine_createWithFolder(t *testing.T) {
})
}
+func TestAccVSphereVirtualMachine_createWithCdrom(t *testing.T) {
+ var vm virtualMachine
+ var locationOpt string
+ var datastoreOpt string
+
+ if v := os.Getenv("VSPHERE_DATACENTER"); v != "" {
+ locationOpt += fmt.Sprintf(" datacenter = \"%s\"\n", v)
+ }
+ if v := os.Getenv("VSPHERE_CLUSTER"); v != "" {
+ locationOpt += fmt.Sprintf(" cluster = \"%s\"\n", v)
+ }
+ if v := os.Getenv("VSPHERE_RESOURCE_POOL"); v != "" {
+ locationOpt += fmt.Sprintf(" resource_pool = \"%s\"\n", v)
+ }
+ if v := os.Getenv("VSPHERE_DATASTORE"); v != "" {
+ datastoreOpt = fmt.Sprintf(" datastore = \"%s\"\n", v)
+ }
+ template := os.Getenv("VSPHERE_TEMPLATE")
+ label := os.Getenv("VSPHERE_NETWORK_LABEL_DHCP")
+ cdromDatastore := os.Getenv("VSPHERE_CDROM_DATASTORE")
+ cdromPath := os.Getenv("VSPHERE_CDROM_PATH")
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ CheckDestroy: testAccCheckVSphereVirtualMachineDestroy,
+ Steps: []resource.TestStep{
+ resource.TestStep{
+ Config: fmt.Sprintf(
+ testAccCheckVsphereVirtualMachineConfig_cdrom,
+ locationOpt,
+ label,
+ datastoreOpt,
+ template,
+ cdromDatastore,
+ cdromPath,
+ ),
+ Check: resource.ComposeTestCheckFunc(
+ testAccCheckVSphereVirtualMachineExists("vsphere_virtual_machine.with_cdrom", &vm),
+ resource.TestCheckResourceAttr(
+ "vsphere_virtual_machine.with_cdrom", "name", "terraform-test-with-cdrom"),
+ resource.TestCheckResourceAttr(
+ "vsphere_virtual_machine.with_cdrom", "vcpu", "2"),
+ resource.TestCheckResourceAttr(
+ "vsphere_virtual_machine.with_cdrom", "memory", "4096"),
+ resource.TestCheckResourceAttr(
+ "vsphere_virtual_machine.with_cdrom", "disk.#", "1"),
+ resource.TestCheckResourceAttr(
+ "vsphere_virtual_machine.with_cdrom", "disk.0.template", template),
+ resource.TestCheckResourceAttr(
+ "vsphere_virtual_machine.with_cdrom", "cdrom.#", "1"),
+ resource.TestCheckResourceAttr(
+ "vsphere_virtual_machine.with_cdrom", "cdrom.0.datastore", cdromDatastore),
+ resource.TestCheckResourceAttr(
+ "vsphere_virtual_machine.with_cdrom", "cdrom.0.path", cdromPath),
+ resource.TestCheckResourceAttr(
+ "vsphere_virtual_machine.with_cdrom", "network_interface.#", "1"),
+ resource.TestCheckResourceAttr(
+ "vsphere_virtual_machine.with_cdrom", "network_interface.0.label", label),
+ ),
+ },
+ },
+ })
+}
+
func testAccCheckVSphereVirtualMachineDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*govmomi.Client)
finder := find.NewFinder(client.Client, true)
@@ -664,7 +729,7 @@ resource "vsphere_virtual_machine" "folder" {
const testAccCheckVSphereVirtualMachineConfig_createWithFolder = `
resource "vsphere_folder" "with_folder" {
- path = "%s"
+ path = "%s"
%s
}
resource "vsphere_virtual_machine" "with_folder" {
@@ -682,3 +747,24 @@ resource "vsphere_virtual_machine" "with_folder" {
}
}
`
+
+const testAccCheckVsphereVirtualMachineConfig_cdrom = `
+resource "vsphere_virtual_machine" "with_cdrom" {
+ name = "terraform-test-with-cdrom"
+%s
+ vcpu = 2
+ memory = 4096
+ network_interface {
+ label = "%s"
+ }
+ disk {
+%s
+ template = "%s"
+ }
+
+ cdrom {
+ datastore = "%s"
+ path = "%s"
+ }
+}
+`
diff --git a/website/source/docs/providers/vsphere/index.html.markdown b/website/source/docs/providers/vsphere/index.html.markdown
index 2138b5084f..ef6ff62952 100644
--- a/website/source/docs/providers/vsphere/index.html.markdown
+++ b/website/source/docs/providers/vsphere/index.html.markdown
@@ -89,6 +89,11 @@ The following environment variables depend on your vSphere environment:
* VSPHERE\_RESOURCE\_POOL
* VSPHERE\_DATASTORE
+The following additional environment variables are needed for running the "Mount ISO as CDROM media" acceptance tests.
+
+ * VSPHERE\_CDROM\_DATASTORE
+ * VSPHERE\_CDROM\_PATH
+
These are used to set and verify attributes on the `vsphere_virtual_machine`
resource in tests.
diff --git a/website/source/docs/providers/vsphere/r/virtual_machine.html.markdown b/website/source/docs/providers/vsphere/r/virtual_machine.html.markdown
index 47791ba381..0593e3c9b4 100644
--- a/website/source/docs/providers/vsphere/r/virtual_machine.html.markdown
+++ b/website/source/docs/providers/vsphere/r/virtual_machine.html.markdown
@@ -46,6 +46,7 @@ The following arguments are supported:
* `dns_servers` - (Optional) List of DNS servers for the virtual network adapter; defaults to 8.8.8.8, 8.8.4.4
* `network_interface` - (Required) Configures virtual network interfaces; see [Network Interfaces](#network-interfaces) below for details.
* `disk` - (Required) Configures virtual disks; see [Disks](#disks) below for details
+* `cdrom` - (Optional) Configures a CDROM device and mounts an image as its media; see [CDROM](#cdrom) below for more details.
* `boot_delay` - (Optional) Time in seconds to wait for machine network to be ready.
* `windows_opt_config` - (Optional) Extra options for clones of Windows machines.
* `linked_clone` - (Optional) Specifies if the new machine is a [linked clone](https://www.vmware.com/support/ws5/doc/ws_clone_overview.html#wp1036396) of another machine or not.
@@ -71,6 +72,9 @@ The `windows_opt_config` block supports:
* `domain_user` - (Optional) User that is a member of the specified domain.
* `domain_user_password` - (Optional) Password for domain user, in plain text.
+
+## Disks
+
The `disk` block supports:
* `template` - (Required if size not provided) Template for this disk.
@@ -79,6 +83,14 @@ The `disk` block supports:
* `iops` - (Optional) Number of virtual iops to allocate for this disk.
* `type` - (Optional) 'eager_zeroed' (the default), or 'thin' are supported options.
+
+## CDROM
+
+The `cdrom` block supports:
+
+* `datastore` - (Required) The name of the datastore where the disk image is stored.
+* `path` - (Required) The absolute path to the image within the datastore.
+
## Attributes Reference
The following attributes are exported: