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
This commit is contained in:
parent
f09d1235d1
commit
449a98a398
|
@ -28,6 +28,18 @@ func resourceComputeDisk() *schema.Resource {
|
||||||
ForceNew: true,
|
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{
|
"image": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
|
@ -129,6 +141,11 @@ func resourceComputeDiskCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
disk.SourceSnapshot = snapshotData.SelfLink
|
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(
|
op, err := config.clientCompute.Disks.Insert(
|
||||||
project, d.Get("zone").(string), disk).Do()
|
project, d.Get("zone").(string), disk).Do()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -168,6 +185,9 @@ func resourceComputeDiskRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
d.Set("self_link", disk.SelfLink)
|
d.Set("self_link", disk.SelfLink)
|
||||||
|
if disk.DiskEncryptionKey != nil && disk.DiskEncryptionKey.Sha256 != "" {
|
||||||
|
d.Set("disk_encryption_key_sha256", disk.DiskEncryptionKey.Sha256)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
func testAccCheckComputeDiskDestroy(s *terraform.State) error {
|
||||||
config := testAccProvider.Meta().(*Config)
|
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: <empty>", 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 {
|
func testAccComputeDisk_basic(diskName string) string {
|
||||||
return fmt.Sprintf(`
|
return fmt.Sprintf(`
|
||||||
resource "google_compute_disk" "foobar" {
|
resource "google_compute_disk" "foobar" {
|
||||||
|
@ -87,3 +129,15 @@ resource "google_compute_disk" "foobar" {
|
||||||
zone = "us-central1-a"
|
zone = "us-central1-a"
|
||||||
}`, diskName)
|
}`, 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)
|
||||||
|
}
|
||||||
|
|
|
@ -75,6 +75,18 @@ func resourceComputeInstance() *schema.Resource {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
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)
|
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)
|
disks = append(disks, &disk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -770,6 +787,24 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error
|
||||||
d.Set("tags_fingerprint", instance.Tags.Fingerprint)
|
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.Set("self_link", instance.SelfLink)
|
||||||
d.SetId(instance.Name)
|
d.SetId(instance.Name)
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
func TestAccComputeInstance_local_ssd(t *testing.T) {
|
||||||
var instance compute.Instance
|
var instance compute.Instance
|
||||||
var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10))
|
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: <empty>", 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 {
|
func testAccCheckComputeInstanceTag(instance *compute.Instance, n string) resource.TestCheckFunc {
|
||||||
return func(s *terraform.State) error {
|
return func(s *terraform.State) error {
|
||||||
if instance.Tags == nil {
|
if instance.Tags == nil {
|
||||||
|
@ -983,6 +1028,39 @@ func testAccComputeInstance_disks(disk, instance string, autodelete bool) string
|
||||||
}`, disk, instance, autodelete)
|
}`, 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 {
|
func testAccComputeInstance_local_ssd(instance string) string {
|
||||||
return fmt.Sprintf(`
|
return fmt.Sprintf(`
|
||||||
resource "google_compute_instance" "local-ssd" {
|
resource "google_compute_instance" "local-ssd" {
|
||||||
|
|
|
@ -32,6 +32,11 @@ The following arguments are supported:
|
||||||
|
|
||||||
- - -
|
- - -
|
||||||
|
|
||||||
|
* `disk_encryption_key_raw` - (Optional) A 256-bit [customer-supplied encryption key]
|
||||||
|
(https://cloud.google.com/compute/docs/disks/customer-supplied-encryption),
|
||||||
|
encoded in [RFC 4648 base64](https://tools.ietf.org/html/rfc4648#section-4)
|
||||||
|
to encrypt this disk.
|
||||||
|
|
||||||
* `image` - (Optional) The image from which to initialize this disk. Either the
|
* `image` - (Optional) The image from which to initialize this disk. Either the
|
||||||
full URL, a contraction of the form "project/name", or just a name (in which
|
full URL, a contraction of the form "project/name", or just a name (in which
|
||||||
case the current project is used).
|
case the current project is used).
|
||||||
|
@ -51,4 +56,9 @@ The following arguments are supported:
|
||||||
In addition to the arguments listed above, the following computed attributes are
|
In addition to the arguments listed above, the following computed attributes are
|
||||||
exported:
|
exported:
|
||||||
|
|
||||||
|
* `disk_encryption_key_sha256` - The [RFC 4648 base64]
|
||||||
|
(https://tools.ietf.org/html/rfc4648#section-4) encoded SHA-256 hash of the
|
||||||
|
[customer-supplied encryption key](https://cloud.google.com/compute/docs/disks/customer-supplied-encryption)
|
||||||
|
that protects this resource.
|
||||||
|
|
||||||
* `self_link` - The URI of the created resource.
|
* `self_link` - The URI of the created resource.
|
||||||
|
|
|
@ -136,6 +136,11 @@ the type is "local-ssd", in which case scratch must be true).
|
||||||
* `device_name` - (Optional) Name with which attached disk will be accessible
|
* `device_name` - (Optional) Name with which attached disk will be accessible
|
||||||
under `/dev/disk/by-id/`
|
under `/dev/disk/by-id/`
|
||||||
|
|
||||||
|
* `disk_encryption_key_raw` - (Optional) A 256-bit [customer-supplied encryption key]
|
||||||
|
(https://cloud.google.com/compute/docs/disks/customer-supplied-encryption),
|
||||||
|
encoded in [RFC 4648 base64](https://tools.ietf.org/html/rfc4648#section-4)
|
||||||
|
to encrypt this disk.
|
||||||
|
|
||||||
The `network_interface` block supports:
|
The `network_interface` block supports:
|
||||||
|
|
||||||
* `network` - (Optional) The name or self_link of the network to attach this interface to.
|
* `network` - (Optional) The name or self_link of the network to attach this interface to.
|
||||||
|
@ -204,3 +209,7 @@ exported:
|
||||||
* `network_interface.0.address` - The internal ip address of the instance, either manually or dynamically assigned.
|
* `network_interface.0.address` - The internal ip address of the instance, either manually or dynamically assigned.
|
||||||
|
|
||||||
* `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).
|
* `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).
|
||||||
|
|
||||||
|
* `disk.0.disk_encryption_key_sha256` - The [RFC 4648 base64](https://tools.ietf.org/html/rfc4648#section-4)
|
||||||
|
encoded SHA-256 hash of the [customer-supplied encryption key]
|
||||||
|
(https://cloud.google.com/compute/docs/disks/customer-supplied-encryption) that protects this resource.
|
Loading…
Reference in New Issue