From d1757f76e99253873f50c3b7d926655d68d79836 Mon Sep 17 00:00:00 2001
From: Nathan McKinley <nmckinley@google.com>
Date: Tue, 22 Jan 2019 19:05:48 +0000
Subject: [PATCH] Initial autogeneration of Spanner instance in Terraform.

Signed-off-by: Modular Magician <magic-modules@google.com>
---
 google/iam_spanner_instance.go                |  34 ++
 google/provider.go                            |   3 +-
 google/provider_spanner_gen.go                |   1 +
 google/resource_spanner_database.go           |   2 -
 google/resource_spanner_instance.go           | 492 +++++++++++-------
 ...esource_spanner_instance_generated_test.go |  87 ++++
 google/resource_spanner_instance_test.go      | 134 -----
 google/spanner_instance_operation.go          |   8 +-
 .../docs/r/cloudbuild_trigger.html.markdown   |   4 +-
 website/docs/r/spanner_instance.html.markdown | 125 +++--
 10 files changed, 534 insertions(+), 356 deletions(-)
 create mode 100644 google/resource_spanner_instance_generated_test.go

diff --git a/google/iam_spanner_instance.go b/google/iam_spanner_instance.go
index c02512060a9..a4787c21ae2 100644
--- a/google/iam_spanner_instance.go
+++ b/google/iam_spanner_instance.go
@@ -2,6 +2,8 @@ package google
 
 import (
 	"fmt"
+	"regexp"
+	"strings"
 
 	"github.com/hashicorp/errwrap"
 	"github.com/hashicorp/terraform/helper/schema"
@@ -110,3 +112,35 @@ func (u *SpannerInstanceIamUpdater) GetMutexKey() string {
 func (u *SpannerInstanceIamUpdater) DescribeResource() string {
 	return fmt.Sprintf("Spanner Instance: %s/%s", u.project, u.instance)
 }
+
+type spannerInstanceId struct {
+	Project  string
+	Instance string
+}
+
+func (s spannerInstanceId) terraformId() string {
+	return fmt.Sprintf("%s/%s", s.Project, s.Instance)
+}
+
+func (s spannerInstanceId) parentProjectUri() string {
+	return fmt.Sprintf("projects/%s", s.Project)
+}
+
+func (s spannerInstanceId) instanceUri() string {
+	return fmt.Sprintf("%s/instances/%s", s.parentProjectUri(), s.Instance)
+}
+
+func (s spannerInstanceId) instanceConfigUri(c string) string {
+	return fmt.Sprintf("%s/instanceConfigs/%s", s.parentProjectUri(), c)
+}
+
+func extractSpannerInstanceId(id string) (*spannerInstanceId, error) {
+	if !regexp.MustCompile("^" + ProjectRegex + "/[a-z0-9-]+$").Match([]byte(id)) {
+		return nil, fmt.Errorf("Invalid spanner id format, expecting {projectId}/{instanceId}")
+	}
+	parts := strings.Split(id, "/")
+	return &spannerInstanceId{
+		Project:  parts[0],
+		Instance: parts[1],
+	}, nil
+}
diff --git a/google/provider.go b/google/provider.go
index f3dd00320c2..7018cf63618 100644
--- a/google/provider.go
+++ b/google/provider.go
@@ -135,6 +135,7 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) {
 		GeneratedRedisResourcesMap,
 		GeneratedResourceManagerResourcesMap,
 		GeneratedSourceRepoResourcesMap,
+		GeneratedSpannerResourcesMap,
 		GeneratedStorageResourcesMap,
 		GeneratedMonitoringResourcesMap,
 		map[string]*schema.Resource{
@@ -197,11 +198,9 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) {
 			"google_kms_crypto_key":                        resourceKmsCryptoKey(),
 			"google_kms_crypto_key_iam_binding":            ResourceIamBindingWithImport(IamKmsCryptoKeySchema, NewKmsCryptoKeyIamUpdater, CryptoIdParseFunc),
 			"google_kms_crypto_key_iam_member":             ResourceIamMemberWithImport(IamKmsCryptoKeySchema, NewKmsCryptoKeyIamUpdater, CryptoIdParseFunc),
-			"google_spanner_instance":                      resourceSpannerInstance(),
 			"google_spanner_instance_iam_binding":          ResourceIamBindingWithImport(IamSpannerInstanceSchema, NewSpannerInstanceIamUpdater, SpannerInstanceIdParseFunc),
 			"google_spanner_instance_iam_member":           ResourceIamMemberWithImport(IamSpannerInstanceSchema, NewSpannerInstanceIamUpdater, SpannerInstanceIdParseFunc),
 			"google_spanner_instance_iam_policy":           ResourceIamPolicyWithImport(IamSpannerInstanceSchema, NewSpannerInstanceIamUpdater, SpannerInstanceIdParseFunc),
-			"google_spanner_database":                      resourceSpannerDatabase(),
 			"google_spanner_database_iam_binding":          ResourceIamBindingWithImport(IamSpannerDatabaseSchema, NewSpannerDatabaseIamUpdater, SpannerDatabaseIdParseFunc),
 			"google_spanner_database_iam_member":           ResourceIamMemberWithImport(IamSpannerDatabaseSchema, NewSpannerDatabaseIamUpdater, SpannerDatabaseIdParseFunc),
 			"google_spanner_database_iam_policy":           ResourceIamPolicyWithImport(IamSpannerDatabaseSchema, NewSpannerDatabaseIamUpdater, SpannerDatabaseIdParseFunc),
diff --git a/google/provider_spanner_gen.go b/google/provider_spanner_gen.go
index 34720f715e3..634d491ed94 100644
--- a/google/provider_spanner_gen.go
+++ b/google/provider_spanner_gen.go
@@ -17,5 +17,6 @@ package google
 import "github.com/hashicorp/terraform/helper/schema"
 
 var GeneratedSpannerResourcesMap = map[string]*schema.Resource{
+	"google_spanner_instance": resourceSpannerInstance(),
 	"google_spanner_database": resourceSpannerDatabase(),
 }
diff --git a/google/resource_spanner_database.go b/google/resource_spanner_database.go
index a117407eb64..2bc481e9544 100644
--- a/google/resource_spanner_database.go
+++ b/google/resource_spanner_database.go
@@ -239,14 +239,12 @@ func resourceSpannerDatabaseEncoder(d *schema.ResourceData, meta interface{}, ob
 func resourceSpannerDatabaseDecoder(d *schema.ResourceData, meta interface{}, res map[string]interface{}) (map[string]interface{}, error) {
 	config := meta.(*Config)
 	d.SetId(res["name"].(string))
-	log.Printf("[DEBUG] name = %s", res["name"])
 	if err := parseImportId([]string{"projects/(?P<project>[^/]+)/instances/(?P<instance>[^/]+)/databases/(?P<name>[^/]+)"}, d, config); err != nil {
 		return nil, err
 	}
 	res["project"] = d.Get("project").(string)
 	res["instance"] = d.Get("instance").(string)
 	res["name"] = d.Get("name").(string)
-	log.Printf("[DEBUG] result %#v", res)
 	id, err := replaceVars(d, config, "{{instance}}/{{name}}")
 	if err != nil {
 		return nil, err
diff --git a/google/resource_spanner_instance.go b/google/resource_spanner_instance.go
index cd052f090d8..6b66dc57620 100644
--- a/google/resource_spanner_instance.go
+++ b/google/resource_spanner_instance.go
@@ -1,16 +1,30 @@
+// ----------------------------------------------------------------------------
+//
+//     ***     AUTO GENERATED CODE    ***    AUTO GENERATED CODE     ***
+//
+// ----------------------------------------------------------------------------
+//
+//     This file is automatically generated by Magic Modules and manual
+//     changes will be clobbered when the file is regenerated.
+//
+//     Please read more about how to change this file in
+//     .github/CONTRIBUTING.md.
+//
+// ----------------------------------------------------------------------------
+
 package google
 
 import (
 	"fmt"
 	"log"
-	"net/http"
+	"reflect"
 	"regexp"
+	"strconv"
 	"strings"
+	"time"
 
 	"github.com/hashicorp/terraform/helper/resource"
 	"github.com/hashicorp/terraform/helper/schema"
-
-	"google.golang.org/api/googleapi"
 	"google.golang.org/api/spanner/v1"
 )
 
@@ -20,307 +34,429 @@ func resourceSpannerInstance() *schema.Resource {
 		Read:   resourceSpannerInstanceRead,
 		Update: resourceSpannerInstanceUpdate,
 		Delete: resourceSpannerInstanceDelete,
+
 		Importer: &schema.ResourceImporter{
-			State: resourceSpannerInstanceImportState,
+			State: resourceSpannerInstanceImport,
 		},
 
-		Schema: map[string]*schema.Schema{
+		Timeouts: &schema.ResourceTimeout{
+			Create: schema.DefaultTimeout(240 * time.Second),
+			Update: schema.DefaultTimeout(240 * time.Second),
+			Delete: schema.DefaultTimeout(240 * time.Second),
+		},
 
+		Schema: map[string]*schema.Schema{
 			"config": {
-				Type:     schema.TypeString,
-				Required: true,
-				ForceNew: true,
+				Type:             schema.TypeString,
+				Required:         true,
+				DiffSuppressFunc: compareSelfLinkOrResourceName,
+			},
+			"display_name": {
+				Type:         schema.TypeString,
+				Required:     true,
+				ValidateFunc: validateRegexp(`^(?:[a-zA-Z](?:[- _a-zA-Z0-9]{2,28}[a-zA-Z0-9])?)$`),
 			},
-
 			"name": {
-				Type:     schema.TypeString,
-				Optional: true,
-				Computed: true,
-				ForceNew: true,
-				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
-					value := v.(string)
-
-					if len(value) < 6 && len(value) > 30 {
-						errors = append(errors, fmt.Errorf(
-							"%q must be between 6 and 30 characters in length", k))
-					}
-					if !regexp.MustCompile("^[a-z0-9-]+$").MatchString(value) {
-						errors = append(errors, fmt.Errorf(
-							"%q can only contain lowercase letters, numbers and hyphens", k))
-					}
-					if !regexp.MustCompile("^[a-z]").MatchString(value) {
-						errors = append(errors, fmt.Errorf(
-							"%q must start with a letter", k))
-					}
-					if !regexp.MustCompile("[a-z0-9]$").MatchString(value) {
-						errors = append(errors, fmt.Errorf(
-							"%q must end with a number or a letter", k))
-					}
-					return
-				},
+				Type:         schema.TypeString,
+				Computed:     true,
+				Optional:     true,
+				ForceNew:     true,
+				ValidateFunc: validateRegexp(`^(?:[a-z](?:[-_a-z0-9]{4,28}[a-z0-9])?)$`),
 			},
-
-			"display_name": {
-				Type:     schema.TypeString,
-				Required: true,
-				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
-					value := v.(string)
-
-					if len(value) < 4 && len(value) > 30 {
-						errors = append(errors, fmt.Errorf(
-							"%q must be between 4 and 30 characters in length", k))
-					}
-					return
-				},
+			"labels": {
+				Type:     schema.TypeMap,
+				Optional: true,
+				Elem:     &schema.Schema{Type: schema.TypeString},
 			},
-
 			"num_nodes": {
 				Type:     schema.TypeInt,
 				Optional: true,
 				Default:  1,
 			},
-
-			"labels": {
-				Type:     schema.TypeMap,
-				Optional: true,
-				Elem:     &schema.Schema{Type: schema.TypeString},
+			"state": {
+				Type:     schema.TypeString,
+				Computed: true,
 			},
-
 			"project": {
 				Type:     schema.TypeString,
 				Optional: true,
 				Computed: true,
 				ForceNew: true,
 			},
-
-			"state": {
-				Type:     schema.TypeString,
-				Computed: true,
-			},
 		},
 	}
 }
 
 func resourceSpannerInstanceCreate(d *schema.ResourceData, meta interface{}) error {
 	config := meta.(*Config)
-	cir := &spanner.CreateInstanceRequest{
-		Instance: &spanner.Instance{},
-	}
 
-	if v, ok := d.GetOk("name"); ok {
-		cir.InstanceId = v.(string)
-	} else {
-		cir.InstanceId = genSpannerInstanceName()
-		d.Set("name", cir.InstanceId)
+	obj := make(map[string]interface{})
+	nameProp, err := expandSpannerInstanceName(d.Get("name"), d, config)
+	if err != nil {
+		return err
+	} else if v, ok := d.GetOkExists("name"); !isEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) {
+		obj["name"] = nameProp
+	}
+	configProp, err := expandSpannerInstanceConfig(d.Get("config"), d, config)
+	if err != nil {
+		return err
+	} else if v, ok := d.GetOkExists("config"); !isEmptyValue(reflect.ValueOf(configProp)) && (ok || !reflect.DeepEqual(v, configProp)) {
+		obj["config"] = configProp
+	}
+	displayNameProp, err := expandSpannerInstanceDisplayName(d.Get("display_name"), d, config)
+	if err != nil {
+		return err
+	} else if v, ok := d.GetOkExists("display_name"); !isEmptyValue(reflect.ValueOf(displayNameProp)) && (ok || !reflect.DeepEqual(v, displayNameProp)) {
+		obj["displayName"] = displayNameProp
+	}
+	nodeCountProp, err := expandSpannerInstanceNum_nodes(d.Get("num_nodes"), d, config)
+	if err != nil {
+		return err
+	} else if v, ok := d.GetOkExists("num_nodes"); !isEmptyValue(reflect.ValueOf(nodeCountProp)) && (ok || !reflect.DeepEqual(v, nodeCountProp)) {
+		obj["nodeCount"] = nodeCountProp
+	}
+	labelsProp, err := expandSpannerInstanceLabels(d.Get("labels"), d, config)
+	if err != nil {
+		return err
+	} else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(labelsProp)) && (ok || !reflect.DeepEqual(v, labelsProp)) {
+		obj["labels"] = labelsProp
 	}
 
-	if v, ok := d.GetOk("labels"); ok {
-		cir.Instance.Labels = convertStringMap(v.(map[string]interface{}))
+	obj, err = resourceSpannerInstanceEncoder(d, meta, obj)
+	if err != nil {
+		return err
 	}
 
-	id, err := buildSpannerInstanceId(d, config)
+	url, err := replaceVars(d, config, "https://spanner.googleapis.com/v1/projects/{{project}}/instances")
 	if err != nil {
 		return err
 	}
 
-	cir.Instance.Config = id.instanceConfigUri(d.Get("config").(string))
-	cir.Instance.DisplayName = d.Get("display_name").(string)
-	cir.Instance.NodeCount = int64(d.Get("num_nodes").(int))
+	log.Printf("[DEBUG] Creating new Instance: %#v", obj)
+	res, err := sendRequestWithTimeout(config, "POST", url, obj, d.Timeout(schema.TimeoutCreate))
+	if err != nil {
+		return fmt.Errorf("Error creating Instance: %s", err)
+	}
+
+	// Store the ID now
+	id, err := replaceVars(d, config, "{{project}}/{{name}}")
+	if err != nil {
+		return fmt.Errorf("Error constructing id: %s", err)
+	}
+	d.SetId(id)
 
-	op, err := config.clientSpanner.Projects.Instances.Create(
-		id.parentProjectUri(), cir).Do()
+	project, err := getProject(d, config)
 	if err != nil {
-		if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == http.StatusConflict {
-			return fmt.Errorf("Error, the name %s is not unique within project %s", id.Instance, id.Project)
-		}
-		return fmt.Errorf("Error, failed to create instance %s: %s", id.terraformId(), err)
+		return err
+	}
+	op := &spanner.Operation{}
+	err = Convert(res, op)
+	if err != nil {
+		return err
 	}
 
-	d.SetId(id.terraformId())
+	waitErr := spannerOperationWaitTime(
+		config.clientSpanner, op, project, "Creating Instance",
+		int(d.Timeout(schema.TimeoutCreate).Minutes()))
 
-	// Wait until it's created
-	timeoutMins := int(d.Timeout(schema.TimeoutCreate).Minutes())
-	waitErr := spannerInstanceOperationWait(config, op, "Creating Spanner instance", timeoutMins)
 	if waitErr != nil {
 		// The resource didn't actually create
 		d.SetId("")
-		return waitErr
+		return fmt.Errorf("Error waiting to create Instance: %s", waitErr)
 	}
 
-	log.Printf("[INFO] Spanner instance %s has been created", id.terraformId())
+	log.Printf("[DEBUG] Finished creating Instance %q: %#v", d.Id(), res)
+
+	// This is useful if the resource in question doesn't have a perfectly consistent API
+	// That is, the Operation for Create might return before the Get operation shows the
+	// completed state of the resource.
+	time.Sleep(5 * time.Second)
+
 	return resourceSpannerInstanceRead(d, meta)
 }
 
 func resourceSpannerInstanceRead(d *schema.ResourceData, meta interface{}) error {
 	config := meta.(*Config)
 
-	id, err := buildSpannerInstanceId(d, config)
+	url, err := replaceVars(d, config, "https://spanner.googleapis.com/v1/projects/{{project}}/instances/{{name}}")
+	if err != nil {
+		return err
+	}
+
+	res, err := sendRequest(config, "GET", url, nil)
+	if err != nil {
+		return handleNotFoundError(err, d, fmt.Sprintf("SpannerInstance %q", d.Id()))
+	}
+
+	res, err = resourceSpannerInstanceDecoder(d, meta, res)
 	if err != nil {
 		return err
 	}
 
-	instance, err := config.clientSpanner.Projects.Instances.Get(
-		id.instanceUri()).Do()
+	project, err := getProject(d, config)
 	if err != nil {
-		return handleNotFoundError(err, d, fmt.Sprintf("Spanner instance %s", id.terraformId()))
+		return err
+	}
+	if err := d.Set("project", project); err != nil {
+		return fmt.Errorf("Error reading Instance: %s", err)
 	}
 
-	d.Set("config", GetResourceNameFromSelfLink(instance.Config))
-	d.Set("labels", instance.Labels)
-	d.Set("display_name", instance.DisplayName)
-	d.Set("num_nodes", instance.NodeCount)
-	d.Set("state", instance.State)
-	d.Set("project", id.Project)
+	if err := d.Set("name", flattenSpannerInstanceName(res["name"], d)); err != nil {
+		return fmt.Errorf("Error reading Instance: %s", err)
+	}
+	if err := d.Set("config", flattenSpannerInstanceConfig(res["config"], d)); err != nil {
+		return fmt.Errorf("Error reading Instance: %s", err)
+	}
+	if err := d.Set("display_name", flattenSpannerInstanceDisplayName(res["displayName"], d)); err != nil {
+		return fmt.Errorf("Error reading Instance: %s", err)
+	}
+	if err := d.Set("num_nodes", flattenSpannerInstanceNum_nodes(res["nodeCount"], d)); err != nil {
+		return fmt.Errorf("Error reading Instance: %s", err)
+	}
+	if err := d.Set("labels", flattenSpannerInstanceLabels(res["labels"], d)); err != nil {
+		return fmt.Errorf("Error reading Instance: %s", err)
+	}
+	if err := d.Set("state", flattenSpannerInstanceState(res["state"], d)); err != nil {
+		return fmt.Errorf("Error reading Instance: %s", err)
+	}
 
 	return nil
 }
 
 func resourceSpannerInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
 	config := meta.(*Config)
-	log.Printf("[INFO] About to update Spanner Instance %s ", d.Id())
-	uir := &spanner.UpdateInstanceRequest{
-		Instance: &spanner.Instance{},
-	}
 
-	id, err := buildSpannerInstanceId(d, config)
+	obj := make(map[string]interface{})
+	configProp, err := expandSpannerInstanceConfig(d.Get("config"), d, config)
 	if err != nil {
 		return err
+	} else if v, ok := d.GetOkExists("config"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, configProp)) {
+		obj["config"] = configProp
 	}
-
-	fieldMask := []string{}
-	if d.HasChange("num_nodes") {
-		fieldMask = append(fieldMask, "nodeCount")
-		uir.Instance.NodeCount = int64(d.Get("num_nodes").(int))
+	displayNameProp, err := expandSpannerInstanceDisplayName(d.Get("display_name"), d, config)
+	if err != nil {
+		return err
+	} else if v, ok := d.GetOkExists("display_name"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, displayNameProp)) {
+		obj["displayName"] = displayNameProp
 	}
-	if d.HasChange("display_name") {
-		fieldMask = append(fieldMask, "displayName")
-		uir.Instance.DisplayName = d.Get("display_name").(string)
+	nodeCountProp, err := expandSpannerInstanceNum_nodes(d.Get("num_nodes"), d, config)
+	if err != nil {
+		return err
+	} else if v, ok := d.GetOkExists("num_nodes"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, nodeCountProp)) {
+		obj["nodeCount"] = nodeCountProp
 	}
-	if d.HasChange("labels") {
-		fieldMask = append(fieldMask, "labels")
-		uir.Instance.Labels = convertStringMap(d.Get("labels").(map[string]interface{}))
+	labelsProp, err := expandSpannerInstanceLabels(d.Get("labels"), d, config)
+	if err != nil {
+		return err
+	} else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, labelsProp)) {
+		obj["labels"] = labelsProp
+	}
+
+	obj, err = resourceSpannerInstanceUpdateEncoder(d, meta, obj)
+
+	url, err := replaceVars(d, config, "https://spanner.googleapis.com/v1/projects/{{project}}/instances/{{name}}")
+	if err != nil {
+		return err
+	}
+
+	log.Printf("[DEBUG] Updating Instance %q: %#v", d.Id(), obj)
+	res, err := sendRequestWithTimeout(config, "PATCH", url, obj, d.Timeout(schema.TimeoutUpdate))
+
+	if err != nil {
+		return fmt.Errorf("Error updating Instance %q: %s", d.Id(), err)
 	}
 
-	uir.FieldMask = strings.Join(fieldMask, ",")
-	op, err := config.clientSpanner.Projects.Instances.Patch(
-		id.instanceUri(), uir).Do()
+	project, err := getProject(d, config)
+	if err != nil {
+		return err
+	}
+	op := &spanner.Operation{}
+	err = Convert(res, op)
 	if err != nil {
 		return err
 	}
 
-	// Wait until it's updated
-	timeoutMins := int(d.Timeout(schema.TimeoutUpdate).Minutes())
-	err = spannerInstanceOperationWait(config, op, "Update Spanner Instance", timeoutMins)
+	err = spannerOperationWaitTime(
+		config.clientSpanner, op, project, "Updating Instance",
+		int(d.Timeout(schema.TimeoutUpdate).Minutes()))
+
 	if err != nil {
 		return err
 	}
 
-	log.Printf("[INFO] Spanner Instance %s has been updated ", id.terraformId())
 	return resourceSpannerInstanceRead(d, meta)
 }
 
 func resourceSpannerInstanceDelete(d *schema.ResourceData, meta interface{}) error {
 	config := meta.(*Config)
 
-	id, err := buildSpannerInstanceId(d, config)
+	url, err := replaceVars(d, config, "https://spanner.googleapis.com/v1/projects/{{project}}/instances/{{name}}")
+	if err != nil {
+		return err
+	}
+
+	var obj map[string]interface{}
+	log.Printf("[DEBUG] Deleting Instance %q", d.Id())
+	res, err := sendRequestWithTimeout(config, "DELETE", url, obj, d.Timeout(schema.TimeoutDelete))
+	if err != nil {
+		return handleNotFoundError(err, d, "Instance")
+	}
+
+	project, err := getProject(d, config)
 	if err != nil {
 		return err
 	}
+	op := &spanner.Operation{}
+	err = Convert(res, op)
+	if err != nil {
+		return err
+	}
+
+	err = spannerOperationWaitTime(
+		config.clientSpanner, op, project, "Deleting Instance",
+		int(d.Timeout(schema.TimeoutDelete).Minutes()))
 
-	_, err = config.clientSpanner.Projects.Instances.Delete(
-		id.instanceUri()).Do()
 	if err != nil {
-		return fmt.Errorf("Error, failed to delete Spanner Instance %s in project %s: %s", id.Instance, id.Project, err)
+		return err
 	}
 
-	d.SetId("")
+	log.Printf("[DEBUG] Finished deleting Instance %q: %#v", d.Id(), res)
 	return nil
 }
 
-func resourceSpannerInstanceImportState(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
+func resourceSpannerInstanceImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
 	config := meta.(*Config)
-	id, err := importSpannerInstanceId(d.Id())
-	if err != nil {
+	if err := parseImportId([]string{"projects/(?P<project>[^/]+)/instances/(?P<name>[^/]+)", "(?P<project>[^/]+)/(?P<name>[^/]+)", "(?P<name>[^/]+)"}, d, config); err != nil {
 		return nil, err
 	}
 
-	if id.Project != "" {
-		d.Set("project", id.Project)
-	} else {
-		project, err := getProject(d, config)
-		if err != nil {
-			return nil, err
-		}
-		id.Project = project
+	// Replace import id for the resource id
+	id, err := replaceVars(d, config, "{{project}}/{{name}}")
+	if err != nil {
+		return nil, fmt.Errorf("Error constructing id: %s", err)
 	}
-
-	d.Set("name", id.Instance)
-	d.SetId(id.terraformId())
+	d.SetId(id)
 
 	return []*schema.ResourceData{d}, nil
 }
 
-func buildSpannerInstanceId(d *schema.ResourceData, config *Config) (*spannerInstanceId, error) {
-	project, err := getProject(d, config)
-	if err != nil {
-		return nil, err
+func flattenSpannerInstanceName(v interface{}, d *schema.ResourceData) interface{} {
+	return v
+}
+
+func flattenSpannerInstanceConfig(v interface{}, d *schema.ResourceData) interface{} {
+	if v == nil {
+		return v
 	}
-	return &spannerInstanceId{
-		Project:  project,
-		Instance: d.Get("name").(string),
-	}, nil
+	return ConvertSelfLinkToV1(v.(string))
 }
 
-func genSpannerInstanceName() string {
-	return resource.PrefixedUniqueId("tfgen-spanid-")[:30]
+func flattenSpannerInstanceDisplayName(v interface{}, d *schema.ResourceData) interface{} {
+	return v
 }
 
-type spannerInstanceId struct {
-	Project  string
-	Instance string
+func flattenSpannerInstanceNum_nodes(v interface{}, d *schema.ResourceData) interface{} {
+	// Handles the string fixed64 format
+	if strVal, ok := v.(string); ok {
+		if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil {
+			return intVal
+		} // let terraform core handle it if we can't convert the string to an int.
+	}
+	return v
+}
+
+func flattenSpannerInstanceLabels(v interface{}, d *schema.ResourceData) interface{} {
+	return v
+}
+
+func flattenSpannerInstanceState(v interface{}, d *schema.ResourceData) interface{} {
+	return v
 }
 
-func (s spannerInstanceId) terraformId() string {
-	return fmt.Sprintf("%s/%s", s.Project, s.Instance)
+func expandSpannerInstanceName(v interface{}, d *schema.ResourceData, config *Config) (interface{}, error) {
+	return v, nil
 }
 
-func (s spannerInstanceId) parentProjectUri() string {
-	return fmt.Sprintf("projects/%s", s.Project)
+func expandSpannerInstanceConfig(v interface{}, d *schema.ResourceData, config *Config) (interface{}, error) {
+	r := regexp.MustCompile("projects/(.+)/instanceConfigs/(.+)")
+	if r.MatchString(v.(string)) {
+		return v.(string), nil
+	}
+
+	project, err := getProject(d, config)
+	if err != nil {
+		return nil, err
+	}
+
+	return fmt.Sprintf("projects/%s/instanceConfigs/%s", project, v.(string)), nil
 }
 
-func (s spannerInstanceId) instanceUri() string {
-	return fmt.Sprintf("%s/instances/%s", s.parentProjectUri(), s.Instance)
+func expandSpannerInstanceDisplayName(v interface{}, d *schema.ResourceData, config *Config) (interface{}, error) {
+	return v, nil
 }
 
-func (s spannerInstanceId) instanceConfigUri(c string) string {
-	return fmt.Sprintf("%s/instanceConfigs/%s", s.parentProjectUri(), c)
+func expandSpannerInstanceNum_nodes(v interface{}, d *schema.ResourceData, config *Config) (interface{}, error) {
+	return v, nil
 }
 
-func importSpannerInstanceId(id string) (*spannerInstanceId, error) {
-	if !regexp.MustCompile("^[a-z0-9-]+$").Match([]byte(id)) &&
-		!regexp.MustCompile("^"+ProjectRegex+"/[a-z0-9-]+$").Match([]byte(id)) {
-		return nil, fmt.Errorf("Invalid spanner instance specifier. " +
-			"Expecting either {projectId}/{instanceId} OR " +
-			"{instanceId} (where project is to be derived from that specified in provider)")
+func expandSpannerInstanceLabels(v interface{}, d *schema.ResourceData, config *Config) (map[string]string, error) {
+	if v == nil {
+		return map[string]string{}, nil
+	}
+	m := make(map[string]string)
+	for k, val := range v.(map[string]interface{}) {
+		m[k] = val.(string)
 	}
+	return m, nil
+}
 
-	parts := strings.Split(id, "/")
-	if len(parts) == 1 {
-		log.Printf("[INFO] Spanner instance import format of {instanceId} specified: %s", id)
-		return &spannerInstanceId{Instance: parts[0]}, nil
+func resourceSpannerInstanceEncoder(d *schema.ResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) {
+	newObj := make(map[string]interface{})
+	newObj["instance"] = obj
+	if obj["name"] == nil {
+		d.Set("name", resource.PrefixedUniqueId("tfgen-spanid-")[:30])
+		newObj["instanceId"] = d.Get("name").(string)
+	} else {
+		newObj["instanceId"] = obj["name"]
 	}
+	delete(obj, "name")
+	return newObj, nil
+}
 
-	log.Printf("[INFO] Spanner instance import format of {projectId}/{instanceId} specified: %s", id)
-	return extractSpannerInstanceId(id)
+func resourceSpannerInstanceUpdateEncoder(d *schema.ResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) {
+	project, err := getProject(d, meta.(*Config))
+	if err != nil {
+		return nil, err
+	}
+	obj["name"] = fmt.Sprintf("projects/%s/instances/%s", project, obj["name"])
+	newObj := make(map[string]interface{})
+	newObj["instance"] = obj
+	updateMask := make([]string, 0)
+	if d.HasChange("num_nodes") {
+		updateMask = append(updateMask, "nodeCount")
+	}
+	if d.HasChange("display_name") {
+		updateMask = append(updateMask, "displayName")
+	}
+	if d.HasChange("labels") {
+		updateMask = append(updateMask, "labels")
+	}
+	newObj["fieldMask"] = strings.Join(updateMask, ",")
+	return newObj, nil
 }
 
-func extractSpannerInstanceId(id string) (*spannerInstanceId, error) {
-	if !regexp.MustCompile("^" + ProjectRegex + "/[a-z0-9-]+$").Match([]byte(id)) {
-		return nil, fmt.Errorf("Invalid spanner id format, expecting {projectId}/{instanceId}")
+func resourceSpannerInstanceDecoder(d *schema.ResourceData, meta interface{}, res map[string]interface{}) (map[string]interface{}, error) {
+	config := meta.(*Config)
+	d.SetId(res["name"].(string))
+	if err := parseImportId([]string{"projects/(?P<project>[^/]+)/instances/(?P<name>[^/]+)"}, d, config); err != nil {
+		return nil, err
+	}
+	res["project"] = d.Get("project").(string)
+	res["name"] = d.Get("name").(string)
+	id, err := replaceVars(d, config, "{{project}}/{{name}}")
+	if err != nil {
+		return nil, err
 	}
-	parts := strings.Split(id, "/")
-	return &spannerInstanceId{
-		Project:  parts[0],
-		Instance: parts[1],
-	}, nil
+	d.SetId(id)
+	return res, nil
 }
diff --git a/google/resource_spanner_instance_generated_test.go b/google/resource_spanner_instance_generated_test.go
new file mode 100644
index 00000000000..94cad392f4a
--- /dev/null
+++ b/google/resource_spanner_instance_generated_test.go
@@ -0,0 +1,87 @@
+// ----------------------------------------------------------------------------
+//
+//     ***     AUTO GENERATED CODE    ***    AUTO GENERATED CODE     ***
+//
+// ----------------------------------------------------------------------------
+//
+//     This file is automatically generated by Magic Modules and manual
+//     changes will be clobbered when the file is regenerated.
+//
+//     Please read more about how to change this file in
+//     .github/CONTRIBUTING.md.
+//
+// ----------------------------------------------------------------------------
+
+package google
+
+import (
+	"fmt"
+	"strings"
+	"testing"
+
+	"github.com/hashicorp/terraform/helper/acctest"
+	"github.com/hashicorp/terraform/helper/resource"
+	"github.com/hashicorp/terraform/terraform"
+)
+
+func TestAccSpannerInstance_spannerInstanceBasicExample(t *testing.T) {
+	t.Parallel()
+
+	context := map[string]interface{}{
+		"random_suffix": acctest.RandString(10),
+	}
+
+	resource.Test(t, resource.TestCase{
+		PreCheck:     func() { testAccPreCheck(t) },
+		Providers:    testAccProviders,
+		CheckDestroy: testAccCheckSpannerInstanceDestroy,
+		Steps: []resource.TestStep{
+			{
+				Config: testAccSpannerInstance_spannerInstanceBasicExample(context),
+			},
+			{
+				ResourceName:      "google_spanner_instance.example",
+				ImportState:       true,
+				ImportStateVerify: true,
+			},
+		},
+	})
+}
+
+func testAccSpannerInstance_spannerInstanceBasicExample(context map[string]interface{}) string {
+	return Nprintf(`
+resource "google_spanner_instance" "example" {
+  config        = "regional-us-central1"
+  display_name  = "Test Spanner Instance"
+  num_nodes     = 2
+  labels {
+    "foo" = "bar"
+  }
+}
+`, context)
+}
+
+func testAccCheckSpannerInstanceDestroy(s *terraform.State) error {
+	for name, rs := range s.RootModule().Resources {
+		if rs.Type != "google_spanner_instance" {
+			continue
+		}
+		if strings.HasPrefix(name, "data.") {
+			continue
+		}
+
+		config := testAccProvider.Meta().(*Config)
+
+		url, err := replaceVarsForTest(rs, "https://spanner.googleapis.com/v1/projects/{{project}}/instances/{{name}}")
+		if err != nil {
+			return err
+		}
+
+		_, err = sendRequest(config, "GET", url, nil)
+		if err == nil {
+			return fmt.Errorf("SpannerInstance still exists at %s", url)
+		}
+	}
+
+	return nil
+}
diff --git a/google/resource_spanner_instance_test.go b/google/resource_spanner_instance_test.go
index c28e01a491a..fb3c7c0e371 100644
--- a/google/resource_spanner_instance_test.go
+++ b/google/resource_spanner_instance_test.go
@@ -2,17 +2,10 @@ package google
 
 import (
 	"fmt"
-	"net/http"
 	"testing"
 
-	"github.com/hashicorp/errwrap"
 	"github.com/hashicorp/terraform/helper/acctest"
 	"github.com/hashicorp/terraform/helper/resource"
-	"github.com/hashicorp/terraform/terraform"
-
-	"strings"
-
-	"google.golang.org/api/googleapi"
 )
 
 // Unit Tests
@@ -47,95 +40,6 @@ func TestSpannerInstanceId_parentProjectUri(t *testing.T) {
 	expectEquals(t, expected, actual)
 }
 
-func TestGenSpannerInstanceName(t *testing.T) {
-	s := genSpannerInstanceName()
-	if len(s) != 30 {
-		t.Fatalf("Expected a 30 char ID to be generated, instead found %d chars", len(s))
-	}
-}
-
-func TestImportSpannerInstanceId(t *testing.T) {
-	sid, e := importSpannerInstanceId("instance456")
-	if e != nil {
-		t.Errorf("Error should have been nil")
-	}
-	expectEquals(t, "", sid.Project)
-	expectEquals(t, "instance456", sid.Instance)
-}
-
-func TestImportSpannerInstanceId_projectAndInstance(t *testing.T) {
-	sid, e := importSpannerInstanceId("project123/instance456")
-	if e != nil {
-		t.Errorf("Error should have been nil")
-	}
-	expectEquals(t, "project123", sid.Project)
-	expectEquals(t, "instance456", sid.Instance)
-}
-
-func TestImportSpannerInstanceId_invalidLeadingSlash(t *testing.T) {
-	sid, e := importSpannerInstanceId("/instance456")
-	expectInvalidSpannerInstanceImport(t, sid, e)
-}
-
-func TestImportSpannerInstanceId_invalidTrailingSlash(t *testing.T) {
-	sid, e := importSpannerInstanceId("project123/")
-	expectInvalidSpannerInstanceImport(t, sid, e)
-}
-
-func TestImportSpannerInstanceId_invalidSingleSlash(t *testing.T) {
-	sid, e := importSpannerInstanceId("/")
-	expectInvalidSpannerInstanceImport(t, sid, e)
-}
-
-func TestImportSpannerInstanceId_invalidMultiSlash(t *testing.T) {
-	sid, e := importSpannerInstanceId("project123/instance456/db789")
-	expectInvalidSpannerInstanceImport(t, sid, e)
-}
-
-func TestImportSpannerInstanceId_projectId(t *testing.T) {
-	shouldPass := []string{
-		"project-id/instance",
-		"123123/instance",
-		"hashicorptest.net:project-123/instance",
-		"123/456",
-	}
-
-	shouldFail := []string{
-		"project-id#/instance",
-		"project-id/instance#",
-		"hashicorptest.net:project-123:invalid:project/instance",
-		"hashicorptest.net:/instance",
-	}
-
-	for _, element := range shouldPass {
-		_, e := importSpannerInstanceId(element)
-		if e != nil {
-			t.Error("importSpannerInstanceId should pass on '" + element + "' but doesn't")
-		}
-	}
-
-	for _, element := range shouldFail {
-		_, e := importSpannerInstanceId(element)
-		if e == nil {
-			t.Error("importSpannerInstanceId should fail on '" + element + "' but doesn't")
-		}
-	}
-}
-
-func expectInvalidSpannerInstanceImport(t *testing.T, sid *spannerInstanceId, e error) {
-	if sid != nil {
-		t.Errorf("Expected spannerInstanceId to be nil")
-		return
-	}
-	if e == nil {
-		t.Errorf("Expected an Error but did not get one")
-		return
-	}
-	if !strings.HasPrefix(e.Error(), "Invalid spanner instance specifier") {
-		t.Errorf("Expecting Error starting with 'Invalid spanner instance specifier'")
-	}
-}
-
 func expectEquals(t *testing.T, expected, actual string) {
 	if actual != expected {
 		t.Fatalf("Expected %s, but got %s", expected, actual)
@@ -222,44 +126,6 @@ func TestAccSpannerInstance_update(t *testing.T) {
 	})
 }
 
-func testAccCheckSpannerInstanceDestroy(s *terraform.State) error {
-	config := testAccProvider.Meta().(*Config)
-
-	for _, rs := range s.RootModule().Resources {
-		if rs.Type != "google_spanner_instance" {
-			continue
-		}
-
-		if rs.Primary.ID == "" {
-			return fmt.Errorf("Unable to verify delete of spanner instance, ID is empty")
-		}
-
-		instanceName := rs.Primary.Attributes["name"]
-		project, err := getTestProject(rs.Primary, config)
-		if err != nil {
-			return err
-		}
-
-		id := spannerInstanceId{
-			Project:  project,
-			Instance: instanceName,
-		}
-		_, err = config.clientSpanner.Projects.Instances.Get(
-			id.instanceUri()).Do()
-
-		if err == nil {
-			return fmt.Errorf("Spanner instance still exists")
-		}
-
-		if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == http.StatusNotFound {
-			return nil
-		}
-		return errwrap.Wrapf("Error verifying spanner instance deleted: {{err}}", err)
-	}
-
-	return nil
-}
-
 func testAccSpannerInstance_basic(name string) string {
 	return fmt.Sprintf(`
 resource "google_spanner_instance" "basic" {
diff --git a/google/spanner_instance_operation.go b/google/spanner_instance_operation.go
index 3c541950c0d..2831cc4833d 100644
--- a/google/spanner_instance_operation.go
+++ b/google/spanner_instance_operation.go
@@ -13,9 +13,13 @@ func (w *SpannerInstanceOperationWaiter) QueryOp() (interface{}, error) {
 	return w.Service.Projects.Instances.Operations.Get(w.Op.Name).Do()
 }
 
-func spannerInstanceOperationWait(config *Config, op *spanner.Operation, activity string, timeoutMinutes int) error {
+func spannerOperationWaitTime(spanner *spanner.Service, op *spanner.Operation, _ string, activity string, timeoutMinutes int) error {
+	if op.Name == "" {
+		// This was a synchronous call - there is no operation to wait for.
+		return nil
+	}
 	w := &SpannerInstanceOperationWaiter{
-		Service: config.clientSpanner,
+		Service: spanner,
 	}
 	if err := w.SetOp(op); err != nil {
 		return err
diff --git a/website/docs/r/cloudbuild_trigger.html.markdown b/website/docs/r/cloudbuild_trigger.html.markdown
index 6827812c26a..e2bc12a12b8 100644
--- a/website/docs/r/cloudbuild_trigger.html.markdown
+++ b/website/docs/r/cloudbuild_trigger.html.markdown
@@ -82,7 +82,7 @@ The following arguments are supported:
 * `ignored_files` -
   (Optional)
   ignoredFiles and includedFiles are file glob matches using http://godoc/pkg/path/filepath#Match
-  extended with support for "**".
+  extended with support for `**`.
   If ignoredFiles and changed files are both empty, then they are not
   used to determine whether or not to trigger a build.
   If ignoredFiles is not empty, then we ignore any files that match any
@@ -92,7 +92,7 @@ The following arguments are supported:
 * `included_files` -
   (Optional)
   ignoredFiles and includedFiles are file glob matches using http://godoc/pkg/path/filepath#Match
-  extended with support for "**".
+  extended with support for `**`.
   If any of the files altered in the commit pass the ignoredFiles filter
   and includedFiles is empty, then as far as this filter is concerned, we
   should trigger the build.
diff --git a/website/docs/r/spanner_instance.html.markdown b/website/docs/r/spanner_instance.html.markdown
index bf06008275e..ced123d3685 100644
--- a/website/docs/r/spanner_instance.html.markdown
+++ b/website/docs/r/spanner_instance.html.markdown
@@ -1,25 +1,53 @@
 ---
+# ----------------------------------------------------------------------------
+#
+#     ***     AUTO GENERATED CODE    ***    AUTO GENERATED CODE     ***
+#
+# ----------------------------------------------------------------------------
+#
+#     This file is automatically generated by Magic Modules and manual
+#     changes will be clobbered when the file is regenerated.
+#
+#     Please read more about how to change this file in
+#     .github/CONTRIBUTING.md.
+#
+# ----------------------------------------------------------------------------
 layout: "google"
 page_title: "Google: google_spanner_instance"
 sidebar_current: "docs-google-spanner-instance"
 description: |-
-  Creates and manages a Google Spanner Instance.
+  An isolated set of Cloud Spanner resources on which databases can be
+  hosted.
 ---
 
 # google\_spanner\_instance
 
-Creates and manages a Google Spanner Instance. For more information, see the [official documentation](https://cloud.google.com/spanner/), or the [JSON API](https://cloud.google.com/spanner/docs/reference/rest/v1/projects.instances).
+An isolated set of Cloud Spanner resources on which databases can be
+hosted.
 
-## Example Usage
 
-Example creating a Spanner instance.
+To get more information about Instance, see:
+
+* [API documentation](https://cloud.google.com/spanner/docs/reference/rest/v1/projects.instances)
+* How-to Guides
+    * [Official Documentation](https://cloud.google.com/spanner/)
+
+<div class = "oics-button" style="float: right; margin: 0 0 -15px">
+  <a href="https://console.cloud.google.com/cloudshell/open?cloudshell_git_repo=https%3A%2F%2Fgithub.com%2Fterraform-google-modules%2Fdocs-examples.git&cloudshell_working_dir=spanner_instance_basic&cloudshell_image=gcr.io%2Fgraphite-cloud-shell-images%2Fterraform%3Alatest&open_in_editor=main.tf&cloudshell_print=.%2Fmotd&cloudshell_tutorial=.%2Ftutorial.md" target="_blank">
+    <img alt="Open in Cloud Shell" src="//gstatic.com/cloudssh/images/open-btn.svg" style="max-height: 44px; margin: 32px auto; max-width: 100%;">
+  </a>
+</div>
+## Example Usage - Spanner Instance Basic
+
 
 ```hcl
-resource "google_spanner_instance" "main" {
-  config       = "regional-europe-west1"
-  display_name = "main-instance"
-  name         = "main-instance"
-  num_nodes    = 1
+resource "google_spanner_instance" "example" {
+  config        = "regional-us-central1"
+  display_name  = "Test Spanner Instance"
+  num_nodes     = 2
+  labels {
+    "foo" = "bar"
+  }
 }
 ```
 
@@ -27,47 +55,72 @@ resource "google_spanner_instance" "main" {
 
 The following arguments are supported:
 
-* `config` - (Required) The name of the instance's configuration (similar but not
-   quite the same as a region) which defines defines the geographic placement and
-   replication of your databases in this instance. It determines where your data
-   is stored. Values are typically of the form `regional-europe-west1` , `us-central` etc.
-   In order to obtain a valid list please consult the
-   [Configuration section of the docs](https://cloud.google.com/spanner/docs/instances).
 
-* `display_name` - (Required) The descriptive name for this instance as it appears
-   in UIs. Can be updated, however should be kept globally unique to avoid confusion.
+* `name` -
+  (Required)
+  A unique identifier for the instance, which cannot be changed after
+  the instance is created. The name must be between 6 and 30 characters
+  in length.
+
+  If not provided, a random string starting with `tf-` will be selected.
+
+* `config` -
+  (Required)
+  The name of the instance's configuration (similar but not
+  quite the same as a region) which defines defines the geographic placement and
+  replication of your databases in this instance. It determines where your data
+  is stored. Values are typically of the form `regional-europe-west1` , `us-central` etc.
+  In order to obtain a valid list please consult the
+  [Configuration section of the docs](https://cloud.google.com/spanner/docs/instances).
+
+* `display_name` -
+  (Required)
+  The descriptive name for this instance as it appears in UIs. Must be
+  unique per project and between 4 and 30 characters in length.
+
 
 - - -
 
-* `name` - (Optional, Computed) The unique name (ID) of the instance. If the name is left
-    blank, Terraform will randomly generate one when the instance is first
-    created.
 
-* `num_nodes` - (Optional, Computed) The number of nodes allocated to this instance.
-   Defaults to `1`. This can be updated after creation.
+* `num_nodes` -
+  (Optional)
+  The number of nodes allocated to this instance.
 
-* `project` - (Optional) The ID of the project in which the resource belongs. If it
-    is not provided, the provider project is used.
+* `labels` -
+  (Optional)
+  An object containing a list of "key": value pairs.
+  Example: { "name": "wrench", "mass": "1.3kg", "count": "3" }.
+* `project` - (Optional) The ID of the project in which the resource belongs.
+    If it is not provided, the provider project is used.
 
-* `labels` - (Optional) A mapping (key/value pairs) of labels to assign to the instance.
 
 ## Attributes Reference
 
-In addition to the arguments listed above, the following computed attributes are
-exported:
+In addition to the arguments listed above, the following computed attributes are exported:
 
-* `state` - The current state of the instance.
 
-## Import
+* `state` -
+  Instance status: `CREATING` or `READY`.
 
-Instances can be imported using their `name` and optionally
-the `project` in which it is defined (Often used when the project is different
-to that defined in the provider), The format is thus either `{instanceId}` or
-`{projectId}/{instanceId}`. e.g.
 
-```
-$ terraform import google_spanner_instance.master instance123
+## Timeouts
+
+This resource provides the following
+[Timeouts](/docs/configuration/resources.html#timeouts) configuration options:
 
-$ terraform import google_spanner_instance.master project123/instance456
+- `create` - Default is 4 minutes.
+- `update` - Default is 4 minutes.
+- `delete` - Default is 4 minutes.
+
+## Import
 
+Instance can be imported using any of these accepted formats:
+
+```
+$ terraform import google_spanner_instance.default projects/{{project}}/instances/{{name}}
+$ terraform import google_spanner_instance.default {{project}}/{{name}}
+$ terraform import google_spanner_instance.default {{name}}
 ```
+
+-> If you're importing a resource with beta features, make sure to include `-provider=google-beta`
+as an argument so that Terraform uses the correct provider to import your resource.