diff --git a/docs/auto_instrumentation/auto_instrumentation.rst b/docs/auto_instrumentation/auto_instrumentation.rst
new file mode 100644
index 00000000000..a45512f7f9d
--- /dev/null
+++ b/docs/auto_instrumentation/auto_instrumentation.rst
@@ -0,0 +1,7 @@
+OpenTelemetry Python Autoinstrumentation
+========================================
+
+.. toctree::
+    :maxdepth: 1
+
+    instrumentor
diff --git a/docs/auto_instrumentation/instrumentor.rst b/docs/auto_instrumentation/instrumentor.rst
new file mode 100644
index 00000000000..c94c0237f57
--- /dev/null
+++ b/docs/auto_instrumentation/instrumentor.rst
@@ -0,0 +1,7 @@
+opentelemetry.auto_instrumentation.instrumentor package
+=======================================================
+
+.. automodule:: opentelemetry.auto_instrumentation.instrumentor
+    :members:
+    :undoc-members:
+    :show-inheritance:
diff --git a/docs/conf.py b/docs/conf.py
index a509f14f5d6..acd44c0c7af 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -18,6 +18,7 @@
 source_dirs = [
     os.path.abspath("../opentelemetry-api/src/"),
     os.path.abspath("../opentelemetry-sdk/src/"),
+    os.path.abspath("../opentelemetry-auto-instrumentation/src/"),
 ]
 
 ext = "../ext"
