Migration of content I am able to migrate organic groups easily enough, but when I attempt to migrate the group content, I am not able to successfully migrate the "parent" group ID(s) (or group audience if you prefer). I could only find snippets of code, and never a complete example, so hopefully I'm missing something simple.

Here is my complete migration code for the group content (as per multiple sources, but for example and per the OG issue page:

  $arguments = $common_arguments + array(
    'description' =&gt; t('Migration of group nodes from Drupal 6'),
    'machine_name' =&gt; 'RanchoNodeGroupNews',
    'source_type' =&gt; 'groupnews',
    'destination_type' =&gt; 'groupnews',
  );
  Migration::registerMigration('RanchoGroupNewsMigration', $arguments['machine_name'], $arguments);

class RanchoGroupNewsMigration extends RanchoNodeMigration {
  public function __construct(array $arguments) {
    parent::__construct($arguments);

    // OG Audience field
    $this-&gt;addFieldMapping('og_group_ref', 'og_group_ref')
         -&gt;separator(',')
         -&gt;sourceMigration('RanchoNodeGroup');
    $this-&gt;addFieldMapping('is_public', 'is_public');        
    // Set the default owner to rm.com
    $this-&gt;addFieldMapping('uid', 'uid')
         -&gt;defaultValue(4);

    $this-&gt;addUnmigratedSources(array(
    ),'DNM', FALSE);

    $this-&gt;addUnmigratedDestinations(array(
    ),'DNM', FALSE);

  }

  protected function query() {
    // Get the default query 
    $query = parent::query();
    $query-&gt;leftJoin('og_ancestry','oga','n.nid = oga.nid');
    $query-&gt;addExpression('GROUP_CONCAT(DISTINCT oga.group_nid)', 'og_group_ref');       
    $query-&gt;addField('oga', 'group_nid', 'group_nid');
    $query-&gt;addField('oga', 'is_public', 'is_public');

    return $query;
  }  

  public function prepareRow($row) {
    if (parent::prepareRow($row) === FALSE) {
      return FALSE;
    } 
    drush_print_r($row-&gt;og_group_ref);    
    return TRUE;
  }

  public function prepare($node, stdClass $row) {
    // Set og field_mode to 'admin' so we can save this group content despite not being a member of the group.
    // See https://www.drupal.org/node/2399997
    if (!empty($node-&gt;og_group_ref[LANGUAGE_NONE])) {
      foreach($node-&gt;og_group_ref[LANGUAGE_NONE] as $delta =&gt; $value) {
        $node-&gt;og_group_ref[LANGUAGE_NONE][$delta]['field_mode'] = 'admin';
      }
    }
    drush_print_r($node-&gt;og_group_ref);
  }
}


The output I receive from drush_print_r is as follows:

drush mi RanchoNodeGroupNews --idlist=60274 --user=1
56288
Array
(
    [und] =&gt; Array
        (
            [0] =&gt; Array
                (
                    [target_id] =&gt; 56288
                    [field_mode] =&gt; admin
                )

        )

)

Processed 1 (1 created, 0 updated, 0 failed, 0 ignored) in 0.2 sec (267/min) - done with 'RanchoNodeGroupNews'         

                          [completed]


Note that nid 56288 is indeed the parent group node, and is the only parent that has been migrated. I am able to edit the group content and set the parent manually. Any idea what's wrong with my code?
Seems like a bug, but needed to add a complete() function as follows:

class RanchoGroupNewsMigration extends RanchoNodeMigration {
  public function __construct(array $arguments) {
    parent::__construct($arguments);

    // OG Audience field
    $this-&gt;addFieldMapping('og_group_ref', 'og_group_ref')
         -&gt;separator(',')
         -&gt;sourceMigration('RanchoNodeGroup');
    $this-&gt;addFieldMapping('group_access','group_content_access');     

    ...

  }

  protected function query() {
    // Get the default query 
    $query = parent::query();
    $query-&gt;leftJoin('og_ancestry','oga','n.nid = oga.nid');
    $query-&gt;addExpression('GROUP_CONCAT(DISTINCT oga.group_nid)', 'og_group_ref');       
    $query-&gt;addField('oga', 'group_nid', 'group_nid');
    $query-&gt;leftJoin('og_access_post','oa','oa.nid = n.nid');
    $query-&gt;addField('oa','og_public','group_content_access');

    return $query;
  }  

  public function prepareRow($row) {
    if (parent::prepareRow($row) === FALSE) {
      return FALSE;
    } 
    // Seems the access setting is actually the opposite in D7, so we need to
    // toggle the boolean here.
    $row-&gt;group_content_access = !$row-&gt;group_content_access;
    return TRUE;
  }

  public function prepare($node, stdClass $row) {
    // Set og field_mode to 'admin' so we can save this group content despite not being a member of the group.
    // See https://www.drupal.org/node/2399997
    if (!empty($node-&gt;og_group_ref[LANGUAGE_NONE])) {
      foreach($node-&gt;og_group_ref[LANGUAGE_NONE] as $delta =&gt; $value) {
        $node-&gt;og_group_ref[LANGUAGE_NONE][$delta]['field_mode'] = 'admin';
      }
    }
  }

  public function complete($node, stdClass $row) {    
    $values = array(
      'entity_type' =&gt; 'node',
      'entity' =&gt; $node-&gt;nid,
      'state' =&gt; OG_STATE_ACTIVE,
      'membership_type' =&gt; OG_MEMBERSHIP_TYPE_DEFAULT,
      'field_name' =&gt; 'og_group_ref',
    );
    // Update the node
    $group_id = $row-&gt;og_group_ref;
    og_group('node', $group_id, $values);
  }
}


The above works regardless the number of groups the node is assigned to.

Note because this took me quite some time to figure out: the og_access and og_public have the opposite meaning from D6 to D7.