docs: def create(vm_=None, call=None):
      '''
      Create a single VM from a data dict
      '''
      if call:
          raise SaltCloudSystemExit(
              'You cannot create an instance with -a or -f.'
          )
  
      try:
          # Check for required profile parameters before sending any API calls.
          if vm_['profile'] and config.is_profile_configured(__opts__,
                                                             __active_provider_name__ or 'ec2',
                                                             vm_['profile'],
                                                             vm_=vm_) is False:
              return False
      except AttributeError:
          pass
  
      # Check for private_key and keyfile name for bootstrapping new instances
      deploy = config.get_cloud_config_value(
          'deploy', vm_, __opts__, default=True
      )
      win_password = config.get_cloud_config_value(
          'win_password', vm_, __opts__, default=''
      )
      key_filename = config.get_cloud_config_value(
          'private_key', vm_, __opts__, search_global=False, default=None
      )
      if deploy:
          # The private_key and keyname settings are only needed for bootstrapping
          # new instances when deploy is True
          _validate_key_path_and_mode(key_filename)
  
      __utils__['cloud.fire_event'](
          'event',
          'starting create',
          'salt/cloud/{0}/creating'.format(vm_['name']),
          args=__utils__['cloud.filter_event']('creating', vm_, ['name', 'profile', 'provider', 'driver']),
          sock_dir=__opts__['sock_dir'],
          transport=__opts__['transport']
      )
      __utils__['cloud.cachedir_index_add'](
          vm_['name'], vm_['profile'], 'ec2', vm_['driver']
      )
  
      vm_['key_filename'] = key_filename
      # wait_for_instance requires private_key
      vm_['private_key'] = key_filename
  
      # Get SSH Gateway config early to verify the private_key,
      # if used, exists or not. We don't want to deploy an instance
      # and not be able to access it via the gateway.
      vm_['gateway'] = get_ssh_gateway_config(vm_)
  
      location = get_location(vm_)
      vm_['location'] = location
  
      log.info('Creating Cloud VM %s in %s', vm_['name'], location)
      vm_['usernames'] = salt.utils.cloud.ssh_usernames(
          vm_,
          __opts__,
          default_users=(
              'ec2-user',  # Amazon Linux, Fedora, RHEL; FreeBSD
              'centos',    # CentOS AMIs from AWS Marketplace
              'ubuntu',    # Ubuntu
              'admin',     # Debian GNU/Linux
              'bitnami',   # BitNami AMIs
              'root'       # Last resort, default user on RHEL 5, SUSE
          )
      )
  
      if 'instance_id' in vm_:
          # This was probably created via another process, and doesn't have
          # things like salt keys created yet, so let's create them now.
          if 'pub_key' not in vm_ and 'priv_key' not in vm_:
              log.debug('Generating minion keys for \'%s\'', vm_['name'])
              vm_['priv_key'], vm_['pub_key'] = salt.utils.cloud.gen_keys(
                  salt.config.get_cloud_config_value(
                      'keysize',
                      vm_,
                      __opts__
                  )
              )
      else:
          # Put together all of the information required to request the instance,
          # and then fire off the request for it
          if keyname(vm_) is None:
              raise SaltCloudSystemExit(
                  'The required \'keyname\' configuration setting is missing from the '
                  '\'ec2\' driver.'
              )
  
          data, vm_ = request_instance(vm_, location)
  
          # If data is a str, it's an error
          if isinstance(data, six.string_types):
              log.error('Error requesting instance: %s', data)
              return {}
  
          # Pull the instance ID, valid for both spot and normal instances
  
          # Multiple instances may have been spun up, get all their IDs
          vm_['instance_id_list'] = []
          for instance in data:
              vm_['instance_id_list'].append(instance['instanceId'])
  
          vm_['instance_id'] = vm_['instance_id_list'].pop()
          if vm_['instance_id_list']:
              # Multiple instances were spun up, get one now, and queue the rest
              queue_instances(vm_['instance_id_list'])
  
      # Wait for vital information, such as IP addresses, to be available
      # for the new instance
      data = query_instance(vm_)
  
      # Now that the instance is available, tag it appropriately. Should
      # mitigate race conditions with tags
      tags = config.get_cloud_config_value('tag',
                                           vm_,
                                           __opts__,
                                           {},
                                           search_global=False)
      if not isinstance(tags, dict):
          raise SaltCloudConfigError(
              '\'tag\' should be a dict.'
          )
  
      for value in six.itervalues(tags):
          if not isinstance(value, six.string_types):
              raise SaltCloudConfigError(
                  '\'tag\' values must be strings. Try quoting the values. '
                  'e.g. "2013-09-19T20:09:46Z".'
              )
  
      tags['Name'] = vm_['name']
  
      __utils__['cloud.fire_event'](
          'event',
          'setting tags',
          'salt/cloud/{0}/tagging'.format(vm_['name']),
          args={'tags': tags},
          sock_dir=__opts__['sock_dir'],
          transport=__opts__['transport']
      )
  
      salt.utils.cloud.wait_for_fun(
          set_tags,
          timeout=30,
          name=vm_['name'],
          tags=tags,
          instance_id=vm_['instance_id'],
          call='action',
          location=location
      )
  
      # Once instance tags are set, tag the spot request if configured
      if 'spot_config' in vm_ and 'tag' in vm_['spot_config']:
  
          if not isinstance(vm_['spot_config']['tag'], dict):
              raise SaltCloudConfigError(
                  '\'tag\' should be a dict.'
              )
  
          for value in six.itervalues(vm_['spot_config']['tag']):
              if not isinstance(value, str):
                  raise SaltCloudConfigError(
                      '\'tag\' values must be strings. Try quoting the values. '
                      'e.g. "2013-09-19T20:09:46Z".'
                  )
  
          spot_request_tags = {}
  
          if 'spotRequestId' not in vm_:
              raise SaltCloudConfigError('Failed to find spotRequestId')
  
          sir_id = vm_['spotRequestId']
  
          spot_request_tags['Name'] = vm_['name']
  
          for k, v in six.iteritems(vm_['spot_config']['tag']):
              spot_request_tags[k] = v
  
          __utils__['cloud.fire_event'](
              'event',
              'setting tags',
              'salt/cloud/spot_request_{0}/tagging'.format(sir_id),
              args={'tags': spot_request_tags},
              sock_dir=__opts__['sock_dir'],
              transport=__opts__['transport']
          )
          salt.utils.cloud.wait_for_fun(
              set_tags,
              timeout=30,
              name=vm_['name'],
              tags=spot_request_tags,
              instance_id=sir_id,
              call='action',
              location=location
          )
  
      network_interfaces = config.get_cloud_config_value(
          'network_interfaces',
          vm_,
          __opts__,
          search_global=False
      )
  
      if network_interfaces:
          _update_enis(network_interfaces, data, vm_)
  
      # At this point, the node is created and tagged, and now needs to be
      # bootstrapped, once the necessary port is available.
      log.info('Created node %s', vm_['name'])
  
      instance = data[0]['instancesSet']['item']
  
      # Wait for the necessary port to become available to bootstrap
      if ssh_interface(vm_) == 'private_ips':
          ip_address = instance['privateIpAddress']
          log.info('Salt node data. Private_ip: %s', ip_address)
      else:
          ip_address = instance['ipAddress']
          log.info('Salt node data. Public_ip: %s', ip_address)
      vm_['ssh_host'] = ip_address
  
      if salt.utils.cloud.get_salt_interface(vm_, __opts__) == 'private_ips':
          salt_ip_address = instance['privateIpAddress']
          log.info('Salt interface set to: %s', salt_ip_address)
      else:
          salt_ip_address = instance['ipAddress']
          log.debug('Salt interface set to: %s', salt_ip_address)
      vm_['salt_host'] = salt_ip_address
  
      if deploy:
          display_ssh_output = config.get_cloud_config_value(
              'display_ssh_output', vm_, __opts__, default=True
          )
  
          vm_ = wait_for_instance(
              vm_, data, ip_address, display_ssh_output
          )
  
      # The instance is booted and accessible, let's Salt it!
      ret = instance.copy()
  
      # Get ANY defined volumes settings, merging data, in the following order
      # 1. VM config
      # 2. Profile config
      # 3. Global configuration
      volumes = config.get_cloud_config_value(
          'volumes', vm_, __opts__, search_global=True
      )
      if volumes:
          __utils__['cloud.fire_event'](
              'event',
              'attaching volumes',
              'salt/cloud/{0}/attaching_volumes'.format(vm_['name']),
              args={'volumes': volumes},
              sock_dir=__opts__['sock_dir'],
              transport=__opts__['transport']
          )
  
          log.info('Create and attach volumes to node %s', vm_['name'])
          created = create_attach_volumes(
              vm_['name'],
              {
                  'volumes': volumes,
                  'zone': ret['placement']['availabilityZone'],
                  'instance_id': ret['instanceId'],
                  'del_all_vols_on_destroy': vm_.get('del_all_vols_on_destroy', False)
              },
              call='action'
          )
          ret['Attached Volumes'] = created
  
      # Associate instance with a ssm document, if present
      ssm_document = config.get_cloud_config_value(
          'ssm_document', vm_, __opts__, None, search_global=False
      )
      if ssm_document:
          log.debug('Associating with ssm document: %s', ssm_document)
          assoc = ssm_create_association(
              vm_['name'],
              {'ssm_document': ssm_document},
              instance_id=vm_['instance_id'],
              call='action'
          )
          if isinstance(assoc, dict) and assoc.get('error', None):
              log.error(
                  'Failed to associate instance %s with ssm document %s',
                  vm_['instance_id'], ssm_document
              )
              return {}
  
      for key, value in six.iteritems(__utils__['cloud.bootstrap'](vm_, __opts__)):
          ret.setdefault(key, value)
  
      log.info('Created Cloud VM \'%s\'', vm_['name'])
      log.debug(
          '\'%s\' VM creation details:\n%s',
          vm_['name'], pprint.pformat(instance)
      )
  
      event_data = {
          'name': vm_['name'],
          'profile': vm_['profile'],
          'provider': vm_['driver'],
          'instance_id': vm_['instance_id'],
      }
      if volumes:
          event_data['volumes'] = volumes
      if ssm_document:
          event_data['ssm_document'] = ssm_document
  
      __utils__['cloud.fire_event'](
          'event',
          'created instance',
          'salt/cloud/{0}/created'.format(vm_['name']),
          args=__utils__['cloud.filter_event']('created', event_data, list(event_data)),
          sock_dir=__opts__['sock_dir'],
          transport=__opts__['transport']
      )
  
      # Ensure that the latest node data is returned
      node = _get_node(instance_id=vm_['instance_id'])
      __utils__['cloud.cache_node'](node, __active_provider_name__, __opts__)
      ret.update(node)
  
      # Add any block device tags specified
      ex_blockdevicetags = {}
      blockdevicemappings_holder = block_device_mappings(vm_)
      if blockdevicemappings_holder:
          for _bd in blockdevicemappings_holder:
              if 'tag' in _bd:
                  ex_blockdevicetags[_bd['DeviceName']] = _bd['tag']
  
      block_device_volume_id_map = {}
  
      if ex_blockdevicetags:
          for _device, _map in six.iteritems(ret['blockDeviceMapping']):
              bd_items = []
              if isinstance(_map, dict):
                  bd_items.append(_map)
              else:
                  for mapitem in _map:
                      bd_items.append(mapitem)
  
              for blockitem in bd_items:
                  if blockitem['deviceName'] in ex_blockdevicetags and 'Name' not in ex_blockdevicetags[blockitem['deviceName']]:
                      ex_blockdevicetags[blockitem['deviceName']]['Name'] = vm_['name']
                  if blockitem['deviceName'] in ex_blockdevicetags:
                      block_device_volume_id_map[blockitem[ret['rootDeviceType']]['volumeId']] = ex_blockdevicetags[blockitem['deviceName']]
  
      if block_device_volume_id_map:
  
          for volid, tags in six.iteritems(block_device_volume_id_map):
              __utils__['cloud.fire_event'](
                  'event',
                  'setting tags',
                  'salt/cloud/block_volume_{0}/tagging'.format(str(volid)),
                  args={'tags': tags},
                  sock_dir=__opts__['sock_dir'],
                  transport=__opts__['transport']
              )
  
              __utils__['cloud.wait_for_fun'](
                  set_tags,
                  timeout=30,
                  name=vm_['name'],
                  tags=tags,
                  resource_id=volid,
                  call='action',
                  location=location
              )
  
      return ret