diff --git a/rules.action.inc b/rules.action.inc
new file mode 100644
index 0000000..557124e
--- /dev/null
+++ b/rules.action.inc
@@ -0,0 +1,49 @@
+<?php
+
+require_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'rules') . "/ui/ui.forms.inc";
+
+/**
+ * Rules 2 actions are not integrated through Drupal actions, so no need
+ * for hook_action_info(). However, VBO insists on calling it for every include,
+ * so in this case it is declared but it returns an empty array.
+ */
+function views_bulk_operations_rules_action_info() {
+  return array();
+}
+
+function views_bulk_operations_rules_action($entity, $context) {
+  // If there was a config form, there's a rules_element.
+  // If not, fallback to the component key.
+  if (isset($context['rules_element'])) {
+    $element = $context['rules_element'];
+    // HACK: Unset the first setting so that passing an entity will work.
+    $settings = array_keys($element->settings);
+    $first_param = reset($settings);
+    unset($element->settings[$first_param]);
+  }
+  else {
+   $element = rules_action('component_' . $context['component_key']);
+  }
+  $result = $element->execute($entity);
+}
+
+function views_bulk_operations_rules_action_form($form, &$form_state, $context) {
+  $element = rules_action('component_' . $context['action']['key']);
+  $element->form($form, $form_state);
+
+  // Hide the form element for the "entity" param.
+  $entity_key = $context['action']['callback arguments']['entity_key'];
+  $form['parameter'][$entity_key]['#access'] = FALSE;
+
+  return $form;
+}
+
+function views_bulk_operations_rules_action_validate($form, $form_state) {
+  rules_ui_form_rules_config_validate($form, $form_state);
+}
+
+function views_bulk_operations_rules_action_submit($form, $form_state) {
+  return array(
+    'rules_element' => $form_state['rules_element'],
+  );
+}
diff --git a/ruleset.action.inc b/ruleset.action.inc
deleted file mode 100644
index 52b7434..0000000
--- a/ruleset.action.inc
+++ /dev/null
@@ -1,30 +0,0 @@
-<?php
-
-function views_bulk_operations_ruleset_action_info() {
-  if (!module_exists('rules')) return array();
-  $actions = array();
-  /*
-  foreach (rules_get_configured_items('rule_sets') as $ruleset_key => $ruleset) {
-    if (count($ruleset['arguments']) == 1) { // For now, we only accept rulesets with one parameter (taken to be the 'type')
-      $arg = key($ruleset['arguments']);
-      $actions["views_bulk_operations_ruleset_action_{$ruleset_key}"] = array(
-        'type' => $ruleset['arguments'][$arg]['type'],
-        'parameters' => array('ruleset' => $ruleset_key),
-        'label' => $ruleset['label'],
-        'configurable' => FALSE,
-        'rules_ignore' => TRUE,
-      );
-      eval(<<<EOS
-if (!function_exists('views_bulk_operations_ruleset_action_{$ruleset_key}')) {
-  function views_bulk_operations_ruleset_action_{$ruleset_key}(&\$object, \$context) {
-    rules_invoke_rule_set(\$context['ruleset'], \$object);
-  }
-}
-EOS
-      );
-    }
-  }
-  */
-  return $actions;
-}
-
diff --git a/views_bulk_operations.module b/views_bulk_operations.module
index 71cb330..68b2f95 100644
--- a/views_bulk_operations.module
+++ b/views_bulk_operations.module
@@ -767,7 +767,10 @@ function _views_bulk_operations_action_do($operation, $entity_id, $entity, $para
       node_save($entity);
     }
   }
-  else {
+  elseif ($operation['source'] == 'rules_action') {
+    call_user_func_array($operation['callback'], array('entity' => $entity, 'context' => $params));
+  }
+  elseif ($operation['source'] == 'operation') {
     $args = array_merge(array(array($entity_id)), $params);
     call_user_func_array($operation['callback'], $args);
   }
diff --git a/views_bulk_operations_handler_field_operations.inc b/views_bulk_operations_handler_field_operations.inc
index f93b040..8e85a46 100644
--- a/views_bulk_operations_handler_field_operations.inc
+++ b/views_bulk_operations_handler_field_operations.inc
@@ -279,6 +279,7 @@ class views_bulk_operations_handler_field_operations extends views_handler_field
       }
     }
 
+    // Merge in Drupal actions, most declared by VBO itself through hook_action_info().
     $action_operations = actions_list() + $this->get_custom_actions();
     foreach ($action_operations as $callback => $operation) {
       $key = isset($operation['key']) ? $operation['key'] : $callback;
@@ -296,6 +297,32 @@ class views_bulk_operations_handler_field_operations extends views_handler_field
       );
     }
 
+    // Merge in actions provided by Rules 2.
+    $entity_info = entity_get_info();
+    $entity_types = array_keys($entity_info);
+    foreach (rules_get_components(FALSE, 'action') as $component_key => $component) {
+      $parameter_info = $component->parameterInfo();
+      $first_parameter = reset($parameter_info);
+      $entity_key = reset(array_keys($parameter_info));
+      // If the first param is not an entity type, skip the component.
+      if (!in_array($first_parameter['type'], $entity_types)) {
+        continue;
+      }
+
+      $operations[$component_key] = array(
+        'key' => $component_key,
+        'label' => $component->label,
+        'callback' => 'views_bulk_operations_rules_action',
+        'callback arguments' => array('component_key' => $component_key, 'entity_key' => $entity_key),
+        'configurable' => count($parameter_info) > 1,
+        'source' => 'rules_action',
+        'type' => $first_parameter['type'],
+        'aggregate' => VBO_AGGREGATE_FORBIDDEN,
+        'access operation' => VBO_ACCESS_OP_UPDATE, // assume edit by default.
+        'permissions' => NULL,
+      );
+    }
+
     uasort($operations, create_function('$a, $b', 'return strcasecmp($a["label"], $b["label"]);'));
     $this->all_operations = $operations;
   }
