From 5e04661a5a533fc36e93c4c856fcd9585e94c18c Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 18 Jan 2017 05:49:48 -0800 Subject: [PATCH] providers/google: Add support for encrypting a disk (#11167) * providers/google: add support for encrypting a disk * providers/google: Add docs for encrypting disks * providers/google: CSEK small fixes: sensitive params and mismatched state files --- resource_compute_disk.go | 20 ++++++++ resource_compute_disk_test.go | 54 +++++++++++++++++++++ resource_compute_instance.go | 35 ++++++++++++++ resource_compute_instance_test.go | 78 +++++++++++++++++++++++++++++++ 4 files changed, 187 insertions(+) diff --git a/resource_compute_disk.go b/resource_compute_disk.go index 5984383f..c8ef8007 100644 --- a/resource_compute_disk.go +++ b/resource_compute_disk.go @@ -28,6 +28,18 @@ func resourceComputeDisk() *schema.Resource { ForceNew: true, }, + "disk_encryption_key_raw": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Sensitive: true, + }, + + "disk_encryption_key_sha256": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "image": &schema.Schema{ Type: schema.TypeString, Optional: true, @@ -129,6 +141,11 @@ func resourceComputeDiskCreate(d *schema.ResourceData, meta interface{}) error { disk.SourceSnapshot = snapshotData.SelfLink } + if v, ok := d.GetOk("disk_encryption_key_raw"); ok { + disk.DiskEncryptionKey = &compute.CustomerEncryptionKey{} + disk.DiskEncryptionKey.RawKey = v.(string) + } + op, err := config.clientCompute.Disks.Insert( project, d.Get("zone").(string), disk).Do() if err != nil { @@ -168,6 +185,9 @@ func resourceComputeDiskRead(d *schema.ResourceData, meta interface{}) error { } d.Set("self_link", disk.SelfLink) + if disk.DiskEncryptionKey != nil && disk.DiskEncryptionKey.Sha256 != "" { + d.Set("disk_encryption_key_sha256", disk.DiskEncryptionKey.Sha256) + } return nil } diff --git a/resource_compute_disk_test.go b/resource_compute_disk_test.go index e18cb994..478144e7 100644 --- a/resource_compute_disk_test.go +++ b/resource_compute_disk_test.go @@ -30,6 +30,28 @@ func TestAccComputeDisk_basic(t *testing.T) { }) } +func TestAccComputeDisk_encryption(t *testing.T) { + diskName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var disk compute.Disk + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeDiskDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeDisk_encryption(diskName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeDiskExists( + "google_compute_disk.foobar", &disk), + testAccCheckEncryptionKey( + "google_compute_disk.foobar", &disk), + ), + }, + }, + }) +} + func testAccCheckComputeDiskDestroy(s *terraform.State) error { config := testAccProvider.Meta().(*Config) @@ -77,6 +99,26 @@ func testAccCheckComputeDiskExists(n string, disk *compute.Disk) resource.TestCh } } +func testAccCheckEncryptionKey(n string, disk *compute.Disk) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + attr := rs.Primary.Attributes["disk_encryption_key_sha256"] + if disk.DiskEncryptionKey == nil && attr != "" { + return fmt.Errorf("Disk %s has mismatched encryption key.\nTF State: %+v\nGCP State: ", n, attr) + } + + if attr != disk.DiskEncryptionKey.Sha256 { + return fmt.Errorf("Disk %s has mismatched encryption key.\nTF State: %+v.\nGCP State: %+v", + n, attr, disk.DiskEncryptionKey.Sha256) + } + return nil + } +} + func testAccComputeDisk_basic(diskName string) string { return fmt.Sprintf(` resource "google_compute_disk" "foobar" { @@ -87,3 +129,15 @@ resource "google_compute_disk" "foobar" { zone = "us-central1-a" }`, diskName) } + +func testAccComputeDisk_encryption(diskName string) string { + return fmt.Sprintf(` +resource "google_compute_disk" "foobar" { + name = "%s" + image = "debian-8-jessie-v20160803" + size = 50 + type = "pd-ssd" + zone = "us-central1-a" + disk_encryption_key_raw = "SGVsbG8gZnJvbSBHb29nbGUgQ2xvdWQgUGxhdGZvcm0=" +}`, diskName) +} diff --git a/resource_compute_instance.go b/resource_compute_instance.go index 40970cfc..c25cd87c 100644 --- a/resource_compute_instance.go +++ b/resource_compute_instance.go @@ -75,6 +75,18 @@ func resourceComputeInstance() *schema.Resource { Type: schema.TypeString, Optional: true, }, + + "disk_encryption_key_raw": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Sensitive: true, + }, + + "disk_encryption_key_sha256": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, }, }, }, @@ -437,6 +449,11 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err disk.DeviceName = v.(string) } + if v, ok := d.GetOk(prefix + ".disk_encryption_key_raw"); ok { + disk.DiskEncryptionKey = &compute.CustomerEncryptionKey{} + disk.DiskEncryptionKey.RawKey = v.(string) + } + disks = append(disks, &disk) } @@ -770,6 +787,24 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error d.Set("tags_fingerprint", instance.Tags.Fingerprint) } + disks := make([]map[string]interface{}, 0, 1) + for i, disk := range instance.Disks { + di := map[string]interface{}{ + "disk": d.Get(fmt.Sprintf("disk.%d.disk", i)), + "image": d.Get(fmt.Sprintf("disk.%d.image", i)), + "type": d.Get(fmt.Sprintf("disk.%d.type", i)), + "scratch": d.Get(fmt.Sprintf("disk.%d.scratch", i)), + "auto_delete": d.Get(fmt.Sprintf("disk.%d.auto_delete", i)), + "size": d.Get(fmt.Sprintf("disk.%d.size", i)), + "device_name": d.Get(fmt.Sprintf("disk.%d.device_name", i)), + } + if disk.DiskEncryptionKey != nil && disk.DiskEncryptionKey.Sha256 != "" { + di["disk_encryption_key_sha256"] = disk.DiskEncryptionKey.Sha256 + } + disks = append(disks, di) + } + d.Set("disk", disks) + d.Set("self_link", instance.SelfLink) d.SetId(instance.Name) diff --git a/resource_compute_instance_test.go b/resource_compute_instance_test.go index 2a254b91..382e5c71 100644 --- a/resource_compute_instance_test.go +++ b/resource_compute_instance_test.go @@ -220,6 +220,30 @@ func TestAccComputeInstance_disksWithAutodelete(t *testing.T) { }) } +func TestAccComputeInstance_diskEncryption(t *testing.T) { + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + var diskName = fmt.Sprintf("instance-testd-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_disks_encryption(diskName, instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceDisk(&instance, instanceName, true, true), + testAccCheckComputeInstanceDisk(&instance, diskName, true, false), + testAccCheckComputeInstanceDiskEncryptionKey("google_compute_instance.foobar", &instance), + ), + }, + }, + }) +} + func TestAccComputeInstance_local_ssd(t *testing.T) { var instance compute.Instance var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) @@ -636,6 +660,27 @@ func testAccCheckComputeInstanceDisk(instance *compute.Instance, source string, } } +func testAccCheckComputeInstanceDiskEncryptionKey(n string, instance *compute.Instance) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + for i, disk := range instance.Disks { + attr := rs.Primary.Attributes[fmt.Sprintf("disk.%d.disk_encryption_key_sha256", i)] + if disk.DiskEncryptionKey == nil && attr != "" { + return fmt.Errorf("Disk %d has mismatched encryption key.\nTF State: %+v\nGCP State: ", i, attr) + } + if disk.DiskEncryptionKey != nil && attr != disk.DiskEncryptionKey.Sha256 { + return fmt.Errorf("Disk %d has mismatched encryption key.\nTF State: %+v\nGCP State: %+v", + i, attr, disk.DiskEncryptionKey.Sha256) + } + } + return nil + } +} + func testAccCheckComputeInstanceTag(instance *compute.Instance, n string) resource.TestCheckFunc { return func(s *terraform.State) error { if instance.Tags == nil { @@ -983,6 +1028,39 @@ func testAccComputeInstance_disks(disk, instance string, autodelete bool) string }`, disk, instance, autodelete) } +func testAccComputeInstance_disks_encryption(disk, instance string) string { + return fmt.Sprintf(` + resource "google_compute_disk" "foobar" { + name = "%s" + size = 10 + type = "pd-ssd" + zone = "us-central1-a" + } + + resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + disk { + image = "debian-8-jessie-v20160803" + disk_encryption_key_raw = "SGVsbG8gZnJvbSBHb29nbGUgQ2xvdWQgUGxhdGZvcm0=" + } + + disk { + disk = "${google_compute_disk.foobar.name}" + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } + }`, disk, instance) +} + func testAccComputeInstance_local_ssd(instance string) string { return fmt.Sprintf(` resource "google_compute_instance" "local-ssd" {