Skip to content

Commit 449a98a

Browse files
danawillowstack72
authored andcommitted
providers/google: Add support for encrypting a disk (hashicorp#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
1 parent f09d123 commit 449a98a

6 files changed

Lines changed: 206 additions & 0 deletions

File tree

builtin/providers/google/resource_compute_disk.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,18 @@ func resourceComputeDisk() *schema.Resource {
2828
ForceNew: true,
2929
},
3030

31+
"disk_encryption_key_raw": &schema.Schema{
32+
Type: schema.TypeString,
33+
Optional: true,
34+
ForceNew: true,
35+
Sensitive: true,
36+
},
37+
38+
"disk_encryption_key_sha256": &schema.Schema{
39+
Type: schema.TypeString,
40+
Computed: true,
41+
},
42+
3143
"image": &schema.Schema{
3244
Type: schema.TypeString,
3345
Optional: true,
@@ -129,6 +141,11 @@ func resourceComputeDiskCreate(d *schema.ResourceData, meta interface{}) error {
129141
disk.SourceSnapshot = snapshotData.SelfLink
130142
}
131143

144+
if v, ok := d.GetOk("disk_encryption_key_raw"); ok {
145+
disk.DiskEncryptionKey = &compute.CustomerEncryptionKey{}
146+
disk.DiskEncryptionKey.RawKey = v.(string)
147+
}
148+
132149
op, err := config.clientCompute.Disks.Insert(
133150
project, d.Get("zone").(string), disk).Do()
134151
if err != nil {
@@ -168,6 +185,9 @@ func resourceComputeDiskRead(d *schema.ResourceData, meta interface{}) error {
168185
}
169186

170187
d.Set("self_link", disk.SelfLink)
188+
if disk.DiskEncryptionKey != nil && disk.DiskEncryptionKey.Sha256 != "" {
189+
d.Set("disk_encryption_key_sha256", disk.DiskEncryptionKey.Sha256)
190+
}
171191

172192
return nil
173193
}

builtin/providers/google/resource_compute_disk_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,28 @@ func TestAccComputeDisk_basic(t *testing.T) {
3030
})
3131
}
3232

33+
func TestAccComputeDisk_encryption(t *testing.T) {
34+
diskName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
35+
var disk compute.Disk
36+
37+
resource.Test(t, resource.TestCase{
38+
PreCheck: func() { testAccPreCheck(t) },
39+
Providers: testAccProviders,
40+
CheckDestroy: testAccCheckComputeDiskDestroy,
41+
Steps: []resource.TestStep{
42+
resource.TestStep{
43+
Config: testAccComputeDisk_encryption(diskName),
44+
Check: resource.ComposeTestCheckFunc(
45+
testAccCheckComputeDiskExists(
46+
"google_compute_disk.foobar", &disk),
47+
testAccCheckEncryptionKey(
48+
"google_compute_disk.foobar", &disk),
49+
),
50+
},
51+
},
52+
})
53+
}
54+
3355
func testAccCheckComputeDiskDestroy(s *terraform.State) error {
3456
config := testAccProvider.Meta().(*Config)
3557

@@ -77,6 +99,26 @@ func testAccCheckComputeDiskExists(n string, disk *compute.Disk) resource.TestCh
7799
}
78100
}
79101

102+
func testAccCheckEncryptionKey(n string, disk *compute.Disk) resource.TestCheckFunc {
103+
return func(s *terraform.State) error {
104+
rs, ok := s.RootModule().Resources[n]
105+
if !ok {
106+
return fmt.Errorf("Not found: %s", n)
107+
}
108+
109+
attr := rs.Primary.Attributes["disk_encryption_key_sha256"]
110+
if disk.DiskEncryptionKey == nil && attr != "" {
111+
return fmt.Errorf("Disk %s has mismatched encryption key.\nTF State: %+v\nGCP State: <empty>", n, attr)
112+
}
113+
114+
if attr != disk.DiskEncryptionKey.Sha256 {
115+
return fmt.Errorf("Disk %s has mismatched encryption key.\nTF State: %+v.\nGCP State: %+v",
116+
n, attr, disk.DiskEncryptionKey.Sha256)
117+
}
118+
return nil
119+
}
120+
}
121+
80122
func testAccComputeDisk_basic(diskName string) string {
81123
return fmt.Sprintf(`
82124
resource "google_compute_disk" "foobar" {
@@ -87,3 +129,15 @@ resource "google_compute_disk" "foobar" {
87129
zone = "us-central1-a"
88130
}`, diskName)
89131
}
132+
133+
func testAccComputeDisk_encryption(diskName string) string {
134+
return fmt.Sprintf(`
135+
resource "google_compute_disk" "foobar" {
136+
name = "%s"
137+
image = "debian-8-jessie-v20160803"
138+
size = 50
139+
type = "pd-ssd"
140+
zone = "us-central1-a"
141+
disk_encryption_key_raw = "SGVsbG8gZnJvbSBHb29nbGUgQ2xvdWQgUGxhdGZvcm0="
142+
}`, diskName)
143+
}

