diff --git a/substrate/frame/executive/README.md b/substrate/frame/executive/README.md
index ae3bbf1a9d9944f3533d69f93fee2492d4536232..e96d07b0843f205a1020e98d01c0cbd5872f344e 100644
--- a/substrate/frame/executive/README.md
+++ b/substrate/frame/executive/README.md
@@ -35,7 +35,13 @@ The default Substrate node template declares the [`Executive`](https://docs.rs/f
 ```rust
 #
 /// Executive: handles dispatch to the various modules.
-pub type Executive = executive::Executive<Runtime, Block, Context, Runtime, AllPallets>;
+pub type Executive = executive::Executive<
+    Runtime,
+    Block,
+    Context,
+    Runtime,
+    AllPallets,
+>;
 ```
 
 ### Custom `OnRuntimeUpgrade` logic
@@ -54,7 +60,14 @@ impl frame_support::traits::OnRuntimeUpgrade for CustomOnRuntimeUpgrade {
     }
 }
 
-pub type Executive = executive::Executive<Runtime, Block, Context, Runtime, AllPallets, CustomOnRuntimeUpgrade>;
+pub type Executive = executive::Executive<
+    Runtime,
+    Block,
+    Context,
+    Runtime,
+    AllPallets,
+    CustomOnRuntimeUpgrade,
+>;
 ```
 
 License: Apache-2.0
diff --git a/substrate/frame/support/procedural/src/construct_runtime/mod.rs b/substrate/frame/support/procedural/src/construct_runtime/mod.rs
index 04bb2ead645d22c9feb0f7d6b2b68235e41aaf12..863df34266591d0ac8b4cf26c6b0df3072c459fc 100644
--- a/substrate/frame/support/procedural/src/construct_runtime/mod.rs
+++ b/substrate/frame/support/procedural/src/construct_runtime/mod.rs
@@ -217,6 +217,7 @@ fn decl_all_pallets<'a>(
 
 	quote!(
 		#types
+
 		/// All pallets included in the runtime as a nested tuple of types.
 		/// Excludes the System pallet.
 		pub type AllPallets = ( #all_pallets );
diff --git a/substrate/frame/support/procedural/src/pallet/expand/pallet_struct.rs b/substrate/frame/support/procedural/src/pallet/expand/pallet_struct.rs
index 57e814b6b8438fde166c73ba6e9e337e1f68abef..96dfdbb4b6f2d212c3ac396dde693fa6d24dac81 100644
--- a/substrate/frame/support/procedural/src/pallet/expand/pallet_struct.rs
+++ b/substrate/frame/support/procedural/src/pallet/expand/pallet_struct.rs
@@ -233,6 +233,25 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream {
 			}
 		}
 
+		impl<#type_impl_gen> #frame_support::traits::PalletsInfoAccess
+			for #pallet_ident<#type_use_gen>
+			#config_where_clause
+		{
+			fn count() -> usize { 1 }
+			fn accumulate(
+				acc: &mut #frame_support::sp_std::vec::Vec<#frame_support::traits::PalletInfoData>
+			) {
+				use #frame_support::traits::PalletInfoAccess;
+				let item = #frame_support::traits::PalletInfoData {
+					index: Self::index(),
+					name: Self::name(),
+					module_name: Self::module_name(),
+					crate_version: Self::crate_version(),
+				};
+				acc.push(item);
+			}
+		}
+
 		#storage_info
 	)
 }
diff --git a/substrate/frame/support/src/dispatch.rs b/substrate/frame/support/src/dispatch.rs
index 6dc7fb8a94cae078ff257f499b1bfe50c33e212b..a492bc12f6a38ac0947ffb4890adcaccf3ba7d3c 100644
--- a/substrate/frame/support/src/dispatch.rs
+++ b/substrate/frame/support/src/dispatch.rs
@@ -2165,6 +2165,22 @@ macro_rules! decl_module {
 			}
 		}
 
+		impl<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?> $crate::traits::PalletsInfoAccess
+			for $mod_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )*
+		{
+			fn count() -> usize { 1 }
+			fn accumulate(acc: &mut $crate::sp_std::vec::Vec<$crate::traits::PalletInfoData>) {
+				use $crate::traits::PalletInfoAccess;
+				let item = $crate::traits::PalletInfoData {
+					index: Self::index(),
+					name: Self::name(),
+					module_name: Self::module_name(),
+					crate_version: Self::crate_version(),
+				};
+				acc.push(item);
+			}
+		}
+
 		// Implement GetCallName for the Call.
 		impl<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?> $crate::dispatch::GetCallName
 			for $call_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )*
diff --git a/substrate/frame/support/src/lib.rs b/substrate/frame/support/src/lib.rs
index f3b00c764bb358a75079abbc5a762960542bcc0e..1b93b5fb5975e4b662792bebff89f5dfa3ca783e 100644
--- a/substrate/frame/support/src/lib.rs
+++ b/substrate/frame/support/src/lib.rs
@@ -1476,11 +1476,11 @@ pub mod pallet_prelude {
 /// * [`traits::OnGenesis`]: contains some logic to write pallet version into storage.
 /// * `PalletErrorTypeInfo`: provides the type information for the pallet error, if defined.
 ///
-/// It declare `type Module` type alias for `Pallet`, used by [`construct_runtime`].
+/// It declares `type Module` type alias for `Pallet`, used by [`construct_runtime`].
 ///
 /// It implements [`traits::PalletInfoAccess`] on `Pallet` to ease access to pallet
 /// informations given by [`frame_support::traits::PalletInfo`].
-/// (The implementation use the associated type `frame_system::Config::PalletInfo`).
+/// (The implementation uses the associated type `frame_system::Config::PalletInfo`).
 ///
 /// It implements [`traits::StorageInfoTrait`] on `Pallet` which give information about all
 /// storages.
diff --git a/substrate/frame/support/src/migrations.rs b/substrate/frame/support/src/migrations.rs
index dc3402440fdd4d6cd704f3882c38132c5c99a2af..c61cbac62a16ba58efcbd8022b55ef60ea05589e 100644
--- a/substrate/frame/support/src/migrations.rs
+++ b/substrate/frame/support/src/migrations.rs
@@ -58,9 +58,9 @@ impl PalletVersionToStorageVersionHelper for T {
 ///
 /// This will remove all `PalletVersion's` from the state and insert the current storage version.
 pub fn migrate_from_pallet_version_to_storage_version<
