diff --git a/google/compute_instance_helpers.go b/google/compute_instance_helpers.go index 927fb8a4a47..abe362657be 100644 --- a/google/compute_instance_helpers.go +++ b/google/compute_instance_helpers.go @@ -329,3 +329,21 @@ func flattenShieldedVmConfig(shieldedVmConfig *computeBeta.ShieldedVmConfig) []m "enable_integrity_monitoring": shieldedVmConfig.EnableIntegrityMonitoring, }} } + +func expandDisplayDevice(d *schema.ResourceData) *computeBeta.DisplayDevice { + if _, ok := d.GetOk("enable_display"); !ok { + return nil + } + return &computeBeta.DisplayDevice{ + EnableDisplay: d.Get("enable_display").(bool), + ForceSendFields: []string{"EnableDisplay"}, + } +} + +func flattenEnableDisplay(displayDevice *computeBeta.DisplayDevice) interface{} { + if displayDevice == nil { + return nil + } + + return displayDevice.EnableDisplay +} diff --git a/google/data_source_google_compute_instance.go b/google/data_source_google_compute_instance.go index c2678fd767d..286d0ae4e87 100644 --- a/google/data_source_google_compute_instance.go +++ b/google/data_source_google_compute_instance.go @@ -140,6 +140,11 @@ func dataSourceGoogleComputeInstanceRead(d *schema.ResourceData, meta interface{ return err } + err = d.Set("enable_display", flattenEnableDisplay(instance.DisplayDevice)) + if err != nil { + return err + } + d.Set("attached_disk", ads) d.Set("cpu_platform", instance.CpuPlatform) d.Set("min_cpu_platform", instance.MinCpuPlatform) diff --git a/google/data_source_google_compute_instance_test.go b/google/data_source_google_compute_instance_test.go index 4193c3fd903..08aab937fce 100644 --- a/google/data_source_google_compute_instance_test.go +++ b/google/data_source_google_compute_instance_test.go @@ -28,6 +28,7 @@ func TestAccDataSourceComputeInstance_basic(t *testing.T) { resource.TestCheckResourceAttr("data.google_compute_instance.bar", "boot_disk.0.initialize_params.0.type", "pd-standard"), resource.TestCheckResourceAttr("data.google_compute_instance.bar", "scratch_disk.0.interface", "SCSI"), resource.TestCheckResourceAttr("data.google_compute_instance.bar", "network_interface.0.access_config.0.network_tier", "PREMIUM"), + resource.TestCheckResourceAttr("data.google_compute_instance.bar", "enable_display", "true"), ), }, }, @@ -119,7 +120,9 @@ resource "google_compute_instance" "foo" { labels = { my_key = "my_value" my_other_key = "my_other_value" - } + } + + enable_display = true } data "google_compute_instance" "bar" { diff --git a/google/resource_compute_instance.go b/google/resource_compute_instance.go index 596741f8539..0eb67fba119 100644 --- a/google/resource_compute_instance.go +++ b/google/resource_compute_instance.go @@ -394,6 +394,11 @@ func resourceComputeInstance() *schema.Resource { }, }, + "enable_display": { + Type: schema.TypeBool, + Optional: true, + }, + "guest_accelerator": { Type: schema.TypeList, Optional: true, @@ -732,6 +737,7 @@ func expandComputeInstance(project string, d *schema.ResourceData, config *Confi Hostname: d.Get("hostname").(string), ForceSendFields: []string{"CanIpForward", "DeletionProtection"}, ShieldedVmConfig: expandShieldedVmConfigs(d), + DisplayDevice: expandDisplayDevice(d), }, nil } @@ -952,6 +958,7 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error d.Set("scheduling", flattenScheduling(instance.Scheduling)) d.Set("guest_accelerator", flattenGuestAccelerators(instance.GuestAccelerators)) d.Set("shielded_instance_config", flattenShieldedVmConfig(instance.ShieldedVmConfig)) + d.Set("enable_display", flattenEnableDisplay(instance.DisplayDevice)) d.Set("cpu_platform", instance.CpuPlatform) d.Set("min_cpu_platform", instance.MinCpuPlatform) d.Set("deletion_protection", instance.DeletionProtection) @@ -1329,9 +1336,9 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err } // Attributes which can only be changed if the instance is stopped - if scopesChange || d.HasChange("service_account.0.email") || d.HasChange("machine_type") || d.HasChange("min_cpu_platform") { + if scopesChange || d.HasChange("service_account.0.email") || d.HasChange("machine_type") || d.HasChange("min_cpu_platform") || d.HasChange("enable_display") { if !d.Get("allow_stopping_for_update").(bool) { - return fmt.Errorf("Changing the machine_type, min_cpu_platform, or service_account on an instance requires stopping it. " + + return fmt.Errorf("Changing the machine_type, min_cpu_platform, service_account, or enable display on an instance requires stopping it. " + "To acknowledge this, please set allow_stopping_for_update = true in your config.") } op, err := config.clientCompute.Instances.Stop(project, zone, instance.Name).Do() @@ -1404,6 +1411,22 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err d.SetPartial("service_account") } + if d.HasChange("enable_display") { + req := &compute.DisplayDevice{ + EnableDisplay: d.Get("enable_display").(bool), + ForceSendFields: []string{"EnableDisplay"}, + } + op, err = config.clientCompute.Instances.UpdateDisplayDevice(project, zone, instance.Name, req).Do() + if err != nil { + return fmt.Errorf("Error updating display device: %s", err) + } + opErr := computeOperationWaitTime(config.clientCompute, op, project, "updating display device", int(d.Timeout(schema.TimeoutUpdate).Minutes())) + if opErr != nil { + return opErr + } + d.SetPartial("enable_display") + } + op, err = config.clientCompute.Instances.Start(project, zone, instance.Name).Do() if err != nil { return errwrap.Wrapf("Error starting instance: {{err}}", err) diff --git a/google/resource_compute_instance_template.go b/google/resource_compute_instance_template.go index 07607e05c14..3143f056ac3 100644 --- a/google/resource_compute_instance_template.go +++ b/google/resource_compute_instance_template.go @@ -188,6 +188,12 @@ func resourceComputeInstanceTemplate() *schema.Resource { ForceNew: true, }, + "enable_display": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + }, + "instance_description": { Type: schema.TypeString, Optional: true, @@ -704,6 +710,7 @@ func resourceComputeInstanceTemplateCreate(d *schema.ResourceData, meta interfac ServiceAccounts: expandServiceAccounts(d.Get("service_account").([]interface{})), Tags: resourceInstanceTags(d), ShieldedVmConfig: expandShieldedVmConfigs(d), + DisplayDevice: expandDisplayDevice(d), } if _, ok := d.GetOk("labels"); ok { @@ -1069,6 +1076,11 @@ func resourceComputeInstanceTemplateRead(d *schema.ResourceData, meta interface{ return fmt.Errorf("Error setting shielded_instance_config: %s", err) } } + if instanceTemplate.Properties.DisplayDevice != nil { + if err = d.Set("enable_display", flattenEnableDisplay(instanceTemplate.Properties.DisplayDevice)); err != nil { + return fmt.Errorf("Error setting enable_display: %s", err) + } + } return nil } diff --git a/google/resource_compute_instance_template_test.go b/google/resource_compute_instance_template_test.go index 0e3dae6e075..ca453fbcb8f 100644 --- a/google/resource_compute_instance_template_test.go +++ b/google/resource_compute_instance_template_test.go @@ -736,6 +736,26 @@ func TestAccComputeInstanceTemplate_shieldedVmConfig2(t *testing.T) { }) } +func TestAccComputeInstanceTemplate_enableDisplay(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceTemplateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeInstanceTemplate_enableDisplay(), + }, + { + ResourceName: "google_compute_instance_template.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testAccCheckComputeInstanceTemplateDestroy(s *terraform.State) error { config := testAccProvider.Meta().(*Config) @@ -1868,3 +1888,29 @@ resource "google_compute_instance_template" "foobar" { } }`, acctest.RandString(10), enableSecureBoot, enableVtpm, enableIntegrityMonitoring) } + +func testAccComputeInstanceTemplate_enableDisplay() string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "centos-7" + project = "gce-uefi-images" +} + +resource "google_compute_instance_template" "foobar" { + name = "instancet-test-%s" + machine_type = "n1-standard-1" + can_ip_forward = false + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + enable_display = true +}`, acctest.RandString(10)) +} diff --git a/google/resource_compute_instance_test.go b/google/resource_compute_instance_test.go index f62aa436c9a..778a6660ac9 100644 --- a/google/resource_compute_instance_test.go +++ b/google/resource_compute_instance_test.go @@ -1201,6 +1201,28 @@ func TestAccComputeInstance_shieldedVmConfig2(t *testing.T) { }) } +func TestAccComputeInstance_enableDisplay(t *testing.T) { + t.Parallel() + + instanceName := fmt.Sprintf("terraform-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeInstance_enableDisplay(instanceName), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{"allow_stopping_for_update"}), + { + Config: testAccComputeInstance_enableDisplayUpdated(instanceName), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{"allow_stopping_for_update"}), + }, + }) +} + func testAccCheckComputeInstanceUpdateMachineType(n string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -3638,3 +3660,61 @@ resource "google_compute_instance" "foobar" { } `, instance, enableSecureBoot, enableVtpm, enableIntegrityMonitoring) } + +func testAccComputeInstance_enableDisplay(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "centos-7" + project = "gce-uefi-images" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + + enable_display = true + + allow_stopping_for_update = true +} +`, instance) +} + +func testAccComputeInstance_enableDisplayUpdated(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "centos-7" + project = "gce-uefi-images" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + + enable_display = false + + allow_stopping_for_update = true +} +`, instance) +} diff --git a/website/docs/d/datasource_compute_instance.html.markdown b/website/docs/d/datasource_compute_instance.html.markdown index 260ffed84c5..6fad0aedd94 100644 --- a/website/docs/d/datasource_compute_instance.html.markdown +++ b/website/docs/d/datasource_compute_instance.html.markdown @@ -87,6 +87,8 @@ The following arguments are supported: * `shielded_instance_config` - The shielded vm config being used by the instance. Structure is documented below. +* `enable_display` -- Whether the instance has virtual displays enabled. + * `network_interface.0.network_ip` - The internal ip address of the instance, either manually or dynamically assigned. * `network_interface.0.access_config.0.nat_ip` - If the instance has an access config, either the given external ip (in the `nat_ip` field) or the ephemeral (generated) ip (if you didn't provide one). @@ -201,4 +203,4 @@ The `shielded_instance_config` block supports: * `enable_vtpm` -- Whether the instance uses vTPM. -* `enable_integrity_monitoring` -- Whether integrity monitoring is enabled for the instance. +* `enable_integrity_monitoring` -- Whether integrity monitoring is enabled for the instance. \ No newline at end of file diff --git a/website/docs/r/compute_instance.html.markdown b/website/docs/r/compute_instance.html.markdown index 9b6efc70c47..9ab8a516ff2 100644 --- a/website/docs/r/compute_instance.html.markdown +++ b/website/docs/r/compute_instance.html.markdown @@ -138,6 +138,10 @@ The following arguments are supported: * `shielded_instance_config` - (Optional) Enable [Shielded VM](https://cloud.google.com/security/shielded-cloud/shielded-vm) on this instance. Shielded VM provides verifiable integrity to prevent against malware and rootkits. Defaults to disabled. Structure is documented below. **Note**: [`shielded_instance_config`](#shielded_instance_config) can only be used with boot images with shielded vm support. See the complete list [here](https://cloud.google.com/compute/docs/images#shielded-images). +* `enable_display` - (Optional) Enable [Virtual Displays](https://cloud.google.com/compute/docs/instances/enable-instance-virtual-display#verify_display_driver) on this instance. +**Note**: [`allow_stopping_for_update`](#allow_stopping_for_update) must be set to true in order to update this field. + + --- The `boot_disk` block supports: diff --git a/website/docs/r/compute_instance_template.html.markdown b/website/docs/r/compute_instance_template.html.markdown index d04cfa16ee0..8bf69fd7971 100644 --- a/website/docs/r/compute_instance_template.html.markdown +++ b/website/docs/r/compute_instance_template.html.markdown @@ -243,6 +243,9 @@ The following arguments are supported: * `shielded_instance_config` - (Optional) Enable [Shielded VM](https://cloud.google.com/security/shielded-cloud/shielded-vm) on this instance. Shielded VM provides verifiable integrity to prevent against malware and rootkits. Defaults to disabled. Structure is documented below. **Note**: [`shielded_instance_config`](#shielded_instance_config) can only be used with boot images with shielded vm support. See the complete list [here](https://cloud.google.com/compute/docs/images#shielded-images). +* `enable_display` - (Optional) Enable [Virtual Displays](https://cloud.google.com/compute/docs/instances/enable-instance-virtual-display#verify_display_driver) on this instance. +**Note**: [`allow_stopping_for_update`](#allow_stopping_for_update) must be set to true in order to update this field. + The `disk` block supports: * `auto_delete` - (Optional) Whether or not the disk should be auto-deleted.