Google Cloud Build + Terraform

As you may know from reading my previous blog posts that I’ve been learning Terraform and what I can do with it, and recently I’ve been trying to find a better way to deploy Terraform rather than doing it locally, I then found Google Cloud Build.

What Cloud Build allows me to do is create my Terraform builds straight from my GitHub, if you want to read about how it actually works you can go here.

Also, just a side note, I only just started learning CloudBuild this week, so I’m no expert.

So how does CloudBuild launch my Terraform builds?

The way CloudBuild works is it allows you to build your code via a config file or a Docker image, I went with creating it via a config file as it allowed me to define how I wanted my Terraform to build. I guess if you had some custom code that needed to be run in a specific way then you would have your own custom docker image and use that.

My CloudBuild Lab

So I have a few Terraform configs and modules but I wanted to see how I could go about using my Minecraft config and how much quicker it would be via CloudBuild, also if you haven’t read / watched my Minecraft Terraform build you can find it here.

So below is my Terraform config that will launch my Minecraft environment, as you can see I don’t have a credentials file since this is been run directly in Google Cloud I can use the CloudBuild service account.

    provider "google" {
      project     = "${var.project_name}"
      region      = "${var.region_name}"
      zone        = "${var.zone_name}"
    }
    
    //Aditional HDD
    resource "google_compute_disk" "default" {
      name = "minecraft-disk"
      type = "pd-ssd"
      zone = "${var.zone_name}"
      physical_block_size_bytes = "16384"
    }
    
    //Static IP
    resource "google_compute_address" "static-ip-address" {
      name = "mcs-ip"
    }
    //Create VM
    resource "google_compute_instance" "default" {
      name = "${var.instance_name}"
      machine_type = "${var.machine_type}"
    
      boot_disk {
          initialize_params {
              image = "${var.instance_image}"
            }
        }
      attached_disk {
          source = "${google_compute_disk.default.self_link}"
      }
      network_interface {
          network = "${google_compute_network.vpc-network.name}"
          subnetwork = "${var.subnetwork}"
          access_config {
              nat_ip = "${google_compute_address.static-ip-address.address}"
    
          }
      }
      metadata = {
          startup-script = <<SCRIPT
          mkdir -p /home/minecraft
          mkfs.ext4 -F -E lazy_itable_init=0,lazy_journal_init=0,discard /dev/disk/by-id/google-minecraft-disk
          mount -o discard,defaults /dev/disk/by-id/google-minecraft-disk /home/minecraft
          apt-get update
          apt-get install -y default-jre-headless
          cd /home/minecraft
          wget https://launcher.mojang.com/v1/objects/3dc3d84a581f14691199cf6831b71ed1296a9fdf/server.jar
          java -Xms1G -Xmx3G -d64 -jar server.jar nogui
          sed -i 's/false/true/g' eula.txt
          java -Xms1G -Xmx3G -d64 -jar server.jar nogui
          SCRIPT
      }
    }
    
    //Network creation
    resource "google_compute_network" "vpc-network" {
        name = "${var.instance_network}"
    }
    //firewall rules
    resource "google_compute_firewall" "default" {
        name = "allow-mcs"
        network= "${google_compute_network.vpc-network.name}"
        allow {
            protocol = "tcp"
            ports    = ["25565"]
        }
        source_ranges = ["0.0.0.0/0"]
    }
    
    resource "google_compute_firewall" "defaultssh" {
        name = "allow-ssh"
        network= "${google_compute_network.vpc-network.name}"
        allow {
            protocol = "tcp"
            ports    = ["22"]
        }
        source_ranges = ["0.0.0.0/0"]
    }
    
    //Bucket
    resource "google_storage_bucket" "default" {
        name     = "nlwilkingminecraftstoragebucket2019"
    }

So for me to run this via CloudBuild I needed to tell CloudBuild how to handle it, and that is done via a cloudbuild.yaml file.

    steps:
    # 1. Fetch the source code
    - name: gcr.io/cloud-builders/git
      id: build-repo
      args: ['clone', 'https://github.com/Techdox/terraform-minecraft-server']
    
    # 2a. Set up GCS & BQ etc. using public terraform Docker image
    - name: hashicorp/terraform
      id: terraform-init
      args: ['init']
      dir: '.'
    
    # 2b. Create the GCS bucket using Terraform
    - name: hashicorp/terraform
      id: terraform-plan
      args: ['plan']
      dir: '.'
    
    # 2b. Create the GCS bucket using Terraform
    - name: hashicorp/terraform
      id: terraform-apply
      args: ['apply', '-auto-approve']
      dir: '.'

So from what you can see above, a number of things are going to happen.

  1. It grabs the source code from my Repo
  2. It builds a Docker instance running Terraform
  3. It runs a Terraform init
  4. It Runs a Terraform plan
  5. It runs a Terraform apply with the auto approve flag.

Cloud Build </figure>

CloudBuild lists the steps that are run and will give you some good feedback if something breaks.

code-capture

What’s so good about using CloudBuild?

CloudBuild makes it easy to manage your build, and you don’t need to worry about having to pull your code from your repo to run it, as well as managing .JSON credentials as it’s all run under a service account, another cool thing I’ve done is made a backend which stores my state file in Google Cloud Storage so that my state is always protected, which was always a concern when running things local.

code-capture

You are not limited to just GitHub either as it supports Googles source repos and Bit bucket.

I’m going to make a video about to later on to really show it off, but until then here’s a good video to watch.

Written on August 16, 2019