Repository: GoogleCloudPlatform/terraform-validator
Pull request #1342 (2023-02-03 18:46:10)

Add manual converter and tests for service_account
<!-- Put a description of what this PR is for here, along with any references to issues that this resolves or contributes to -->

Add google_service_account for terraform validator
The converter is not existing and has to be created manually. Also, adding information into the converter list and add test cases.

Referring to https://github.com/GoogleCloudPlatform/terraform-validator/issues/1290

<!--
Replace each [ ] with [X] to check it. Switch to the preview view to make it easier to click on links.
These steps will speed up the review process, and we appreciate you spending time on them before sending
your code to be reviewed.
-->
If this PR is for Terraform, I acknowledge that I have:

- [ ] Searched through the [issue tracker](https://github.com/hashicorp/terraform-provider-google/issues) for an open issue that this either resolves or contributes to, commented on it to claim it, and written "fixes {url}" or "part of {url}" in this PR description. If there were no relevant open issues, I opened one and commented that I would like to work on it (not necessary for very small changes).
- [ ] [Generated Terraform](https://github.com/GoogleCloudPlatform/magic-modules#generating-the-terraform-providers), and ran `make test` and `make lint` to ensure it passes unit and linter tests.
- [X] Ensured that all new fields I added that can be set by a user appear in at least one [example](https://github.com/GoogleCloudPlatform/magic-modules/tree/main/mmv1/templates/terraform/examples) (for generated resources) or [third_party test](https://github.com/GoogleCloudPlatform/magic-modules/tree/main/mmv1/third_party/terraform/tests) (for handwritten resources or update tests).
- [ ] [Ran](https://github.com/hashicorp/terraform-provider-google/blob/main/.github/CONTRIBUTING.md#tests) relevant acceptance tests (If the acceptance tests do not yet pass or you are unable to run them, please let your reviewer know).
- [X] Read the [Release Notes Guide](https://github.com/GoogleCloudPlatform/magic-modules/blob/main/.ci/RELEASE_NOTES_GUIDE.md) before writing my release note below.

<!-- AUTOCHANGELOG for Downstream PRs.

Please select one of the following "release-note:" headings:
    - release-note:enhancement
    - release-note:bug
    - release-note:note
    - release-note:new-resource
    - release-note:new-datasource
    - release-note:deprecation
    - release-note:breaking-change
    - release-note:none
    
Unless you choose release-note:none, please add a release note.

See .ci/RELEASE_NOTES_GUIDE.md for writing good release notes.

You can add more release note blocks if you want more than one CHANGELOG
entry for this PR.
-->
**Release Note Template for Downstream PRs (will be copied)**

```release-note:none
```

Derived from https://github.com/GoogleCloudPlatform/magic-modules/pull/7146


Patch:
diff --git a/converters/google/resources/resource_converters.go b/converters/google/resources/resource_converters.go
index 68a9c3be..c0b21079 100644
--- a/converters/google/resources/resource_converters.go
+++ b/converters/google/resources/resource_converters.go
@@ -252,5 +252,6 @@ func ResourceConverters() map[string][]ResourceConverter {
 		"google_organization_iam_custom_role": {resourceConverterOrganizationIAMCustomRole()},
 		"google_vpc_access_connector":         {resourceConverterVPCAccessConnector()},
 		"google_logging_metric":               {resourceConverterLoggingMetric()},
+		"google_service_account":              {resourceConverterServiceAccount()},
 	}
 }
diff --git a/converters/google/resources/service_account.go b/converters/google/resources/service_account.go
new file mode 100644
index 00000000..5e7e92cd
--- /dev/null
+++ b/converters/google/resources/service_account.go
@@ -0,0 +1,138 @@
+package google
+
+import (
+	"fmt"
+	"reflect"
+)
+
+const ServiceAccountAssetType string = "iam.googleapis.com/ServiceAccount"
+
+func resourceConverterServiceAccount() ResourceConverter {
+	return ResourceConverter{
+		AssetType: ServiceAccountAssetType,
+		Convert:   GetServiceAccountCaiObject,
+	}
+}
+
+func GetServiceAccountCaiObject(d TerraformResourceData, config *Config) ([]Asset, error) {
+	name, err := assetName(d, config, "//iam.googleapis.com/projects/{{project}}/serviceAccounts/{{unique_id}}")
+	if err != nil {
+		return []Asset{}, err
+	}
+	if obj, err := GetServiceAccountApiObject(d, config); err == nil {
+		return []Asset{{
+			Name: name,
+			Type: ServiceAccountAssetType,
+			Resource: &AssetResource{
+				Version:              "v1",
+				DiscoveryDocumentURI: "https://iam.googleapis.com/$discovery/rest",
+				DiscoveryName:        "ServiceAccount",
+				Data:                 obj,
+			},
+		}}, nil
+	} else {
+		return []Asset{}, err
+	}
+}
+
+func GetServiceAccountApiObject(d TerraformResourceData, config *Config) (map[string]interface{}, error) {
+	obj := make(map[string]interface{})
+
+	project, err := getProject(d, config)
+	if err != nil {
+		return nil, err
+	}
+
+	descriptionProp, err := expandServiceAccountDescription(d.Get("description"), d, config)
+	if err != nil {
+		return nil, err
+	} else if v, ok := d.GetOkExists("description"); !isEmptyValue(reflect.ValueOf(descriptionProp)) && (ok || !reflect.DeepEqual(v, descriptionProp)) {
+		obj["description"] = descriptionProp
+	}
+
+	emailProp, err := expandServiceAccountDescription(d.Get("email"), d, config)
+	if err != nil {
+		return nil, err
+	} else if v, ok := d.GetOkExists("email"); !isEmptyValue(reflect.ValueOf(emailProp)) && (ok || !reflect.DeepEqual(v, emailProp)) {
+		obj["email"] = emailProp
+	}
+
+	displayNameProp, err := expandServiceAccountDisplayName(d.Get("display_name"), d, config)
+	if err != nil {
+		return nil, err
+	} else if v, ok := d.GetOkExists("display_name"); !isEmptyValue(reflect.ValueOf(displayNameProp)) && (ok || !reflect.DeepEqual(v, displayNameProp)) {
+		obj["displayName"] = displayNameProp
+	}
+
+	nameProp, err := expandServiceAccountName(d.Get("name"), d, config)
+	if err != nil {
+		return nil, err
+	} else if v, ok := d.GetOkExists("name"); !isEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) {
+		obj["name"] = nameProp
+	}
+
+	disabledProp, err := expandServiceAccountDisabled(d.Get("disabled"), d, config)
+	if err != nil {
+		return nil, err
+	} else if v, ok := d.GetOkExists("disabled"); !isEmptyValue(reflect.ValueOf(disabledProp)) && (ok || !reflect.DeepEqual(v, disabledProp)) {
+		obj["disabled"] = disabledProp
+	}
+
+	uniqueIdProp, err := expandServiceAccountUniqueId(d.Get("unique_id"), d, config)
+	if err != nil {
+		return nil, err
+	} else if v, ok := d.GetOkExists("unique_id"); !isEmptyValue(reflect.ValueOf(uniqueIdProp)) && (ok || !reflect.DeepEqual(v, uniqueIdProp)) {
+		obj["uniqueId"] = uniqueIdProp
+	}
+
+	projectProp, err := expandServiceAccountProject(d.Get("project"), d, config)
+	if err != nil {
+		return nil, err
+	} else if v, ok := d.GetOkExists("project"); !isEmptyValue(reflect.ValueOf(projectProp)) && (ok || !reflect.DeepEqual(v, projectProp)) {
+		obj["projectId"] = projectProp
+	}
+
+	accountIdProp, err := expandServiceAccountId(d.Get("account_id"), d, config)
+	if err != nil {
+		return nil, err
+	} else if v, ok := d.GetOkExists("account_id"); !isEmptyValue(reflect.ValueOf(accountIdProp)) && (ok || !reflect.DeepEqual(v, accountIdProp)) {
+		accountId := accountIdProp
+		if _, ok := obj["email"]; !ok {
+			// Generating email when the service account is being created (email not present)
+			obj["email"] = fmt.Sprintf("%s@%s.iam.gserviceaccount.com", accountId, project)
+		}
+	}
+	return obj, nil
+}
+
+func expandServiceAccountId(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
+	return v, nil
+}
+
+func expandServiceAccountDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
+	return v, nil
+}
+
+func expandServiceAccountDisplayName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
+	return v, nil
+}
+
+func expandServiceAccountName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
+	return v, nil
+}
+
+func expandServiceAccountEmail(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
+	return v, nil
+}
+
+func expandServiceAccountDisabled(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
+	return v, nil
+}
+
+func expandServiceAccountUniqueId(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
+	return v, nil
+}
+
+func expandServiceAccountProject(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
+	return v, nil
+}


