vsphere/resource_vsphere_virtual_machine.go
func resourceVSphereVirtualMachineCreateClone(d *schema.ResourceData, meta interface{}) (*object.VirtualMachine, error) {
	log.Printf("[DEBUG] %s: VM being created from clone", resourceVSphereVirtualMachineIDString(d))
	client := meta.(*VSphereClient).vimClient

	// Find the folder based off the path to the resource pool. Basically what we
	// are saying here is that the VM folder that we are placing this VM in needs
	// to be in the same hierarchy as the resource pool - so in other words, the
	// same datacenter.
	poolID := d.Get("resource_pool_id").(string)
	pool, err := resourcepool.FromID(client, poolID)
	if err != nil {
		return nil, fmt.Errorf("could not find resource pool ID %q: %s", poolID, err)
	}
	fo, err := folder.VirtualMachineFolderFromObject(client, pool, d.Get("folder").(string))
	if err != nil {
		return nil, err
	}

	// Expand the clone spec. We get the source VM here too.
	cloneSpec, srcVM, err := vmworkflow.ExpandVirtualMachineCloneSpec(d, client)
	if err != nil {
		return nil, err
	}

	// Start the clone
	name := d.Get("name").(string)
	timeout := d.Get("clone.0.timeout").(int)
	var vm *object.VirtualMachine
	if _, ok := d.GetOk("datastore_cluster_id"); ok {
		vm, err = resourceVSphereVirtualMachineCreateCloneWithSDRS(d, meta, srcVM, fo, name, cloneSpec, timeout)
	} else {
		vm, err = virtualmachine.Clone(client, srcVM, fo, name, cloneSpec, timeout)
	}
	if err != nil {
		return nil, fmt.Errorf("error cloning virtual machine: %s", err)
	}

	// The VM has been created. We still need to do post-clone configuration, and
	// while the resource should have an ID until this is done, we need it to go
	// through post-clone rollback workflows. All rollback functions will remove
	// the ID after it has done its rollback.
	//
	// It's generally safe to not rollback after the initial re-configuration is
	// fully complete and we move on to sending the customization spec.
	vprops, err := virtualmachine.Properties(vm)
	if err != nil {
		return nil, resourceVSphereVirtualMachineRollbackCreate(
			d,
			meta,
			vm,
			fmt.Errorf("cannot fetch properties of created virtual machine: %s", err),
		)
	}
	log.Printf("[DEBUG] VM %q - UUID is %q", vm.InventoryPath, vprops.Config.Uuid)
	d.SetId(vprops.Config.Uuid)

	// Before starting or proceeding any further, we need to normalize the
	// configuration of the newly cloned VM. This is basically a subset of update
	// with the stipulation that there is currently no state to help move this
	// along.
	cfgSpec, err := expandVirtualMachineConfigSpec(d, client)
	if err != nil {
		return nil, resourceVSphereVirtualMachineRollbackCreate(
			d,
			meta,
			vm,
			fmt.Errorf("error in virtual machine configuration: %s", err),
		)
	}

	// To apply device changes, we need the current devicecfgSpec from the config
	// info. We then filter this list through the same apply process we did for
	// create, which will apply the changes in an incremental fashion.
	devices := object.VirtualDeviceList(vprops.Config.Hardware.Device)
	var delta []types.BaseVirtualDeviceConfigSpec
	// First check the state of our SCSI bus. Normalize it if we need to.
	devices, delta, err = virtualdevice.NormalizeSCSIBus(devices, d.Get("scsi_type").(string), d.Get("scsi_controller_count").(int), d.Get("scsi_bus_sharing").(string))
	if err != nil {
		return nil, resourceVSphereVirtualMachineRollbackCreate(
			d,
			meta,
			vm,
			fmt.Errorf("error normalizing SCSI bus post-clone: %s", err),
		)
	}
	cfgSpec.DeviceChange = virtualdevice.AppendDeviceChangeSpec(cfgSpec.DeviceChange, delta...)
	// Disks
	devices, delta, err = virtualdevice.DiskPostCloneOperation(d, client, devices)
	if err != nil {
		return nil, resourceVSphereVirtualMachineRollbackCreate(
			d,
			meta,
			vm,
			fmt.Errorf("error processing disk changes post-clone: %s", err),
		)
	}
	cfgSpec.DeviceChange = virtualdevice.AppendDeviceChangeSpec(cfgSpec.DeviceChange, delta...)
	// Network devices
	devices, delta, err = virtualdevice.NetworkInterfacePostCloneOperation(d, client, devices)
	if err != nil {
		return nil, resourceVSphereVirtualMachineRollbackCreate(
			d,
			meta,
			vm,
			fmt.Errorf("error processing network device changes post-clone: %s", err),
		)
	}
	cfgSpec.DeviceChange = virtualdevice.AppendDeviceChangeSpec(cfgSpec.DeviceChange, delta...)
	// CDROM
	devices, delta, err = virtualdevice.CdromPostCloneOperation(d, client, devices)
	if err != nil {
		return nil, resourceVSphereVirtualMachineRollbackCreate(
			d,
			meta,
			vm,
			fmt.Errorf("error processing CDROM device changes post-clone: %s", err),
		)
	}
	cfgSpec.DeviceChange = virtualdevice.AppendDeviceChangeSpec(cfgSpec.DeviceChange, delta...)
	log.Printf("[DEBUG] %s: Final device list: %s", resourceVSphereVirtualMachineIDString(d), virtualdevice.DeviceListString(devices))
	log.Printf("[DEBUG] %s: Final device change cfgSpec: %s", resourceVSphereVirtualMachineIDString(d), virtualdevice.DeviceChangeString(cfgSpec.DeviceChange))

	// Perform updates
	if _, ok := d.GetOk("datastore_cluster_id"); ok {
		err = resourceVSphereVirtualMachineUpdateReconfigureWithSDRS(d, meta, vm, cfgSpec)
	} else {
		err = virtualmachine.Reconfigure(vm, cfgSpec)
	}
	if err != nil {
		return nil, resourceVSphereVirtualMachineRollbackCreate(
			d,
			meta,
			vm,
			fmt.Errorf("error reconfiguring virtual machine: %s", err),
		)
	}

	var cw *virtualMachineCustomizationWaiter
	// Send customization spec if any has been defined.
	if len(d.Get("clone.0.customize").([]interface{})) > 0 {
		family, err := resourcepool.OSFamily(client, pool, d.Get("guest_id").(string))
		if err != nil {
			return nil, fmt.Errorf("cannot find OS family for guest ID %q: %s", d.Get("guest_id").(string), err)
		}
		custSpec := vmworkflow.ExpandCustomizationSpec(d, family)
		cw = newVirtualMachineCustomizationWaiter(client, vm, d.Get("clone.0.customize.0.timeout").(int))
		if err := virtualmachine.Customize(vm, custSpec); err != nil {
			// Roll back the VMs as per the error handling in reconfigure.
			if derr := resourceVSphereVirtualMachineDelete(d, meta); derr != nil {
				return nil, fmt.Errorf(formatVirtualMachinePostCloneRollbackError, vm.InventoryPath, err, derr)
			}
			d.SetId("")
			return nil, fmt.Errorf("error sending customization spec: %s", err)
		}
	}
	// Finally time to power on the virtual machine!
	if err := virtualmachine.PowerOn(vm); err != nil {
		return nil, fmt.Errorf("error powering on virtual machine: %s", err)
	}
	// If we customized, wait on customization.
	if cw != nil {
		log.Printf("[DEBUG] %s: Waiting for VM customization to complete", resourceVSphereVirtualMachineIDString(d))
		<-cw.Done()
		if err := cw.Err(); err != nil {
			return nil, fmt.Errorf(formatVirtualMachineCustomizationWaitError, vm.InventoryPath, err)
		}
	}
	// Clone is complete and ready to return
	return vm, nil
}
func resourceVSphereVirtualMachineCreateClone(d *schema.ResourceData, meta interface{}) (*object.VirtualMachine, error) {
	log.Printf("[DEBUG] %s: VM being created from clone", resourceVSphereVirtualMachineIDString(d))
	client := meta.(*VSphereClient).vimClient

	// Find the folder based off the path to the resource pool. Basically what we
	// are saying here is that the VM folder that we are placing this VM in needs
	// to be in the same hierarchy as the resource pool - so in other words, the
	// same datacenter.
	poolID := d.Get("resource_pool_id").(string)
	pool, err := resourcepool.FromID(client, poolID)
	if err != nil {
		return nil, fmt.Errorf("could not find resource pool ID %q: %s", poolID, err)
	}
	fo, err := folder.VirtualMachineFolderFromObject(client, pool, d.Get("folder").(string))
	if err != nil {
		return nil, err
	}

	// Expand the clone spec. We get the source VM here too.
	cloneSpec, srcVM, err := vmworkflow.ExpandVirtualMachineCloneSpec(d, client)
	if err != nil {
		return nil, err
	}

	// Start the clone
	name := d.Get("name").(string)
	timeout := d.Get("clone.0.timeout").(int)
	var vm *object.VirtualMachine
	if _, ok := d.GetOk("datastore_cluster_id"); ok {
		vm, err = resourceVSphereVirtualMachineCreateCloneWithSDRS(d, meta, srcVM, fo, name, cloneSpec, timeout)
	} else {
		vm, err = virtualmachine.Clone(client, srcVM, fo, name, cloneSpec, timeout)
	}
	if err != nil {
		return nil, fmt.Errorf("error cloning virtual machine: %s", err)
	}

	// The VM has been created. We still need to do post-clone configuration, and
	// while the resource should have an ID until this is done, we need it to go
	// through post-clone rollback workflows. All rollback functions will remove
	// the ID after it has done its rollback.
	//
	// It's generally safe to not rollback after the initial re-configuration is
	// fully complete and we move on to sending the customization spec.
	vprops, err := virtualmachine.Properties(vm)
	if err != nil {
		return nil, resourceVSphereVirtualMachineRollbackCreate(
			d,
			meta,
			vm,
			fmt.Errorf("cannot fetch properties of created virtual machine: %s", err),
		)
	}
	log.Printf("[DEBUG] VM %q - UUID is %q", vm.InventoryPath, vprops.Config.Uuid)
	d.SetId(vprops.Config.Uuid)

	// Before starting or proceeding any further, we need to normalize the
	// configuration of the newly cloned VM. This is basically a subset of update
	// with the stipulation that there is currently no state to help move this
	// along.
	cfgSpec, err := expandVirtualMachineConfigSpec(d, client)
	if err != nil {
		return nil, resourceVSphereVirtualMachineRollbackCreate(
			d,
			meta,
			vm,
			fmt.Errorf("error in virtual machine configuration: %s", err),
		)
	}

	// To apply device changes, we need the current devicecfgSpec from the config
	// info. We then filter this list through the same apply process we did for
	// create, which will apply the changes in an incremental fashion.
	devices := object.VirtualDeviceList(vprops.Config.Hardware.Device)
	var delta []types.BaseVirtualDeviceConfigSpec
	// First check the state of our SCSI bus. Normalize it if we need to.
	devices, delta, err = virtualdevice.NormalizeSCSIBus(devices, d.Get("scsi_type").(string), d.Get("scsi_controller_count").(int), d.Get("scsi_bus_sharing").(string))
	if err != nil {
		return nil, resourceVSphereVirtualMachineRollbackCreate(
			d,
			meta,
			vm,
			fmt.Errorf("error normalizing SCSI bus post-clone: %s", err),
		)
	}
	cfgSpec.DeviceChange = virtualdevice.AppendDeviceChangeSpec(cfgSpec.DeviceChange, delta...)
	// Disks
	devices, delta, err = virtualdevice.DiskPostCloneOperation(d, client, devices)
	if err != nil {
		return nil, resourceVSphereVirtualMachineRollbackCreate(
			d,
			meta,
			vm,
			fmt.Errorf("error processing disk changes post-clone: %s", err),
		)
	}
	cfgSpec.DeviceChange = virtualdevice.AppendDeviceChangeSpec(cfgSpec.DeviceChange, delta...)
	// Network devices
	devices, delta, err = virtualdevice.NetworkInterfacePostCloneOperation(d, client, devices)
	if err != nil {
		return nil, resourceVSphereVirtualMachineRollbackCreate(
			d,
			meta,
			vm,
			fmt.Errorf("error processing network device changes post-clone: %s", err),
		)
	}
	cfgSpec.DeviceChange = virtualdevice.AppendDeviceChangeSpec(cfgSpec.DeviceChange, delta...)
	// CDROM
	devices, delta, err = virtualdevice.CdromPostCloneOperation(d, client, devices)
	if err != nil {
		return nil, resourceVSphereVirtualMachineRollbackCreate(
			d,
			meta,
			vm,
			fmt.Errorf("error processing CDROM device changes post-clone: %s", err),
		)
	}
	cfgSpec.DeviceChange = virtualdevice.AppendDeviceChangeSpec(cfgSpec.DeviceChange, delta...)
	log.Printf("[DEBUG] %s: Final device list: %s", resourceVSphereVirtualMachineIDString(d), virtualdevice.DeviceListString(devices))
	log.Printf("[DEBUG] %s: Final device change cfgSpec: %s", resourceVSphereVirtualMachineIDString(d), virtualdevice.DeviceChangeString(cfgSpec.DeviceChange))

	// Perform updates
	if _, ok := d.GetOk("datastore_cluster_id"); ok {
		err = resourceVSphereVirtualMachineUpdateReconfigureWithSDRS(d, meta, vm, cfgSpec)
	} else {
		err = virtualmachine.Reconfigure(vm, cfgSpec)
	}
	if err != nil {
		return nil, resourceVSphereVirtualMachineRollbackCreate(
			d,
			meta,
			vm,
			fmt.Errorf("error reconfiguring virtual machine: %s", err),
		)
	}

	var cw *virtualMachineCustomizationWaiter
	// Send customization spec if any has been defined.
	if len(d.Get("clone.0.customize").([]interface{})) > 0 {
		family, err := resourcepool.OSFamily(client, pool, d.Get("guest_id").(string))
		if err != nil {
			return nil, fmt.Errorf("cannot find OS family for guest ID %q: %s", d.Get("guest_id").(string), err)
		}
		custSpec := vmworkflow.ExpandCustomizationSpec(d, family)
		cw = newVirtualMachineCustomizationWaiter(client, vm, d.Get("clone.0.customize.0.timeout").(int))
		if err := virtualmachine.Customize(vm, custSpec); err != nil {
			// Roll back the VMs as per the error handling in reconfigure.
			if derr := resourceVSphereVirtualMachineDelete(d, meta); derr != nil {
				return nil, fmt.Errorf(formatVirtualMachinePostCloneRollbackError, vm.InventoryPath, err, derr)
			}
			d.SetId("")
			return nil, fmt.Errorf("error sending customization spec: %s", err)
		}
	}
	// Finally time to power on the virtual machine!
	if err := virtualmachine.PowerOn(vm); err != nil {
		return nil, fmt.Errorf("error powering on virtual machine: %s", err)
	}
	// If we customized, wait on customization.
	if cw != nil {
		log.Printf("[DEBUG] %s: Waiting for VM customization to complete", resourceVSphereVirtualMachineIDString(d))
		<-cw.Done()
		if err := cw.Err(); err != nil {
			return nil, fmt.Errorf(formatVirtualMachineCustomizationWaitError, vm.InventoryPath, err)
		}
	}
	// Clone is complete and ready to return
	return vm, nil
}
func resourceVSphereVirtualMachineCreateClone ( d * schema . ResourceData , meta interface { } ) ( * object . VirtualMachine , error ) { log . Printf ( " " , resourceVSphereVirtualMachineIDString ( d ) ) client := meta . ( * VSphereClient ) . vimClient // Find the folder based off the path to the resource pool. Basically what we // are saying here is that the VM folder that we are placing this VM in needs // to be in the same hierarchy as the resource pool - so in other words, the // same datacenter. poolID := d . Get ( " " ) . ( string ) pool , err := resourcepool . FromID ( client , poolID ) if err != nil { return nil , fmt . Errorf ( " " , poolID , err ) } fo , err := folder . VirtualMachineFolderFromObject ( client , pool , d . Get ( " " ) . ( string ) ) if err != nil { return nil , err } // Expand the clone spec. We get the source VM here too. cloneSpec , srcVM , err := vmworkflow . ExpandVirtualMachineCloneSpec ( d , client ) if err != nil { return nil , err } // Start the clone name := d . Get ( " " ) . ( string ) timeout := d . Get ( " " ) . ( int ) var vm * object . VirtualMachine if _ , ok := d . GetOk ( " " ) ; ok { vm , err = resourceVSphereVirtualMachineCreateCloneWithSDRS ( d , meta , srcVM , fo , name , cloneSpec , timeout ) } else { vm , err = virtualmachine . Clone ( client , srcVM , fo , name , cloneSpec , timeout ) } if err != nil { return nil , fmt . Errorf ( " " , err ) } // The VM has been created. We still need to do post-clone configuration, and // while the resource should have an ID until this is done, we need it to go // through post-clone rollback workflows. All rollback functions will remove // the ID after it has done its rollback. // // It's generally safe to not rollback after the initial re-configuration is // fully complete and we move on to sending the customization spec. vprops , err := virtualmachine . Properties ( vm ) if err != nil { return nil , resourceVSphereVirtualMachineRollbackCreate ( d , meta , vm , fmt . Errorf ( " " , err ) , ) } log . Printf ( " " , vm . InventoryPath , vprops . Config . Uuid ) d . SetId ( vprops . Config . Uuid ) // Before starting or proceeding any further, we need to normalize the // configuration of the newly cloned VM. This is basically a subset of update // with the stipulation that there is currently no state to help move this // along. cfgSpec , err := expandVirtualMachineConfigSpec ( d , client ) if err != nil { return nil , resourceVSphereVirtualMachineRollbackCreate ( d , meta , vm , fmt . Errorf ( " " , err ) , ) } // To apply device changes, we need the current devicecfgSpec from the config // info. We then filter this list through the same apply process we did for // create, which will apply the changes in an incremental fashion. devices := object . VirtualDeviceList ( vprops . Config . Hardware . Device ) var delta [ ] types . BaseVirtualDeviceConfigSpec // First check the state of our SCSI bus. Normalize it if we need to. devices , delta , err = virtualdevice . NormalizeSCSIBus ( devices , d . Get ( " " ) . ( string ) , d . Get ( " " ) . ( int ) , d . Get ( " " ) . ( string ) ) if err != nil { return nil , resourceVSphereVirtualMachineRollbackCreate ( d , meta , vm , fmt . Errorf ( " " , err ) , ) } cfgSpec . DeviceChange = virtualdevice . AppendDeviceChangeSpec ( cfgSpec . DeviceChange , delta ... ) // Disks devices , delta , err = virtualdevice . DiskPostCloneOperation ( d , client , devices ) if err != nil { return nil , resourceVSphereVirtualMachineRollbackCreate ( d , meta , vm , fmt . Errorf ( " " , err ) , ) } cfgSpec . DeviceChange = virtualdevice . AppendDeviceChangeSpec ( cfgSpec . DeviceChange , delta ... ) // Network devices devices , delta , err = virtualdevice . NetworkInterfacePostCloneOperation ( d , client , devices ) if err != nil { return nil , resourceVSphereVirtualMachineRollbackCreate ( d , meta , vm , fmt . Errorf ( " " , err ) , ) } cfgSpec . DeviceChange = virtualdevice . AppendDeviceChangeSpec ( cfgSpec . DeviceChange , delta ... ) // CDROM devices , delta , err = virtualdevice . CdromPostCloneOperation ( d , client , devices ) if err != nil { return nil , resourceVSphereVirtualMachineRollbackCreate ( d , meta , vm , fmt . Errorf ( " " , err ) , ) } cfgSpec . DeviceChange = virtualdevice . AppendDeviceChangeSpec ( cfgSpec . DeviceChange , delta ... ) log . Printf ( " " , resourceVSphereVirtualMachineIDString ( d ) , virtualdevice . DeviceListString ( devices ) ) log . Printf ( " " , resourceVSphereVirtualMachineIDString ( d ) , virtualdevice . DeviceChangeString ( cfgSpec . DeviceChange ) ) // Perform updates if _ , ok := d . GetOk ( " " ) ; ok { err = resourceVSphereVirtualMachineUpdateReconfigureWithSDRS ( d , meta , vm , cfgSpec ) } else { err = virtualmachine . Reconfigure ( vm , cfgSpec ) } if err != nil { return nil , resourceVSphereVirtualMachineRollbackCreate ( d , meta , vm , fmt . Errorf ( " " , err ) , ) } var cw * virtualMachineCustomizationWaiter // Send customization spec if any has been defined. if len ( d . Get ( " " ) . ( [ ] interface { } ) ) > 0 { family , err := resourcepool . OSFamily ( client , pool , d . Get ( " " ) . ( string ) ) if err != nil { return nil , fmt . Errorf ( " " , d . Get ( " " ) . ( string ) , err ) } custSpec := vmworkflow . ExpandCustomizationSpec ( d , family ) cw = newVirtualMachineCustomizationWaiter ( client , vm , d . Get ( " " ) . ( int ) ) if err := virtualmachine . Customize ( vm , custSpec ) ; err != nil { // Roll back the VMs as per the error handling in reconfigure. if derr := resourceVSphereVirtualMachineDelete ( d , meta ) ; derr != nil { return nil , fmt . Errorf ( formatVirtualMachinePostCloneRollbackError , vm . InventoryPath , err , derr ) } d . SetId ( " " ) return nil , fmt . Errorf ( " " , err ) } } // Finally time to power on the virtual machine! if err := virtualmachine . PowerOn ( vm ) ; err != nil { return nil , fmt . Errorf ( " " , err ) } // If we customized, wait on customization. if cw != nil { log . Printf ( " " , resourceVSphereVirtualMachineIDString ( d ) ) <- cw . Done ( ) if err := cw . Err ( ) ; err != nil { return nil , fmt . Errorf ( formatVirtualMachineCustomizationWaitError , vm . InventoryPath , err ) } } // Clone is complete and ready to return return vm , nil }
// resourceVSphereVirtualMachineCreateClone contains the clone VM deploy
// path. The VM is returned.
resourceVSphereVirtualMachineCreateClone contains the clone VM deploy path . The VM is returned .