builtin/providers/google/resource_compute_instance.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,18 @@ func resourceComputeInstance() *schema.Resource {
7575
Type: schema.TypeString,
7676
Optional: true,
7777
},
78+
79+
"disk_encryption_key_raw": &schema.Schema{
80+
Type: schema.TypeString,
81+
Optional: true,
82+
ForceNew: true,
83+
Sensitive: true,
84+
},
85+
86+
"disk_encryption_key_sha256": &schema.Schema{
87+
Type: schema.TypeString,
88+
Computed: true,
89+
},
7890
},
7991
},
8092
},
@@ -437,6 +449,11 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err
437449
disk.DeviceName = v.(string)
438450
}
439451

452+
if v, ok := d.GetOk(prefix + ".disk_encryption_key_raw"); ok {
453+
disk.DiskEncryptionKey = &compute.CustomerEncryptionKey{}
454+
disk.DiskEncryptionKey.RawKey = v.(string)
455+
}
456+
440457
disks = append(disks, &disk)
441458
}
442459

@@ -770,6 +787,24 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error
770787
d.Set("tags_fingerprint", instance.Tags.Fingerprint)
771788
}
772789

790+
disks := make([]map[string]interface{}, 0, 1)
791+
for i, disk := range instance.Disks {
792+
di := map[string]interface{}{
793+
"disk": d.Get(fmt.Sprintf("disk.%d.disk", i)),
794+
"image": d.Get(fmt.Sprintf("disk.%d.image", i)),
795+
"type": d.Get(fmt.Sprintf("disk.%d.type", i)),
796+
"scratch": d.Get(fmt.Sprintf("disk.%d.scratch", i)),
797+
"auto_delete": d.Get(fmt.Sprintf("disk.%d.auto_delete", i)),
798+
"size": d.Get(fmt.Sprintf("disk.%d.size", i)),
799+
"device_name": d.Get(fmt.Sprintf("disk.%d.device_name", i)),
800+
}
801+
if disk.DiskEncryptionKey != nil && disk.DiskEncryptionKey.Sha256 != "" {
802+
di["disk_encryption_key_sha256"] = disk.DiskEncryptionKey.Sha256
803+
}
804+
disks = append(disks, di)
805+
}
806+
d.Set("disk", disks)
807+
773808
d.Set("self_link", instance.SelfLink)
774809
d.SetId(instance.Name)
775810

builtin/providers/google/resource_compute_instance_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,30 @@ func TestAccComputeInstance_disksWithAutodelete(t *testing.T) {
220220
})
221221
}
222222

223+
func TestAccComputeInstance_diskEncryption(t *testing.T) {
224+
var instance compute.Instance
225+
var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10))
226+
var diskName = fmt.Sprintf("instance-testd-%s", acctest.RandString(10))
227+
228+
resource.Test(t, resource.TestCase{
229+
PreCheck: func() { testAccPreCheck(t) },
230+
Providers: testAccProviders,
231+
CheckDestroy: testAccCheckComputeInstanceDestroy,
232+
Steps: []resource.TestStep{
233+
resource.TestStep{
234+
Config: testAccComputeInstance_disks_encryption(diskName, instanceName),
235+
Check: resource.ComposeTestCheckFunc(
236+
testAccCheckComputeInstanceExists(
237+
"google_compute_instance.foobar", &instance),
238+
testAccCheckComputeInstanceDisk(&instance, instanceName, true, true),
239+
testAccCheckComputeInstanceDisk(&instance, diskName, true, false),
240+
testAccCheckComputeInstanceDiskEncryptionKey("google_compute_instance.foobar", &instance),
241+
),
242+
},
243+
},
244+
})
245+
}
246+
223247
func TestAccComputeInstance_local_ssd(t *testing.T) {
224248
var instance compute.Instance
225249
var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10))
@@ -636,6 +660,27 @@ func testAccCheckComputeInstanceDisk(instance *compute.Instance, source string,
636660
}
637661
}
638662

