diff --git a/sites/all/modules/unl/cron.php b/sites/all/modules/unl/cron.php
index fa18e461b0a968756fb6107cc9d76d7aa6ebdfd3..3ecaf98eb2d8ec53583442dc376cd912fa66e221 100644
--- a/sites/all/modules/unl/cron.php
+++ b/sites/all/modules/unl/cron.php
@@ -9,6 +9,7 @@ chdir(dirname(__FILE__) . '/../../../..');
 define('DRUPAL_ROOT', getcwd());
 
 require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
+require_once dirname(__FILE__) . '/includes/common.php';
 drupal_override_server_variables();
 drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
 
@@ -167,26 +168,6 @@ function unl_remove_page_aliases() {
   }
 }
 
-
-function _unl_get_sites_subdir($uri, $trim_subdomain = TRUE) {
-  $path_parts = parse_url($uri);
-  if ($trim_subdomain && substr($path_parts['host'], -7) == 'unl.edu') {
-    $path_parts['host'] = 'unl.edu';
-  }
-  $sites_subdir = $path_parts['host'] . $path_parts['path'];
-  $sites_subdir = strtr($sites_subdir, array('/' => '.'));
-
-  while (substr($sites_subdir, 0, 1) == '.') {
-    $sites_subdir = substr($sites_subdir, 1);
-  }
-  while (substr($sites_subdir, -1) == '.') {
-    $sites_subdir = substr($sites_subdir, 0, -1);
-  }
-  
-  return $sites_subdir;
-}
-
-
 function unl_add_site($site_path, $uri, $clean_url, $db_prefix, $site_id) {
   if (substr($site_path, 0, 1) == '/') {
     $site_path = substr($site_path, 1);
@@ -195,7 +176,7 @@ function unl_add_site($site_path, $uri, $clean_url, $db_prefix, $site_id) {
     $site_path = substr($site_path, 0, -1);
   }
   
-  $sites_subdir = _unl_get_sites_subdir($uri);
+  $sites_subdir = unl_get_sites_subdir($uri);
   
   $database = $GLOBALS['databases']['default']['default'];
   $db_url = $database['driver']
@@ -236,7 +217,7 @@ function unl_remove_site($site_path, $uri, $db_prefix, $site_id) {
   $db_prefix .= '_' . $database['prefix'];
   
   
-  $sites_subdir = _unl_get_sites_subdir($uri);
+  $sites_subdir = unl_get_sites_subdir($uri);
   $sites_subdir = DRUPAL_ROOT . '/sites/' . $sites_subdir;
   $sites_subdir = realpath($sites_subdir);
   
@@ -276,8 +257,8 @@ function unl_remove_site($site_path, $uri, $db_prefix, $site_id) {
 
 function unl_add_alias($site_uri, $base_uri, $path, $alias_id) {
   $alias_uri = $base_uri . $path;
-  $real_config_dir = _unl_get_sites_subdir($site_uri);
-  $alias_config_dir = _unl_get_sites_subdir($alias_uri, FALSE);
+  $real_config_dir = unl_get_sites_subdir($site_uri);
+  $alias_config_dir = unl_get_sites_subdir($alias_uri, FALSE);
   $result = symlink($real_config_dir, DRUPAL_ROOT . '/sites/' . $alias_config_dir);
   
   if ($path) {
@@ -289,7 +270,7 @@ function unl_add_alias($site_uri, $base_uri, $path, $alias_id) {
 
 function unl_remove_alias($base_uri, $path, $alias_id) {
   $alias_uri = $base_uri . $path;
-  $alias_config_dir = _unl_get_sites_subdir($alias_uri, FALSE);
+  $alias_config_dir = unl_get_sites_subdir($alias_uri, FALSE);
   unlink(DRUPAL_ROOT . '/sites/' . $alias_config_dir);
   
   unl_remove_site_from_htaccess($alias_id, TRUE);
diff --git a/sites/all/modules/unl/includes/common.php b/sites/all/modules/unl/includes/common.php
index 87430fdd9872259fb9c0abda6b60b5b338f9d12a..67a66e5850ecbb3be66e998d592703e7be2cc4df 100644
--- a/sites/all/modules/unl/includes/common.php
+++ b/sites/all/modules/unl/includes/common.php
@@ -42,3 +42,40 @@ function unl_shared_variable_get($name, $default = NULL) {
 
   return unserialize($data[0]->value);
 }
+
+/**
+ * Given a URI, will return the name of the directory for that site in the sites directory.
+ */
+function unl_get_sites_subdir($uri, $trim_subdomain = TRUE) {
+  $path_parts = parse_url($uri);
+  if ($trim_subdomain && substr($path_parts['host'], -7) == 'unl.edu') {
+    $path_parts['host'] = 'unl.edu';
+  }
+  $sites_subdir = $path_parts['host'] . $path_parts['path'];
+  $sites_subdir = strtr($sites_subdir, array('/' => '.'));
+
+  while (substr($sites_subdir, 0, 1) == '.') {
+    $sites_subdir = substr($sites_subdir, 1);
+  }
+  while (substr($sites_subdir, -1) == '.') {
+    $sites_subdir = substr($sites_subdir, 0, -1);
+  }
+
+  return $sites_subdir;
+}
+
+/**
+ * Given a URI of an existing site, will return settings defined in that site's settings.php
+ */
+function unl_get_site_settings($uri) {
+  $settings_file = DRUPAL_ROOT . '/sites/' . unl_get_sites_subdir($uri) . '/settings.php';
+  if (!is_readable($settings_file)) {
+    throw new Exception('No settings.php exists for site at ' . $uri);
+  }
+  
+  require $settings_file;
+  unset($uri);
+  unset($settings_file);
+  
+  return get_defined_vars();
+} 
\ No newline at end of file
diff --git a/sites/all/modules/unl/unl.module b/sites/all/modules/unl/unl.module
index c8ddc6f07df419df7886d9d7680a4c47d9b20c24..1ee06bf4f1fd2d90687fd8e5bf23d69d675154d2 100644
--- a/sites/all/modules/unl/unl.module
+++ b/sites/all/modules/unl/unl.module
@@ -354,6 +354,16 @@ function unl_menu() {
       'file'             => 'unl_site_creation.php',
     );
 
+    $items['admin/sites/unl/user-audit'] = array(
+      'title'            => 'User Audit',
+      'description'      => 'Find out which sites users have access to.',
+      'access arguments' => array('unl site creation'),
+      'page callback'    => 'drupal_get_form',
+      'page arguments'   => array('unl_user_audit'),
+      'type'             => MENU_LOCAL_TASK,
+      'file'             => 'unl_site_creation.php',
+    );
+
     $items['admin/sites/unl/wdn_registry'] = array(
       'title'            => 'WDN Registry',
       'description'      => 'Settings for the connection to the WDN Registry.',
diff --git a/sites/all/modules/unl/unl_site_creation.php b/sites/all/modules/unl/unl_site_creation.php
index d082e12886e0af1bcfe2fb109d23fb3da39fce97..eeb323232fcbc70cdfa1ba718e2f0fc116bf4be5 100644
--- a/sites/all/modules/unl/unl_site_creation.php
+++ b/sites/all/modules/unl/unl_site_creation.php
@@ -657,6 +657,121 @@ function _unl_get_install_status_text($id) {
   return $installed;
 }
 
+/**
+ * Callback for the path admin/sites/unl/user-audit
+ * Presents a form to query what roles (if any) a user has on each site.
+ */
+
+function unl_user_audit($form, &$form_state) {
+  $form['root'] = array(
+    '#type' => 'fieldset',
+    '#title' => 'User Audit',
+  );
+  
+  $form['root']['username'] = array(
+    '#type' => 'textfield',
+    '#title' => 'Username',
+    '#required' => TRUE,
+  );
+  
+  /*
+  $form['root']['ignore_shared_roles'] = array(
+    '#type' => 'checkbox',
+    '#title' => 'Ignore Shared Roles',
+  );
+  */
+  
+  $form['root']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => 'Search',
+  );
+  
+  // If no user input has been received yet, return the base form. 
+  if (!isset($form_state['values']) || !$form_state['values']['username']) {
+    return $form;
+  }
+  
+  
+  // Otherwise, since we have a username, we can query the sub-sites and return a list of roles for each.
+  $username = $form_state['values']['username'];
+  
+  $sites = db_select('unl_sites', 's')
+    ->fields('s', array('site_id', 'db_prefix', 'installed', 'site_path', 'uri'))
+    ->execute()
+    ->fetchAll();
+  
+  $audit_map = array();
+  foreach ($sites as $site) {
+    $shared_prefix = unl_get_shared_db_prefix();
+    $prefix = $site->db_prefix;
+    
+    try {
+      $site_settings = unl_get_site_settings($site->uri);
+      $site_db_config = $site_settings['databases']['default']['default'];
+      $roles_are_shared = is_array($site_db_config['prefix']) && array_key_exists('role', $site_db_config['prefix']);
+      
+      /*
+      // If the site uses shared roles, ignore it if the user wants us to.
+      if ($roles_are_shared && $form_state['values']['ignore_shared_roles']) {
+        continue;
+      }
+      */
+      
+      $role_names = db_query(
+        "SELECT r.name "
+        . "FROM {$prefix}_{$shared_prefix}users AS u "
+        . "JOIN {$prefix}_{$shared_prefix}users_roles AS m "
+        . "  ON u.uid = m.uid "
+        . 'JOIN ' . ($roles_are_shared ? '' : $prefix . '_') . $shared_prefix . 'role AS r '
+        . "  ON m.rid = r.rid "
+        . "WHERE u.name = :name",
+        array(':name' => $username)
+      )->fetchCol();
+      
+      if (count($role_names) == 0) {
+        continue;
+      }
+      
+      $audit_map[] = array(
+        'data' => l($site->uri, $site->uri),
+        'children' => $role_names,
+      );
+    } catch (Exception $e) {
+      // Either the site has no settings.php or the db_prefix is wrong.
+      drupal_set_message('Error querying database for site ' . $site->uri, 'warning');
+    } 
+  }
+  
+  $form['results'] = array(
+    '#type' => 'fieldset',
+    '#title' => 'Results',
+  );
+  
+  if (count($audit_map) > 0) {
+    $form['results']['roles'] = array(
+      '#theme' => 'item_list',
+      '#title' => 'The user "' . $username . '" belongs to the following sites as a member of the listed roles.',
+      '#type'  => 'ul',
+      '#items' => $audit_map,
+    );
+  } else {
+    $form['results']['roles'] = array(
+      '#type' => 'item',
+      '#title' => 'The user "' . $username . '" does not belong to any roles on any sites.',
+    );
+  }
+  
+  return $form;
+}
+
+/**
+ * Submit handler for unl_user_audit form.
+ * Simply tells the form to rebuild itself with the user supplied data.
+ */
+function unl_user_audit_submit($form, &$form_state) {
+  $form_state['rebuild'] = TRUE;
+}
+
 function theme_unl_table($variables) {
   $form = $variables['form'];
   foreach (element_children($form['rows']) as $row_index) {