diff --git a/src/python/grpcio/.gitignore b/src/python/grpcio/.gitignore
index efbe1737bad51717489ea7b280dd255eb9518682..4c02b8d14dcbbeb3e029f98633aed9de5bb1dcc8 100644
--- a/src/python/grpcio/.gitignore
+++ b/src/python/grpcio/.gitignore
@@ -6,3 +6,4 @@ dist/
 *.egg/
 *.eggs/
 doc/
+_grpcio_metadata.py
diff --git a/src/python/grpcio/commands.py b/src/python/grpcio/commands.py
index 605d9d5612007550a9f4f920d5ff14e1a7f7b691..89c0fbf0f3f1abb0facb26d7eadf6bada3909d40 100644
--- a/src/python/grpcio/commands.py
+++ b/src/python/grpcio/commands.py
@@ -34,6 +34,7 @@ import os.path
 import sys
 
 import setuptools
+from setuptools.command import build_py
 
 _CONF_PY_ADDENDUM = """
 extensions.append('sphinx.ext.napoleon')
@@ -74,3 +75,28 @@ class SphinxDocumentation(setuptools.Command):
       conf_file.write(_CONF_PY_ADDENDUM)
     sphinx.main(['', os.path.join('doc', 'src'), os.path.join('doc', 'build')])
 
+
+class BuildProjectMetadata(setuptools.Command):
+  """Command to generate project metadata in a module."""
+
+  description = ''
+  user_options = []
+
+  def initialize_options(self):
+    pass
+
+  def finalize_options(self):
+    pass
+
+  def run(self):
+    with open('grpc/_grpcio_metadata.py', 'w') as module_file:
+      module_file.write('__version__ = """{}"""'.format(
+          self.distribution.get_version()))
+
+
+class BuildPy(build_py.build_py):
+  """Custom project build command."""
+
+  def run(self):
+    self.run_command('build_project_metadata')
+    build_py.build_py.run(self)
diff --git a/src/python/grpcio/setup.py b/src/python/grpcio/setup.py
index e408f2ace9d4fde87f2cabfb1758cdb349354802..caa71a4f7c81b5c778abf2235b7e955d802575e2 100644
--- a/src/python/grpcio/setup.py
+++ b/src/python/grpcio/setup.py
@@ -98,6 +98,8 @@ _SETUP_REQUIRES = (
 
 _COMMAND_CLASS = {
     'doc': commands.SphinxDocumentation,
+    'build_project_metadata': commands.BuildProjectMetadata,
+    'build_py': commands.BuildPy,
 }
 
 setuptools.setup(