-	AllPallets: PalletVersionToStorageVersionHelper,
+	Pallets: PalletVersionToStorageVersionHelper,
 >(
 	db_weight: &RuntimeDbWeight,
 ) -> Weight {
-	AllPallets::migrate(db_weight)
+	Pallets::migrate(db_weight)
 }
diff --git a/substrate/frame/support/src/traits.rs b/substrate/frame/support/src/traits.rs
index 5ac0208dc203340d107fe40075e24a665bb0fd2e..513267c5c8ba6d5d93248c764a4b72c02c2daf14 100644
--- a/substrate/frame/support/src/traits.rs
+++ b/substrate/frame/support/src/traits.rs
@@ -63,7 +63,8 @@ pub use randomness::Randomness;
 mod metadata;
 pub use metadata::{
 	CallMetadata, CrateVersion, GetCallMetadata, GetCallName, GetStorageVersion, PalletInfo,
-	PalletInfoAccess, StorageVersion, STORAGE_VERSION_STORAGE_KEY_POSTFIX,
+	PalletInfoAccess, PalletInfoData, PalletsInfoAccess, StorageVersion,
+	STORAGE_VERSION_STORAGE_KEY_POSTFIX,
 };
 
 mod hooks;
diff --git a/substrate/frame/support/src/traits/metadata.rs b/substrate/frame/support/src/traits/metadata.rs
index e60cf8be8a41c13bfb92fe9333c36fa64612f4a4..0da76f7585aca6c346d84c7974a4a90574d7d013 100644
--- a/substrate/frame/support/src/traits/metadata.rs
+++ b/substrate/frame/support/src/traits/metadata.rs
@@ -19,6 +19,7 @@
 
 use codec::{Decode, Encode};
 use sp_runtime::RuntimeDebug;
+use sp_std::prelude::*;
 
 /// Provides information about the pallet itself and its setup in the runtime.
 ///
@@ -35,6 +36,19 @@ pub trait PalletInfo {
 	fn crate_version<P: 'static>() -> Option<CrateVersion>;
 }
 
+/// Information regarding an instance of a pallet.
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)]
+pub struct PalletInfoData {
+	/// Index of the pallet as configured in the runtime.
+	pub index: usize,
+	/// Name of the pallet as configured in the runtime.
+	pub name: &'static str,
+	/// Name of the Rust module containing the pallet.
+	pub module_name: &'static str,
+	/// Version of the crate containing the pallet.
+	pub crate_version: CrateVersion,
+}
+
 /// Provides information about the pallet itself and its setup in the runtime.
 ///
 /// Declare some information and access the information provided by [`PalletInfo`] for a specific
@@ -50,6 +64,49 @@ pub trait PalletInfoAccess {
 	fn crate_version() -> CrateVersion;
 }
 
