Familiar Day #2: Terraforming Mars
Published: 2019-06-19 22:23:00

*Image from creative commons user [Kimika Ying](https://www.flickr.com/photos/48054321@N05) licensed under [CC BY-NC-SA 2.0](https://creativecommons.org/licenses/by-nc-sa/2.0/?ref=ccsearch&atype=rich)*


Welcome back to my series on building my [open source Tabletop RPG assistant](https://github.com/NickBlow/familiar) from scratch. In case you haven't read it [here's](https://dev.to/nickblow/familiar-day-1-4nkm) part one, in which I outline a set of high level goals for my project. 

Today I'm actually going to be writing some code (🎉🎉🎉🎉🎉). [Terraform](https://www.terraform.io) configuration to be exact. The Terraform getting started guide covers only AWS, so this might end up being a useful guide for GCP users. Maybe. I'm using the latest version of Terraform (at the time of writing, Terraform v0.12.2). 

I won't be going through how to install Terraform, but on my Mac it was as simple as `brew install terraform`.


###Why Google Cloud Platform?
I use AWS a lot, and it's really good. They're always adding improvements at a rate of knots. Azure, I've not really used, but also seems like a good cloud platform. However, for a personal project which is price sensitive, Google Cloud has the most generous free tier, with the only *always free* compute resources (not counting FaaS, which will come up later). 

####Disclaimer about my knowledge level
I'm by no means a Terraform or GCP expert, and don't claim to be - part of my aim with this project is to learn more about those tools. I will strive to provide accurate and detailed information, and to explicitly point out where I am unsure of best practices. Please let me know if there's anything I can improve, or if I got anything wrong! Making mistakes is the best way of learning.

##Let's get to the code
###Setting up a provider
First off, we need a provider. A provider lets Terraform know what sets of APIs to use, and how to create resources. There's a useful get started guide for GCP on the [Terraform Site](https://www.terraform.io/docs/providers/google/getting_started.html).


We're using [Google Cloud](https://cloud.google.com/), so first let's set up an account there, and download the [GCloud SDK](https://cloud.google.com/sdk/). Once we have downloaded that we need to initialise it with `gcloud init`.


#### Create a service account
Following the instructions [here](https://cloud.google.com/iam/docs/creating-managing-service-accounts#iam-service-accounts-create-console). We're doing this on the console as the CLI interface for this is in beta. For the roles, we really should be using the [principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege) and only give our service role access to the things it needs to do. We're probably going to be a bit broader on the role than we need to be to start with as we're not entirely sure what we'll need, but we can trim this down later. 

For now let's add the following permissions: `Compute Engine > Compute Admin`, `DNS > DNS Administrator`, `Datastore > Cloud Datastore Owner`, `Storage > Storage Admin` and `App Engine > App Engine Admin` as we'll almost certainly need those. Let's leave Kubernetes for now.

Here's what the roles look like: ![Role Dashboard](https://thepracticaldev.s3.amazonaws.com/i/q4xwfcopoebmze167cy4.png)

I created a key, downloaded it in JSON and saved it to `~/.gcp/terraform-sa.json`.

Ideally, from here on out we will not touch the GCP console. Realistically, it's useful to check on resources etc. but aside from changing permissions on the service role, we should be doing everything from the CLI and terraform.

####Our terraform configuration
Terraform is really composable, and lets you split things into multiple files really easily, but for now let's stick it all in one file. I'm a big fan of getting things working, then refactoring early and often.

First off, let's create some [variables](https://www.terraform.io/docs/configuration/variables.html) so that all the configuration is generalizable. I've added them to a `variables.tf` file which I'm not sure I will commit to source control yet - I'll just not commit it, and if I decide what to do, I'll stick it in .gitignore. We'll probably revisit this choice later.

```terraform
variable "region" {
  type = string
  default ="YOUR_REGION_HERE"
}

variable "project_id" {
    type = string
    default = "YOUR_PROJECT_ID_HERE"
}

variable "service_account_location" {
    type = string
    default = "YOUR_SERVICE_ACCOUNT_LOCATION_HERE"
}

variable "zone" {
  type = string
  default = "YOUR_ZONE_HERE"
}


```

Fill in the default values above, save the file, then let's go to our main.tf file. Service account location refers to where you stored your service account JSON file above.

**⚠️WARNING⚠️: The Google Compute Engine Free tier only applies to regions us-west1, us-central1, us-east1**. A full list of regions and zones can be found [here](https://cloud.google.com/compute/docs/regions-zones/).
 

We're going to use the "google" provider, but we might upgrade to the "google-beta" provider later as there are some beta features I have my eye on, like [triggering Cloud Functions from Firestore changes](https://firebase.google.com/docs/firestore/extend-with-functions).

For now, let's take an example compute instance configuration from the [getting started guide](https://www.terraform.io/docs/providers/google/getting_started.html).

My `main.tf` now looks like this:

```terraform
provider "google" {
  credentials = "${file(var.service_account_location)}"
  project     = var.project_id
  region      = var.region
  zone = var.zone
}

resource "google_compute_instance" "vm_instance" {
  name         = "terraform-instance"
  machine_type = "f1-micro"

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

  network_interface {
    # A default network is created for all GCP projects
    network       = "default"
    access_config {
    }
  }
}

```
*Note: the syntax highlighting doesn't work because they changed how [expressions](https://www.terraform.io/docs/configuration/expressions.html) work in Terraform 0.12. It compiles correctly, I promise!*


Let's `cd` into the folder with the terraform files, in *Familiar* it's creatively called 'terraform'. It should have two files `variables.tf` and `main.tf`.

Run `terraform init` to initialize everything, then `terraform apply` to show the execution plan. Check it all looks sane, then type `yes`. 

After a few seconds:
![Terraform's complete!](https://thepracticaldev.s3.amazonaws.com/i/eskqdokw0tl6lcvwb0d1.png)

Let's go into the Google Cloud console - 
![Our running instance](https://thepracticaldev.s3.amazonaws.com/i/rodd70f6hbloijaau5i5.png)

Awesome 🥳! We've set up some infrastructure. It doesn't do much yet, but we'll get to that in time.

## Next time:

(Subject to change) We're going to do something real with terraform, set up some storage and set up a [Terraform backend](https://www.terraform.io/docs/backends/) to store our configuration, as this will make it easier to manage with multiple people, and is generally good practice.