diff --git a/mmv1/third_party/terraform/data_sources/data_source_google_compute_instance_template.go.erb b/mmv1/third_party/terraform/data_sources/data_source_google_compute_instance_template.go.erb index bb97048dbfc9..f8de80f62aec 100644 --- a/mmv1/third_party/terraform/data_sources/data_source_google_compute_instance_template.go.erb +++ b/mmv1/third_party/terraform/data_sources/data_source_google_compute_instance_template.go.erb @@ -22,16 +22,22 @@ func DataSourceGoogleComputeInstanceTemplate() *schema.Resource { Type: schema.TypeString, Optional: true, } + dsSchema["self_link_unique"] = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + } dsSchema["most_recent"] = &schema.Schema{ Type: schema.TypeBool, Optional: true, } // Set 'Optional' schema elements - addOptionalFieldsToSchema(dsSchema, "name", "filter", "most_recent", "project") + addOptionalFieldsToSchema(dsSchema, "name", "filter", "most_recent", "project", "self_link_unique") - dsSchema["name"].ExactlyOneOf = []string{"name", "filter"} - dsSchema["filter"].ExactlyOneOf = []string{"name", "filter"} + mutuallyExclusive:= []string{"name", "filter", "self_link_unique"} + for _, n:= range mutuallyExclusive { + dsSchema[n].ExactlyOneOf = mutuallyExclusive + } return &schema.Resource{ Read: datasourceComputeInstanceTemplateRead, @@ -73,8 +79,11 @@ func datasourceComputeInstanceTemplateRead(d *schema.ResourceData, meta interfac return fmt.Errorf("your filter has returned %d instance template(s). Please refine your filter or set most_recent to return exactly one instance template", len(templates.Items)) } + if v, ok := d.GetOk("self_link_unique"); ok { + return retrieveInstanceFromUniqueId(d, meta, project, v.(string)) + } - return fmt.Errorf("one of name or filters must be set") + return fmt.Errorf("one of name, filters or self_link_unique must be set") } func retrieveInstance(d *schema.ResourceData, meta interface{}, project, name string) error { @@ -83,6 +92,14 @@ func retrieveInstance(d *schema.ResourceData, meta interface{}, project, name st return resourceComputeInstanceTemplateRead(d, meta) } +func retrieveInstanceFromUniqueId(d *schema.ResourceData, meta interface{}, project, self_link_unique string) error { + normalId, _ := parseUniqueId(self_link_unique) + d.SetId(normalId) + d.Set("self_link_unique", self_link_unique) + + return resourceComputeInstanceTemplateRead(d, meta) +} + // ByCreationTimestamp implements sort.Interface for []*InstanceTemplate based on // the CreationTimestamp field. type ByCreationTimestamp []*compute.InstanceTemplate diff --git a/mmv1/third_party/terraform/resources/resource_compute_instance_from_template.go.erb b/mmv1/third_party/terraform/resources/resource_compute_instance_from_template.go.erb index dfaf9b6becd1..6663e717f7d4 100644 --- a/mmv1/third_party/terraform/resources/resource_compute_instance_from_template.go.erb +++ b/mmv1/third_party/terraform/resources/resource_compute_instance_from_template.go.erb @@ -123,8 +123,7 @@ func resourceComputeInstanceFromTemplateCreate(d *schema.ResourceData, meta inte return err } - sourceInstanceTemplate := d.Get("source_instance_template").(string) - + sourceInstanceTemplate:= ConvertToUniqueIdWhenPresent(d.Get("source_instance_template").(string)) tpl, err := ParseInstanceTemplateFieldValue(sourceInstanceTemplate, d, config) if err != nil { return err diff --git a/mmv1/third_party/terraform/resources/resource_compute_instance_group_manager.go.erb b/mmv1/third_party/terraform/resources/resource_compute_instance_group_manager.go.erb index 39eb4f2f767c..76fcd3e16aab 100644 --- a/mmv1/third_party/terraform/resources/resource_compute_instance_group_manager.go.erb +++ b/mmv1/third_party/terraform/resources/resource_compute_instance_group_manager.go.erb @@ -55,7 +55,7 @@ func ResourceComputeInstanceGroupManager() *schema.Resource { "instance_template": { Type: schema.TypeString, Required: true, - DiffSuppressFunc: compareSelfLinkRelativePaths, + DiffSuppressFunc: compareSelfLinkRelativePathsIgnoreParams, Description: `The full URL to an instance template from which all new instances of this version will be created.`, }, @@ -495,6 +495,33 @@ func ResourceComputeInstanceGroupManager() *schema.Resource { } } +func parseUniqueId(s string) (string, string) { + splits:= strings.SplitN(s, "?uniqueId=", 2) + if len(splits) == 2 { + return splits[0], splits[1] + } + return s, "" +} + +func compareSelfLinkRelativePathsIgnoreParams(_unused1, old, new string, _unused2 *schema.ResourceData) bool { + oldName, oldUniqueId:= parseUniqueId(old) + newName, newUniqueId:= parseUniqueId(new) + if oldUniqueId != "" && newUniqueId != "" && oldUniqueId != newUniqueId { + return false + } + return compareSelfLinkRelativePaths(_unused1, oldName, newName, _unused2) +} + +func ConvertToUniqueIdWhenPresent(s string) string { + original, uniqueId:= parseUniqueId(s) + if uniqueId != "" { + splits:= strings.Split(original, "/") + splits[len(splits)-1] = uniqueId + return strings.Join(splits, "/") + } + return s +} + func getNamedPorts(nps []interface{}) []*compute.NamedPort { namedPorts := make([]*compute.NamedPort, 0, len(nps)) for _, v := range nps { @@ -1160,7 +1187,7 @@ func expandVersions(configured []interface{}) []*compute.InstanceGroupManagerVer version := compute.InstanceGroupManagerVersion{ Name: data["name"].(string), - InstanceTemplate: data["instance_template"].(string), + InstanceTemplate: ConvertToUniqueIdWhenPresent(data["instance_template"].(string)), TargetSize: expandFixedOrPercent(data["target_size"].([]interface{})), } diff --git a/mmv1/third_party/terraform/resources/resource_compute_instance_template.go.erb b/mmv1/third_party/terraform/resources/resource_compute_instance_template.go.erb index 5396ad7a0f0a..5b470c77e965 100644 --- a/mmv1/third_party/terraform/resources/resource_compute_instance_template.go.erb +++ b/mmv1/third_party/terraform/resources/resource_compute_instance_template.go.erb @@ -670,6 +670,12 @@ be from 0 to 999,999,999 inclusive.`, Description: `The URI of the created resource.`, }, + "self_link_unique": { + Type: schema.TypeString, + Computed: true, + Description: `A special URI of the created resource that uniquely identifies this instance template.`, + }, + "service_account": { Type: schema.TypeList, MaxItems: 1, @@ -1256,6 +1262,8 @@ func resourceComputeInstanceTemplateCreate(d *schema.ResourceData, meta interfac // Store the ID now d.SetId(fmt.Sprintf("projects/%s/global/instanceTemplates/%s", project, instanceTemplate.Name)) + // And also the unique ID + d.Set("self_link_unique", fmt.Sprintf("%v?uniqueId=%v", d.Id(), op.TargetId)) err = ComputeOperationWaitTime(config, op, project, "Creating Instance Template", userAgent, d.Timeout(schema.TimeoutCreate)) if err != nil { @@ -1498,12 +1506,16 @@ func resourceComputeInstanceTemplateRead(d *schema.ResourceData, meta interface{ return err } - splits := strings.Split(d.Id(), "/") + idStr := d.Id() + if v, ok := d.GetOk("self_link_unique"); ok && v != "" { + idStr = ConvertToUniqueIdWhenPresent(v.(string)) + } + + splits := strings.Split(idStr, "/") instanceTemplate, err := config.NewComputeClient(userAgent).InstanceTemplates.Get(project, splits[len(splits)-1]).Do() if err != nil { return handleNotFoundError(err, d, fmt.Sprintf("Instance Template %q", d.Get("name").(string))) } - // Set the metadata fingerprint if there is one. if instanceTemplate.Properties.Metadata != nil { if err = d.Set("metadata_fingerprint", instanceTemplate.Properties.Metadata.Fingerprint); err != nil { @@ -1545,6 +1557,9 @@ func resourceComputeInstanceTemplateRead(d *schema.ResourceData, meta interface{ if err = d.Set("self_link", instanceTemplate.SelfLink); err != nil { return fmt.Errorf("Error setting self_link: %s", err) } + if err = d.Set("self_link_unique", fmt.Sprintf("%v?uniqueId=%v", instanceTemplate.SelfLink, instanceTemplate.Id)); err != nil { + return fmt.Errorf("Error setting self_link_unique: %s", err) + } if err = d.Set("name", instanceTemplate.Name); err != nil { return fmt.Errorf("Error setting name: %s", err) } diff --git a/mmv1/third_party/terraform/resources/resource_compute_region_instance_group_manager.go.erb b/mmv1/third_party/terraform/resources/resource_compute_region_instance_group_manager.go.erb index d5a622749bda..86b98d1f441f 100644 --- a/mmv1/third_party/terraform/resources/resource_compute_region_instance_group_manager.go.erb +++ b/mmv1/third_party/terraform/resources/resource_compute_region_instance_group_manager.go.erb @@ -56,7 +56,7 @@ func ResourceComputeRegionInstanceGroupManager() *schema.Resource { "instance_template": { Type: schema.TypeString, Required: true, - DiffSuppressFunc: compareSelfLinkRelativePaths, + DiffSuppressFunc: compareSelfLinkRelativePathsIgnoreParams, Description: `The full URL to an instance template from which all new instances of this version will be created.`, }, diff --git a/mmv1/third_party/terraform/tests/data_source_google_compute_instance_template_test.go b/mmv1/third_party/terraform/tests/data_source_google_compute_instance_template_test.go index 391ebc02eb5b..6b2dbcf5687a 100644 --- a/mmv1/third_party/terraform/tests/data_source_google_compute_instance_template_test.go +++ b/mmv1/third_party/terraform/tests/data_source_google_compute_instance_template_test.go @@ -69,6 +69,31 @@ func TestAccInstanceTemplateDatasource_filter_mostRecent(t *testing.T) { }) } +func TestAccInstanceTemplateDatasource_self_link_unique(t *testing.T) { + t.Parallel() + + VcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccInstanceTemplate_self_link_unique(GetTestProjectFromEnv(), RandString(t, 10)), + Check: resource.ComposeTestCheckFunc( + checkDataSourceStateMatchesResourceStateWithIgnores( + "data.google_compute_instance_template.default", + "google_compute_instance_template.default", + // we don't compare the id here as we start this test from a self_link_unique url + // and the resource's ID will have the standard format project/projectname/global/instanceTemplates/tf-test-template-random + map[string]struct{}{ + "id": {}, + }, + ), + ), + }, + }, + }) +} + func testAccInstanceTemplate_name(project, suffix string) string { return Nprintf(` resource "google_compute_instance_template" "default" { @@ -238,3 +263,31 @@ data "google_compute_instance_template" "default" { } `, map[string]interface{}{"project": project, "suffix": suffix}) } + +func testAccInstanceTemplate_self_link_unique(project, suffix string) string { + return Nprintf(` +resource "google_compute_instance_template" "default" { + name = "tf-test-template-%{suffix}" + description = "Example template." + + machine_type = "e2-small" + + tags = ["foo", "bar"] + + disk { + source_image = "cos-cloud/cos-stable" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } +} + +data "google_compute_instance_template" "default" { + project = "%{project}" + self_link_unique = google_compute_instance_template.default.self_link_unique +} +`, map[string]interface{}{"project": project, "suffix": suffix}) +} diff --git a/mmv1/third_party/terraform/tests/resource_compute_instance_from_template_test.go.erb b/mmv1/third_party/terraform/tests/resource_compute_instance_from_template_test.go.erb index 16d5cbc337da..f6d2e7e9bf0a 100644 --- a/mmv1/third_party/terraform/tests/resource_compute_instance_from_template_test.go.erb +++ b/mmv1/third_party/terraform/tests/resource_compute_instance_from_template_test.go.erb @@ -44,6 +44,34 @@ func TestAccComputeInstanceFromTemplate_basic(t *testing.T) { }) } +func TestAccComputeInstanceFromTemplate_self_link_unique(t *testing.T) { + t.Parallel() + + var instance compute.Instance + instanceName := fmt.Sprintf("tf-test-%s", RandString(t, 10)) + templateName := fmt.Sprintf("tf-test-%s", RandString(t, 10)) + resourceName := "google_compute_instance_from_template.foobar" + + VcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: TestAccProviders, + CheckDestroy: testAccCheckComputeInstanceFromTemplateDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeInstanceFromTemplate_self_link_unique(instanceName, templateName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists(t, resourceName, &instance), + + // Check that fields were set based on the template + resource.TestCheckResourceAttr(resourceName, "machine_type", "n1-standard-1"), + resource.TestCheckResourceAttr(resourceName, "attached_disk.#", "1"), + resource.TestCheckResourceAttr(resourceName, "scheduling.0.automatic_restart", "false"), + ), + }, + }, + }) +} + func TestAccComputeInstanceFromRegionTemplate_basic(t *testing.T) { t.Parallel() @@ -418,6 +446,80 @@ resource "google_compute_instance_from_template" "foobar" { `, template, template, instance) } +func testAccComputeInstanceFromTemplate_self_link_unique(instance, template string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-11" + project = "debian-cloud" +} + +resource "google_compute_disk" "foobar" { + name = "%s" + image = data.google_compute_image.my_image.self_link + size = 10 + type = "pd-ssd" + zone = "us-central1-a" +} + +resource "google_compute_instance_template" "foobar" { + name = "%s" + machine_type = "n1-standard-1" // can't be e2 because of local-ssd + + disk { + source = google_compute_disk.foobar.name + auto_delete = false + boot = true + } + + disk { + disk_type = "local-ssd" + type = "SCRATCH" + interface = "NVME" + disk_size_gb = 375 + } + + disk { + source_image = data.google_compute_image.my_image.self_link + auto_delete = true + disk_size_gb = 100 + boot = false + disk_type = "pd-ssd" + type = "PERSISTENT" + } + + network_interface { + network = "default" + } + + metadata = { + foo = "bar" + } + + scheduling { + automatic_restart = true + } + + can_ip_forward = true +} + +resource "google_compute_instance_from_template" "foobar" { + name = "%s" + zone = "us-central1-a" + + source_instance_template = google_compute_instance_template.foobar.self_link_unique + + // Overrides + can_ip_forward = false + labels = { + my_key = "my_value" + } + scheduling { + automatic_restart = false + } +} +`, template, template, instance) +} + func testAccComputeInstanceFromTemplate_overrideBootDisk(templateDisk, overrideDisk, template, instance string) string { return fmt.Sprintf(` data "google_compute_image" "my_image" { diff --git a/mmv1/third_party/terraform/tests/resource_compute_instance_group_manager_test.go.erb b/mmv1/third_party/terraform/tests/resource_compute_instance_group_manager_test.go.erb index d40d674f234f..0fa3f6e635cb 100644 --- a/mmv1/third_party/terraform/tests/resource_compute_instance_group_manager_test.go.erb +++ b/mmv1/third_party/terraform/tests/resource_compute_instance_group_manager_test.go.erb @@ -68,6 +68,79 @@ func testSweepComputeInstanceGroupManager(region string) error { return nil } +func TestInstanceGroupManager_parseUniqueId(t *testing.T) { + expectations:= map[string][]string { + "projects/imre-test/global/instanceTemplates/example-template-custom?uniqueId=123": []string{"projects/imre-test/global/instanceTemplates/example-template-custom", "123"}, + "https://www.googleapis.com/compute/v1/projects/imre-test/global/instanceTemplates/example-template-custom?uniqueId=123": []string{"https://www.googleapis.com/compute/v1/projects/imre-test/global/instanceTemplates/example-template-custom", "123"}, + "projects/imre-test/global/instanceTemplates/example-template-custom": []string{"projects/imre-test/global/instanceTemplates/example-template-custom", ""}, + "https://www.googleapis.com/compute/v1/projects/imre-test/global/instanceTemplates/example-template-custom": []string{"https://www.googleapis.com/compute/v1/projects/imre-test/global/instanceTemplates/example-template-custom", ""}, + "example-template-custom?uniqueId=123": []string{"example-template-custom", "123"}, + + // this test demonstrates that uniqueIds can't override eachother + "projects/imre-test/global/instanceTemplates/example?uniqueId=123?uniqueId=456": []string{"projects/imre-test/global/instanceTemplates/example", "123?uniqueId=456"}, + } + + for k, v:= range expectations { + aName, aUniqueId := parseUniqueId(k) + if v[0] != aName { + t.Errorf("parseUniqueId failed; name of %v should be %v, not %v", k, v[0], aName) + } + if v[1] != aUniqueId { + t.Errorf("parseUniqueId failed; uniqueId of %v should be %v, not %v", k, v[1], aUniqueId) + } + } +} + +func TestInstanceGroupManager_compareInstanceTemplate(t *testing.T) { + shouldAllMatch := []string { + // uniqueId not present + "https://www.googleapis.com/compute/v1/projects/imre-test/global/instanceTemplates/example-template-custom", + "projects/imre-test/global/instanceTemplates/example-template-custom", + // uniqueId present + "https://www.googleapis.com/compute/v1/projects/imre-test/global/instanceTemplates/example-template-custom?uniqueId=123", + "projects/imre-test/global/instanceTemplates/example-template-custom?uniqueId=123", + } + shouldNotMatch := map[string]string { + // mismatching name + "https://www.googleapis.com/compute/v1/projects/imre-test/global/instanceTemplates/example-template-custom": "projects/imre-test/global/instanceTemplates/example-template-custom2", + "projects/imre-test/global/instanceTemplates/example-template-custom": "https://www.googleapis.com/compute/v1/projects/imre-test/global/instanceTemplates/example-template-custom2", + // matching name, but mismatching uniqueId + "https://www.googleapis.com/compute/v1/projects/imre-test/global/instanceTemplates/example-template-custom?uniqueId=123": "projects/imre-test/global/instanceTemplates/example-template-custom?uniqueId=1234", + "projects/imre-test/global/instanceTemplates/example-template-custom?uniqueId=123": "https://www.googleapis.com/compute/v1/projects/imre-test/global/instanceTemplates/example-template-custom?uniqueId=1234", + } + for _, v1 := range shouldAllMatch { + for _, v2:= range shouldAllMatch { + if !compareSelfLinkRelativePathsIgnoreParams("", v1, v2, nil) { + t.Fatalf("compareSelfLinkRelativePathsIgnoreParams did not match (and should have) %v and %v", v1, v2) + } + } + } + + for v1, v2 := range shouldNotMatch { + if compareSelfLinkRelativePathsIgnoreParams("", v1, v2, nil) { + t.Fatalf("compareSelfLinkRelativePathsIgnoreParams did match (and shouldn't) %v and %v", v1, v2) + } + } +} + +func TestInstanceGroupManager_convertUniqueId(t *testing.T) { + matches:= map[string]string { + // uniqueId not present (should return the same) + "https://www.googleapis.com/compute/v1/projects/imre-test/global/instanceTemplates/example-template-custom": "https://www.googleapis.com/compute/v1/projects/imre-test/global/instanceTemplates/example-template-custom", + "projects/imre-test/global/instanceTemplates/example-template-custom": "projects/imre-test/global/instanceTemplates/example-template-custom", + // uniqueId present (should return the last component replaced) + "https://www.googleapis.com/compute/v1/projects/imre-test/global/instanceTemplates/example-template-custom?uniqueId=123": "https://www.googleapis.com/compute/v1/projects/imre-test/global/instanceTemplates/123", + "projects/imre-test/global/instanceTemplates/example-template-custom?uniqueId=123": "projects/imre-test/global/instanceTemplates/123", + "tf-test-igm-8amncgtq22?uniqueId=8361222501423044003": "8361222501423044003", + } + for input, expected := range matches { + actual:= ConvertToUniqueIdWhenPresent(input) + if actual != expected { + t.Fatalf("invalid return value by ConvertToUniqueIdWhenPresent for input %v; expected: %v, actual: %v", input, expected, actual) + } + } +} + func TestAccInstanceGroupManager_basic(t *testing.T) { t.Parallel() @@ -100,6 +173,38 @@ func TestAccInstanceGroupManager_basic(t *testing.T) { }) } +func TestAccInstanceGroupManager_self_link_unique(t *testing.T) { + t.Parallel() + + template := fmt.Sprintf("tf-test-igm-%s", RandString(t, 10)) + target := fmt.Sprintf("tf-test-igm-%s", RandString(t, 10)) + igm1 := fmt.Sprintf("tf-test-igm-%s", RandString(t, 10)) + igm2 := fmt.Sprintf("tf-test-igm-%s", RandString(t, 10)) + + VcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckInstanceGroupManagerDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccInstanceGroupManager_self_link_unique(template, target, igm1, igm2), + }, + { + ResourceName: "google_compute_instance_group_manager.igm-basic", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"status"}, + }, + { + ResourceName: "google_compute_instance_group_manager.igm-no-tp", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"status"}, + }, + }, + }) +} + func TestAccInstanceGroupManager_targetSizeZero(t *testing.T) { t.Parallel() @@ -526,6 +631,72 @@ resource "google_compute_instance_group_manager" "igm-no-tp" { `, template, target, igm1, igm2) } +func testAccInstanceGroupManager_self_link_unique(template, target, igm1, igm2 string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-11" + project = "debian-cloud" +} + +resource "google_compute_instance_template" "igm-basic" { + name = "%s" + machine_type = "e2-medium" + can_ip_forward = false + tags = ["foo", "bar"] + + disk { + source_image = data.google_compute_image.my_image.self_link + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } +} + +resource "google_compute_target_pool" "igm-basic" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + session_affinity = "CLIENT_IP_PROTO" +} + +resource "google_compute_instance_group_manager" "igm-basic" { + description = "Terraform test instance group manager" + name = "%s" + + version { + name = "prod" + instance_template = google_compute_instance_template.igm-basic.self_link_unique + } + + target_pools = [google_compute_target_pool.igm-basic.self_link] + base_instance_name = "tf-test-igm-basic" + zone = "us-central1-c" + target_size = 2 + list_managed_instances_results = "PAGINATED" +} + +resource "google_compute_instance_group_manager" "igm-no-tp" { + description = "Terraform test instance group manager" + name = "%s" + + version { + name = "prod" + instance_template = google_compute_instance_template.igm-basic.self_link + } + + base_instance_name = "tf-test-igm-no-tp" + zone = "us-central1-c" + target_size = 2 +} +`, template, target, igm1, igm2) +} + func testAccInstanceGroupManager_targetSizeZero(template, igm string) string { return fmt.Sprintf(` data "google_compute_image" "my_image" { diff --git a/mmv1/third_party/terraform/tests/resource_compute_instance_template_test.go.erb b/mmv1/third_party/terraform/tests/resource_compute_instance_template_test.go.erb index a851fd6e46be..cf60dd88233d 100644 --- a/mmv1/third_party/terraform/tests/resource_compute_instance_template_test.go.erb +++ b/mmv1/third_party/terraform/tests/resource_compute_instance_template_test.go.erb @@ -6,6 +6,7 @@ import ( "fmt" "reflect" "regexp" + "strconv" "strings" "testing" "time" @@ -1399,6 +1400,20 @@ func testAccCheckComputeInstanceTemplateExistsInProject(t *testing.T, n, p strin if found.Name != templateName { return fmt.Errorf("Instance template not found") } + if strings.Contains(rs.Primary.ID, "uniqueId") { + return fmt.Errorf("unique ID is not supposed to be present in the Terraform resource ID") + } + selfLink:= rs.Primary.Attributes["self_link"] + if strings.Contains(selfLink, "uniqueId") { + return fmt.Errorf("unique ID is not supposed to be present in selfLink") + } + + actualSelfLinkUnique:= rs.Primary.Attributes["self_link_unique"] + foundId:= strconv.FormatUint(found.Id, 10) + expectedSelfLinkUnique:= selfLink + "?uniqueId="+foundId + if actualSelfLinkUnique != expectedSelfLinkUnique { + return fmt.Errorf("self_link_unique should be %v but it is: %v", expectedSelfLinkUnique, actualSelfLinkUnique) + } *instanceTemplate = *found @@ -3371,7 +3386,7 @@ resource "google_compute_disk" "persistent" { } resource "google_compute_snapshot" "snapshot" { - name = "my-snapshot" + name = "tf-test-my-snapshot-%{random_suffix}" source_disk = google_compute_disk.persistent.id zone = "us-central1-a" snapshot_encryption_key { diff --git a/mmv1/third_party/terraform/website/docs/d/compute_instance_template.html.markdown b/mmv1/third_party/terraform/website/docs/d/compute_instance_template.html.markdown index ebb5dbb19681..1c8be88576c1 100644 --- a/mmv1/third_party/terraform/website/docs/d/compute_instance_template.html.markdown +++ b/mmv1/third_party/terraform/website/docs/d/compute_instance_template.html.markdown @@ -24,19 +24,28 @@ data "google_compute_instance_template" "generic-regex" { filter = "name != generic-tpl-20200107" most_recent = true } + +# by unique ID +data "google_compute_instance_template" "generic" { + self_link_unique = "https://www.googleapis.com/compute/v1/projects/your-project-name/global/instanceTemplates/example-template-custom?uniqueId=1234" +} + ``` ## Argument Reference The following arguments are supported: -- `name` - (Optional) The name of the instance template. One of `name` or `filter` must be provided. +- `name` - (Optional) The name of the instance template. One of `name`, `filter` or `self_link_unique` must be provided. - `filter` - (Optional) A filter to retrieve the instance templates. See [gcloud topic filters](https://cloud.google.com/sdk/gcloud/reference/topic/filters) for reference. - If multiple instance templates match, either adjust the filter or specify `most_recent`. One of `name` or `filter` must be provided. + If multiple instance templates match, either adjust the filter or specify `most_recent`. + One of `name`, `filter` or `self_link_unique` must be provided. + +- `self_link_unique` - (Optional) The self_link_unique URI of the instance template. One of `name`, `filter` or `self_link_unique` must be provided. -- `most_recent` - (Optional) If `filter` is provided, ensures the most recent template is returned when multiple instance templates match. One of `name` or `filter` must be provided. +- `most_recent` - (Optional) If `filter` is provided, ensures the most recent template is returned when multiple instance templates match. One of `name`, `filter` or `self_link_unique` must be provided. --- @@ -303,6 +312,9 @@ The `disk_encryption_key` block supports: * `self_link` - The URI of the created resource. +* `self_link_unique` - A special URI of the created resource that uniquely identifies this instance template with the following format: `projects/{{project}}/global/instanceTemplates/{{name}}?uniqueId={{uniqueId}}` +Referencing an instance template via this attribute prevents Time of Check to Time of Use attacks when the instance template resides in a shared/untrusted environment. + * `tags_fingerprint` - The unique fingerprint of the tags. [1]: /docs/providers/google/r/compute_instance_group_manager.html diff --git a/mmv1/third_party/terraform/website/docs/r/compute_instance_from_template.html.markdown b/mmv1/third_party/terraform/website/docs/r/compute_instance_from_template.html.markdown index 4c300b8aa616..5d8c8103d3b5 100644 --- a/mmv1/third_party/terraform/website/docs/r/compute_instance_from_template.html.markdown +++ b/mmv1/third_party/terraform/website/docs/r/compute_instance_from_template.html.markdown @@ -45,7 +45,7 @@ resource "google_compute_instance_from_template" "tpl" { name = "instance-from-template" zone = "us-central1-a" - source_instance_template = google_compute_instance_template.tpl.id + source_instance_template = google_compute_instance_template.tpl.self_link_unique // Override fields from instance template can_ip_forward = false @@ -63,7 +63,8 @@ The following arguments are supported: Changing this forces a new resource to be created. * `source_instance_template` - (Required) Name or self link of an instance - template to create the instance based on. + template to create the instance based on. It is recommended to reference + instance templates through their unique id (`self_link_unique` attribute). - - - diff --git a/mmv1/third_party/terraform/website/docs/r/compute_instance_group_manager.html.markdown b/mmv1/third_party/terraform/website/docs/r/compute_instance_group_manager.html.markdown index 2a6f26db5ca6..db82546f9c1e 100644 --- a/mmv1/third_party/terraform/website/docs/r/compute_instance_group_manager.html.markdown +++ b/mmv1/third_party/terraform/website/docs/r/compute_instance_group_manager.html.markdown @@ -36,7 +36,7 @@ resource "google_compute_instance_group_manager" "appserver" { zone = "us-central1-a" version { - instance_template = google_compute_instance_template.appserver.id + instance_template = google_compute_instance_template.appserver.self_link_unique } all_instances_config { @@ -76,12 +76,12 @@ resource "google_compute_instance_group_manager" "appserver" { version { name = "appserver" - instance_template = google_compute_instance_template.appserver.id + instance_template = google_compute_instance_template.appserver.self_link_unique } version { name = "appserver-canary" - instance_template = google_compute_instance_template.appserver-canary.id + instance_template = google_compute_instance_template.appserver-canary.self_link_unique target_size { fixed = 1 } @@ -250,7 +250,7 @@ all_instances_config { ```hcl version { name = "appserver-canary" - instance_template = google_compute_instance_template.appserver-canary.id + instance_template = google_compute_instance_template.appserver-canary.self_link_unique target_size { fixed = 1 @@ -261,7 +261,7 @@ version { ```hcl version { name = "appserver-canary" - instance_template = google_compute_instance_template.appserver-canary.id + instance_template = google_compute_instance_template.appserver-canary.self_link_unique target_size { percent = 20 @@ -271,7 +271,7 @@ version { * `name` - (Required) - Version name. -* `instance_template` - (Required) - The full URL to an instance template from which all new instances of this version will be created. +* `instance_template` - (Required) - The full URL to an instance template from which all new instances of this version will be created. It is recommended to reference instance templates through their unique id (`self_link_unique` attribute). * `target_size` - (Optional) - The number of instances calculated as a fixed number or a percentage depending on the settings. Structure is [documented below](#nested_target_size). diff --git a/mmv1/third_party/terraform/website/docs/r/compute_instance_template.html.markdown b/mmv1/third_party/terraform/website/docs/r/compute_instance_template.html.markdown index 7ab6ed9a5da8..b995f223b94c 100644 --- a/mmv1/third_party/terraform/website/docs/r/compute_instance_template.html.markdown +++ b/mmv1/third_party/terraform/website/docs/r/compute_instance_template.html.markdown @@ -640,6 +640,9 @@ exported: * `self_link` - The URI of the created resource. +* `self_link_unique` - A special URI of the created resource that uniquely identifies this instance template with the following format: `projects/{{project}}/global/instanceTemplates/{{name}}?uniqueId={{uniqueId}}` +Referencing an instance template via this attribute prevents Time of Check to Time of Use attacks when the instance template resides in a shared/untrusted environment. + * `tags_fingerprint` - The unique fingerprint of the tags. [1]: /docs/providers/google/r/compute_instance_group_manager.html diff --git a/mmv1/third_party/terraform/website/docs/r/compute_region_instance_group_manager.html.markdown b/mmv1/third_party/terraform/website/docs/r/compute_region_instance_group_manager.html.markdown index d8fc9a22dde4..45e90554be0e 100644 --- a/mmv1/third_party/terraform/website/docs/r/compute_region_instance_group_manager.html.markdown +++ b/mmv1/third_party/terraform/website/docs/r/compute_region_instance_group_manager.html.markdown @@ -42,7 +42,7 @@ resource "google_compute_region_instance_group_manager" "appserver" { distribution_policy_zones = ["us-central1-a", "us-central1-f"] version { - instance_template = google_compute_instance_template.appserver.id + instance_template = google_compute_instance_template.appserver.self_link_unique } all_instances_config { @@ -80,11 +80,11 @@ resource "google_compute_region_instance_group_manager" "appserver" { target_size = 5 version { - instance_template = google_compute_instance_template.appserver.id + instance_template = google_compute_instance_template.appserver.self_link_unique } version { - instance_template = google_compute_instance_template.appserver-canary.id + instance_template = google_compute_instance_template.appserver-canary.self_link_unique target_size { fixed = 1 } @@ -259,7 +259,7 @@ The `auto_healing_policies` block supports: ```hcl version { name = "appserver-canary" - instance_template = google_compute_instance_template.appserver-canary.id + instance_template = google_compute_instance_template.appserver-canary.self_link_unique target_size { fixed = 1 @@ -270,7 +270,7 @@ version { ```hcl version { name = "appserver-canary" - instance_template = google_compute_instance_template.appserver-canary.id + instance_template = google_compute_instance_template.appserver-canary.self_link_unique target_size { percent = 20