diff --git a/FWCore/Framework/src/Schedule.cc b/FWCore/Framework/src/Schedule.cc
index cb17d567b9dc0..47eaa78865da8 100644
--- a/FWCore/Framework/src/Schedule.cc
+++ b/FWCore/Framework/src/Schedule.cc
@@ -232,6 +232,14 @@ namespace edm {
 
       std::map<BranchKey, BranchKey> aliasKeys;  // Used to search for duplicates or clashes.
 
+      // Auxiliary search structure to support wildcard for friendlyClassName
+      std::multimap<std::string, BranchKey> moduleLabelToBranches;
+      for (auto const& prod : preg.productList()) {
+        if (processName == prod.second.processName()) {
+          moduleLabelToBranches.emplace(prod.first.moduleLabel(), prod.first);
+        }
+      }
+
       // Now, loop over the alias information and store it in aliasMap.
       for (std::string const& alias : aliases) {
         ParameterSet const& aliasPSet = proc_pset.getParameterSet(alias);
@@ -243,7 +251,39 @@ namespace edm {
             std::string friendlyClassName = pset.getParameter<std::string>("type");
             std::string productInstanceName = pset.getParameter<std::string>("fromProductInstance");
             std::string instanceAlias = pset.getParameter<std::string>("toProductInstance");
-            if (productInstanceName == star) {
+
+            if (friendlyClassName == star) {
+              bool processHasLabel = false;
+              bool match = false;
+              for (auto it = moduleLabelToBranches.lower_bound(moduleLabel);
+                   it != moduleLabelToBranches.end() && it->first == moduleLabel;
+                   ++it) {
+                processHasLabel = true;
+                if (productInstanceName != star and productInstanceName != it->second.productInstanceName()) {
+                  continue;
+                }
+                match = true;
+
+                checkAndInsertAlias(it->second.friendlyClassName(),
+                                    moduleLabel,
+                                    it->second.productInstanceName(),
+                                    processName,
+                                    alias,
+                                    instanceAlias,
+                                    preg,
+                                    aliasMap,
+                                    aliasKeys);
+              }
+              if (not match and processHasLabel) {
+                // No product was found matching the alias.
+                // We throw an exception only if a module with the specified module label was created in this process.
+                // Note that if that condition is ever relatex, it  might be best to throw an exception with different
+                // message (omitting productInstanceName) in case 'productInstanceName == start'
+                throw Exception(errors::Configuration, "EDAlias parameter set mismatch\n")
+                    << "There are no products with module label '" << moduleLabel << "' and product instance name '"
+                    << productInstanceName << "'.\n";
+              }
+            } else if (productInstanceName == star) {
               bool match = false;
               BranchKey lowerBound(friendlyClassName, moduleLabel, empty, empty);
               for (ProductRegistry::ProductList::const_iterator it = preg.productList().lower_bound(lowerBound);
diff --git a/FWCore/Integration/test/BuildFile.xml b/FWCore/Integration/test/BuildFile.xml
index c94ad0eb4b776..82849d492f1e2 100644
--- a/FWCore/Integration/test/BuildFile.xml
+++ b/FWCore/Integration/test/BuildFile.xml
@@ -189,6 +189,7 @@
     <use name="FWCore/TestProcessor"/>
     <use name="DataFormats/Provenance"/>
     <use name="catch2"/>
+    <use name="fmt"/>
   </bin>
 
   <library file="TestPSetAnalyzer.cc" name="TestPSetAnalyzer">
diff --git a/FWCore/Integration/test/EDAlias_t.cpp b/FWCore/Integration/test/EDAlias_t.cpp
index d1d2c63ed52de..af65577b2901e 100644
--- a/FWCore/Integration/test/EDAlias_t.cpp
+++ b/FWCore/Integration/test/EDAlias_t.cpp
@@ -6,6 +6,7 @@
 #include "FWCore/TestProcessor/interface/TestProcessor.h"
 
 #include <iostream>
+#include <fmt/format.h>
 
 static constexpr auto s_tag = "[EDAlias]";
 
@@ -116,3 +117,241 @@ process.moduleToTest(process.test, cms.Task(process.intprod))
                             Catch::Contains("One has module label") && Catch::Contains("the other has module label"));
   }
 }
+
+////////////////////////////////////////
+TEST_CASE("Configuration with all products of a module", s_tag) {
+  const std::string baseConfig{
+      R"_(from FWCore.TestProcessor.TestProcess import *
+import FWCore.ParameterSet.Config as cms
+
+process = TestProcess()
+
+process.intprod = cms.EDProducer('ManyIntProducer', ivalue = cms.int32(2),
+    values = cms.VPSet(
+        cms.PSet(instance=cms.string('foo'),value=cms.int32(3)),
+        cms.PSet(instance=cms.string('another'),value=cms.int32(4)),
+    )
+)
+
+process.intalias = cms.EDAlias(intprod = cms.EDAlias.allProducts())
+
+# Module to be tested can not be an EDAlias
+process.test = cms.EDProducer('AddIntsProducer',
+    labels = cms.VInputTag('intalias', ('intalias', 'foo'), ('intalias', 'another'))
+)
+
+process.moduleToTest(process.test, cms.Task(process.intprod))
+)_"};
+
+  edm::test::TestProcessor::Config config{baseConfig};
+
+  SECTION("Base configuration is OK") { REQUIRE_NOTHROW(edm::test::TestProcessor(config)); }
+
+  SECTION("No event data") {
+    edm::test::TestProcessor tester(config);
+    REQUIRE_NOTHROW(tester.test());
+  }
+
+  SECTION("beginJob and endJob only") {
+    edm::test::TestProcessor tester(config);
+    REQUIRE_NOTHROW(tester.testBeginAndEndJobOnly());
+  }
+
+  SECTION("Run with no LuminosityBlocks") {
+    edm::test::TestProcessor tester(config);
+    REQUIRE_NOTHROW(tester.testRunWithNoLuminosityBlocks());
+  }
+
+  SECTION("LuminosityBlock with no Events") {
+    edm::test::TestProcessor tester(config);
+    REQUIRE_NOTHROW(tester.testLuminosityBlockWithNoEvents());
+  }
+
+  SECTION("Getting value") {
+    edm::test::TestProcessor tester(config);
+    auto event = tester.test();
+    REQUIRE(event.get<edmtest::IntProduct>()->value == 9);
+  }
+}
+
+TEST_CASE("Configuration with all products of a module with a given product instance name", s_tag) {
+  const std::string baseConfig{
+      R"_(from FWCore.TestProcessor.TestProcess import *
+import FWCore.ParameterSet.Config as cms
+
+process = TestProcess()
+
+process.intprod = cms.EDProducer('ManyIntProducer', ivalue = cms.int32(2),
+    values = cms.VPSet(
+        cms.PSet(instance=cms.string('foo'),value=cms.int32(3)),
+        cms.PSet(instance=cms.string('another'),value=cms.int32(4)),
+    )
+)
+
+process.intalias = cms.EDAlias(intprod = cms.VPSet(cms.PSet(type = cms.string('*'), fromProductInstance = cms.string('another'))))
+
+# Module to be tested can not be an EDAlias
+process.test = cms.EDProducer('AddIntsProducer',
+    labels = cms.VInputTag({})
+)
+
+process.moduleToTest(process.test, cms.Task(process.intprod))
+)_"};
+
+  edm::test::TestProcessor::Config config{fmt::format(baseConfig, "'intalias:another'")};
+
+  SECTION("Base configuration is OK") { REQUIRE_NOTHROW(edm::test::TestProcessor(config)); }
+
+  SECTION("No event data") {
+    edm::test::TestProcessor tester(config);
+    REQUIRE_NOTHROW(tester.test());
+  }
+
+  SECTION("beginJob and endJob only") {
+    edm::test::TestProcessor tester(config);
+    REQUIRE_NOTHROW(tester.testBeginAndEndJobOnly());
+  }
+
+  SECTION("Run with no LuminosityBlocks") {
+    edm::test::TestProcessor tester(config);
+    REQUIRE_NOTHROW(tester.testRunWithNoLuminosityBlocks());
+  }
+
+  SECTION("LuminosityBlock with no Events") {
+    edm::test::TestProcessor tester(config);
+    REQUIRE_NOTHROW(tester.testLuminosityBlockWithNoEvents());
+  }
+
+  SECTION("Getting value") {
+    edm::test::TestProcessor tester(config);
+    auto event = tester.test();
+    REQUIRE(event.get<edmtest::IntProduct>()->value == 4);
+  }
+
+  SECTION("Other product instances are not aliased") {
+    {
+      edm::test::TestProcessor::Config config{fmt::format(baseConfig, "'intalias:foo'")};
+      edm::test::TestProcessor tester(config);
+      REQUIRE_THROWS_WITH(tester.test(), Catch::Contains("ProductNotFound"));
+    }
+    {
+      edm::test::TestProcessor::Config config{fmt::format(baseConfig, "'intalias'")};
+      edm::test::TestProcessor tester(config);
+      REQUIRE_THROWS_WITH(tester.test(), Catch::Contains("ProductNotFound"));
+    }
+  }
+}
+
+////////////////////////////////////////
+TEST_CASE("Configuration with all products of two modules", s_tag) {
+  const std::string baseConfig{
+      R"_(from FWCore.TestProcessor.TestProcess import *
+import FWCore.ParameterSet.Config as cms
+
+process = TestProcess()
+
+process.intprod = cms.EDProducer('ManyIntProducer', ivalue = cms.int32(2),
+    values = cms.VPSet(
+        cms.PSet(instance=cms.string('foo'),value=cms.int32(3)),
+        cms.PSet(instance=cms.string('another'),value=cms.int32(4)),
+    )
+)
+process.intprod2 = cms.EDProducer('ManyIntProducer', ivalue = cms.int32(20),
+    values = cms.VPSet(
+        cms.PSet(instance=cms.string('foo2'),value=cms.int32(30)),
+        cms.PSet(instance=cms.string('another2'),value=cms.int32(40)),
+    )
+)
+
+process.intalias = cms.EDAlias(
+    intprod = cms.EDAlias.allProducts(),
+    # can't use allProducts() because the product instance '' would lead to duplicate brances to be aliased
+    intprod2 = cms.VPSet(
+        cms.PSet(type = cms.string('*'), fromProductInstance = cms.string('foo2')),
+        cms.PSet(type = cms.string('*'), fromProductInstance = cms.string('another2')),
+    )
+)
+
+# Module to be tested can not be an EDAlias
+process.test = cms.EDProducer('AddIntsProducer',
+    labels = cms.VInputTag('intalias', ('intalias', 'foo'), ('intalias', 'another'), ('intalias', 'foo2'), ('intalias', 'another2'))
+)
+
+process.moduleToTest(process.test, cms.Task(process.intprod, process.intprod2))
+)_"};
+
+  edm::test::TestProcessor::Config config{baseConfig};
+
+  SECTION("Base configuration is OK") { REQUIRE_NOTHROW(edm::test::TestProcessor(config)); }
+
+  SECTION("No event data") {
+    edm::test::TestProcessor tester(config);
+    REQUIRE_NOTHROW(tester.test());
+  }
+
+  SECTION("beginJob and endJob only") {
+    edm::test::TestProcessor tester(config);
+    REQUIRE_NOTHROW(tester.testBeginAndEndJobOnly());
+  }
+
+  SECTION("Run with no LuminosityBlocks") {
+    edm::test::TestProcessor tester(config);
+    REQUIRE_NOTHROW(tester.testRunWithNoLuminosityBlocks());
+  }
+
+  SECTION("LuminosityBlock with no Events") {
+    edm::test::TestProcessor tester(config);
+    REQUIRE_NOTHROW(tester.testLuminosityBlockWithNoEvents());
+  }
+
+  SECTION("Getting value") {
+    edm::test::TestProcessor tester(config);
+    auto event = tester.test();
+    REQUIRE(event.get<edmtest::IntProduct>()->value == 79);
+  }
+}
+
+////////////////////////////////////////
+TEST_CASE("No products found with wildcards", s_tag) {
+  const std::string baseConfig{
+      R"_(from FWCore.TestProcessor.TestProcess import *
+import FWCore.ParameterSet.Config as cms
+
+process = TestProcess()
+
+process.intprod = cms.EDProducer('ManyIntProducer', ivalue = cms.int32(2),
+    values = cms.VPSet(
+        cms.PSet(instance=cms.string('foo'),value=cms.int32(3)),
+        cms.PSet(instance=cms.string('another'),value=cms.int32(4)),
+    )
+)
+
+process.intalias = cms.EDAlias({})
+
+# Module to be tested can not be an EDAlias
+process.test = cms.EDProducer('AddIntsProducer',
+    labels = cms.VInputTag('intalias')
+)
+
+process.moduleToTest(process.test, cms.Task(process.intprod))
+)_"};
+
+  SECTION("Type wildcard") {
+    edm::test::TestProcessor::Config config{fmt::format(
+        baseConfig,
+        "intprod = cms.VPSet(cms.PSet(type = cms.string('*'), fromProductInstance = cms.string('nonexistent')))")};
+
+    REQUIRE_THROWS_WITH(
+        edm::test::TestProcessor(config),
+        Catch::Contains("There are no products with module label 'intprod' and product instance name 'nonexistent'"));
+  }
+
+  SECTION("Instance wildcard") {
+    edm::test::TestProcessor::Config config{
+        fmt::format(baseConfig, "intprod = cms.VPSet(cms.PSet(type = cms.string('nonexistentType')))")};
+
+    REQUIRE_THROWS_WITH(edm::test::TestProcessor(config),
+                        Catch::Contains("There are no products of type 'nonexistentType'") &&
+                            Catch::Contains("with module label 'intprod'"));
+  }
+}
diff --git a/FWCore/ParameterSet/python/Types.py b/FWCore/ParameterSet/python/Types.py
index c7714120794be..6b9c9b3156d52 100644
--- a/FWCore/ParameterSet/python/Types.py
+++ b/FWCore/ParameterSet/python/Types.py
@@ -1362,6 +1362,15 @@ class EDAlias(_ConfigureComponent,_Labelable,_Parameterizable):
     def __init__(self,*arg,**kargs):
         super(EDAlias,self).__init__(**kargs)
 
+    @staticmethod
+    def allProducts():
+        """A helper to specify that all products of a module are to be aliased for. Example usage:
+        process.someAlias = cms.EDAlias(
+            aliasForModuleLabel = cms.EDAlias.allProducts()
+        )
+        """
+        return VPSet(PSet(type = string('*')))
+
     def clone(self, *args, **params):
         returnValue = EDAlias.__new__(type(self))
         myparams = self.parameters_()
@@ -1974,6 +1983,22 @@ def testEDAlias(self):
             self.assertTrue(hasattr(aliasfoo4, "foo3"))
             self.assertEqual(aliasfoo4.foo3[0].type, "Foo3")
 
+            aliasfoo5 = EDAlias(foo5 = EDAlias.allProducts())
+            self.assertEqual(len(aliasfoo5.foo5), 1)
+            self.assertEqual(aliasfoo5.foo5[0].type.value(), "*")
+            self.assertFalse(hasattr(aliasfoo5.foo5[0], "fromProductInstance"))
+            self.assertFalse(hasattr(aliasfoo5.foo5[0], "toProductInstance"))
+
+            aliasfoo6 = aliasfoo5.clone(foo5 = None, foo6 = EDAlias.allProducts())
+            self.assertFalse(hasattr(aliasfoo6, "foo5"))
+            self.assertTrue(hasattr(aliasfoo6, "foo6"))
+            self.assertEqual(len(aliasfoo6.foo6), 1)
+            self.assertEqual(aliasfoo6.foo6[0].type.value(), "*")
+
+            aliasfoo7 = EDAlias(foo5 = EDAlias.allProducts(), foo6 = EDAlias.allProducts())
+            self.assertEqual(len(aliasfoo7.foo5), 1)
+            self.assertEqual(len(aliasfoo7.foo6), 1)
+
         def testFileInPath(self):
             f = FileInPath("FWCore/ParameterSet/python/Types.py")
             self.assertEqual(f.configValue(), "'FWCore/ParameterSet/python/Types.py'")