Test patch:
diff --git a/test/cli_test.go b/test/cli_test.go
index 660caaf5..d3a03232 100644
--- a/test/cli_test.go
+++ b/test/cli_test.go
@@ -142,6 +142,8 @@ func TestCLI(t *testing.T) {
 		{name: "example_secret_manager_secret_iam_binding"},
 		{name: "example_secret_manager_secret_iam_member"},
 		{name: "example_secret_manager_secret_iam_policy"},
+		{name: "example_service_account"},
+		{name: "example_service_account_update"},
 		{name: "example_spanner_database"},
 		{name: "example_spanner_database_iam_binding"},
 		{name: "example_spanner_database_iam_member"},
diff --git a/test/read_test.go b/test/read_test.go
index 4e46f2bb..0c5a43ff 100644
--- a/test/read_test.go
+++ b/test/read_test.go
@@ -104,6 +104,8 @@ func TestReadPlannedAssetsCoverage(t *testing.T) {
 		{name: "example_secret_manager_secret_iam_binding"},
 		{name: "example_secret_manager_secret_iam_member"},
 		{name: "example_secret_manager_secret_iam_policy"},
+		{name: "example_service_account"},
+		{name: "example_service_account_update"},
 		{name: "example_spanner_database"},
 		{name: "example_spanner_database_iam_binding"},
 		{name: "example_spanner_database_iam_member"},
diff --git a/testdata/templates/example_compute_instance.json b/testdata/templates/example_compute_instance.json
index 9d76795f..0f07c68c 100644
--- a/testdata/templates/example_compute_instance.json
+++ b/testdata/templates/example_compute_instance.json
@@ -68,5 +68,20 @@
         }
       }
     }