diff --git a/docs/examples/auto-instrumentation/README.md b/docs/examples/auto-instrumentation/README.md
new file mode 100644
index 00000000000..46b0b44b2c8
--- /dev/null
+++ b/docs/examples/auto-instrumentation/README.md
@@ -0,0 +1,112 @@
+# Overview
+
+This example shows how to use auto-instrumentation in OpenTelemetry. This example is also based on a previous example
+for OpenTracing that can be found [here](https://github.com/yurishkuro/opentracing-tutorial/tree/master/python).
+
+This example uses 2 scripts whose main difference is they being instrumented manually or not:
+
+1. `server_instrumented.py` which has been instrumented manually
+2. `server_uninstrumented.py` which has not been instrumented manually
+
+The former will be run without the automatic instrumentation agent and the latter with the automatic instrumentation
+agent. They should produce the same result, showing that the automatic instrumentation agent does the equivalent
+of what manual instrumentation does.
+
+In order to understand this better, here is the relevant part of both scripts:
+
+## Manually instrumented server
+
+`server_instrumented.py`
+
+```python
+@app.route("/server_request")
+def server_request():
+    with tracer.start_as_current_span(
+        "server_request",
+        parent=propagators.extract(
+            lambda dict_, key: dict_.get(key, []), request.headers
+        )["current-span"],
+    ):
+        print(request.args.get("param"))
+        return "served"
+```
+
+## Publisher not instrumented manually
+
+`server_uninstrumented.py`
+
+```python
+@app.route("/server_request")
+def server_request():
+    print(request.args.get("param"))
+    return "served"
+```
+
+# Preparation
+
+This example will be executed in a separate virtual environment:
+
+```sh
+$ mkdir auto_instrumentation
+$ virtualenv auto_instrumentation
+$ source auto_instrumentation/bin/activate
+```
+
+# Installation
+
+```sh
+$ pip install opentelemetry-api
+$ pip install opentelemetry-sdk
+$ pip install opentelemetry-auto-instrumentation
+$ pip install ext/opentelemetry-ext-flask
+$ pip install flask
+$ pip install requests
+```
+
+# Execution
+
+## Execution of the manually instrumented server
+
+This is done in 2 separate consoles, one to run each of the scripts that make up this example:
+
+```sh
+$ source auto_instrumentation/bin/activate
+$ python opentelemetry-python/opentelemetry-auto-instrumentation/example/server_instrumented.py
+```
+
+```sh
+$ source auto_instrumentation/bin/activate
+$ python opentelemetry-python/opentelemetry-auto-instrumentation/example/client.py testing
+```
+
+The execution of `server_instrumented.py` should return an output similar to:
+
+```sh
+Hello, testing!
+Span(name="serv_request", context=SpanContext(trace_id=0x9c0e0ce8f7b7dbb51d1d6e744a4dad49, span_id=0xd1ba3ec4c76a0d7f, trace_state={}), kind=SpanKind.INTERNAL, parent=None, start_time=2020-03-19T00:06:31.275719Z, end_time=2020-03-19T00:06:31.275920Z)
+127.0.0.1 - - [18/Mar/2020 18:06:31] "GET /serv_request?helloStr=Hello%2C+testing%21 HTTP/1.1" 200 -
+```
+
+## Execution of an automatically instrumented server
+
+Now, kill the execution of `server_instrumented.py` with `ctrl + c` and run this instead:
+
+```sh
+$ opentelemetry-auto-instrumentation opentelemetry-python/opentelemetry-auto-instrumentation/example/server_uninstrumented.py
+```
+
+In the console where you previously executed `client.py`, run again this again:
+
+```sh
+$ python opentelemetry-python/opentelemetry-auto-instrumentation/example/client.py testing
+```
+
+The execution of `server_uninstrumented.py` should return an output similar to:
+
+```sh
+Hello, testing!
+Span(name="serv_request", context=SpanContext(trace_id=0xf26b28b5243e48f5f96bfc753f95f3f0, span_id=0xbeb179a095d087ed, trace_state={}), kind=SpanKind.SERVER, parent=<opentelemetry.trace.DefaultSpan object at 0x7f1a20a54908>, start_time=2020-03-19T00:24:18.828561Z, end_time=2020-03-19T00:24:18.845127Z)
+127.0.0.1 - - [18/Mar/2020 18:24:18] "GET /serv_request?helloStr=Hello%2C+testing%21 HTTP/1.1" 200 -
+```
+
+As you can see, both outputs are equivalentsince the automatic instrumentation does what the manual instrumentation does too.
diff --git a/docs/examples/auto-instrumentation/client.py b/docs/examples/auto-instrumentation/client.py
new file mode 100644
index 00000000000..c8301003be7
--- /dev/null
+++ b/docs/examples/auto-instrumentation/client.py
@@ -0,0 +1,50 @@
+# Copyright The OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from sys import argv
+
+from flask import Flask
+from requests import get
+
+from opentelemetry import propagators, trace
+from opentelemetry.sdk.trace import TracerProvider
+from opentelemetry.sdk.trace.export import (
+    ConsoleSpanExporter,
+    SimpleExportSpanProcessor,
+)
+
+app = Flask(__name__)
+
+trace.set_tracer_provider(TracerProvider())
+tracer = trace.get_tracer_provider().get_tracer(__name__)
+
+trace.get_tracer_provider().add_span_processor(
+    SimpleExportSpanProcessor(ConsoleSpanExporter())
+)
+
+
+assert len(argv) == 2
+
+with tracer.start_as_current_span("client"):
+
+    with tracer.start_as_current_span("client-server"):
+        headers = {}
+        propagators.inject(dict.__setitem__, headers)
+        requested = get(
+            "http://localhost:8082/server_request",
+            params={"param": argv[1]},
+            headers=headers,
+        )
+
+        assert requested.status_code == 200
diff --git a/docs/examples/auto-instrumentation/server_instrumented.py b/docs/examples/auto-instrumentation/server_instrumented.py
new file mode 100644
index 00000000000..1c78aab15d8
--- /dev/null
+++ b/docs/examples/auto-instrumentation/server_instrumented.py
@@ -0,0 +1,47 @@
+# Copyright The OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from flask import Flask, request
+
+from opentelemetry import propagators, trace
+from opentelemetry.sdk.trace import TracerProvider
+from opentelemetry.sdk.trace.export import (
+    ConsoleSpanExporter,
+    SimpleExportSpanProcessor,
+)
+
+app = Flask(__name__)
+
+trace.set_tracer_provider(TracerProvider())
+tracer = trace.get_tracer_provider().get_tracer(__name__)
+
+trace.get_tracer_provider().add_span_processor(
+    SimpleExportSpanProcessor(ConsoleSpanExporter())
+)
+
+
+@app.route("/server_request")
+def server_request():
+    with tracer.start_as_current_span(
+        "server_request",
+        parent=propagators.extract(
+            lambda dict_, key: dict_.get(key, []), request.headers
+        )["current-span"],
+    ):
+        print(request.args.get("param"))
+        return "served"
+
+
+if __name__ == "__main__":
+    app.run(port=8082)
diff --git a/docs/examples/auto-instrumentation/server_uninstrumented.py b/docs/examples/auto-instrumentation/server_uninstrumented.py
new file mode 100644
index 00000000000..b8360341ab2
--- /dev/null
+++ b/docs/examples/auto-instrumentation/server_uninstrumented.py
@@ -0,0 +1,40 @@
+# Copyright The OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from flask import Flask, request
+
+from opentelemetry import trace
+from opentelemetry.sdk.trace import TracerProvider
+from opentelemetry.sdk.trace.export import (
+    ConsoleSpanExporter,
+    SimpleExportSpanProcessor,
+)
+
+app = Flask(__name__)
+
+trace.set_tracer_provider(TracerProvider())
+
+trace.get_tracer_provider().add_span_processor(
+    SimpleExportSpanProcessor(ConsoleSpanExporter())
+)
+
+
+@app.route("/server_request")
+def server_request():
+    print(request.args.get("param"))
+    return "served"
+
+
+if __name__ == "__main__":
+    app.run(port=8082)
diff --git a/docs/examples/opentelemetry-example-app/src/opentelemetry_example_app/flask_example.py b/docs/examples/opentelemetry-example-app/src/opentelemetry_example_app/flask_example.py
index a37048b7d49..21a9962d864 100644
--- a/docs/examples/opentelemetry-example-app/src/opentelemetry_example_app/flask_example.py
+++ b/docs/examples/opentelemetry-example-app/src/opentelemetry_example_app/flask_example.py
@@ -21,7 +21,7 @@
 
 import opentelemetry.ext.http_requests
 from opentelemetry import trace
-from opentelemetry.ext.flask import instrument_app
+from opentelemetry.ext.flask import FlaskInstrumentor
 from opentelemetry.sdk.trace import TracerProvider
 from opentelemetry.sdk.trace.export import (
     ConsoleSpanExporter,
@@ -33,9 +33,9 @@
     SimpleExportSpanProcessor(ConsoleSpanExporter())
 )
 
+FlaskInstrumentor().instrument()
 app = flask.Flask(__name__)
 opentelemetry.ext.http_requests.enable(trace.get_tracer_provider())
-instrument_app(app)
 
 
 @app.route("/")
diff --git a/docs/index.rst b/docs/index.rst
index fd60e72f037..2d26e24f839 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -61,7 +61,6 @@ install <https://pip.pypa.io/en/stable/reference/pip_install/#editable-installs>
 
     getting-started
 
-
 .. toctree::
     :maxdepth: 1
     :caption: OpenTelemetry Python Packages
@@ -69,6 +68,7 @@ install <https://pip.pypa.io/en/stable/reference/pip_install/#editable-installs>
 
     api/api
     sdk/sdk
+    auto_instrumentation/auto_instrumentation
 
 .. toctree::
     :maxdepth: 2
diff --git a/ext/opentelemetry-ext-flask/setup.py b/ext/opentelemetry-ext-flask/setup.py
index df9742c9006..84b33c23b22 100644
--- a/ext/opentelemetry-ext-flask/setup.py
+++ b/ext/opentelemetry-ext-flask/setup.py
@@ -23,4 +23,11 @@
 with open(VERSION_FILENAME) as f:
     exec(f.read(), PACKAGE_INFO)
 
-setuptools.setup(version=PACKAGE_INFO["__version__"])
+setuptools.setup(
+    version=PACKAGE_INFO["__version__"],
+    entry_points={
+        "opentelemetry_instrumentor": [
+            "flask = opentelemetry.ext.flask:FlaskInstrumentor"
+        ]
+    },
+)
diff --git a/ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask/__init__.py b/ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask/__init__.py
index 11c027ecbcc..02b0de8652e 100644
--- a/ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask/__init__.py
+++ b/ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask/__init__.py
@@ -34,12 +34,12 @@ def hello():
 
 import logging
 
-from flask import request as flask_request
+import flask
 
 import opentelemetry.ext.wsgi as otel_wsgi
 from opentelemetry import context, propagators, trace
+from opentelemetry.auto_instrumentation.instrumentor import BaseInstrumentor
 from opentelemetry.ext.flask.version import __version__
-from opentelemetry.trace.propagation import get_span_from_context
 from opentelemetry.util import time_ns
 
 logger = logging.getLogger(__name__)
@@ -50,82 +50,105 @@ def hello():
 _ENVIRON_TOKEN = "opentelemetry-flask.token"
 
 
-def instrument_app(flask):
-    """Makes the passed-in Flask object traced by OpenTelemetry.
+class _InstrumentedFlask(flask.Flask):
+    def __init__(self, *args, **kwargs):
+
+        super().__init__(*args, **kwargs)
+
+        # Single use variable here to avoid recursion issues.
+        wsgi = self.wsgi_app
+
+        def wrapped_app(environ, start_response):
+            # We want to measure the time for route matching, etc.
+            # In theory, we could start the span here and use
+            # update_name later but that API is "highly discouraged" so
+            # we better avoid it.
+            environ[_ENVIRON_STARTTIME_KEY] = time_ns()
+
+            def _start_response(status, response_headers, *args, **kwargs):
+                span = flask.request.environ.get(_ENVIRON_SPAN_KEY)
+                if span:
+                    otel_wsgi.add_response_attributes(
+                        span, status, response_headers
+                    )
+                else:
+                    logger.warning(
+                        "Flask environ's OpenTelemetry span "
+                        "missing at _start_response(%s)",
+                        status,
+                    )
+
+                return start_response(
+                    status, response_headers, *args, **kwargs
+                )
+
+            return wsgi(environ, _start_response)
+
+        self.wsgi_app = wrapped_app
+
+        @self.before_request
+        def _before_flask_request():
+            environ = flask.request.environ
+            span_name = (
+                flask.request.endpoint
+                or otel_wsgi.get_default_span_name(environ)
+            )
+            token = context.attach(
+                propagators.extract(otel_wsgi.get_header_from_environ, environ)
+            )
+
+            tracer = trace.get_tracer(__name__, __version__)
+
+            attributes = otel_wsgi.collect_request_attributes(environ)
+            if flask.request.url_rule:
+                # For 404 that result from no route found, etc, we
+                # don't have a url_rule.
+                attributes["http.route"] = flask.request.url_rule.rule
+            span = tracer.start_span(
+                span_name,
+                kind=trace.SpanKind.SERVER,
+                attributes=attributes,
+                start_time=environ.get(_ENVIRON_STARTTIME_KEY),
+            )
+            activation = tracer.use_span(span, end_on_exit=True)
+            activation.__enter__()
+            environ[_ENVIRON_ACTIVATION_KEY] = activation
+            environ[_ENVIRON_SPAN_KEY] = span
+            environ[_ENVIRON_TOKEN] = token
+
+        @self.teardown_request
+        def _teardown_flask_request(exc):
+            activation = flask.request.environ.get(_ENVIRON_ACTIVATION_KEY)
+            if not activation:
+                logger.warning(
+                    "Flask environ's OpenTelemetry activation missing"
+                    "at _teardown_flask_request(%s)",
+                    exc,
+                )
+                return
+
+            if exc is None:
+                activation.__exit__(None, None, None)
+            else:
+                activation.__exit__(
+                    type(exc), exc, getattr(exc, "__traceback__", None)
+                )
+            context.detach(flask.request.environ.get(_ENVIRON_TOKEN))
 
-    You must not call this function multiple times on the same Flask object.
+
+class FlaskInstrumentor(BaseInstrumentor):
+    """A instrumentor for flask.Flask
+
+    See `BaseInstrumentor`
     """
 
-    wsgi = flask.wsgi_app
+    def __init__(self):
+        super().__init__()
+        self._original_flask = None
 
-    def wrapped_app(environ, start_response):
-        # We want to measure the time for route matching, etc.
-        # In theory, we could start the span here and use update_name later
-        # but that API is "highly discouraged" so we better avoid it.
-        environ[_ENVIRON_STARTTIME_KEY] = time_ns()
+    def _instrument(self):
+        self._original_flask = flask.Flask
+        flask.Flask = _InstrumentedFlask
 
-        def _start_response(status, response_headers, *args, **kwargs):
-            span = flask_request.environ.get(_ENVIRON_SPAN_KEY)
-            if span:
-                otel_wsgi.add_response_attributes(
-                    span, status, response_headers
-                )
-            else:
-                logger.warning(
-                    "Flask environ's OpenTelemetry span missing at _start_response(%s)",
-                    status,
-                )
-            return start_response(status, response_headers, *args, **kwargs)
-
-        return wsgi(environ, _start_response)
-
-    flask.wsgi_app = wrapped_app
-
-    flask.before_request(_before_flask_request)
-    flask.teardown_request(_teardown_flask_request)
-
-
-def _before_flask_request():
-    environ = flask_request.environ
-    span_name = flask_request.endpoint or otel_wsgi.get_default_span_name(
-        environ
-    )
-    token = context.attach(
-        propagators.extract(otel_wsgi.get_header_from_environ, environ)
-    )
-
-    tracer = trace.get_tracer(__name__, __version__)
-
-    attributes = otel_wsgi.collect_request_attributes(environ)
-    if flask_request.url_rule:
-        # For 404 that result from no route found, etc, we don't have a url_rule.
-        attributes["http.route"] = flask_request.url_rule.rule
-    span = tracer.start_span(
-        span_name,
-        kind=trace.SpanKind.SERVER,
-        attributes=attributes,
-        start_time=environ.get(_ENVIRON_STARTTIME_KEY),
-    )
-    activation = tracer.use_span(span, end_on_exit=True)
-    activation.__enter__()
-    environ[_ENVIRON_ACTIVATION_KEY] = activation
-    environ[_ENVIRON_SPAN_KEY] = span
-    environ[_ENVIRON_TOKEN] = token
-
-
-def _teardown_flask_request(exc):
-    activation = flask_request.environ.get(_ENVIRON_ACTIVATION_KEY)
-    if not activation:
-        logger.warning(
-            "Flask environ's OpenTelemetry activation missing at _teardown_flask_request(%s)",
-            exc,
-        )
-        return
-
-    if exc is None:
-        activation.__exit__(None, None, None)
-    else:
-        activation.__exit__(
-            type(exc), exc, getattr(exc, "__traceback__", None)
-        )
-    context.detach(flask_request.environ.get(_ENVIRON_TOKEN))
+    def _uninstrument(self):
+        flask.Flask = self._original_flask
diff --git a/ext/opentelemetry-ext-flask/tests/conftest.py b/ext/opentelemetry-ext-flask/tests/conftest.py
new file mode 100644
index 00000000000..22a587ab2e6
--- /dev/null
+++ b/ext/opentelemetry-ext-flask/tests/conftest.py
@@ -0,0 +1,24 @@
+# Copyright 2020, OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from opentelemetry.ext.flask import FlaskInstrumentor
+
+_FLASK_INSTRUMENTOR = FlaskInstrumentor()
+
+
+def pytest_sessionstart(session):  # pylint: disable=unused-argument
+    _FLASK_INSTRUMENTOR.instrument()
+
+
+def pytest_sessionfinish(session):  # pylint: disable=unused-argument
+    _FLASK_INSTRUMENTOR.uninstrument()
diff --git a/ext/opentelemetry-ext-flask/tests/test_flask_integration.py b/ext/opentelemetry-ext-flask/tests/test_flask_integration.py
index 9f61920ce94..b63424b905a 100644
--- a/ext/opentelemetry-ext-flask/tests/test_flask_integration.py
+++ b/ext/opentelemetry-ext-flask/tests/test_flask_integration.py
@@ -18,7 +18,6 @@
 from werkzeug.test import Client
 from werkzeug.wrappers import BaseResponse
 
-import opentelemetry.ext.flask as otel_flask
 from opentelemetry import trace as trace_api
 from opentelemetry.ext.testutil.wsgitestutil import WsgiTestBase
 
@@ -43,6 +42,8 @@ def expected_attributes(override_attributes):
 
 class TestFlaskIntegration(WsgiTestBase):
     def setUp(self):
+        # No instrumentation code is here because it is present in the
+        # conftest.py file next to this file.
         super().setUp()
 
         self.app = Flask(__name__)
@@ -54,7 +55,6 @@ def hello_endpoint(helloid):
 
         self.app.route("/hello/<int:helloid>")(hello_endpoint)
 
-        otel_flask.instrument_app(self.app)
         self.client = Client(self.app, BaseResponse)
 
     def test_only_strings_in_environ(self):
diff --git a/opentelemetry-auto-instrumentation/CHANGELOG.md b/opentelemetry-auto-instrumentation/CHANGELOG.md
new file mode 100644
index 00000000000..825c32f0d03
--- /dev/null
+++ b/opentelemetry-auto-instrumentation/CHANGELOG.md
@@ -0,0 +1 @@
+# Changelog
diff --git a/opentelemetry-auto-instrumentation/MANIFEST.in b/opentelemetry-auto-instrumentation/MANIFEST.in
new file mode 100644
index 00000000000..191b7d19592
--- /dev/null
+++ b/opentelemetry-auto-instrumentation/MANIFEST.in
@@ -0,0 +1,7 @@
+prune tests
+graft src
+global-exclude *.pyc
+global-exclude *.pyo
+global-exclude __pycache__/*
+include MANIFEST.in
+include README.rst
diff --git a/opentelemetry-auto-instrumentation/README.rst b/opentelemetry-auto-instrumentation/README.rst
new file mode 100644
index 00000000000..b153072ae5a
--- /dev/null
+++ b/opentelemetry-auto-instrumentation/README.rst
@@ -0,0 +1,19 @@
+OpenTelemetry Auto Instrumentation
+==================================
+
+|pypi|
+
+.. |pypi| image:: https://badge.fury.io/py/opentelemetry-auto-instrumentation.svg
+   :target: https://pypi.org/project/opentelemetry-auto-instrumentation/
+
+Installation
+------------
+
+::
+
+    pip install opentelemetry-auto-instrumentation
+
+References
+----------
+
+* `OpenTelemetry Project <https://opentelemetry.io/>`_
diff --git a/opentelemetry-auto-instrumentation/setup.cfg b/opentelemetry-auto-instrumentation/setup.cfg
new file mode 100644
index 00000000000..182b15866fc
--- /dev/null
+++ b/opentelemetry-auto-instrumentation/setup.cfg
@@ -0,0 +1,50 @@
+# Copyright 2019, OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+[metadata]
+name = opentelemetry-auto-instrumentation
+description = Auto Instrumentation for OpenTelemetry Python
+long_description = file: README.rst
+long_description_content_type = text/x-rst
+author = OpenTelemetry Authors
+author_email = cncf-opentelemetry-contributors@lists.cncf.io
+url = https://github.com/open-telemetry/opentelemetry-python/tree/master/opentelemetry-auto-instrumentation"
+platforms = any
+license = Apache-2.0
+classifiers =
+    Development Status :: 3 - Alpha
+    Intended Audience :: Developers
+    License :: OSI Approved :: Apache Software License
+    Programming Language :: Python
+    Programming Language :: Python :: 3
+    Programming Language :: Python :: 3.4
+    Programming Language :: Python :: 3.5
+    Programming Language :: Python :: 3.6
+    Programming Language :: Python :: 3.7
+
+[options]
+python_requires = >=3.4
+package_dir=
+    =src
+packages=find_namespace:
+zip_safe = False
+include_package_data = True
+install_requires = opentelemetry-api==0.6.dev0
+
+[options.packages.find]
+where = src
+
+[options.entry_points]
+console_scripts =
+    opentelemetry-auto-instrumentation = opentelemetry.auto_instrumentation.auto_instrumentation:run
diff --git a/opentelemetry-auto-instrumentation/setup.py b/opentelemetry-auto-instrumentation/setup.py
new file mode 100644
index 00000000000..86f8faedbcf
--- /dev/null
+++ b/opentelemetry-auto-instrumentation/setup.py
@@ -0,0 +1,27 @@
+# Copyright The OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+import setuptools
+
+BASE_DIR = os.path.dirname(__file__)
+VERSION_FILENAME = os.path.join(
+    BASE_DIR, "src", "opentelemetry", "auto_instrumentation", "version.py"
+)
+PACKAGE_INFO = {}
+with open(VERSION_FILENAME) as f:
+    exec(f.read(), PACKAGE_INFO)
+
+setuptools.setup(version=PACKAGE_INFO["__version__"],)
diff --git a/opentelemetry-auto-instrumentation/src/opentelemetry/auto_instrumentation/__init__.py b/opentelemetry-auto-instrumentation/src/opentelemetry/auto_instrumentation/__init__.py
new file mode 100644
index 00000000000..dfafb5386a9
--- /dev/null
+++ b/opentelemetry-auto-instrumentation/src/opentelemetry/auto_instrumentation/__init__.py
@@ -0,0 +1,28 @@
+# Copyright The OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Usage
+-----
+
+This package provides a command that automatically instruments a program:
+
+::
+
+    opentelemetry-auto-instrumentation program.py
+
+The code in ``program.py`` needs to use one of the packages for which there is
+an OpenTelemetry extension. For a list of the available extensions please check
+`here <https://opentelemetry-python.readthedocsio/>`_.
+"""
diff --git a/opentelemetry-auto-instrumentation/src/opentelemetry/auto_instrumentation/auto_instrumentation.py b/opentelemetry-auto-instrumentation/src/opentelemetry/auto_instrumentation/auto_instrumentation.py
new file mode 100644
index 00000000000..00ccf6a0ea9
--- /dev/null
+++ b/opentelemetry-auto-instrumentation/src/opentelemetry/auto_instrumentation/auto_instrumentation.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+
+# Copyright The OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from logging import getLogger
+from runpy import run_path
+from sys import argv
+
+from pkg_resources import iter_entry_points
+
+logger = getLogger(__file__)
+
+
+def run() -> None:
+
+    for entry_point in iter_entry_points("opentelemetry_instrumentor"):
+        try:
+            entry_point.load()().instrument()  # type: ignore
+            logger.debug("Instrumented %s", entry_point.name)
+
+        except Exception:  # pylint: disable=broad-except
+            logger.exception("Instrumenting of %s failed", entry_point.name)
+
+    run_path(argv[1], run_name="__main__")  # type: ignore
diff --git a/opentelemetry-auto-instrumentation/src/opentelemetry/auto_instrumentation/instrumentor.py b/opentelemetry-auto-instrumentation/src/opentelemetry/auto_instrumentation/instrumentor.py
new file mode 100644
index 00000000000..9deb6b15238
--- /dev/null
+++ b/opentelemetry-auto-instrumentation/src/opentelemetry/auto_instrumentation/instrumentor.py
@@ -0,0 +1,65 @@
+# Copyright The OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# type: ignore
+
+"""
+OpenTelemetry Base Instrumentor
+"""
+
+from abc import ABC, abstractmethod
+from logging import getLogger
+
+_LOG = getLogger(__name__)
+
+
+class BaseInstrumentor(ABC):
+    """An ABC for instrumentors"""
+
+    def __init__(self):
+        self._is_instrumented = False
+
+    @abstractmethod
+    def _instrument(self) -> None:
+        """Instrument"""
+
+    @abstractmethod
+    def _uninstrument(self) -> None:
+        """Uninstrument"""
+
+    def instrument(self) -> None:
+        """Instrument"""
+
+        if not self._is_instrumented:
+            result = self._instrument()
+            self._is_instrumented = True
+            return result
+
+        _LOG.warning("Attempting to instrument while already instrumented")
+
+        return None
+
+    def uninstrument(self) -> None:
+        """Uninstrument"""
+
+        if self._is_instrumented:
+            result = self._uninstrument()
+            self._is_instrumented = False
+            return result
+
+        _LOG.warning("Attempting to uninstrument while already uninstrumented")
+
+        return None
+
+
+__all__ = ["BaseInstrumentor"]
diff --git a/opentelemetry-auto-instrumentation/src/opentelemetry/auto_instrumentation/version.py b/opentelemetry-auto-instrumentation/src/opentelemetry/auto_instrumentation/version.py
new file mode 100644
index 00000000000..0941210ca3f
--- /dev/null
+++ b/opentelemetry-auto-instrumentation/src/opentelemetry/auto_instrumentation/version.py
@@ -0,0 +1,15 @@
+# Copyright The OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+__version__ = "0.6.dev0"
diff --git a/opentelemetry-auto-instrumentation/tests/__init__.py b/opentelemetry-auto-instrumentation/tests/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/opentelemetry-auto-instrumentation/tests/test_instrumentor.py b/opentelemetry-auto-instrumentation/tests/test_instrumentor.py
new file mode 100644
index 00000000000..1324213536c
--- /dev/null
+++ b/opentelemetry-auto-instrumentation/tests/test_instrumentor.py
@@ -0,0 +1,44 @@
+# Copyright The OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# type: ignore
+
+from logging import WARNING
+from unittest import TestCase
+
+from opentelemetry.auto_instrumentation.instrumentor import BaseInstrumentor
+
+
+class TestInstrumentor(TestCase):
+    def test_protect(self):
+        class Instrumentor(BaseInstrumentor):
+            def _instrument(self):
+                return "instrumented"
+
+            def _uninstrument(self):
+                return "uninstrumented"
+
+        instrumentor = Instrumentor()
+
+        with self.assertLogs(level=WARNING):
+            self.assertIs(instrumentor.uninstrument(), None)
+
+        self.assertEqual(instrumentor.instrument(), "instrumented")
+
+        with self.assertLogs(level=WARNING):
+            self.assertIs(instrumentor.instrument(), None)
+
+        self.assertEqual(instrumentor.uninstrument(), "uninstrumented")
+
+        with self.assertLogs(level=WARNING):
+            self.assertIs(instrumentor.uninstrument(), None)
diff --git a/tox.ini b/tox.ini
index 3c0023bd47d..6e64bdbd441 100644
--- a/tox.ini
+++ b/tox.ini
@@ -12,6 +12,10 @@ envlist =
     py3{4,5,6,7,8}-test-sdk
     pypy3-test-sdk
 
+    ; opentelemetry-auto-instrumentation
+    py3{4,5,6,7,8}-test-auto-instrumentation
+    pypy3-test-auto-instrumentation
+
     ; opentelemetry-example-app
     py3{4,5,6,7,8}-test-example-app
     pypy3-test-example-app
@@ -43,6 +47,7 @@ envlist =
     ; opentelemetry-ext-mysql
     py3{4,5,6,7,8}-test-ext-mysql
     pypy3-test-ext-mysql
+
     ; opentelemetry-ext-otcollector
     py3{4,5,6,7,8}-test-ext-otcollector
     ; ext-otcollector intentionally excluded from pypy3
@@ -101,6 +106,7 @@ setenv =
 changedir =
   test-api: opentelemetry-api/tests
   test-sdk: opentelemetry-sdk/tests
+  test-auto-instrumentation: opentelemetry-auto-instrumentation/tests
   test-ext-http-requests: ext/opentelemetry-ext-http-requests/tests
   test-ext-jaeger: ext/opentelemetry-ext-jaeger/tests
   test-ext-dbapi: ext/opentelemetry-ext-dbapi/tests
@@ -122,7 +128,9 @@ commands_pre =
   python -m pip install -U pip setuptools wheel
   test: pip install {toxinidir}/opentelemetry-api
   test-sdk: pip install {toxinidir}/opentelemetry-sdk
+  test-auto-instrumentation: pip install {toxinidir}/opentelemetry-auto-instrumentation
   example-app: pip install {toxinidir}/opentelemetry-sdk
+  example-app: pip install {toxinidir}/opentelemetry-auto-instrumentation
   example-app: pip install {toxinidir}/ext/opentelemetry-ext-http-requests
   example-app: pip install {toxinidir}/ext/opentelemetry-ext-wsgi
   example-app: pip install {toxinidir}/ext/opentelemetry-ext-flask
@@ -139,6 +147,7 @@ commands_pre =
   wsgi,flask: pip install {toxinidir}/ext/opentelemetry-ext-testutil
   wsgi,flask: pip install {toxinidir}/ext/opentelemetry-ext-wsgi
   wsgi,flask: pip install {toxinidir}/opentelemetry-sdk
+  flask: pip install {toxinidir}/opentelemetry-auto-instrumentation
   flask: pip install {toxinidir}/ext/opentelemetry-ext-flask[test]
   dbapi: pip install {toxinidir}/ext/opentelemetry-ext-dbapi
   mysql: pip install {toxinidir}/ext/opentelemetry-ext-dbapi