663+
func testAccCheckComputeInstanceDiskEncryptionKey(n string, instance *compute.Instance) resource.TestCheckFunc {
664+
return func(s *terraform.State) error {
665+
rs, ok := s.RootModule().Resources[n]
666+
if !ok {
667+
return fmt.Errorf("Not found: %s", n)
668+
}
669+
670+
for i, disk := range instance.Disks {
671+
attr := rs.Primary.Attributes[fmt.Sprintf("disk.%d.disk_encryption_key_sha256", i)]
672+
if disk.DiskEncryptionKey == nil && attr != "" {
673+
return fmt.Errorf("Disk %d has mismatched encryption key.\nTF State: %+v\nGCP State: <empty>", i, attr)
674+
}
675+
if disk.DiskEncryptionKey != nil && attr != disk.DiskEncryptionKey.Sha256 {
676+
return fmt.Errorf("Disk %d has mismatched encryption key.\nTF State: %+v\nGCP State: %+v",
677+
i, attr, disk.DiskEncryptionKey.Sha256)
678+
}
679+
}
680+
return nil
681+
}
682+
}
683+
639684
func testAccCheckComputeInstanceTag(instance *compute.Instance, n string) resource.TestCheckFunc {
640685
return func(s *terraform.State) error {
641686
if instance.Tags == nil {
@@ -983,6 +1028,39 @@ func testAccComputeInstance_disks(disk, instance string, autodelete bool) string
9831028
}`, disk, instance, autodelete)
9841029
}
9851030

1031+
func testAccComputeInstance_disks_encryption(disk, instance string) string {
1032+
return fmt.Sprintf(`
1033+
resource "google_compute_disk" "foobar" {
1034+
name = "%s"
1035+
size = 10
1036+
type = "pd-ssd"
1037+
zone = "us-central1-a"
1038+
}
1039+
1040+
resource "google_compute_instance" "foobar" {
1041+
name = "%s"
1042+
machine_type = "n1-standard-1"
1043+
zone = "us-central1-a"
1044+
1045+
disk {
1046+
image = "debian-8-jessie-v20160803"
1047+
disk_encryption_key_raw = "SGVsbG8gZnJvbSBHb29nbGUgQ2xvdWQgUGxhdGZvcm0="
1048+
}
1049+
1050+
disk {
1051+
disk = "${google_compute_disk.foobar.name}"
1052+
}
1053+
1054+
network_interface {
1055+
network = "default"
1056+
}
1057+
1058+
metadata {
1059+
foo = "bar"
1060+
}
1061+
}`, disk, instance)
1062+
}
1063+
9861064
func testAccComputeInstance_local_ssd(instance string) string {
9871065
return fmt.Sprintf(`
9881066
resource "google_compute_instance" "local-ssd" {

website/source/docs/providers/google/r/compute_disk.html.markdown

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ The following arguments are supported:
3232

3333
- - -
3434

35+
* `disk_encryption_key_raw` - (Optional) A 256-bit [customer-supplied encryption key]
36+
(https://cloud.google.com/compute/docs/disks/customer-supplied-encryption),
37+
encoded in [RFC 4648 base64](https://tools.ietf.org/html/rfc4648#section-4)
38+
to encrypt this disk.
39+
3540
* `image` - (Optional) The image from which to initialize this disk. Either the
3641
full URL, a contraction of the form "project/name", or just a name (in which
3742
case the current project is used).
@@ -51,4 +56,9 @@ The following arguments are supported:
5156
In addition to the arguments listed above, the following computed attributes are
5257
exported:
5358

59+
* `disk_encryption_key_sha256` - The [RFC 4648 base64]
60+
(https://tools.ietf.org/html/rfc4648#section-4) encoded SHA-256 hash of the
61+
[customer-supplied encryption key](https://cloud.google.com/compute/docs/disks/customer-supplied-encryption)
62+
that protects this resource.
63+
5464
* `self_link` - The URI of the created resource.

website/source/docs/providers/google/r/compute_instance.html.markdown

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,11 @@ the type is "local-ssd", in which case scratch must be true).
136136
* `device_name` - (Optional) Name with which attached disk will be accessible
137137
under `/dev/disk/by-id/`
138138

139+
* `disk_encryption_key_raw` - (Optional) A 256-bit [customer-supplied encryption key]
140+
(https://cloud.google.com/compute/docs/disks/customer-supplied-encryption),
141+
encoded in [RFC 4648 base64](https://tools.ietf.org/html/rfc4648#section-4)
142+
to encrypt this disk.
143+
139144
The `network_interface` block supports:
140145

141146
* `network` - (Optional) The name or self_link of the network to attach this interface to.
@@ -204,3 +209,7 @@ exported:
204209
* `network_interface.0.address` - The internal ip address of the instance, either manually or dynamically assigned.
205210

206211
* `network_interface.0.access_config.0.assigned_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).
212+
213+
* `disk.0.disk_encryption_key_sha256` - The [RFC 4648 base64](https://tools.ietf.org/html/rfc4648#section-4)
214+
encoded SHA-256 hash of the [customer-supplied encryption key]
215+
(https://cloud.google.com/compute/docs/disks/customer-supplied-encryption) that protects this resource.

0 commit comments

Comments
 (0)