Creating a Drupal user with user_save doesn't automatically join an existing CiviCRM contact record I have a custom module that will create a Drupal user programmatically using user_save as follows:

  //set up the user fields
  $fields = array(
    'mail' =&gt; $email,
    'pass' =&gt; $password,
    'name' =&gt; $username,
    'status' =&gt; 1,
    'init' =&gt; $email,
    'access' =&gt; REQUEST_TIME,
    'roles' =&gt; array(2 =&gt; 'authenticated user'),
  );

  //the first parameter is left blank so a new user is created
  $account = user_save('', $fields);


I have first done a check using the CiviCRM API to make sure that if an existing contact is already in the database and has a Drupal account associated with it, we don't create a new one. Also I check to make sure the email address isn't registered from the Drupal side.

The problem is, when I create a new user, it is not linking up to an existing CiviCRM contact record that uses the same email address and I'm ending up with a duplicate contact record.

How can I force the site to link to an existing record if the email address is a match?
The function to synchronize Drupal users and CiviCRM contacts is CRM_Core_BAO_UFMatch::synchronize().

Since you're calling user_save(), CiviCRM's hook_user_insert implementation should get triggered (from user_module_invoke('insert', $edit, $account, $category); in user_save().

CiviCRM's civicrm_user_insert() checks whether it thinks you are on a CiviCRM callback already*, then calls either civicrm_register_data() which will in turn call CRM_Core_BAO_UFMatch::synchronize(), or CRM_Core_BAO_UFMatch::synchronize() directly.

To deal with this you could trace your way through CiviCRM's code (drupal/civicrm.module, drupal/civicrm_user.inc, CRM/Core/BAO/UFMatch.php) and work out which path your code is triggering; ultimately you want to end up with a call to CRM_Core_BAO_UFMatch::synchronize() which passes in the parameters for CiviCRM to correctly match the existing and new data.

You could also just call that function directly from your custom code; that might be less robust as in future you might end up triggering the synchronize call twice ...