+  },
+  {
+    "name": "//iam.googleapis.com/projects/{{.Provider.project}}/serviceAccounts/placeholder-unique-id",
+    "asset_type": "iam.googleapis.com/ServiceAccount",
+    "ancestry_path": "{{.Ancestry}}/project/{{.Provider.project}}",
+    "resource": {
+      "version": "v1",
+      "discovery_document_uri": "https://iam.googleapis.com/$discovery/rest",
+      "discovery_name": "ServiceAccount",
+      "parent": "//cloudresourcemanager.googleapis.com/projects/{{.Provider.project}}",
+      "data": {
+        "displayName": "Service Account",
+        "email": "service-account-id@{{.Provider.project}}.iam.gserviceaccount.com"
+      }
+    }
   }
 ]
diff --git a/testdata/templates/example_container_cluster.json b/testdata/templates/example_container_cluster.json
index de33241f..68b75fd4 100644
--- a/testdata/templates/example_container_cluster.json
+++ b/testdata/templates/example_container_cluster.json
@@ -41,5 +41,20 @@
         "name": "my-node-pool"
       }
     }
+  },
+  {
+    "name": "//iam.googleapis.com/projects/{{.Provider.project}}/serviceAccounts/placeholder-unique-id",
+    "asset_type": "iam.googleapis.com/ServiceAccount",
+    "ancestry_path": "{{.Ancestry}}/project/{{.Provider.project}}",
+    "resource": {
+      "version": "v1",
+      "discovery_document_uri": "https://iam.googleapis.com/$discovery/rest",
+      "discovery_name": "ServiceAccount",
+      "parent": "//cloudresourcemanager.googleapis.com/projects/{{.Provider.project}}",
+      "data": {
+        "displayName": "Service Account",
+        "email": "service-account-id@{{.Provider.project}}.iam.gserviceaccount.com"
+      }
+    }
   }
 ]
