diff --git a/azurerm/internal/common/client_options.go b/azurerm/internal/common/client_options.go
index abe91cd852fd..8bd3552adaac 100644
--- a/azurerm/internal/common/client_options.go
+++ b/azurerm/internal/common/client_options.go
@@ -11,6 +11,7 @@ import (
 	"github.com/Azure/go-autorest/autorest/azure"
 	"github.com/hashicorp/go-azure-helpers/sender"
 	"github.com/hashicorp/terraform/httpclient"
+	"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features"
 	"github.com/terraform-providers/terraform-provider-azurerm/version"
 )
 
@@ -27,10 +28,12 @@ type ClientOptions struct {
 	ResourceManagerEndpoint   string
 	StorageAuthorizer         autorest.Authorizer
 
-	PollingDuration             time.Duration
 	SkipProviderReg             bool
 	DisableCorrelationRequestID bool
 	Environment                 azure.Environment
+
+	// TODO: remove me in 2.0
+	PollingDuration time.Duration
 }
 
 func (o ClientOptions) ConfigureClient(c *autorest.Client, authorizer autorest.Authorizer) {
@@ -38,11 +41,15 @@ func (o ClientOptions) ConfigureClient(c *autorest.Client, authorizer autorest.A
 
 	c.Authorizer = authorizer
 	c.Sender = sender.BuildSender("AzureRM")
-	c.PollingDuration = o.PollingDuration
 	c.SkipResourceProviderRegistration = o.SkipProviderReg
 	if !o.DisableCorrelationRequestID {
 		c.RequestInspector = WithCorrelationRequestID(CorrelationRequestID())
 	}
+
+	// TODO: remove in 2.0
+	if !features.SupportsCustomTimeouts() {
+		c.PollingDuration = o.PollingDuration
+	}
 }
 
 func setUserAgent(client *autorest.Client, tfVersion, partnerID string) {
diff --git a/azurerm/internal/features/custom_timeouts.go b/azurerm/internal/features/custom_timeouts.go
new file mode 100644
index 000000000000..a7743fc3ef51
--- /dev/null
+++ b/azurerm/internal/features/custom_timeouts.go
@@ -0,0 +1,22 @@
+package features
+
+import (
+	"os"
+	"strings"
+)
+
+// SupportsCustomTimeouts returns whether Custom Timeouts are supported
+//
+// This feature allows Resources to define Custom Timeouts for Creation, Updating and Deletion
+// which helps work with Azure resources that take longer to provision/delete.
+// When this feature is disabled, all resources have a hard-coded timeout of 3 hours.
+//
+// This feature-toggle defaults to off in 1.x versions of the Azure Provider, however this will
+// become the default behaviour in version 2.0 of the Azure Provider. As outlined in the announcement
+// for v2.0 of the Azure Provider: https://github.com/terraform-providers/terraform-provider-azurerm/issues/2807
+//
+// Operators wishing to adopt this behaviour can opt-into this behaviour in 1.x versions of the
+// Azure Provider by setting the Environment Variable 'ARM_PROVIDER_CUSTOM_TIMEOUTS' to 'true'
+func SupportsCustomTimeouts() bool {
+	return strings.EqualFold(os.Getenv("ARM_PROVIDER_CUSTOM_TIMEOUTS"), "true")
+}
diff --git a/azurerm/internal/features/custom_timeouts_test.go b/azurerm/internal/features/custom_timeouts_test.go
new file mode 100644
index 000000000000..cd260cf573b4
--- /dev/null
+++ b/azurerm/internal/features/custom_timeouts_test.go
@@ -0,0 +1,55 @@
+package features
+
+import (
+	"os"
+	"testing"
+)
+
+func TestCustomTimeouts(t *testing.T) {
+	testData := []struct {
+		name     string
+		value    string
+		expected bool
+	}{
+		{
+			name:     "unset",
+			value:    "",
+			expected: false,
+		},
+		{
+			name:     "disabled lower-case",
+			value:    "false",
+			expected: false,
+		},
+		{
+			name:     "disabled upper-case",
+			value:    "FALSE",
+			expected: false,
+		},
+		{
+			name:     "enabled lower-case",
+			value:    "true",
+			expected: true,
+		},
+		{
+			name:     "enabled upper-case",
+			value:    "TRUE",
+			expected: true,
+		},
+		{
+			name:     "invalid",
+			value:    "pandas",
+			expected: false,
+		},
+	}
+
+	for _, v := range testData {
+		t.Logf("[DEBUG] Test %q..", v.name)
+
+		os.Setenv("ARM_PROVIDER_CUSTOM_TIMEOUTS", v.value)
+		actual := SupportsCustomTimeouts()
+		if v.expected != actual {
+			t.Fatalf("Expected %t but got %t", v.expected, actual)
+		}
+	}
+}
diff --git a/azurerm/internal/timeouts/determine.go b/azurerm/internal/timeouts/determine.go
new file mode 100644
index 000000000000..7928459c7701
--- /dev/null
+++ b/azurerm/internal/timeouts/determine.go
@@ -0,0 +1,66 @@
+package timeouts
+
+import (
+	"context"
+	"time"
+
+	"github.com/hashicorp/terraform/helper/schema"
+	"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features"
+)
+
+// TODO: tests for this
+
+// ForCreate returns the context wrapped with the timeout for an Create operation
+//
+// If the 'SupportsCustomTimeouts' feature toggle is enabled - this is wrapped with a context
+// Otherwise this returns the default context
+func ForCreate(ctx context.Context, d *schema.ResourceData) (context.Context, context.CancelFunc) {
+	return buildWithTimeout(ctx, d.Timeout(schema.TimeoutCreate))
+}
+
+// ForCreateUpdate returns the context wrapped with the timeout for an combined Create/Update operation
+//
+// If the 'SupportsCustomTimeouts' feature toggle is enabled - this is wrapped with a context
+// Otherwise this returns the default context
+func ForCreateUpdate(ctx context.Context, d *schema.ResourceData) (context.Context, context.CancelFunc) {
+	if d.IsNewResource() {
+		return ForCreate(ctx, d)
+	}
+
+	return ForUpdate(ctx, d)
+}
+
+// ForDelete returns the context wrapped with the timeout for an Delete operation
+//
+// If the 'SupportsCustomTimeouts' feature toggle is enabled - this is wrapped with a context
+// Otherwise this returns the default context
+func ForDelete(ctx context.Context, d *schema.ResourceData) (context.Context, context.CancelFunc) {
+	return buildWithTimeout(ctx, d.Timeout(schema.TimeoutDelete))
+}
+
+// ForRead returns the context wrapped with the timeout for an Read operation
+//
+// If the 'SupportsCustomTimeouts' feature toggle is enabled - this is wrapped with a context
+// Otherwise this returns the default context
+func ForRead(ctx context.Context, d *schema.ResourceData) (context.Context, context.CancelFunc) {
+	return buildWithTimeout(ctx, d.Timeout(schema.TimeoutRead))
+}
+
+// ForUpdate returns the context wrapped with the timeout for an Update operation
+//
+// If the 'SupportsCustomTimeouts' feature toggle is enabled - this is wrapped with a context
+// Otherwise this returns the default context
+func ForUpdate(ctx context.Context, d *schema.ResourceData) (context.Context, context.CancelFunc) {
+	return buildWithTimeout(ctx, d.Timeout(schema.TimeoutUpdate))
+}
+
+func buildWithTimeout(ctx context.Context, timeout time.Duration) (context.Context, context.CancelFunc) {
+	if features.SupportsCustomTimeouts() {
+		return context.WithTimeout(ctx, timeout)
+	}
+
+	nullFunc := func() {
+		// do nothing on cancel since timeouts aren't enabled
+	}
+	return ctx, nullFunc
+}
diff --git a/azurerm/provider.go b/azurerm/provider.go
index 0138b9fbc463..6023a7b26e8d 100644
--- a/azurerm/provider.go
+++ b/azurerm/provider.go
@@ -5,11 +5,13 @@ import (
 	"log"
 	"os"
 	"strings"
+	"time"
 
 	"github.com/hashicorp/go-azure-helpers/authentication"
 	"github.com/hashicorp/terraform/helper/schema"
 	"github.com/hashicorp/terraform/terraform"
 	"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate"
+	"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features"
 	"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/common"
 	"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/compute"
 	"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
@@ -476,6 +478,29 @@ func Provider() terraform.ResourceProvider {
 		}
 	}
 
+	// TODO: remove all of this in 2.0 once Custom Timeouts are supported
+	if features.SupportsCustomTimeouts() {
+		// default everything to 3 hours for now
+		for _, v := range resources {
+			if v.Timeouts == nil {
+				v.Timeouts = &schema.ResourceTimeout{
+					Create:  schema.DefaultTimeout(3 * time.Hour),
+					Update:  schema.DefaultTimeout(3 * time.Hour),
+					Delete:  schema.DefaultTimeout(3 * time.Hour),
+					Default: schema.DefaultTimeout(3 * time.Hour),
+
+					// Read is the only exception, since if it's taken more than 5 minutes something's seriously wrong
+					Read: schema.DefaultTimeout(5 * time.Minute),
+				}
+			}
+		}
+	} else {
+		// ensure any timeouts configured on the resources are removed until 2.0
+		for _, v := range resources {
+			v.Timeouts = nil
+		}
+	}
+
 	p := &schema.Provider{
 		Schema: map[string]*schema.Schema{
 			"subscription_id": {
diff --git a/azurerm/resource_arm_resource_group.go b/azurerm/resource_arm_resource_group.go
index b72ed31b2aa8..837fcc147bd2 100644
--- a/azurerm/resource_arm_resource_group.go
+++ b/azurerm/resource_arm_resource_group.go
@@ -8,6 +8,7 @@ import (
 	"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
 	"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features"
 	"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags"
+	"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts"
 
 	"github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-05-01/resources"
 	"github.com/hashicorp/terraform/helper/schema"
@@ -37,7 +38,8 @@ func resourceArmResourceGroup() *schema.Resource {
 
 func resourceArmResourceGroupCreateUpdate(d *schema.ResourceData, meta interface{}) error {
 	client := meta.(*ArmClient).resource.GroupsClient
-	ctx := meta.(*ArmClient).StopContext
+	ctx, cancel := timeouts.ForCreateUpdate(meta.(*ArmClient).StopContext, d)
+	defer cancel()
 
 	name := d.Get("name").(string)
 	location := azure.NormalizeLocation(d.Get("location").(string))
@@ -77,7 +79,8 @@ func resourceArmResourceGroupCreateUpdate(d *schema.ResourceData, meta interface
 
 func resourceArmResourceGroupRead(d *schema.ResourceData, meta interface{}) error {
 	client := meta.(*ArmClient).resource.GroupsClient
-	ctx := meta.(*ArmClient).StopContext
+	ctx, cancel := timeouts.ForRead(meta.(*ArmClient).StopContext, d)
+	defer cancel()
 
 	id, err := azure.ParseAzureResourceID(d.Id())
 	if err != nil {
@@ -106,7 +109,8 @@ func resourceArmResourceGroupRead(d *schema.ResourceData, meta interface{}) erro
 
 func resourceArmResourceGroupDelete(d *schema.ResourceData, meta interface{}) error {
 	client := meta.(*ArmClient).resource.GroupsClient
-	ctx := meta.(*ArmClient).StopContext
+	ctx, cancel := timeouts.ForDelete(meta.(*ArmClient).StopContext, d)
+	defer cancel()
 
 	id, err := azure.ParseAzureResourceID(d.Id())
 	if err != nil {