custom-terraform

Allows user to bring in their own custom terraform module

This module allows a user to bring in their own custom terraform code into the opta ecosystem, to use in tandem with their other opta modules, and even reference them. All a user needs to do is specify the path (absolute or relative to the opta yaml) to your module with the path_to_module input, and the desired inputs to your module (if any) via the terraform_inputs input.

Example/Demo

Suppose you have an opta gcp environment written in gcp-env.yaml and you want to deploy your custom terraform module “blah” that creates something you want (in our case a vm instance). What you could do is create a service for your environment which uses custom-terraform to call your module (NOTE: custom-terraform doesn’t need to be in an opta service– it can be in the environment too). For our example, let’s say that the file structure looks like so:

.
├── README.md
├── gcp-env.yaml
└── dummy-service
    ├── blah
    │   └── main.tf
    └── opta.yaml

The new service is written in dummy-service/opta.yaml and looks like this:

environments:
  - name: gcp-example
    path: "../gcp-env.yaml"
name: baloney
modules:
  - type: custom-terraform
    name: vm1
    path_to_module: "./blah"
    terraform_inputs:
      hello: "world"
      subnet_self_link: "{parent.private_subnet_self_link}"
# You can call it multiple times if you like
#  - type: custom-terraform
#    name: vm2
#    path_to_module: "./blah"
#    terraform_inputs:
#      hello: "world2"
#      subnet_self_link: "{parent.private_subnet_self_link}"

You can see that the path to your module is specified by path_to_module (you can use relative or absolute paths), as are the expected inputs hello and subnet_self_link. Note that you can use opta interpolation to use variables or the outputs of the parent environment or other modules as input.

Lastly, you can use the following as content to the main.tf file of the blah module to complete the example/demo:

variable "hello" {
  type = string
}

variable "subnet_self_link" {
  type = string
}

data "google_compute_subnetwork" "my-subnetwork" {
  self_link = var.subnet_self_link
}

resource "random_string" "suffix" {
  length = 4
  upper = false
  special = false
}

resource "google_service_account" "default" {
  account_id   = "custom-terraform-${random_string.suffix.result}"
  display_name = "Service Account"
}

resource "google_compute_firewall" "k8s_extra_rules" {
  name      = "custom-terraform-${random_string.suffix.result}"
  network   = data.google_compute_subnetwork.my-subnetwork.network
  direction = "INGRESS"
  allow {
    protocol = "tcp"
    ports    = ["22"]
  }
  source_ranges = ["0.0.0.0/0"]
  target_tags   = ["open-to-public-${random_string.suffix.result}"]
}

resource "google_compute_instance" "default" {
  name         = "test-${random_string.suffix.result}"
  machine_type = "n2-standard-4"
  zone         = "us-central1-a"

  tags = ["open-to-public-${random_string.suffix.result}"]

  boot_disk {
    initialize_params {
      image = "debian-cloud/debian-9"
    }
  }

  // Local SSD disk
  scratch_disk {
    interface = "SCSI"
  }

  network_interface {
    subnetwork = var.subnet_self_link
    access_config {
      // Ephemeral public IP
    }
  }

  metadata = {
    foo = "bar"
  }

  metadata_startup_script = "echo ${var.hello} > /test.txt"

  service_account {
    # Google recommends custom service accounts that have cloud-platform scope and permissions granted via IAM Roles.
    email  = google_service_account.default.email
    scopes = ["cloud-platform"]
  }
}

Once you opta apply the service you should see your new compute instance up and running in the GCP console and be able to ssh into it.

Fields

Name Description Default Required
path_to_module The path to your terraform module None True
terraform_inputs The variables which you wish to pass into your custom module. {} False