vsphere/virtual_machine_config_structure.go
func expandVAppConfig(d *schema.ResourceData, client *govmomi.Client) (*types.VmConfigSpec, error) {
	if !d.HasChange("vapp") {
		return nil, nil
	}

	// Many vApp config values, such as IP address, will require a
	// restart of the machine to properly apply. We don't necessarily
	// know which ones they are, so we will restart for every change.
	d.Set("reboot_required", true)

	var props []types.VAppPropertySpec

	_, new := d.GetChange("vapp")
	newMap := make(map[string]interface{})

	newVApps := new.([]interface{})
	if newVApps != nil && len(newVApps) > 0 && newVApps[0] != nil {
		newVApp := newVApps[0].(map[string]interface{})
		if props, ok := newVApp["properties"].(map[string]interface{}); ok {
			newMap = props
		}
	}

	uuid := d.Id()
	if uuid == "" {
		// No virtual machine has been created, this usually means that this is a
		// brand new virtual machine. vApp properties are not supported on this
		// workflow, so if there are any defined, return an error indicating such.
		// Return with a no-op otherwise.
		if len(newMap) > 0 {
			return nil, fmt.Errorf("vApp properties can only be set on cloned virtual machines")
		}
		return nil, nil
	}
	vm, _ := virtualmachine.FromUUID(client, d.Id())
	vmProps, _ := virtualmachine.Properties(vm)
	if vmProps.Config.VAppConfig == nil {
		return nil, fmt.Errorf("this VM lacks a vApp configuration and cannot have vApp properties set on it")
	}
	allProperties := vmProps.Config.VAppConfig.GetVmConfigInfo().Property

	for _, p := range allProperties {
		defaultValue := " "
		if p.DefaultValue != "" {
			defaultValue = p.DefaultValue
		}
		prop := types.VAppPropertySpec{
			ArrayUpdateSpec: types.ArrayUpdateSpec{
				Operation: types.ArrayUpdateOperationEdit,
			},
			Info: &types.VAppPropertyInfo{
				Key:   p.Key,
				Id:    p.Id,
				Value: defaultValue,
			},
		}

		newValue, ok := newMap[p.Id]
		if ok {
			prop.Info.Value = newValue.(string)
			delete(newMap, p.Id)
		}
		props = append(props, prop)
	}

	if len(newMap) > 0 {
		return nil, fmt.Errorf("unsupported vApp properties in vapp.properties: %+v", reflect.ValueOf(newMap).MapKeys())
	}

	return &types.VmConfigSpec{
		Property: props,
	}, nil
}
func expandVAppConfig(d *schema.ResourceData, client *govmomi.Client) (*types.VmConfigSpec, error) {
	if !d.HasChange("vapp") {
		return nil, nil
	}

	// Many vApp config values, such as IP address, will require a
	// restart of the machine to properly apply. We don't necessarily
	// know which ones they are, so we will restart for every change.
	d.Set("reboot_required", true)

	var props []types.VAppPropertySpec

	_, new := d.GetChange("vapp")
	newMap := make(map[string]interface{})

	newVApps := new.([]interface{})
	if newVApps != nil && len(newVApps) > 0 && newVApps[0] != nil {
		newVApp := newVApps[0].(map[string]interface{})
		if props, ok := newVApp["properties"].(map[string]interface{}); ok {
			newMap = props
		}
	}

	uuid := d.Id()
	if uuid == "" {
		// No virtual machine has been created, this usually means that this is a
		// brand new virtual machine. vApp properties are not supported on this
		// workflow, so if there are any defined, return an error indicating such.
		// Return with a no-op otherwise.
		if len(newMap) > 0 {
			return nil, fmt.Errorf("vApp properties can only be set on cloned virtual machines")
		}
		return nil, nil
	}
	vm, _ := virtualmachine.FromUUID(client, d.Id())
	vmProps, _ := virtualmachine.Properties(vm)
	if vmProps.Config.VAppConfig == nil {
		return nil, fmt.Errorf("this VM lacks a vApp configuration and cannot have vApp properties set on it")
	}
	allProperties := vmProps.Config.VAppConfig.GetVmConfigInfo().Property

	for _, p := range allProperties {
		defaultValue := " "
		if p.DefaultValue != "" {
			defaultValue = p.DefaultValue
		}
		prop := types.VAppPropertySpec{
			ArrayUpdateSpec: types.ArrayUpdateSpec{
				Operation: types.ArrayUpdateOperationEdit,
			},
			Info: &types.VAppPropertyInfo{
				Key:   p.Key,
				Id:    p.Id,
				Value: defaultValue,
			},
		}

		newValue, ok := newMap[p.Id]
		if ok {
			prop.Info.Value = newValue.(string)
			delete(newMap, p.Id)
		}
		props = append(props, prop)
	}

	if len(newMap) > 0 {
		return nil, fmt.Errorf("unsupported vApp properties in vapp.properties: %+v", reflect.ValueOf(newMap).MapKeys())
	}

	return &types.VmConfigSpec{
		Property: props,
	}, nil
}
// expandVAppConfig reads in all the vapp key/value pairs and returns
// the appropriate VmConfigSpec.
//
// We track changes to keys to determine if any have been removed from
// configuration - if they have, we add them with an empty value to ensure
// they are removed from vAppConfig on the update.