+/// Provide information about a bunch of pallets.
+pub trait PalletsInfoAccess {
+	/// The number of pallets' information that this type represents.
+	///
+	/// You probably don't want this function but `infos()` instead.
+	fn count() -> usize {
+		0
+	}
+
+	/// Extend the given vector by all of the pallets' information that this type represents.
+	///
+	/// You probably don't want this function but `infos()` instead.
+	fn accumulate(_accumulator: &mut Vec<PalletInfoData>) {}
+
+	/// All of the pallets' information that this type represents.
+	fn infos() -> Vec<PalletInfoData> {
+		let mut result = Vec::with_capacity(Self::count());
+		Self::accumulate(&mut result);
+		result
+	}
+}
+
+impl PalletsInfoAccess for () {}
+impl<T: PalletsInfoAccess> PalletsInfoAccess for (T,) {
+	fn count() -> usize {
+		T::count()
+	}
+	fn accumulate(acc: &mut Vec<PalletInfoData>) {
+		T::accumulate(acc)
+	}
+}
+
+impl<T1: PalletsInfoAccess, T2: PalletsInfoAccess> PalletsInfoAccess for (T1, T2) {
+	fn count() -> usize {
+		T1::count() + T2::count()
+	}
+	fn accumulate(acc: &mut Vec<PalletInfoData>) {
+		// The AllPallets type tuplises the pallets in reverse order, so we unreverse them here.
+		T2::accumulate(acc);
+		T1::accumulate(acc);
+	}
+}
+
 /// The function and pallet name of the Call.
 #[derive(Clone, Eq, PartialEq, Default, RuntimeDebug)]
 pub struct CallMetadata {
@@ -206,6 +263,37 @@ pub trait GetStorageVersion {
 mod tests {
 	use super::*;
 
+	struct Pallet1;
+	impl PalletInfoAccess for Pallet1 {
+		fn index() -> usize {
+			1
+		}
+		fn name() -> &'static str {
+			"Pallet1"
+		}
+		fn module_name() -> &'static str {
+			"pallet1"
+		}
+		fn crate_version() -> CrateVersion {
+			CrateVersion::new(1, 0, 0)
+		}
+	}
+	struct Pallet2;
+	impl PalletInfoAccess for Pallet2 {
+		fn index() -> usize {
+			2
+		}
+		fn name() -> &'static str {
+			"Pallet2"
+		}
+		fn module_name() -> &'static str {
+			"pallet2"
+		}
+		fn crate_version() -> CrateVersion {
+			CrateVersion::new(1, 0, 0)
+		}
+	}
+
 	#[test]
 	fn check_storage_version_ordering() {
 		let version = StorageVersion::new(1);
diff --git a/substrate/frame/support/test/tests/pallet_instance.rs b/substrate/frame/support/test/tests/pallet_instance.rs
index 34586e841421618031bbb57d7ef04e405374e8fe..3a1009402d6f2e07be1c46d3ab290530d2339c55 100644
--- a/substrate/frame/support/test/tests/pallet_instance.rs
+++ b/substrate/frame/support/test/tests/pallet_instance.rs
@@ -505,6 +505,48 @@ fn storage_expand() {
 	});
 }
 
+#[test]
+fn pallet_metadata_expands() {
+	use frame_support::traits::{CrateVersion, PalletInfoData, PalletsInfoAccess};
+	let mut infos = AllPalletsWithSystem::infos();
+	infos.sort_by_key(|x| x.index);
+	assert_eq!(
+		infos,
+		vec![
+			PalletInfoData {
+				index: 0,
+				name: "System",
+				module_name: "frame_system",
+				crate_version: CrateVersion { major: 4, minor: 0, patch: 0 },
+			},
+			PalletInfoData {
+				index: 1,
+				name: "Example",
+				module_name: "pallet",
+				crate_version: CrateVersion { major: 3, minor: 0, patch: 0 },
+			},
+			PalletInfoData {
+				index: 2,
+				name: "Instance1Example",
+				module_name: "pallet",
+				crate_version: CrateVersion { major: 3, minor: 0, patch: 0 },
+			},
+			PalletInfoData {
+				index: 3,
+				name: "Example2",
+				module_name: "pallet2",
+				crate_version: CrateVersion { major: 3, minor: 0, patch: 0 },
+			},
+			PalletInfoData {
+				index: 4,
+				name: "Instance1Example2",
+				module_name: "pallet2",
+				crate_version: CrateVersion { major: 3, minor: 0, patch: 0 },
+			},
+		]
+	);
+}
+
 #[test]
 fn pallet_hooks_expand() {
 	TestExternalities::default().execute_with(|| {