diff --git a/testdata/templates/example_service_account.json b/testdata/templates/example_service_account.json
new file mode 100644
index 00000000..91d56691
--- /dev/null
+++ b/testdata/templates/example_service_account.json
@@ -0,0 +1,18 @@
+[
+  {
+    "name": "//iam.googleapis.com/projects/{{.Provider.project}}/serviceAccounts/placeholder-unique-id",
+    "asset_type": "iam.googleapis.com/ServiceAccount",
+    "ancestry_path": "{{.Ancestry}}/project/{{.Provider.project}}",
+    "resource": {
+      "version": "v1",
+      "discovery_document_uri": "https://iam.googleapis.com/$discovery/rest",
+      "discovery_name": "ServiceAccount",
+      "parent": "//cloudresourcemanager.googleapis.com/projects/{{.Provider.project}}",
+      "data": {
+        "displayName": "Service Account",
+        "email": "service-account-id@{{.Provider.project}}.iam.gserviceaccount.com",
+        "description": "Service Account Description"
+      }
+    }
+  }  
+]
\ No newline at end of file
diff --git a/testdata/templates/example_service_account.tf b/testdata/templates/example_service_account.tf
new file mode 100644
index 00000000..e8009b7c
--- /dev/null
+++ b/testdata/templates/example_service_account.tf
@@ -0,0 +1,35 @@
+/**
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+terraform {
+  required_providers {
+    google = {
+      source = "hashicorp/google"
+      version = "~> {{.Provider.version}}"
+    }
+  }
+}
+
+provider "google" {
+  {{if .Provider.credentials }}credentials = "{{.Provider.credentials}}"{{end}}
+}
+
+resource "google_service_account" "service_account" {
+  account_id   = "service-account-id"
+  display_name = "Service Account"
+  description  = "Service Account Description"
+  disabled     = false
+}
\ No newline at end of file
diff --git a/testdata/templates/example_service_account.tfplan.json b/testdata/templates/example_service_account.tfplan.json
new file mode 100644
index 00000000..943ab619
--- /dev/null
+++ b/testdata/templates/example_service_account.tfplan.json
@@ -0,0 +1,100 @@
+{
+  "format_version": "1.1",
+  "terraform_version": "1.3.6",
+  "planned_values": {
+    "root_module": {
+      "resources": [
+        {
+          "address": "google_service_account.service_account",
+          "mode": "managed",
+          "type": "google_service_account",
+          "name": "service_account",
+          "provider_name": "registry.terraform.io/hashicorp/google",
+          "schema_version": 0,
+          "values": {
+            "account_id": "service-account-id",
+            "description": "Service Account Description",
+            "disabled": false,
+            "display_name": "Service Account",
+            "timeouts": null
+          },
+          "sensitive_values": {}
+        }
+      ]
+    }
+  },
+  "resource_changes": [
+    {
+      "address": "google_service_account.service_account",
+      "mode": "managed",
+      "type": "google_service_account",
+      "name": "service_account",
+      "provider_name": "registry.terraform.io/hashicorp/google",
+      "change": {
+        "actions": [
+          "create"
+        ],
+        "before": null,
+        "after": {
+          "account_id": "service-account-id",
+          "description": "Service Account Description",
+          "disabled": false,
+          "display_name": "Service Account",
+          "timeouts": null
+        },
+        "after_unknown": {
+          "email": true,
+          "id": true,
+          "member": true,
+          "name": true,
+          "project": true,
+          "unique_id": true
+        },
+        "before_sensitive": false,
+        "after_sensitive": {}
+      }
+    }
+  ],
+  "configuration": {
+    "provider_config": {
+      "google": {
+        "name": "google",
+        "full_name": "registry.terraform.io/hashicorp/google",
+        "expressions": {
+          "project": {
+            "constant_value": "{{.Provider.project}}"
+          },
+          "region": {
+            "constant_value": "asia-southeast-1"
+          }
+        }
+      }
+    },
+    "root_module": {
+      "resources": [
+        {
+          "address": "google_service_account.service_account",
+          "mode": "managed",
+          "type": "google_service_account",
+          "name": "service_account",
+          "provider_config_key": "google",
+          "expressions": {
+            "account_id": {
+              "constant_value": "service-account-id"
+            },
+            "description": {
+              "constant_value": "Service Account Description"
+            },
+            "disabled": {
+              "constant_value": false
+            },
+            "display_name": {
+              "constant_value": "Service Account"
+            }
+          },
+          "schema_version": 0
+        }
+      ]
+    }
+  }
+}
\ No newline at end of file
diff --git a/testdata/templates/example_service_account_update.json b/testdata/templates/example_service_account_update.json
new file mode 100644
index 00000000..68dc5d42
--- /dev/null
+++ b/testdata/templates/example_service_account_update.json
@@ -0,0 +1,21 @@
+[
+  {
+    "name": "//iam.googleapis.com/projects/{{.Provider.project}}/serviceAccounts/108592438577779299646",
+    "asset_type": "iam.googleapis.com/ServiceAccount",
+    "ancestry_path": "{{.Ancestry}}/project/{{.Provider.project}}",
+    "resource": {
+      "version": "v1",
+      "discovery_document_uri": "https://iam.googleapis.com/$discovery/rest",
+      "discovery_name": "ServiceAccount",
+      "parent": "//cloudresourcemanager.googleapis.com/projects/{{.Provider.project}}",
+      "data": {
+        "displayName": "Service Account",
+        "email": "service-account-id@{{.Provider.project}}.iam.gserviceaccount.com",
+        "description": "Service Account Description",
+        "name": "projects/{{.Provider.project}}/serviceAccounts/service-account-id@{{.Provider.project}}.iam.gserviceaccount.com",
+        "projectId": "{{.Provider.project}}",
+        "uniqueId": "108592438577779299646"
+      }
+    }
+  }  
+]
\ No newline at end of file
diff --git a/testdata/templates/example_service_account_update.tf b/testdata/templates/example_service_account_update.tf
new file mode 100644
index 00000000..f2bcec25
--- /dev/null
+++ b/testdata/templates/example_service_account_update.tf
@@ -0,0 +1,35 @@
+/**
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+terraform {
+  required_providers {
+    google = {
+      source = "hashicorp/google"
+      version = "~> {{.Provider.version}}"
+    }
+  }
+}
+
+provider "google" {
+  {{if .Provider.credentials }}credentials = "{{.Provider.credentials}}"{{end}}
+}
+
+resource "google_service_account" "service_account" {
+  account_id   = "service-account-id"
+  display_name = "Service Account"
+  description  = "Service Account Description"
+  disabled     = false
+}
diff --git a/testdata/templates/example_service_account_update.tfplan.json b/testdata/templates/example_service_account_update.tfplan.json
new file mode 100644
index 00000000..42adfd13
--- /dev/null
+++ b/testdata/templates/example_service_account_update.tfplan.json
@@ -0,0 +1,140 @@
+{
+  "format_version": "0.1",
+  "terraform_version": "0.12.31",
+  "planned_values": {
+    "root_module": {
+      "resources": [
+        {
+          "address": "google_service_account.service_account",
+          "mode": "managed",
+          "type": "google_service_account",
+          "name": "service_account",
+          "provider_name": "google",
+          "schema_version": 0,
+          "values": {
+            "account_id": "service-account-id",
+            "description": "Service Account Description",
+            "disabled": false,
+            "display_name": "Service Account",
+            "email": "service-account-id@{{.Provider.project}}.iam.gserviceaccount.com",
+            "id": "projects/{{.Provider.project}}/serviceAccounts/service-account-id@{{.Provider.project}}.iam.gserviceaccount.com",
+            "name": "projects/{{.Provider.project}}/serviceAccounts/service-account-id@{{.Provider.project}}.iam.gserviceaccount.com",
+            "project": "{{.Provider.project}}",
+            "timeouts": null,
+            "unique_id": "108592438577779299646"
+          }
+        }
+      ]
+    }
+  },
+  "resource_changes": [
+    {
+      "address": "google_service_account.service_account",
+      "mode": "managed",
+      "type": "google_service_account",
+      "name": "service_account",
+      "provider_name": "google",
+      "change": {
+        "actions": [
+          "update"
+        ],
+        "before": {
+          "account_id": "service-account-id",
+          "description": "Service Account Description",
+          "disabled": false,
+          "display_name": "Service Account 1",
+          "email": "service-account-id@{{.Provider.project}}.iam.gserviceaccount.com",
+          "id": "projects/{{.Provider.project}}/serviceAccounts/service-account-id@{{.Provider.project}}.iam.gserviceaccount.com",
+          "name": "projects/{{.Provider.project}}/serviceAccounts/service-account-id@{{.Provider.project}}.iam.gserviceaccount.com",
+          "project": "{{.Provider.project}}",
+          "timeouts": null,
+          "unique_id": "108592438577779299646"
+        },
+        "after": {
+          "account_id": "service-account-id",
+          "description": "Service Account Description",
+          "disabled": false,
+          "display_name": "Service Account",
+          "email": "service-account-id@{{.Provider.project}}.iam.gserviceaccount.com",
+          "id": "projects/{{.Provider.project}}/serviceAccounts/service-account-id@{{.Provider.project}}.iam.gserviceaccount.com",
+          "name": "projects/{{.Provider.project}}/serviceAccounts/service-account-id@{{.Provider.project}}.iam.gserviceaccount.com",
+          "project": "{{.Provider.project}}",
+          "timeouts": null,
+          "unique_id": "108592438577779299646"
+        },
+        "after_unknown": {}
+      }
+    }
+  ],
+  "prior_state": {
+    "format_version": "0.1",
+    "terraform_version": "0.12.31",
+    "values": {
+      "root_module": {
+        "resources": [
+          {
+            "address": "google_service_account.service_account",
+            "mode": "managed",
+            "type": "google_service_account",
+            "name": "service_account",
+            "provider_name": "google",
+            "schema_version": 0,
+            "values": {
+              "account_id": "service-account-id",
+              "description": "Service Account Description",
+              "disabled": false,
+              "display_name": "Service Account 1",
+              "email": "service-account-id@{{.Provider.project}}.iam.gserviceaccount.com",
+              "id": "projects/{{.Provider.project}}/serviceAccounts/service-account-id@{{.Provider.project}}.iam.gserviceaccount.com",
+              "name": "projects/{{.Provider.project}}/serviceAccounts/service-account-id@{{.Provider.project}}.iam.gserviceaccount.com",
+              "project": "{{.Provider.project}}",
+              "timeouts": null,
+              "unique_id": "108592438577779299646"
+            }
+          }
+        ]
+      }
+    }
+  },
+  "configuration": {
+    "provider_config": {
+      "google": {
+        "name": "google",
+        "expressions": {
+          "project": {
+            "constant_value": "{{.Provider.project}}"
+          },
+          "region": {
+            "constant_value": "asia-southeast-1"
+          }
+        }
+      }
+    },
+    "root_module": {
+      "resources": [
+        {
+          "address": "google_service_account.service_account",
+          "mode": "managed",
+          "type": "google_service_account",
+          "name": "service_account",
+          "provider_config_key": "google",
+          "expressions": {
+            "account_id": {
+              "constant_value": "service-account-id"
+            },
+            "description": {
+              "constant_value": "Service Account Description"
+            },
+            "disabled": {
+              "constant_value": false
+            },
+            "display_name": {
+              "constant_value": "Service Account"
+            }
+          },
+          "schema_version": 0
+        }
+      ]
+    }
+  }
+}
\ No newline at end of file
diff --git a/testdata/templates/example_service_account_update.tfstate b/testdata/templates/example_service_account_update.tfstate
new file mode 100644
index 00000000..1b351b32
--- /dev/null
+++ b/testdata/templates/example_service_account_update.tfstate
@@ -0,0 +1,33 @@
+{
+  "version": 4,
+  "terraform_version": "0.12.31",
+  "serial": 1,
+  "lineage": "753c5d7c-1d38-33ba-9fe2-740244c61234",
+  "outputs": {},
+  "resources": [
+    {
+      "mode": "managed",
+      "type": "google_service_account",
+      "name": "service_account",
+      "provider": "provider.google",
+      "instances": [
+        {
+          "schema_version": 0,
+          "attributes": {
+            "account_id": "service-account-id",
+            "description": "Service Account Description",
+            "disabled": false,
+            "display_name": "Service Account 1",
+            "email": "service-account-id@{{.Provider.project}}.iam.gserviceaccount.com",
+            "id": "projects/{{.Provider.project}}/serviceAccounts/service-account-id@{{.Provider.project}}.iam.gserviceaccount.com",
+            "name": "projects/{{.Provider.project}}/serviceAccounts/service-account-id@{{.Provider.project}}.iam.gserviceaccount.com",
+            "project": "{{.Provider.project}}",
+            "timeouts": null,
+            "unique_id": "108592438577779299646"
+          },
+          "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMDB9fQ=="
+        }
+      ]
+    }
+  ]
+}