diff --git a/modules/aggregator2/lib/Aggregator.php b/modules/aggregator2/lib/Aggregator.php
index 61ec7b003a34c9c3d71eccfbb2d213738fea1329..6d4db6acf287a9539ed5bbac7ec629aa7533e5b0 100644
--- a/modules/aggregator2/lib/Aggregator.php
+++ b/modules/aggregator2/lib/Aggregator.php
@@ -67,6 +67,41 @@ class sspmod_aggregator2_Aggregator {
 	protected $cacheGenerated;
 
 
+    /**
+     * An array of entity IDs to exclude from the aggregate.
+     *
+     * @var string[]|null
+     */
+    protected $excluded;
+
+
+    /**
+     * An indexed array of protocols to filter the aggregate by. keys can be any of:
+     *
+     * - urn:oasis:names:tc:SAML:1.1:protocol
+     * - urn:oasis:names:tc:SAML:2.0:protocol
+     *
+     * Values will be true if enabled, false otherwise.
+     *
+     * @var string[]|null
+     */
+    protected $protocols;
+
+
+    /**
+     * An array of roles to filter the aggregate by. Keys can be any of:
+     *
+     * - SAML2_XML_md_IDPSSODescriptor
+     * - SAML2_XML_md_SPSSODescriptor
+     * - SAML2_XML_md_AttributeAuthorityDescriptor
+     *
+     * Values will be true if enabled, false otherwise.
+     *
+     * @var string[]|null
+     */
+    protected $roles;
+
+
 	/**
 	 * The key we should use to sign the metadata.
 	 *
@@ -151,6 +186,12 @@ class sspmod_aggregator2_Aggregator {
 			$this->cacheTag = sha1(serialize($config));
 		}
 
+        // configure entity IDs excluded by default
+        $this->excludeEntities($config->getArrayize('exclude', null));
+
+        // configure filters
+        $this->setFilters($config->getArrayize('filter', null));
+
 		$this->validLength = $config->getInteger('valid.length', 7*24*60*60);
 
 		$globalConfig = SimpleSAML_Configuration::getInstance();
@@ -424,6 +465,159 @@ class sspmod_aggregator2_Aggregator {
 	}
 
 
+    /**
+     * Recursively traverse the children of an EntitiesDescriptor, removing those entities listed in the $entities
+     * property. Returns the EntitiesDescriptor with the entities filtered out.
+     *
+     * @param SAML2_XML_md_EntitiesDescriptor $descriptor The EntitiesDescriptor from where to exclude entities.
+     *
+     * @return SAML2_XML_md_EntitiesDescriptor The EntitiesDescriptor with excluded entities filtered out.
+     */
+    protected function exclude(SAML2_XML_md_EntitiesDescriptor $descriptor)
+    {
+        if (empty($this->excluded)) {
+            return $descriptor;
+        }
+
+        $filtered = array();
+        foreach ($descriptor->children as $child) {
+            if ($child instanceof SAML2_XML_md_EntityDescriptor) {
+                if (in_array($child->entityID, $this->excluded)) {
+                    continue;
+                }
+                $filtered[] = $child;
+            }
+
+            if ($child instanceof SAML2_XML_md_EntitiesDescriptor) {
+                $filtered[] = $this->exclude($child);
+            }
+        }
+
+        $descriptor->children = $filtered;
+        return $descriptor;
+    }
+
+
+    /**
+     * Recursively traverse the children of an EntitiesDescriptor, keeping only those entities with the roles listed in
+     * the $roles property, and support for the protocols listed in the $protocols property. Returns the
+     * EntitiesDescriptor containing only those entities.
+     *
+     * @param SAML2_XML_md_EntitiesDescriptor $descriptor The EntitiesDescriptor to filter.
+     *
+     * @return SAML2_XML_md_EntitiesDescriptor The EntitiesDescriptor with only the entities filtered.
+     */
+    protected function filter(SAML2_XML_md_EntitiesDescriptor $descriptor)
+    {
+        if ($this->roles === null || $this->protocols === null) {
+            return $descriptor;
+        }
+
+        $enabled_roles = array_keys($this->roles, true);
+        $enabled_protos = array_keys($this->protocols, true);
+
+        $filtered = array();
+        foreach ($descriptor->children as $child) {
+            if ($child instanceof SAML2_XML_md_EntityDescriptor) {
+                foreach ($child->RoleDescriptor as $role) {
+                    if (in_array(get_class($role), $enabled_roles)) {
+                        // we found a role descriptor that is enabled by our filters, check protocols
+                        if (array_intersect($enabled_protos, $role->protocolSupportEnumeration) !== array()) {
+                            // it supports some protocol we have enabled, add it
+                            $filtered[] = $child;
+                            break;
+                        }
+                    }
+                }
+
+            }
+
+            if ($child instanceof SAML2_XML_md_EntitiesDescriptor) {
+                $filtered[] = $this->filter($child);
+            }
+        }
+
+        $descriptor->children = $filtered;
+        return $descriptor;
+    }
+
+
+    /**
+     * Set this aggregator to exclude a set of entities from the resulting aggregate.
+     *
+     * @param array|null $entities The entity IDs of the entities to exclude.
+     */
+    public function excludeEntities($entities)
+    {
+        assert('is_array($entities) || is_null($entities)');
+
+        if ($entities === null) {
+            return;
+        }
+        $this->excluded = $entities;
+        sort($this->excluded);
+        $this->cacheId = sha1($this->cacheId . serialize($this->excluded));
+    }
+
+
+    /**
+     * Set the internal filters according to one or more options:
+     *
+     * - 'saml2': all SAML2.0-capable entities.
+     * - 'shib13': all SHIB1.3-capable entities.
+     * - 'saml20-idp': all SAML2.0-capable identity providers.
+     * - 'saml20-sp': all SAML2.0-capable service providers.
+     * - 'saml20-aa': all SAML2.0-capable attribute authorities.
+     * - 'shib13-idp': all SHIB1.3-capable identity providers.
+     * - 'shib13-sp': all SHIB1.3-capable service providers.
+     * - 'shib13-aa': all SHIB1.3-capable attribute authorities.
+     *
+     * @param array|null $set An array of the different roles and protocols to filter by.
+     */
+    public function setFilters($set)
+    {
+        assert('is_array($set) || is_null($set)');
+
+        if ($set === null) {
+            return;
+        }
+
+        // configure filters
+        $this->protocols = array(
+            SAML2_Const::NS_SAMLP                  => TRUE,
+            'urn:oasis:names:tc:SAML:1.1:protocol' => TRUE,
+        );
+        $this->roles = array(
+            'SAML2_XML_md_IDPSSODescriptor'             => TRUE,
+            'SAML2_XML_md_SPSSODescriptor'              => TRUE,
+            'SAML2_XML_md_AttributeAuthorityDescriptor' => TRUE,
+        );
+
+        // now translate from the options we have, to specific protocols and roles
+
+        // check SAML 2.0 protocol
+        $options = array('saml2', 'saml20-idp', 'saml20-sp', 'saml20-aa');
+        $this->protocols[SAML2_Const::NS_SAMLP] = (array_intersect($set, $options) !== array());
+
+        // check SHIB 1.3 protocol
+        $options = array('shib13', 'shib13-idp', 'shib13-sp', 'shib13-aa');
+        $this->protocols['urn:oasis:names:tc:SAML:1.1:protocol'] = (array_intersect($set, $options) !== array());
+
+        // check IdP
+        $options = array('saml2', 'shib13', 'saml20-idp', 'shib13-idp');
+        $this->roles['SAML2_XML_md_IDPSSODescriptor'] = (array_intersect($set, $options) !== array());
+
+        // check SP
+        $options = array('saml2', 'shib13', 'saml20-sp', 'shib13-sp');
+        $this->roles['SAML2_XML_md_SPSSODescriptor'] = (array_intersect($set, $options) !== array());
+
+        // check AA
+        $options = array('saml2', 'shib13', 'saml20-aa', 'shib13-aa');
+        $this->roles['SAML2_XML_md_AttributeAuthorityDescriptor'] = (array_intersect($set, $options) !== array());
+
+        $this->cacheId = sha1($this->cacheId . serialize($this->protocols) . serialize($this->roles));
+    }
+
 	/**
 	 * Retrieve the complete, signed metadata as text.
 	 *
@@ -435,6 +629,8 @@ class sspmod_aggregator2_Aggregator {
 	public function updateCachedMetadata() {
 
 		$ed = $this->getEntitiesDescriptor();
+        $ed = $this->exclude($ed);
+        $ed = $this->filter($ed);
 		$this->addSignature($ed);
 
 		$xml = $ed->toXML();
diff --git a/modules/aggregator2/www/get.php b/modules/aggregator2/www/get.php
index 8ace0a5061301ed683cd0df25ef070d85f2c8a4d..1a07714b65cd3523f12a50032d8fd8db127c8a03 100644
--- a/modules/aggregator2/www/get.php
+++ b/modules/aggregator2/www/get.php
@@ -5,7 +5,19 @@ if (!isset($_REQUEST['id'])) {
 }
 $id = (string) $_REQUEST['id'];
 
+$set = null;
+if (isset($_REQUEST['set'])) {
+    $set = explode(',', $_REQUEST['set']);
+}
+
+$excluded_entities = null;
+if (isset($_REQUEST['exclude'])) {
+    $excluded_entities = explode(',', $_REQUEST['exclude']);
+}
+
 $aggregator = sspmod_aggregator2_Aggregator::getAggregator($id);
+$aggregator->setFilters($set);
+$aggregator->excludeEntities($excluded_entities);
 $xml = $aggregator->getMetadata();
 
 $mimetype = 'application/samlmetadata+xml';