Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions examples/delete-vms-without-cmek/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Automatic Labelling from Localhost

This example demonstrates how to use the
[root module][root-module] and the
[event-project-log-entry submodule][event-project-log-entry-submodule]
to configure a system
which responds to Compute VM creation events by deleting any VM instances created with disks not encrypted using a customer-managed encryption key.

## Usage

To provision this example, populate `terraform.tfvars` with the [required variables](#inputs) and run the following commands within
this directory:

- `terraform init` to initialize the directory
- `terraform plan` to generate the execution plan
- `terraform apply` to apply the execution plan
- `terraform destroy` to destroy the infrastructure

[^]: (autogen_docs_start)

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|:----:|:-----:|:-----:|
| project\_id | The ID of the project to which resources will be applied. | string | n/a | yes |
| region | The region in which resources will be applied. | string | n/a | yes |

[^]: (autogen_docs_end)

## Requirements

The following sections describe the requirements which must be met in
order to invoke this module. The requirements of the
[root module][root-module-requirements] and the
[event-project-log-entry submodule][event-project-log-entry-submodule-requirements]
must also be met.

### Software Dependencies

The following software dependencies must be installed on the system
from which this module will be invoked:

- [Terraform][terraform-site] v0.11.Z

### IAM Roles

The Service Account which will be used to invoke this module must have
the following IAM roles:

- Pub/Sub Admin: `roles/pubsub.admin`
- Storage Admin: `roles/storage.admin`
- Cloud Functions Developer: `roles/cloudfunctions.developer`
- Logging Config Writer: `roles/logging.configWriter`

### APIs

The project against which this module will be invoked must have the
following APIs enabled:

- Compute Engine API: `compute.googleapis.com`

[event-project-log-entry-submodule-requirements]: ../../modules/event-project-log-entry/README.md#requirements
[event-project-log-entry-submodule]: ../../modules/event-project-log-entry
[root-module-requirements]: ../../README.md#requirements
[root-module]: ../..
[terraform-site]: https://terraform.io/
125 changes: 125 additions & 0 deletions examples/delete-vms-without-cmek/function_source/cmek.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
Copyright 2019 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cmek

import (
"context"
"encoding/json"
"errors"
"fmt"
"log"
"time"

"cloud.google.com/go/pubsub"
"google.golang.org/api/compute/v1"
)

type creationEvent struct {
Resource struct {
Labels struct {
InstanceID string `json:"instance_id"`
ProjectID string `json:"project_id"`
Zone string `json:"zone"`
} `json:"labels"`
} `json:"resource"`
}

var computeService *compute.Service

func init() {
var err error

computeService, err = compute.NewService(context.Background())
if err != nil {
log.Fatalf("Could not create compute service: %v\n", err)
}
}

func ReceiveMessage(ctx context.Context, msg *pubsub.Message) error {
var event creationEvent
json.Unmarshal([]byte(msg.Data), &event)
labels := event.Resource.Labels
fmt.Printf("Checking to see if VM instance has unencrypted disks %v\n", labels)
encrypted, err := isEncrypted(labels.ProjectID, labels.Zone, labels.InstanceID)
if err != nil {
fmt.Printf("Error while trying to determine if VM is encrypted. Error: %v\n", err)
return err
}
if !encrypted {
fmt.Printf("Found VM instance with unencrypted disk %v\n", labels)
fmt.Printf("Deleting VM instance %v\n", labels)
err = deleteVM(labels.ProjectID, labels.Zone, labels.InstanceID)
if err != nil {
fmt.Printf("Error while trying to delete vm instance. Error: %v\n", err)
return err
}
fmt.Printf("Successfully deleted VM instance %v\n", labels)
} else {
fmt.Printf("No unencrypted disks found on VM instance %v\n", labels)
}
return nil
}

func deleteVM(projectID string, zoneID string, instanceID string) error {
operation, err := computeService.Instances.Delete(projectID, zoneID, instanceID).Do()
if err != nil {
return err
}
err = waitForOperation(computeService, projectID, zoneID, operation)
return err
}

func waitForOperation(computeService *compute.Service, projectID string, zoneID string, operation *compute.Operation) error {
for {
operation, err := computeService.ZoneOperations.Get(projectID, zoneID, operation.Name).Do()
if err != nil {
return err
}

if operation.Status != "DONE" {
time.Sleep(2 * time.Second)
} else {
if operation.Error != nil {
fmt.Printf("%v", operation.Error)
return errors.New("Operation error")
}
return nil
}
}
return nil
}

func isEncrypted(projectID string, zoneID string, instanceID string) (bool, error) {
ctx := context.Background()
computeService, err := compute.NewService(ctx)
if err != nil {
return true, err
}

instance, err := computeService.Instances.Get(projectID, zoneID, instanceID).Do()
if err != nil {
return true, err
}

for _, disk := range instance.Disks {
if disk.DiskEncryptionKey == nil {
return false, nil
}
}

return true, nil
}
8 changes: 8 additions & 0 deletions examples/delete-vms-without-cmek/function_source/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module github.com/terraform-google-modules/terraform-google-event-function

go 1.11

require (
cloud.google.com/go v0.38.0
google.golang.org/api v0.4.0
)
58 changes: 58 additions & 0 deletions examples/delete-vms-without-cmek/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

terraform {
required_version = "~> 0.11.0"
}

provider "archive" {
version = "~> 1.0"
}

provider "google" {
version = "~> 2.1"
}

provider "random" {
version = "~> 2.0"
}

resource "random_pet" "main" {
separator = "-"
}

module "event_project_log_entry" {
source = "../../modules/event-project-log-entry"

filter = "resource.type=\"gce_instance\" jsonPayload.event_subtype=\"compute.instances.insert\" jsonPayload.event_type=\"GCE_OPERATION_DONE\""
name = "${random_pet.main.id}"
project_id = "${var.project_id}"
}

module "localhost_function" {
source = "../.."

description = "Deletes VMs created with disks not encrypted with CMEK"
entry_point = "ReceiveMessage"
runtime = "go111"
timeout_s = "240"

event_trigger = "${module.event_project_log_entry.function_event_trigger}"
name = "${random_pet.main.id}"
project_id = "${var.project_id}"
region = "${var.region}"
source_directory = "${path.module}/function_source"
}
26 changes: 26 additions & 0 deletions examples/delete-vms-without-cmek/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

variable "project_id" {
type = "string"
description = "The ID of the project to which resources will be applied."
}

variable "region" {
type = "string"
description = "The region in which resources will be applied."
}